-- | This is an implementation of the modified Merkle Patricia database -- described in the Ethereum Yellowpaper -- (). 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