dfinity-radix-tree 0.1.0 → 0.1.1
raw patch · 9 files changed
+354/−257 lines, 9 filesdep +mtlPVP ok
version bump matches the API change (PVP)
Dependencies added: mtl
API changes (from Hackage documentation)
+ Network.DFINITY.RadixTree: contentsMerkleizedRadixTree :: RadixDatabase config m database => RadixTree database -> m [(ByteString, ByteString)]
+ Network.DFINITY.RadixTree: contentsNonMerkleizedRadixTree :: RadixDatabase config m database => RadixTree database -> m [(ByteString, ByteString)]
+ Network.DFINITY.RadixTree: contentsRadixTree :: RadixDatabase config m database => RadixTree database -> m [(ByteString, ByteString)]
+ Network.DFINITY.RadixTree: lookupRadixTree :: RadixDatabase config m database => ByteString -> RadixTree database -> m (Maybe (ByteString, RadixTree database))
+ Network.DFINITY.RadixTree: printRadixTree :: MonadIO m => RadixDatabase config m database => RadixTree database -> m ()
Files
- CHANGELOG.md +6/−0
- README.md +36/−41
- dfinity-radix-tree.cabal +4/−3
- src/Network/DFINITY/RadixTree.hs +207/−107
- src/Network/DFINITY/RadixTree/Lenses.hs +14/−14
- src/Network/DFINITY/RadixTree/Memory.hs +16/−16
- src/Network/DFINITY/RadixTree/Types.hs +46/−37
- src/Network/DFINITY/RadixTree/Utilities.hs +1/−1
- test/Main.hs +24/−38
CHANGELOG.md view
@@ -1,3 +1,9 @@+0.1.1 Enzo Haussecker <enzo@dfinity.org> Thu Jul 12 2018++ * Create aliases for verbose functions+ * Add useful functions for debugging+ * Clean-up documentation+ 0.1.0 Enzo Haussecker <enzo@dfinity.org> Tue Jul 03 2018 * Abstract database to type class
README.md view
@@ -5,16 +5,17 @@ [](https://www.gnu.org/licenses/gpl-3.0) ## Overview-This library allows you to construct a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree) on top of any underlying key–value database. It works by organizing your key–value pairs into a binary radix tree, which is well suited for storing large dictionaries of fairly random keys, and is optimized for storing keys of the same length.+This library allows you to construct a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree) on top of any underlying key–value database. It works by organizing your key–value pairs into a binary [radix tree](https://en.wikipedia.org/wiki/Radix_tree), which is well suited for storing large dictionaries of fairly random keys, and is optimized for storing keys of the same length. ## Usage-Define your database as an instance of the `RadixDatabase` type class. An instance for [LevelDB](http://hackage.haskell.org/package/leveldb-haskell) is already provided.+Define your database as an instance of the [`RadixDatabase`](https://hackage.haskell.org/package/dfinity-radix-tree/docs/Network-DFINITY-RadixTree.html#t:RadixDatabase) type class. An instance for [LevelDB](http://hackage.haskell.org/package/leveldb-haskell) is already provided. ```haskell {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -import Control.Monad.Trans.Resource-import Database.LevelDB+import Control.Monad.Trans.Resource (MonadResource)+import Database.LevelDB (DB, Options, defaultReadOptions, defaultWriteOptions, get, open, put)+ import Network.DFINITY.RadixTree instance MonadResource m => RadixDatabase (FilePath, Options) m DB where@@ -22,57 +23,51 @@ load database = get database defaultReadOptions store database = put database defaultWriteOptions ```-Create a `RadixTree` that is parameterized by your database. If you want to make things more explicit, then define some simple type aliases and wrapper functions.+Create a [`RadixTree`](https://hackage.haskell.org/package/dfinity-radix-tree/docs/Network-DFINITY-RadixTree.html#t:RadixTree) that is parameterized by your database. If you want to make things more explicit, then you can define a simple type alias and wrapper function. ```haskell-import Control.Monad.Trans.Resource-import Data.ByteString-import Database.LevelDB+import Control.Monad.Trans.Resource (MonadResource)+import Database.LevelDB (DB, Options(..), defaultOptions)+ import Network.DFINITY.RadixTree -type MerkleTree = RadixTree DB-type MerkleRoot = RadixRoot+type RadixTree' = RadixTree DB -createMerkleTree+createRadixTree' :: MonadResource m => FilePath -- Database.- -> Maybe MerkleRoot -- State root.- -> m MerkleTree-createMerkleTree path root =+ -> Maybe RadixRoot -- State root.+ -> m RadixTree'+createRadixTree' path root = createRadixTree bloomSize cacheSize root (path, options) where bloomSize = 262144 cacheSize = 2048- options = defaultOptions {createIfMissing = True}--insertMerkleTree- :: MonadResource m- => ByteString -- Key.- -> ByteString -- Value.- -> MerkleTree -- Tree.- -> m MerkleTree-insertMerkleTree = insertRadixTree+ options = defaultOptions {createIfMissing = True}+```+Using the definitions above, you can create a radix tree, perform some basic operations on it, and see that its contents is uniquely defined by its [`RadixRoot`](https://hackage.haskell.org/package/dfinity-radix-tree/docs/Network-DFINITY-RadixTree.html#t:RadixRoot).+```haskell+{-# LANGUAGE OverloadedStrings #-} -deleteMerkleTree- :: MonadResource m- => ByteString -- Key.- -> MerkleTree -- Tree.- -> m MerkleTree-deleteMerkleTree = deleteRadixTree+import Control.Monad.IO.Class (liftIO)+import Control.Monad.Trans.Resource (runResourceT)+import Data.ByteString.Base16 (encode)+import Data.ByteString.Char8 (unpack)+import Data.ByteString.Short (fromShort) -merkleizeMerkleTree- :: MonadResource m- => MerkleTree -- Tree.- -> m (MerkleRoot, MerkleTree)-merkleizeMerkleTree = merkleizeRadixTree+import Network.DFINITY.RadixTree -lookupMerkleTree- :: MonadResource m- => ByteString -- Key.- -> MerkleTree -- Tree.- -> m (Maybe (ByteString, MerkleTree))-lookupMerkleTree = lookupMerkleizedRadixTree+main :: IO ()+main = runResourceT $ do+ tree <- createRadixTree' "/path/to/database" Nothing+ tree' <- insertRadixTree "Hello" "World" tree+ root <- fst <$> merkleizeRadixTree tree'+ liftIO $ putStrLn $ "State Root: 0x" ++ pretty root+ where pretty = unpack . encode . fromShort ```-Using the API above, we can perform operations on the tree as a proxy for the database and see that its contents is uniquely defined by its `MerkleRoot`.+Running the program above should produce the following result.+```+State Root: 0x621f6e4c28b18e58d374c9236daa1a0ccba16550+``` ## Contribute
dfinity-radix-tree.cabal view
@@ -1,5 +1,5 @@ Name: dfinity-radix-tree-Version: 0.1.0+Version: 0.1.1 Synopsis: A generic data integrity layer. Description: This library allows you to construct a Merkle tree on top of any underlying@@ -36,6 +36,7 @@ lens-simple, leveldb-haskell, lrucaching,+ mtl, resourcet, semigroups, serialise@@ -65,10 +66,10 @@ base16-bytestring, bytestring, cmdargs,+ containers, data-default-class, dfinity-radix-tree,- leveldb-haskell,- resourcet,+ mtl, text, unordered-containers Default-Language:
src/Network/DFINITY/RadixTree.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-} {-# OPTIONS -Wall #-} {-# OPTIONS -Werror=incomplete-patterns #-}@@ -19,9 +20,9 @@ RadixDatabase(..) -- ** Types+ , RadixError(..) , RadixRoot , RadixTree- , RadixError(..) -- ** Create , createRadixTree@@ -37,6 +38,7 @@ , merkleizeRadixTree -- ** Query+ , lookupRadixTree , lookupMerkleizedRadixTree , lookupNonMerkleizedRadixTree @@ -49,6 +51,10 @@ , sinkMerkleizedRadixTree -- ** Debug+ , contentsRadixTree+ , contentsMerkleizedRadixTree+ , contentsNonMerkleizedRadixTree+ , printRadixTree , printMerkleizedRadixTree , printNonMerkleizedRadixTree @@ -167,13 +173,13 @@ searchRadixTree :: RadixDatabase config m database => Bool -- ^ Overwrite state root?- -> (RadixTree database -> m (Maybe (RadixBranch, RadixCache))) -- ^ Loading strategy.+ -> (RadixTree database -> m (Maybe (RadixNode, RadixCache))) -- ^ Loading strategy. -> ByteString -- ^ Key. -> RadixTree database -- ^ Radix tree. -> m (Either RadixError RadixSearchResult) {-# SPECIALISE searchRadixTree :: Bool- -> (RadixTree DB -> ResourceT IO (Maybe (RadixBranch, RadixCache)))+ -> (RadixTree DB -> ResourceT IO (Maybe (RadixNode, RadixCache))) -> ByteString -> RadixTree DB -> ResourceT IO (Either RadixError RadixSearchResult) #-}@@ -181,12 +187,12 @@ let key' = toBits key let tree' = tree `bool` setRoot _radixCheckpoint tree $ flag loop Nothing [] [] [] key' tree' where- loop implicit branches roots prefixes key tree@RadixTree {..} = do- -- Load the root branch.+ loop implicit roots nodes prefixes key tree@RadixTree {..} = do+ -- Load the root node. result <- strategy tree case result of Nothing -> pure $ Left $ StateRootDoesNotExist _radixRoot- Just (branch@RadixBranch {..}, cache') -> do+ Just (node@RadixNode {..}, cache') -> do -- Calculate the prefix and overflow. let bits = maybe id (:) implicit $ maybe [] toBits _radixPrefix let prefix = matchBits bits key@@ -194,7 +200,7 @@ let overflow = drop n bits -- Update the accumulators. let roots' = _radixRoot:roots- let branches' = branch:branches+ let nodes' = node:nodes let prefixes' = prefix:prefixes let key' = drop n key -- Check the termination criteria.@@ -202,13 +208,13 @@ let bit = head key' let child = bool _radixLeft _radixRight bit if List.null key' || residue || isNothing child- then pure $ Right (fromList roots', fromList branches', fromList prefixes', overflow, key', cache')+ then pure $ Right (fromList roots', fromList nodes', fromList prefixes', overflow, key', cache') else do -- Recurse. let root' = fromJust child let tree' = setCache cache' $ setRoot root' tree let implicit' = Just bit- loop implicit' branches' roots' prefixes' key' tree'+ loop implicit' roots' nodes' prefixes' key' tree' -- | -- Search for a value in a Merkleized radix tree.@@ -278,10 +284,10 @@ seq bloom $ setBloom bloom $ setBuffer buffer $ setRoot root tree where prefix = createPrefix $ toBits key- branch = setPrefix prefix $ Just value `setLeaf` def- root = createRoot branch+ node = setPrefix prefix $ Just value `setLeaf` def+ root = createRoot node bloom = Bloom.insert root _radixBloom- buffer = storeHot root branch _radixBuffer+ buffer = storeHot root node _radixBuffer -- TODO (enzo): Documentation. insertRadixTreeAt@@ -290,14 +296,14 @@ -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE insertRadixTreeAt #-}-insertRadixTreeAt (_:|roots, branch:|branches, prefix:|_, _, _, cache) value tree@RadixTree {..} =+insertRadixTreeAt (_:|roots, node:|nodes, prefix:|_, _, _, cache) value tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where- branch' = Just value `setLeaf` branch- root' = createRoot branch'- parent = listToMaybe $ zip3 roots branches prefix+ node' = Just value `setLeaf` node+ root' = createRoot node'+ parent = listToMaybe $ zip3 roots nodes prefix bloom = flip insertList _radixBloom $ root':roots- buffer = merkleSpoof root' parent $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root' parent $ storeHot root' node' _radixBuffer state = bool _radixRoot root' $ isNothing parent -- TODO (enzo): Documentation.@@ -307,18 +313,18 @@ -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE insertRadixTreeAfter #-}-insertRadixTreeAfter (_:|roots, branch:|branches, prefix:|_, _, keyOverflow, cache) value tree@RadixTree {..} =+insertRadixTreeAfter (_:|roots, node:|nodes, prefix:|_, _, keyOverflow, cache) value tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where prefix' = createPrefix $ drop 1 keyOverflow- branch' = setPrefix prefix' $ Just value `setLeaf` def- root' = createRoot branch'- branch'' = test `setChild` Just root' $ branch- root'' = createRoot branch''+ node' = setPrefix prefix' $ Just value `setLeaf` def+ root' = createRoot node'+ node'' = test `setChild` Just root' $ node+ root'' = createRoot node'' test = head keyOverflow- parent = listToMaybe $ zip3 roots branches prefix+ parent = listToMaybe $ zip3 roots nodes prefix bloom = flip insertList _radixBloom $ root'':root':roots- buffer = merkleSpoof root'' parent $ storeHot root'' branch'' $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root'' parent $ storeHot root'' node'' $ storeHot root' node' _radixBuffer state = bool _radixRoot root'' $ isNothing parent -- TODO (enzo): Documentation.@@ -328,19 +334,19 @@ -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE insertRadixTreeBefore #-}-insertRadixTreeBefore (_:|roots, branch:|branches, prefix:|_, prefixOverflow, _, cache) value tree@RadixTree {..} =+insertRadixTreeBefore (_:|roots, node:|nodes, prefix:|_, prefixOverflow, _, cache) value tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where prefix' = createPrefix $ drop 1 prefixOverflow- branch' = setPrefix prefix' branch- root' = createRoot branch'+ node' = setPrefix prefix' node+ root' = createRoot node' prefix'' = createPrefix $ drop 1 prefix `bool` prefix $ isNothing parent- branch'' = setPrefix prefix'' $ test `setChild` Just root' $ Just value `setLeaf` def- root'' = createRoot branch''+ node'' = setPrefix prefix'' $ test `setChild` Just root' $ Just value `setLeaf` def+ root'' = createRoot node'' test = head prefixOverflow- parent = listToMaybe $ zip3 roots branches prefix+ parent = listToMaybe $ zip3 roots nodes prefix bloom = flip insertList _radixBloom $ root'':root':roots- buffer = merkleSpoof root'' parent $ storeHot root'' branch'' $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root'' parent $ storeHot root'' node'' $ storeHot root' node' _radixBuffer state = bool _radixRoot root'' $ isNothing parent -- TODO (enzo): Documentation.@@ -350,23 +356,23 @@ -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE insertRadixTreeBetween #-}-insertRadixTreeBetween (_:|roots, branch:|branches, prefix:|_, prefixOverflow, keyOverflow, cache) value tree@RadixTree {..} =+insertRadixTreeBetween (_:|roots, node:|nodes, prefix:|_, prefixOverflow, keyOverflow, cache) value tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where prefix' = createPrefix $ drop 1 keyOverflow- branch' = setPrefix prefix' $ Just value `setLeaf` def- root' = createRoot branch'+ node' = setPrefix prefix' $ Just value `setLeaf` def+ root' = createRoot node' prefix'' = createPrefix $ drop 1 prefixOverflow- branch'' = setPrefix prefix'' branch- root'' = createRoot branch''+ node'' = setPrefix prefix'' node+ root'' = createRoot node'' prefix''' = createPrefix $ drop 1 prefix `bool` prefix $ isNothing parent- branch''' = setPrefix prefix''' $ setChildren children def- root''' = createRoot branch'''+ node''' = setPrefix prefix''' $ setChildren children def+ root''' = createRoot node''' test = head keyOverflow children = bool id swap test (Just root', Just root'')- parent = listToMaybe $ zip3 roots branches prefix+ parent = listToMaybe $ zip3 roots nodes prefix bloom = flip insertList _radixBloom $ root''':root'':root':roots- buffer = merkleSpoof root''' parent $ storeHot root''' branch''' $ storeHot root'' branch'' $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root''' parent $ storeHot root''' node''' $ storeHot root'' node'' $ storeHot root' node' _radixBuffer state = bool _radixRoot root''' $ isNothing parent -- |@@ -385,38 +391,38 @@ then pure tree else searchNonMerkleizedRadixTree key tree >>= \ case Left err -> throw err- Right result@(_, branches, prefix:|_, [], [], cache) ->- case branches of+ Right result@(_, nodes, prefix:|_, [], [], cache) ->+ case nodes of -- No children and no parent.- RadixBranch _ Nothing Nothing _:|[] ->+ RadixNode _ Nothing Nothing _:|[] -> pure $ deleteRadixTreeNoChildrenNoParent result tree -- No children and parent with leaf.- RadixBranch _ Nothing Nothing _:|parent:_ | isJust $ getLeaf parent ->+ RadixNode _ Nothing Nothing _:|parent:_ | isJust $ getLeaf parent -> pure $ deleteRadixTreeNoChildrenParentWithLeaf result tree -- No children and parent without leaf.- RadixBranch _ Nothing Nothing _:|parent:_ -> do+ RadixNode _ Nothing Nothing _:|parent:_ -> do let test = not $ head prefix let root = fromJust $ getChild test parent loadHot root _radixBuffer cache _radixDatabase >>= \ case Nothing -> throw $ StateRootDoesNotExist root- Just (branch, cache') ->- pure $ deleteRadixTreeNoChildrenParentWithoutLeaf result branch cache' test tree+ Just (node, cache') ->+ pure $ deleteRadixTreeNoChildrenParentWithoutLeaf result node cache' test tree -- One left child.- RadixBranch _ child Nothing _:|_ | isJust child -> do+ RadixNode _ child Nothing _:|_ | isJust child -> do let test = False let root = fromJust child loadHot root _radixBuffer cache _radixDatabase >>= \ case Nothing -> throw $ StateRootDoesNotExist root- Just (branch, cache') ->- pure $ deleteRadixTreeOneChild result branch cache' test tree+ Just (node, cache') ->+ pure $ deleteRadixTreeOneChild result node cache' test tree -- One right child.- RadixBranch _ Nothing child _:|_ | isJust child -> do+ RadixNode _ Nothing child _:|_ | isJust child -> do let test = True let root = fromJust child loadHot root _radixBuffer cache _radixDatabase >>= \ case Nothing -> throw $ StateRootDoesNotExist root- Just (branch, cache') ->- pure $ deleteRadixTreeOneChild result branch cache' test tree+ Just (node, cache') ->+ pure $ deleteRadixTreeOneChild result node cache' test tree -- Two children. _ -> pure $ deleteRadixTreeTwoChildren result tree Right _ -> pure tree@@ -440,15 +446,15 @@ -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE deleteRadixTreeNoChildrenParentWithLeaf #-}-deleteRadixTreeNoChildrenParentWithLeaf (_:|_:roots, _:|branch:branches, prefix:|prefixes, _, _, cache) tree@RadixTree {..} =+deleteRadixTreeNoChildrenParentWithLeaf (_:|_:roots, _:|node:nodes, prefix:|prefixes, _, _, cache) tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where- branch' = setChild test Nothing branch- root' = createRoot branch'+ node' = setChild test Nothing node+ root' = createRoot node' test = head prefix- parent = listToMaybe $ zip3 roots branches $ map head prefixes+ parent = listToMaybe $ zip3 roots nodes $ map head prefixes bloom = flip insertList _radixBloom $ root':roots- buffer = merkleSpoof root' parent $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root' parent $ storeHot root' node' _radixBuffer state = bool _radixRoot root' $ isNothing parent deleteRadixTreeNoChildrenParentWithLeaf _ _ = throw $ InvalidArgument "unknown parent"@@ -456,22 +462,22 @@ -- TODO (enzo): Documentation. deleteRadixTreeNoChildrenParentWithoutLeaf :: RadixSearchResult -- ^ Search result.- -> RadixBranch -- ^ Branch.- -> RadixCache -- ^ Cache.+ -> RadixNode -- ^ Radix node.+ -> RadixCache -- ^ Radix cache. -> Bool -- ^ Lineage. -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE deleteRadixTreeNoChildrenParentWithoutLeaf #-}-deleteRadixTreeNoChildrenParentWithoutLeaf (_:|_:roots, _:|_:branches, _:|prefixes, _, _, _) branch@RadixBranch {..} cache test tree@RadixTree {..} =+deleteRadixTreeNoChildrenParentWithoutLeaf (_:|_:roots, _:|_:nodes, _:|prefixes, _, _, _) node@RadixNode {..} cache test tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where prefix' = createPrefix $ drop 1 bits `bool` bits $ isNothing parent- branch' = setPrefix prefix' branch- root' = createRoot branch'+ node' = setPrefix prefix' node+ root' = createRoot node' bits = head prefixes ++ test:maybe [] toBits _radixPrefix- parent = listToMaybe $ zip3 roots branches $ map head prefixes+ parent = listToMaybe $ zip3 roots nodes $ map head prefixes bloom = flip insertList _radixBloom $ root':roots- buffer = merkleSpoof root' parent $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root' parent $ storeHot root' node' _radixBuffer state = bool _radixRoot root' $ isNothing parent deleteRadixTreeNoChildrenParentWithoutLeaf _ _ _ _ _ = throw $ InvalidArgument "unknown parent"@@ -479,22 +485,22 @@ -- TODO (enzo): Documentation. deleteRadixTreeOneChild :: RadixSearchResult -- ^ Search result.- -> RadixBranch -- ^ Branch.- -> RadixCache -- ^ Cache.+ -> RadixNode -- ^ Radix node.+ -> RadixCache -- ^ Radix cache. -> Bool -- ^ Lineage. -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE deleteRadixTreeOneChild #-}-deleteRadixTreeOneChild (_:|roots, _:|branches, prefix:|_, _, _, _) branch@RadixBranch {..} cache test tree@RadixTree {..} =+deleteRadixTreeOneChild (_:|roots, _:|nodes, prefix:|_, _, _, _) node@RadixNode {..} cache test tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where prefix' = createPrefix $ drop 1 bits `bool` bits $ isNothing parent- branch' = setPrefix prefix' branch- root' = createRoot branch'+ node' = setPrefix prefix' node+ root' = createRoot node' bits = prefix ++ test:maybe [] toBits _radixPrefix- parent = listToMaybe $ zip3 roots branches prefix+ parent = listToMaybe $ zip3 roots nodes prefix bloom = flip insertList _radixBloom $ root':roots- buffer = merkleSpoof root' parent $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root' parent $ storeHot root' node' _radixBuffer state = bool _radixRoot root' $ isNothing parent -- TODO (enzo): Documentation.@@ -503,34 +509,34 @@ -> RadixTree database -- ^ Radix tree. -> RadixTree database {-# INLINABLE deleteRadixTreeTwoChildren #-}-deleteRadixTreeTwoChildren (_:|roots, branch:|branches, prefix:|_, _, _, cache) tree@RadixTree {..} =+deleteRadixTreeTwoChildren (_:|roots, node:|nodes, prefix:|_, _, _, cache) tree@RadixTree {..} = seq bloom $ setBloom bloom $ setBuffer buffer $ setCache cache $ setRoot state tree where- branch' = setLeaf Nothing branch- root' = createRoot branch'- parent = listToMaybe $ zip3 roots branches prefix+ node' = setLeaf Nothing node+ root' = createRoot node'+ parent = listToMaybe $ zip3 roots nodes prefix bloom = flip insertList _radixBloom $ root':roots- buffer = merkleSpoof root' parent $ storeHot root' branch' _radixBuffer+ buffer = merkleSpoof root' parent $ storeHot root' node' _radixBuffer state = bool _radixRoot root' $ isNothing parent -- | -- Lookup a value in a radix tree.-lookupRadixTree+lookupRadixTree' :: RadixDatabase config m database => (ByteString -> RadixTree database -> m (Either RadixError RadixSearchResult)) -- ^ Search algorithm. -> ByteString -- ^ Key. -> RadixTree database -- ^ Radix tree. -> m (Maybe (ByteString, RadixTree database))-{-# SPECIALISE lookupRadixTree+{-# SPECIALISE lookupRadixTree' :: (ByteString -> RadixTree DB -> ResourceT IO (Either RadixError RadixSearchResult)) -> ByteString -> RadixTree DB -> ResourceT IO (Maybe (ByteString, RadixTree DB)) #-}-lookupRadixTree search key tree = do+lookupRadixTree' search key tree = do found <- search key tree case found of Left err -> throw err- Right (_, RadixBranch {..}:|_, _, prefixOverflow, keyOverflow, cache') ->+ Right (_, RadixNode {..}:|_, _, prefixOverflow, keyOverflow, cache') -> if not $ List.null prefixOverflow && List.null keyOverflow then pure Nothing else pure $ do@@ -539,6 +545,19 @@ pure (value, tree') -- |+-- A convenient alias for `lookupNonMerkleizedRadixTree`.+lookupRadixTree+ :: RadixDatabase config m database+ => ByteString -- ^ Key.+ -> RadixTree database -- ^ Radix tree.+ -> m (Maybe (ByteString, RadixTree database))+{-# SPECIALISE lookupRadixTree+ :: ByteString+ -> RadixTree DB+ -> ResourceT IO (Maybe (ByteString, RadixTree DB)) #-}+lookupRadixTree = lookupNonMerkleizedRadixTree++-- | -- Lookup a value in a Merkleized radix tree. lookupMerkleizedRadixTree :: RadixDatabase config m database@@ -549,7 +568,7 @@ :: ByteString -> RadixTree DB -> ResourceT IO (Maybe (ByteString, RadixTree DB)) #-}-lookupMerkleizedRadixTree = lookupRadixTree searchMerkleizedRadixTree+lookupMerkleizedRadixTree = lookupRadixTree' searchMerkleizedRadixTree -- | -- Lookup a value in a non-Merkleized radix tree.@@ -562,20 +581,20 @@ :: ByteString -> RadixTree DB -> ResourceT IO (Maybe (ByteString, RadixTree DB)) #-}-lookupNonMerkleizedRadixTree = lookupRadixTree searchNonMerkleizedRadixTree+lookupNonMerkleizedRadixTree = lookupRadixTree' searchNonMerkleizedRadixTree -- |--- Mask a branch in a Merkleized radix tree.+-- Mask a node in a Merkleized radix tree. merkleSpoof :: RadixRoot -- ^ State root.- -> Maybe (RadixRoot, RadixBranch, Bool) -- ^ Parent.+ -> Maybe (RadixRoot, RadixNode, Bool) -- ^ Parent. -> RadixBuffer -- ^ Buffer. -> RadixBuffer {-# INLINABLE merkleSpoof #-} merkleSpoof mask = \ case Nothing -> id- Just (root, branch, test) ->- storeHot root $ test `setChild` Just mask $ branch+ Just (root, node, test) ->+ storeHot root $ test `setChild` Just mask $ node -- | -- Merkleize a radix tree. This will flush the buffer to disk.@@ -596,31 +615,31 @@ if not $ Bloom.elem root _radixBloom then pure (root, cache) else do- -- Load the root branch.+ -- Load the root node. result <- loadHot root _radixBuffer cache _radixDatabase case result of Nothing -> throw $ StateRootDoesNotExist root- Just (branch@RadixBranch {..}, cache') ->+ Just (node@RadixNode {..}, cache') -> case (_radixLeft, _radixRight) of -- No children. (Nothing, Nothing) ->- storeCold branch cache' _radixDatabase+ storeCold node cache' _radixDatabase -- One left child. (Just child, Nothing) -> do (root', cache'') <- loop child cache'- let branch' = False `setChild` Just root' $ branch- storeCold branch' cache'' _radixDatabase+ let node' = False `setChild` Just root' $ node+ storeCold node' cache'' _radixDatabase -- One right child. (Nothing, Just child) -> do (root', cache'') <- loop child cache'- let branch' = True `setChild` Just root' $ branch- storeCold branch' cache'' _radixDatabase+ let node' = True `setChild` Just root' $ node+ storeCold node' cache'' _radixDatabase -- Two children. (Just left, Just right) -> do (root', cache'') <- loop left cache' (root'', cache''') <- loop right cache''- let branch' = setChildren (Just root', Just root'') branch- storeCold branch' cache''' _radixDatabase+ let node' = setChildren (Just root', Just root'') node+ storeCold node' cache''' _radixDatabase -- | -- Create a conduit from a Merkleized radix tree.@@ -659,7 +678,7 @@ case result of Nothing -> pure () Just bytes -> do- let RadixBranch {..} = deserialise $ fromStrict bytes+ let RadixNode {..} = deserialise $ fromStrict bytes let success = all id $ zipWith (==) mask $ toBits $ fromShort _radixCheckpoint when success $ yield bytes forM_ [_radixLeft, _radixRight] $ \ case@@ -689,7 +708,7 @@ Nothing -> pure $ Left $ keys want Just bytes -> case deserialiseOrFail $ fromStrict bytes of- Right RadixBranch {..} -> do+ Right RadixNode {..} -> do let key = Byte.take 20 $ hash bytes let root = toShort key let wanted = member root want@@ -741,35 +760,116 @@ loop3 buffer'' want' candidates -- |+-- Get the contents of a radix tree.+contentsRadixTree'+ :: RadixDatabase config m database+ => Bool -- ^ Overwrite state root?+ -> (RadixTree database -> m (Maybe (RadixNode, RadixCache))) -- ^ Loading strategy.+ -> RadixTree database -- ^ Radix tree.+ -> m [(ByteString, ByteString)]+{-# SPECIALISE contentsRadixTree'+ :: Bool+ -> (RadixTree DB -> ResourceT IO (Maybe (RadixNode, RadixCache)))+ -> RadixTree DB+ -> ResourceT IO [(ByteString, ByteString)] #-}+contentsRadixTree' flag strategy = \ tree@RadixTree {..} -> do+ let tree' = tree `bool` setRoot _radixCheckpoint tree $ flag+ loop tree' [] [] where+ loop tree@RadixTree {..} prefix accum = do+ result <- strategy tree+ case fst <$> result of+ Nothing -> throw $ StateRootDoesNotExist _radixRoot+ Just RadixNode {..} -> do+ let prefix' = prefix ++ maybe [] toBits _radixPrefix+ let key = fromBits prefix'+ let accum' = maybe accum (\ value -> (key, value):accum) _radixLeaf+ let children = [(,False) <$> _radixLeft, (,True) <$> _radixRight]+ flip foldM accum' `flip` children $ \ accum'' -> \ case+ Nothing -> pure accum''+ Just (root, test) -> do+ let tree' = setRoot root tree+ let prefix'' = prefix' ++ [test]+ loop tree' prefix'' accum''++-- |+-- A convenient alias for `contentsNonMerkleizedRadixTree`.+contentsRadixTree+ :: RadixDatabase config m database+ => RadixTree database -- ^ Radix tree.+ -> m [(ByteString, ByteString)]+{-# SPECIALISE contentsRadixTree+ :: RadixTree DB+ -> ResourceT IO [(ByteString, ByteString)] #-}+contentsRadixTree = contentsNonMerkleizedRadixTree++-- |+-- Get the contents of a Merkleized radix tree.+contentsMerkleizedRadixTree+ :: RadixDatabase config m database+ => RadixTree database -- ^ Radix tree.+ -> m [(ByteString, ByteString)]+{-# SPECIALISE contentsMerkleizedRadixTree+ :: RadixTree DB+ -> ResourceT IO [(ByteString, ByteString)] #-}+contentsMerkleizedRadixTree =+ contentsRadixTree' True $ \ RadixTree {..} ->+ loadCold _radixRoot _radixCache _radixDatabase++-- |+-- Get the contents of a non-Merkleized radix tree.+contentsNonMerkleizedRadixTree+ :: RadixDatabase config m database+ => RadixTree database -- ^ Radix tree.+ -> m [(ByteString, ByteString)]+{-# SPECIALISE contentsNonMerkleizedRadixTree+ :: RadixTree DB+ -> ResourceT IO [(ByteString, ByteString)] #-}+contentsNonMerkleizedRadixTree =+ contentsRadixTree' False $ \ RadixTree {..} ->+ loadHot _radixRoot _radixBuffer _radixCache _radixDatabase++-- | -- Print a radix tree.-printRadixTree+printRadixTree' :: MonadIO m => RadixDatabase config m database => Bool -- ^ Overwrite state root?- -> (RadixTree database -> m (Maybe (RadixBranch, RadixCache))) -- ^ Loading strategy.+ -> (RadixTree database -> m (Maybe (RadixNode, RadixCache))) -- ^ Loading strategy. -> RadixTree database -- ^ Radix tree. -> m ()-{-# SPECIALISE printRadixTree+{-# SPECIALISE printRadixTree' :: Bool- -> (RadixTree DB -> ResourceT IO (Maybe (RadixBranch, RadixCache)))+ -> (RadixTree DB -> ResourceT IO (Maybe (RadixNode, RadixCache))) -> RadixTree DB -> ResourceT IO () #-}-printRadixTree flag strategy = \ tree@RadixTree {..} -> do+printRadixTree' flag strategy = \ tree@RadixTree {..} -> do let tree' = tree `bool` setRoot _radixCheckpoint tree $ flag loop tree' 0 where loop tree@RadixTree {..} i = do result <- strategy tree case fst <$> result of Nothing -> throw $ StateRootDoesNotExist _radixRoot- Just branch@RadixBranch {..} -> do+ Just node@RadixNode {..} -> do let indent = (++) $ concat $ replicate i "|"- liftIO $ putStrLn $ indent $ show branch+ liftIO $ putStrLn $ indent $ show node let j = i + 1 forM_ [_radixLeft, _radixRight] $ \ case Nothing -> pure () Just root -> setRoot root tree `loop` j -- |+-- A convenient alias for `printNonMerkleizedRadixTree`.+printRadixTree+ :: MonadIO m+ => RadixDatabase config m database+ => RadixTree database -- ^ Radix tree.+ -> m ()+{-# SPECIALISE printRadixTree+ :: RadixTree DB+ -> ResourceT IO () #-}+printRadixTree = printNonMerkleizedRadixTree++-- | -- Print a Merkleized radix tree. printMerkleizedRadixTree :: MonadIO m@@ -780,7 +880,7 @@ :: RadixTree DB -> ResourceT IO () #-} printMerkleizedRadixTree =- printRadixTree True $ \ RadixTree {..} ->+ printRadixTree' True $ \ RadixTree {..} -> loadCold _radixRoot _radixCache _radixDatabase -- |@@ -794,5 +894,5 @@ :: RadixTree DB -> ResourceT IO () #-} printNonMerkleizedRadixTree =- printRadixTree False $ \ RadixTree {..} ->+ printRadixTree' False $ \ RadixTree {..} -> loadHot _radixRoot _radixBuffer _radixCache _radixDatabase
src/Network/DFINITY/RadixTree/Lenses.hs view
@@ -35,25 +35,25 @@ import Network.DFINITY.RadixTree.Types -makeLenses ''RadixBranch+makeLenses ''RadixNode makeLenses ''RadixTree -getPrefix :: RadixBranch -> Maybe RadixPrefix+getPrefix :: RadixNode -> Maybe RadixPrefix getPrefix = view radixPrefix -getLeft :: RadixBranch -> Maybe RadixRoot+getLeft :: RadixNode -> Maybe RadixRoot getLeft = view radixLeft -getRight :: RadixBranch -> Maybe RadixRoot+getRight :: RadixNode -> Maybe RadixRoot getRight = view radixRight -getChild :: Bool -> RadixBranch -> Maybe RadixRoot+getChild :: Bool -> RadixNode -> Maybe RadixRoot getChild = bool getLeft getRight -getChildren :: RadixBranch -> (Maybe RadixRoot, Maybe RadixRoot)-getChildren branch = (getLeft branch, getRight branch)+getChildren :: RadixNode -> (Maybe RadixRoot, Maybe RadixRoot)+getChildren node = (getLeft node, getRight node) -getLeaf :: RadixBranch -> Maybe ByteString+getLeaf :: RadixNode -> Maybe ByteString getLeaf = view radixLeaf getBloom :: RadixTree database -> RadixBloom@@ -71,22 +71,22 @@ getRoot :: RadixTree database -> RadixRoot getRoot = view radixRoot -setPrefix :: Maybe RadixPrefix -> RadixBranch -> RadixBranch+setPrefix :: Maybe RadixPrefix -> RadixNode -> RadixNode setPrefix = set radixPrefix -setLeft :: Maybe RadixRoot -> RadixBranch -> RadixBranch+setLeft :: Maybe RadixRoot -> RadixNode -> RadixNode setLeft = set radixLeft -setRight :: Maybe RadixRoot -> RadixBranch -> RadixBranch+setRight :: Maybe RadixRoot -> RadixNode -> RadixNode setRight = set radixRight -setChild :: Bool -> Maybe RadixRoot -> RadixBranch -> RadixBranch+setChild :: Bool -> Maybe RadixRoot -> RadixNode -> RadixNode setChild = bool setLeft setRight -setChildren :: (Maybe RadixRoot, Maybe RadixRoot) -> RadixBranch -> RadixBranch+setChildren :: (Maybe RadixRoot, Maybe RadixRoot) -> RadixNode -> RadixNode setChildren (left, right) = setLeft left . setRight right -setLeaf :: Maybe ByteString -> RadixBranch -> RadixBranch+setLeaf :: Maybe ByteString -> RadixNode -> RadixNode setLeaf = set radixLeaf setBloom :: RadixBloom -> RadixTree database -> RadixTree database
src/Network/DFINITY/RadixTree/Memory.hs view
@@ -25,16 +25,16 @@ -> RadixBuffer -> RadixCache -> database- -> m (Maybe (RadixBranch, RadixCache))+ -> m (Maybe (RadixNode, RadixCache)) {-# SPECIALISE loadHot :: RadixRoot -> RadixBuffer -> RadixCache -> DB- -> ResourceT IO (Maybe (RadixBranch, RadixCache)) #-}+ -> ResourceT IO (Maybe (RadixNode, RadixCache)) #-} loadHot root buffer cache database = case Map.lookup root buffer of- Just branch -> pure $ Just (branch, cache)+ Just node -> pure $ Just (node, cache) Nothing -> loadCold root cache database loadCold@@ -42,49 +42,49 @@ => RadixRoot -> RadixCache -> database- -> m (Maybe (RadixBranch, RadixCache))+ -> m (Maybe (RadixNode, RadixCache)) {-# SPECIALISE loadCold :: RadixRoot -> RadixCache -> DB- -> ResourceT IO (Maybe (RadixBranch, RadixCache)) #-}+ -> ResourceT IO (Maybe (RadixNode, RadixCache)) #-} loadCold root cache database = case LRU.lookup root cache of- Just (branch, cache') ->- seq cache' $ seq branch $ pure $ Just (branch, cache')+ Just (node, cache') ->+ seq cache' $ seq node $ pure $ Just (node, cache') Nothing -> do let key = fromShort root result <- load database key case result of Just bytes -> do- let branch = deserialise $ fromStrict bytes- let cache' = LRU.insert root branch cache- seq cache' $ seq branch $ pure $ Just (branch, cache')+ let node = deserialise $ fromStrict bytes+ let cache' = LRU.insert root node cache+ seq cache' $ seq node $ pure $ Just (node, cache') Nothing -> pure $ Nothing storeHot :: RadixRoot- -> RadixBranch+ -> RadixNode -> RadixBuffer -> RadixBuffer storeHot = Map.insert storeCold :: RadixDatabase config m database- => RadixBranch+ => RadixNode -> RadixCache -> database -> m (RadixRoot, RadixCache) {-# SPECIALISE storeCold- :: RadixBranch+ :: RadixNode -> RadixCache -> DB -> ResourceT IO (RadixRoot, RadixCache) #-}-storeCold branch cache database = do+storeCold node cache database = do store database key bytes seq cache' $ pure (root, cache') where- bytes = toStrict $ serialise branch+ bytes = toStrict $ serialise node key = Byte.take 20 $ hash bytes root = toShort key- cache' = LRU.insert root branch cache+ cache' = LRU.insert root node cache
src/Network/DFINITY/RadixTree/Types.hs view
@@ -8,11 +8,12 @@ module Network.DFINITY.RadixTree.Types ( RadixBloom- , RadixBranch(..)+ , RadixBranch , RadixBuffer , RadixCache , RadixDatabase(..) , RadixError(..)+ , RadixNode(..) , RadixPrefix(..) , RadixRoot , RadixSearchResult@@ -25,6 +26,7 @@ import Control.DeepSeq (NFData(..)) import Control.Exception (Exception) import Control.Monad (void)+import Control.Monad.State.Strict as State (StateT, get, modify) import Control.Monad.Trans.Resource (MonadResource) import Crypto.Hash.SHA256 (hash) import Data.BloomFilter (Bloom)@@ -37,10 +39,10 @@ import Data.Default.Class (Default(..)) import Data.List.NonEmpty (NonEmpty) import Data.LruCache (LruCache)-import Data.Map.Strict (Map)+import Data.Map.Strict as Map (Map, insert, lookup) import Data.Maybe (isJust) import Data.Monoid ((<>))-import Database.LevelDB (DB, Options, defaultReadOptions, defaultWriteOptions, get, open, put)+import Database.LevelDB as LevelDB (DB, Options, defaultReadOptions, defaultWriteOptions, get, open, put) import Text.Printf (printf) import Network.DFINITY.RadixTree.Bits@@ -48,27 +50,55 @@ type RadixBloom = Bloom RadixRoot -data RadixBranch- = RadixBranch+type RadixBranch = [RadixNode]++type RadixBuffer = Map RadixRoot RadixNode++type RadixCache = LruCache RadixRoot RadixNode++class Monad m => RadixDatabase config m database | database -> config where+ create :: config -> m database+ load :: database -> ByteString -> m (Maybe ByteString)+ store :: database -> ByteString -> ByteString -> m ()++instance Monad m => RadixDatabase () (StateT (Map ByteString ByteString) m) () where+ create = pure+ load _ key = Map.lookup key <$> State.get+ store _ key = modify . insert key++instance MonadResource m => RadixDatabase (FilePath, Options) m DB where+ create = uncurry open+ load database = LevelDB.get database defaultReadOptions+ store database = put database defaultWriteOptions++data RadixError+ = InvalidArgument String+ | StateRootDoesNotExist RadixRoot+ deriving (Data, Eq, Show)++instance Exception RadixError++data RadixNode+ = RadixNode { _radixPrefix :: Maybe RadixPrefix , _radixLeft :: Maybe RadixRoot , _radixRight :: Maybe RadixRoot , _radixLeaf :: Maybe ByteString } deriving (Data, Eq) -instance NFData RadixBranch where- rnf RadixBranch {..} =+instance NFData RadixNode where+ rnf RadixNode {..} = rnf _radixPrefix `seq` rnf _radixLeft `seq` rnf _radixRight `seq` rnf _radixLeaf `seq` () -instance Default RadixBranch where- def = RadixBranch Nothing Nothing Nothing Nothing+instance Default RadixNode where+ def = RadixNode Nothing Nothing Nothing Nothing -instance Serialise RadixBranch where- encode RadixBranch {..} =+instance Serialise RadixNode where+ encode RadixNode {..} = encodeListLen len <> encodeMaybe CBOR.encode _radixPrefix <> encodeMaybe encodeSide left <>@@ -84,10 +114,10 @@ left <- decodeMaybe $ toShort <$> decodeSide right <- decodeMaybe $ toShort <$> decodeSide leaf <- decodeLeaf len- pure $ RadixBranch prefix left right leaf+ pure $ RadixNode prefix left right leaf -instance Show RadixBranch where- show branch@RadixBranch {..} =+instance Show RadixNode where+ show node@RadixNode {..} = case color 7 . unpack <$> _radixLeaf of Nothing -> printf "%s@[%s,%s,%s]" root prefix left right Just leaf -> printf "%s@[%s,%s,%s,%s]" root prefix left right leaf@@ -95,32 +125,11 @@ color :: Int -> String -> String color = printf "\ESC[9%dm%s\ESC[0m" format = take 8 . unpack . Base16.encode- root = color 4 $ format $ hash $ toStrict $ serialise branch+ root = color 4 $ format $ hash $ toStrict $ serialise node prefix = color 7 $ maybe "null" show _radixPrefix left = color 4 $ maybe "null" format $ fromShort <$> _radixLeft right = color 4 $ maybe "null" format $ fromShort <$> _radixRight -type RadixBuffer = Map RadixRoot RadixBranch--type RadixCache = LruCache RadixRoot RadixBranch--class Monad m => RadixDatabase config m database | database -> config where- create :: config -> m database- load :: database -> ByteString -> m (Maybe ByteString)- store :: database -> ByteString -> ByteString -> m ()--instance MonadResource m => RadixDatabase (FilePath, Options) m DB where- create = uncurry open- load database = get database defaultReadOptions- store database = put database defaultWriteOptions--data RadixError- = InvalidArgument String- | StateRootDoesNotExist RadixRoot- deriving (Data, Eq, Show)--instance Exception RadixError- data RadixPrefix = RadixPrefix { _radixBitLen :: Int@@ -157,7 +166,7 @@ type RadixRoot = ShortByteString -type RadixSearchResult = (NonEmpty RadixRoot, NonEmpty RadixBranch, NonEmpty [Bool], [Bool], [Bool], RadixCache)+type RadixSearchResult = (NonEmpty RadixRoot, NonEmpty RadixNode, NonEmpty [Bool], [Bool], [Bool], RadixCache) data RadixTree database = RadixTree
src/Network/DFINITY/RadixTree/Utilities.hs view
@@ -21,7 +21,7 @@ then Nothing else Just $ fromBits bits -createRoot :: RadixBranch -> RadixRoot+createRoot :: RadixNode -> RadixRoot createRoot = toShort . Byte.take 20 . hashlazy . serialise defaultRoot :: RadixRoot
test/Main.hs view
@@ -8,19 +8,20 @@ module Main where -import Control.Monad (foldM_, mzero)+import Control.Arrow (first)+import Control.Monad (foldM_, mzero, void) import Control.Monad.IO.Class (MonadIO(..))-import Control.Monad.Trans.Resource (MonadResource, runResourceT)+import Control.Monad.State.Strict (StateT, runStateT) import Data.Aeson (FromJSON(..), Object, Value(..), eitherDecode) import Data.ByteString.Base16 (decode, encode) import Data.ByteString.Char8 (ByteString, unpack) import Data.ByteString.Lazy.Char8 as Lazy (readFile) import Data.ByteString.Short (fromShort) import Data.Default.Class (Default(..))-import Data.HashMap.Strict as Map (elems, lookup)+import Data.HashMap.Strict as HashMap (elems, lookup)+import Data.Map.Strict (Map, empty) import Data.Text as Text (Text, drop) import Data.Text.Encoding (encodeUtf8)-import Database.LevelDB (DB, Options(..)) import System.Console.CmdArgs (Data, cmdArgs) import Network.DFINITY.RadixTree@@ -28,12 +29,11 @@ data Args = Args { json :: FilePath- , path :: FilePath , test :: String } deriving Data instance Default Args where- def = Args "test/tests.json" "test/testdb" "*"+ def = Args "test/tests.json" "*" data Op = Insert ByteString ByteString@@ -54,11 +54,9 @@ Merkleize value -> "Merkleize" ++ pretty value where pretty = mappend " 0x" . unpack . encode -parse- :: Object- -> Maybe Op+parse :: Object -> Maybe Op parse object = do- op <- Map.lookup "op" object+ op <- HashMap.lookup "op" object case op of "set" -> Insert <$> get "key" object <*> get "value" object "delete" -> Delete <$> get "key" object@@ -66,21 +64,14 @@ "stateRoot" -> Merkleize <$> get "value" object _ -> Nothing -get- :: Text- -> Object- -> Maybe ByteString+get :: Text -> Object -> Maybe ByteString get key object = do- value <- Map.lookup key object+ value <- HashMap.lookup key object case value of String text -> Just $ fst $ decode $ encodeUtf8 $ Text.drop 2 text _ -> Nothing -step- :: MonadResource m- => RadixTree DB- -> Op- -> m (RadixTree DB)+step :: RadixTree () -> Op -> StateT (Map ByteString ByteString) IO (RadixTree ()) step tree op = do liftIO $ print op case op of@@ -97,24 +88,20 @@ case result of Just (value', tree') | value == value' -> pure tree' Just (value', _) -> throw- [ "Expecting value "- , ", but received value "- , " for key "- ] [value, value', key]+ ["Expecting value ", ", but received value ", " for key "]+ [value, value', key] Nothing -> throw- [ "Expecting value "- , ", but received no value for key "- ] [value, key]+ ["Expecting value ", ", but received no value for key "]+ [value, key] Merkleize value -> do- (fromShort -> value', tree') <- merkleizeRadixTree tree+ (value', tree') <- first fromShort <$> merkleizeRadixTree tree if value == value' then pure tree' else throw- [ "Expecting state root "- , ", but received state root "- ] [value, value']- where- throw err = fail . concat . zipWith mappend err . map show+ ["Expecting state root ", ", but received state root "]+ [value, value']+ where+ throw err = fail . concat . zipWith mappend err . map show main :: IO () main = do@@ -122,11 +109,10 @@ contents <- Lazy.readFile json case eitherDecode contents of Left err -> fail err- Right vctors -> runResourceT $ do- let options = def {createIfMissing = True}- tree <- createRadixTree 262144 2048 Nothing (path, options)+ Right vctors -> void $ flip runStateT empty $ do+ tree <- createRadixTree 262144 2048 Nothing () if test == "*" then foldM_ step tree `mapM_` elems vctors- else case Map.lookup test vctors of- Nothing -> fail $ "unknown test vector: " ++ test+ else case HashMap.lookup test vctors of+ Nothing -> fail $ "Unknown test vector " ++ show test Just ops -> foldM_ step tree $ concat [ops]