merkle-patricia-db-0.1.0: src/Blockchain/Database/MerklePatricia.hs
-- | This is an implementation of the modified Merkle Patricia database
-- described in the Ethereum Yellowpaper
-- (<http://gavwood.com/paper.pdf>). This modified version works like a
-- canonical Merkle Patricia database, but includes certain
-- optimizations. In particular, a new type of "shortcut node" has been
-- added to represent multiple traditional nodes that fall in a linear
-- string (ie- a stretch of parent child nodes where no branch choices
-- exist).
--
-- A Merkle Patricia Database effeciently retains its full history, and a
-- snapshot of all key-value pairs at a given time can be looked up using
-- a "stateRoot" (a pointer to the root of the tree representing that
-- data). Many of the functions in this module work by updating this
-- object, so for anything more complicated than a single update, use of
-- the state monad is recommended.
--
-- The underlying data is actually stored in LevelDB. This module
-- provides the logic to organize the key-value pairs in the appropriate
-- Patricia Merkle Tree.
module Blockchain.Database.MerklePatricia (
Key, Val, MPDB(..), StateRoot(..),
openMPDB, emptyTriePtr, sha2StateRoot, unboxStateRoot,
putKeyVal, getKeyVal, deleteKey, keyExists,
initializeBlank
) where
import Control.Monad.Trans.Resource
import Data.Default
import Data.Maybe (isJust)
import qualified Database.LevelDB as DB
import Blockchain.Data.RLP
import Blockchain.Database.MerklePatricia.Internal
-- | Adds a new key/value pair.
putKeyVal::MonadResource m=>MPDB -- ^ The object containing the current stateRoot.
->Key -- ^ Key of the data to be inserted.
->Val -- ^ Value of the new data
->m MPDB -- ^ The object containing the stateRoot to the data after the insert.
putKeyVal db = unsafePutKeyVal db . keyToSafeKey
-- | Retrieves all key/value pairs whose key starts with the given parameter.
getKeyVal::MonadResource m=>MPDB -- ^ Object containing the current stateRoot.
-> Key -- ^ Key of the data to be inserted.
-> m (Maybe Val) -- ^ The requested value.
getKeyVal db key = do
vals <- unsafeGetKeyVals db (keyToSafeKey key)
return $
if not (null vals)
then Just $ snd (head vals)
-- Since we hash the keys, it's impossible
-- for vals to have more than one item
else Nothing
-- | Deletes a key (and its corresponding data) from the database.
--
-- Note that the key/value pair will still be present in the history, and
-- can be accessed by using an older 'MPDB' object.
deleteKey::MonadResource m=>MPDB -- ^ The object containing the current stateRoot.
->Key -- ^ The key to be deleted.
->m MPDB -- ^ The object containing the stateRoot to the data after the delete.
deleteKey db = unsafeDeleteKey db . keyToSafeKey
-- | Returns True is a key exists.
keyExists::MonadResource m=>MPDB -- ^ The object containing the current stateRoot.
->Key -- ^ The key to be deleted.
->m Bool -- ^ True if the key exists
keyExists db key = isJust <$> getKeyVal db key
-- | Initialize the DB by adding a blank stateroot.
initializeBlank::MonadResource m=>MPDB -- ^ The object containing the current stateRoot.
->m ()
initializeBlank db =
let bytes = rlpSerialize $ rlpEncode (0::Integer)
StateRoot key = emptyTriePtr
in DB.put (ldb db) def key bytes