mirror of https://github.com/voidlizard/hbs2
wip
This commit is contained in:
parent
ca29851f6d
commit
ce9510efeb
|
@ -63,7 +63,9 @@ library
|
||||||
import: shared-properties
|
import: shared-properties
|
||||||
|
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
HBS2.Hash
|
, HBS2.Defaults
|
||||||
|
, HBS2.Hash
|
||||||
|
, HBS2.Merkle
|
||||||
, HBS2.Clock
|
, HBS2.Clock
|
||||||
, HBS2.Prelude
|
, HBS2.Prelude
|
||||||
, HBS2.Prelude.Plated
|
, HBS2.Prelude.Plated
|
||||||
|
@ -86,6 +88,7 @@ library
|
||||||
, hashable
|
, hashable
|
||||||
, interpolatedstring-perl6
|
, interpolatedstring-perl6
|
||||||
, memory
|
, memory
|
||||||
|
, microlens-platform
|
||||||
, prettyprinter
|
, prettyprinter
|
||||||
, safe
|
, safe
|
||||||
, serialise
|
, serialise
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
module HBS2.Defaults where
|
||||||
|
|
||||||
|
defChunkSize :: Integer
|
||||||
|
defChunkSize = 500
|
||||||
|
|
||||||
|
defBlockSize :: Integer
|
||||||
|
defBlockSize = 256 * 1024
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
{-# Language TemplateHaskell #-}
|
||||||
|
{-# Language DeriveFunctor #-}
|
||||||
|
module HBS2.Merkle where
|
||||||
|
|
||||||
|
import Codec.Serialise
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.ByteString.Lazy qualified as LBS
|
||||||
|
import Data.ByteString qualified as BS
|
||||||
|
import Data.Data
|
||||||
|
import Data.Foldable (traverse_)
|
||||||
|
import Data.List qualified as List
|
||||||
|
import Lens.Micro.Platform
|
||||||
|
import Safe
|
||||||
|
import GHC.Generics
|
||||||
|
|
||||||
|
import HBS2.Hash
|
||||||
|
|
||||||
|
class Foldable t => Chunks t a where
|
||||||
|
chunks :: Int -> a -> t a
|
||||||
|
|
||||||
|
instance Chunks [] ByteString where
|
||||||
|
chunks i bs = go [] bs
|
||||||
|
where
|
||||||
|
size = fromIntegral i
|
||||||
|
go acc x | BS.null x = acc
|
||||||
|
| BS.length x <= size = acc <> [x]
|
||||||
|
| otherwise = go (acc <> [BS.take size x]) (BS.drop size x)
|
||||||
|
|
||||||
|
instance Chunks [] LBS.ByteString where
|
||||||
|
chunks i bs = go [] bs
|
||||||
|
where
|
||||||
|
size = fromIntegral i
|
||||||
|
go acc x | LBS.null x = acc
|
||||||
|
| LBS.length x <= size = acc <> [x]
|
||||||
|
| otherwise = go (acc <> [LBS.take size x]) (LBS.drop size x)
|
||||||
|
|
||||||
|
instance Chunks [] [a] where
|
||||||
|
chunks i xs = go xs
|
||||||
|
where
|
||||||
|
go [] = []
|
||||||
|
go es | length es <= i = [es]
|
||||||
|
| otherwise = let (p, ps) = List.splitAt i es in p : go ps
|
||||||
|
|
||||||
|
|
||||||
|
data PTree a = T [PTree a] | L a
|
||||||
|
deriving stock (Show, Functor, Generic)
|
||||||
|
|
||||||
|
|
||||||
|
instance Foldable PTree where
|
||||||
|
foldr fn acc (L a) = fn a acc
|
||||||
|
foldr fn acc (T xs) = go acc xs
|
||||||
|
where
|
||||||
|
go b [] = b
|
||||||
|
go b (y:ys) = foldr fn (go b ys) y
|
||||||
|
|
||||||
|
instance Traversable PTree where
|
||||||
|
traverse fn (L a) = L <$> fn a
|
||||||
|
traverse fn (T xs) = T <$> traverse (traverse fn) xs
|
||||||
|
|
||||||
|
newtype MaxNum a = MaxNum a
|
||||||
|
newtype MaxSize a = MaxSize a
|
||||||
|
|
||||||
|
|
||||||
|
newtype MNodeData =
|
||||||
|
MNodeData
|
||||||
|
{ _mnodeHeight :: Integer
|
||||||
|
}
|
||||||
|
deriving stock (Generic,Data,Show)
|
||||||
|
|
||||||
|
makeLenses ''MNodeData
|
||||||
|
|
||||||
|
instance Serialise MNodeData
|
||||||
|
|
||||||
|
data MTree a = MNode MNodeData [Hash HbSync] | MLeaf a
|
||||||
|
deriving stock (Generic,Data,Show)
|
||||||
|
|
||||||
|
instance Serialise a => Serialise (MTree a)
|
||||||
|
|
||||||
|
newMNode :: Integer -> [Hash HbSync] -> MTree a
|
||||||
|
newMNode h = MNode (MNodeData h)
|
||||||
|
|
||||||
|
toPTree :: (Chunks [] a)
|
||||||
|
=> MaxSize Int
|
||||||
|
-> MaxNum Int
|
||||||
|
-> a
|
||||||
|
-> PTree a
|
||||||
|
|
||||||
|
toPTree (MaxSize s) (MaxNum n) items | n <= 1 =
|
||||||
|
case T $ fmap L (chunks s items) of
|
||||||
|
T [L x] -> L x
|
||||||
|
_ -> T []
|
||||||
|
|
||||||
|
toPTree (MaxSize s) (MaxNum n) items = go $ T (fmap L (chunks s items))
|
||||||
|
where
|
||||||
|
go (T []) = T []
|
||||||
|
go (T [L x]) = L x
|
||||||
|
go (T xs) | length xs <= n = T xs
|
||||||
|
| otherwise = go $ T $ fmap go [ T x | x <- chunks n xs ]
|
||||||
|
|
||||||
|
go leaf = leaf
|
||||||
|
|
||||||
|
|
||||||
|
makeMerkle :: (Monad m, Serialise a, Serialise (MTree a))
|
||||||
|
=> Integer -- | initial height
|
||||||
|
-> PTree a
|
||||||
|
-> ((Hash HbSync, MTree a, LBS.ByteString) -> m ())
|
||||||
|
-> m (Hash HbSync)
|
||||||
|
|
||||||
|
makeMerkle h0 pt f = fst <$> go h0 pt
|
||||||
|
where
|
||||||
|
go hx (T xs) = do
|
||||||
|
rs <- mapM (go hx) xs
|
||||||
|
let hxx = maximumDef hx (fmap snd rs)
|
||||||
|
let o = newMNode hxx (fmap fst rs)
|
||||||
|
let bs = serialise o
|
||||||
|
let h = hashObject bs
|
||||||
|
f (h, o, bs)
|
||||||
|
pure (h, 1+hxx)
|
||||||
|
|
||||||
|
go hx (L x) = do
|
||||||
|
let o = MLeaf x
|
||||||
|
let bs = serialise o
|
||||||
|
let h = hashObject bs
|
||||||
|
f (h, o, bs)
|
||||||
|
pure (h, 1+hx)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
walkMerkle' :: (Serialise (MTree a), Monad m)
|
||||||
|
=> Hash HbSync
|
||||||
|
-> ( Hash HbSync -> m (Maybe LBS.ByteString) )
|
||||||
|
-> ( MTree a -> m () )
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
walkMerkle' root flookup sink = go root
|
||||||
|
where
|
||||||
|
go hash = do
|
||||||
|
t <- (deserialise <$>) <$> flookup hash
|
||||||
|
case t of
|
||||||
|
Just n@(MLeaf _) -> sink n
|
||||||
|
Just n@(MNode _ hashes) -> sink n >> traverse_ go hashes
|
||||||
|
Nothing -> pure ()
|
||||||
|
|
||||||
|
walkMerkle :: (Serialise (MTree a), Monad m)
|
||||||
|
=> Hash HbSync
|
||||||
|
-> ( Hash HbSync -> m (Maybe LBS.ByteString) )
|
||||||
|
-> ( a -> m () )
|
||||||
|
-> m ()
|
||||||
|
|
||||||
|
walkMerkle root flookup sink = walkMerkle' root flookup withTree
|
||||||
|
where
|
||||||
|
withTree = \case
|
||||||
|
(MLeaf s) -> sink s
|
||||||
|
(MNode _ _) -> pure ()
|
||||||
|
|
||||||
|
-- walkMerkle root flookup sink = go root
|
||||||
|
-- where
|
||||||
|
-- go hash = do
|
||||||
|
-- t <- (deserialise <$>) <$> flookup hash
|
||||||
|
-- case t of
|
||||||
|
-- Nothing -> pure ()
|
||||||
|
-- Just (MLeaf s) -> sink s
|
||||||
|
-- Just (MNode _ hashes) -> traverse_ go hashes
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
module HBS2.Prelude
|
module HBS2.Prelude
|
||||||
( module Data.String
|
( module Data.String
|
||||||
|
, module HBS2.Defaults
|
||||||
|
-- , module HBS2.Prelude
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.String (IsString(..))
|
import Data.String (IsString(..))
|
||||||
|
|
||||||
|
import HBS2.Defaults
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
module HBS2.Prelude.Plated
|
module HBS2.Prelude.Plated
|
||||||
( module HBS2.Prelude.Plated
|
( module HBS2.Prelude.Plated
|
||||||
|
, module HBS2.Prelude
|
||||||
, module Data.Data
|
, module Data.Data
|
||||||
, module Data.Generics.Uniplate.Operations
|
, module Data.Generics.Uniplate.Operations
|
||||||
, Data
|
|
||||||
, Generic
|
, Generic
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ import Data.Generics.Uniplate.Operations
|
||||||
import GHC.Generics(Generic)
|
import GHC.Generics(Generic)
|
||||||
import Safe
|
import Safe
|
||||||
|
|
||||||
|
import HBS2.Prelude
|
||||||
|
|
||||||
uniLastMay :: forall to from . (Data from, Data to) => from -> Maybe to
|
uniLastMay :: forall to from . (Data from, Data to) => from -> Maybe to
|
||||||
uniLastMay = lastMay . universeBi
|
uniLastMay = lastMay . universeBi
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Revision history for hbs2
|
||||||
|
|
||||||
|
## 0.1.0.0 -- YYYY-mm-dd
|
||||||
|
|
||||||
|
* First version. Released on an unsuspecting world.
|
|
@ -0,0 +1,30 @@
|
||||||
|
Copyright (c) 2023,
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of nor the names of other
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,83 @@
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.ByteString qualified as B
|
||||||
|
import Data.Function
|
||||||
|
import Options.Applicative
|
||||||
|
import Prettyprinter
|
||||||
|
import System.IO
|
||||||
|
|
||||||
|
import Streaming.Prelude qualified as S
|
||||||
|
import Streaming qualified as S
|
||||||
|
|
||||||
|
import HBS2.Storage
|
||||||
|
import HBS2.Storage.Simple
|
||||||
|
import HBS2.Prelude
|
||||||
|
import HBS2.Prelude.Plated
|
||||||
|
import HBS2.Merkle
|
||||||
|
import HBS2.Hash
|
||||||
|
|
||||||
|
newtype HashRef = HashRef (Hash HbSync)
|
||||||
|
deriving newtype (Eq,Ord,IsString,Pretty)
|
||||||
|
deriving stock (Data)
|
||||||
|
|
||||||
|
newtype OptInputFile = OptInputFile { unOptFile :: FilePath }
|
||||||
|
deriving newtype (Eq,Ord,IsString)
|
||||||
|
deriving stock (Data)
|
||||||
|
|
||||||
|
newtype Opts =
|
||||||
|
Opts {
|
||||||
|
optInputFile :: Maybe OptInputFile
|
||||||
|
}
|
||||||
|
deriving stock (Data)
|
||||||
|
|
||||||
|
readChunked :: MonadIO m => Handle -> Int -> S.Stream (S.Of ByteString) m ()
|
||||||
|
readChunked handle size = fuu
|
||||||
|
where
|
||||||
|
fuu = fix \next -> do
|
||||||
|
chunk <- liftIO do
|
||||||
|
B.hGet handle size
|
||||||
|
unless (B.null chunk) do
|
||||||
|
S.yield chunk
|
||||||
|
next
|
||||||
|
|
||||||
|
runStore :: Opts -> IO ()
|
||||||
|
runStore opts = do
|
||||||
|
|
||||||
|
let fname = uniLastMay @OptInputFile opts
|
||||||
|
|
||||||
|
handle <- maybe (pure stdin) (flip openFile ReadMode . unOptFile) fname
|
||||||
|
|
||||||
|
hashes <- readChunked handle (fromIntegral defBlockSize) -- FIXME: to settings!
|
||||||
|
& S.map (hashObject . B.copy)
|
||||||
|
& S.map HashRef
|
||||||
|
& S.toList_
|
||||||
|
|
||||||
|
let pt = toPTree (MaxSize 2048) (MaxNum 2048) hashes
|
||||||
|
|
||||||
|
mapM_ (print . pretty) hashes
|
||||||
|
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = join . customExecParser (prefs showHelpOnError) $
|
||||||
|
info (helper <*> parser)
|
||||||
|
( fullDesc
|
||||||
|
<> header "hbsync block fetch"
|
||||||
|
<> progDesc "fetches blocks from hbsync peers"
|
||||||
|
)
|
||||||
|
where
|
||||||
|
parser :: Parser (IO ())
|
||||||
|
parser = hsubparser ( command "store" (info pStore (progDesc "store block"))
|
||||||
|
)
|
||||||
|
|
||||||
|
common = do
|
||||||
|
pure ()
|
||||||
|
|
||||||
|
pStore = do
|
||||||
|
_ <- common
|
||||||
|
file <- optional $ strArgument ( metavar "FILE" )
|
||||||
|
pure $ runStore ( Opts file )
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
cabal-version: 3.0
|
||||||
|
name: hbs2
|
||||||
|
version: 0.1.0.0
|
||||||
|
-- synopsis:
|
||||||
|
-- description:
|
||||||
|
license: BSD-3-Clause
|
||||||
|
license-file: LICENSE
|
||||||
|
-- author:
|
||||||
|
-- maintainer:
|
||||||
|
-- copyright:
|
||||||
|
category: Network
|
||||||
|
build-type: Simple
|
||||||
|
extra-doc-files: CHANGELOG.md
|
||||||
|
-- extra-source-files:
|
||||||
|
|
||||||
|
|
||||||
|
common shared-properties
|
||||||
|
ghc-options:
|
||||||
|
-Wall
|
||||||
|
-- -fno-warn-unused-matches
|
||||||
|
-- -fno-warn-unused-do-bind
|
||||||
|
-- -Werror=missing-methods
|
||||||
|
-- -Werror=incomplete-patterns
|
||||||
|
-- -fno-warn-unused-binds
|
||||||
|
-threaded
|
||||||
|
-rtsopts
|
||||||
|
"-with-rtsopts=-N4 -A64m -AL256m -I0"
|
||||||
|
|
||||||
|
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
default-extensions:
|
||||||
|
ApplicativeDo
|
||||||
|
, BangPatterns
|
||||||
|
, BlockArguments
|
||||||
|
, ConstraintKinds
|
||||||
|
, DataKinds
|
||||||
|
, DeriveDataTypeable
|
||||||
|
, DeriveGeneric
|
||||||
|
, DerivingStrategies
|
||||||
|
, DerivingVia
|
||||||
|
, ExtendedDefaultRules
|
||||||
|
, FlexibleContexts
|
||||||
|
, FlexibleInstances
|
||||||
|
, GADTs
|
||||||
|
, GeneralizedNewtypeDeriving
|
||||||
|
, ImportQualifiedPost
|
||||||
|
, LambdaCase
|
||||||
|
, MultiParamTypeClasses
|
||||||
|
, OverloadedStrings
|
||||||
|
, QuasiQuotes
|
||||||
|
, ScopedTypeVariables
|
||||||
|
, StandaloneDeriving
|
||||||
|
, TupleSections
|
||||||
|
, TypeApplications
|
||||||
|
, TypeFamilies
|
||||||
|
|
||||||
|
|
||||||
|
executable hbs2
|
||||||
|
import: shared-properties
|
||||||
|
main-is: Main.hs
|
||||||
|
-- other-modules:
|
||||||
|
-- other-extensions:
|
||||||
|
build-depends: base ^>=4.15.1.0, hbs2-core, hbs2-storage-simple
|
||||||
|
, aeson
|
||||||
|
, async
|
||||||
|
, base58-bytestring
|
||||||
|
, binary
|
||||||
|
, bytestring
|
||||||
|
, cborg
|
||||||
|
, clock
|
||||||
|
, containers
|
||||||
|
, cryptonite
|
||||||
|
, deepseq
|
||||||
|
, hashable
|
||||||
|
, interpolatedstring-perl6
|
||||||
|
, memory
|
||||||
|
, optparse-applicative
|
||||||
|
, prettyprinter
|
||||||
|
, safe
|
||||||
|
, serialise
|
||||||
|
, streaming
|
||||||
|
, text
|
||||||
|
, uniplate
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hs-source-dirs: .
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue