small-encrypted-block wip

This commit is contained in:
Dmitry Zuikov 2023-12-26 07:34:23 +03:00
parent 8bc92062bd
commit f0d469766e
26 changed files with 678 additions and 57 deletions

View File

@ -0,0 +1,43 @@
TODO: storage-weak-refs
Сделать ссылки, которые удаляются по достижению каких-то событий (например, истечение ttl).
Полезно, что бы не разрастался периметр к моменту, когда заработает сборка мусора.
Нужно, например, что бы кэшировать групповые ключи, так как сейчас сложно
сделать чистые (детерминированные) групповые ключи, да это и опасно.
Кейс: сохраняем ссылку на ключ, как SomeRef например.
По истечению заданного ttl ссылка должна быть удалена.
Как, например?
1. Это свойство hbs2-storage, не hbs2-peer
2. Поддержать тип --- обёртку?
Например, сейчас для любого типа ключа вычисляется хэш,
и ссылка пишется.
Можно добавить какую-то обёртку с метаданными, что писалась
не только ссылка, но и метаданные, например:
data WithExpiration t a = WithExpiration t a
instance Hashed (WithExpiration t a) where
hash (WithExpiration t a) = hash a
внутри сторейджа собирать метаданные в формате
[(String,String)] и писать в некий файл
A/X.metadata в виде:
key: value
key: value
key: value
3. Тогда при чтении (?) или время от временени:
читаем метаданные, смотрим, если ссылка протухла
--- то удаляем её.

View File

@ -88,6 +88,7 @@ library
, HBS2.Data.Types.Refs , HBS2.Data.Types.Refs
, HBS2.Data.Types.SignedBox , HBS2.Data.Types.SignedBox
, HBS2.Data.Types.EncryptedBox , HBS2.Data.Types.EncryptedBox
, HBS2.Data.Types.SmallEncryptedBlock
, HBS2.Data.Bundle , HBS2.Data.Bundle
, HBS2.Defaults , HBS2.Defaults
, HBS2.Events , HBS2.Events

View File

@ -2,6 +2,7 @@ module HBS2.Data.Types
( module X ( module X
-- , module HBS2.Data.Types.Crypto -- , module HBS2.Data.Types.Crypto
, AsSyntax(..) , AsSyntax(..)
, LoadedRef(..)
) )
where where
@ -16,3 +17,4 @@ import HBS2.Data.Types.Peer as X
newtype AsSyntax c = AsSyntax c newtype AsSyntax c = AsSyntax c

View File

@ -11,8 +11,14 @@ import HBS2.Net.Proto.Types
import HBS2.Prelude import HBS2.Prelude
import Codec.Serialise(serialise) import Codec.Serialise(serialise)
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Data import Data.Data
class RefMetaData a where
refMetaData :: a -> [(String, String)]
refMetaData = const mempty
newtype HashRef = HashRef { fromHashRef :: Hash HbSync } newtype HashRef = HashRef { fromHashRef :: Hash HbSync }
deriving newtype (Eq,Ord,IsString,Pretty,Hashable,Hashed HbSync) deriving newtype (Eq,Ord,IsString,Pretty,Hashable,Hashed HbSync)
deriving stock (Data,Generic,Show) deriving stock (Data,Generic,Show)
@ -72,6 +78,8 @@ type ForSomeRefKey a = ( Hashed HbSync a )
newtype SomeRefKey a = SomeRefKey a newtype SomeRefKey a = SomeRefKey a
instance RefMetaData (SomeRefKey a)
instance Hashed HbSync (SomeRefKey a) => Pretty (SomeRefKey a) where instance Hashed HbSync (SomeRefKey a) => Pretty (SomeRefKey a) where
pretty a = pretty $ hashObject @HbSync a pretty a = pretty $ hashObject @HbSync a
-- instance Hashed HbSync (SomeRefKey a) => Pretty (AsBase58 (SomeRefKey a)) where -- instance Hashed HbSync (SomeRefKey a) => Pretty (AsBase58 (SomeRefKey a)) where
@ -88,8 +96,25 @@ newtype RefAlias = RefAlias { unRefAlias :: HashRef }
instance Hashed HbSync RefAlias where instance Hashed HbSync RefAlias where
hashObject (RefAlias h) = fromHashRef h hashObject (RefAlias h) = fromHashRef h
refAlias :: (Hashed HbSync ref, RefMetaData ref) => ref -> RefAlias2
refAlias x = RefAlias2 (Map.fromList $ refMetaData x) (HashRef $ hashObject @HbSync x)
refAlias :: Hashed HbSync ref => ref -> RefAlias data RefAlias2 =
refAlias x = RefAlias (HashRef $ hashObject @HbSync x) RefAlias2 { unRefAliasMeta :: Map String String
, unRefAlias2 :: HashRef
}
deriving stock (Eq,Ord,Show,Generic)
instance Hashed HbSync RefAlias2 where
hashObject (RefAlias2 _ h) = fromHashRef h
instance Serialise RefAlias2
instance Pretty RefAlias2 where
pretty (RefAlias2 _ h) = pretty h
instance RefMetaData RefAlias2 where
refMetaData x = Map.toList (unRefAliasMeta x)
type LoadedRef a = Either HashRef a

View File

@ -0,0 +1,20 @@
module HBS2.Data.Types.SmallEncryptedBlock where
import HBS2.Prelude.Plated
import HBS2.Data.Types.Refs
import HBS2.Data.Types.EncryptedBox
import Data.ByteString (ByteString)
import Codec.Serialise
data SmallEncryptedBlock t =
SmallEncryptedBlock
{ sebGK0 :: HashRef -- ^ gk0
, sebNonce :: ByteString
, sebBox :: EncryptedBox t
}
deriving stock (Generic)
instance Serialise (SmallEncryptedBlock t)

View File

@ -42,7 +42,7 @@ type family HashType ( a :: Type) where
type HbSyncHash = HashType HbSync type HbSyncHash = HashType HbSync
newtype instance Hash HbSync = newtype instance Hash HbSync =
HbSyncHash ByteString HbSyncHash { fromHbSyncHash :: ByteString }
deriving stock (Eq,Ord,Data,Generic) deriving stock (Eq,Ord,Data,Generic)
deriving newtype (Hashable,Show) deriving newtype (Hashable,Show)

View File

@ -3,6 +3,7 @@
{-# Language UndecidableInstances #-} {-# Language UndecidableInstances #-}
{-# Language AllowAmbiguousTypes #-} {-# Language AllowAmbiguousTypes #-}
{-# Language ConstraintKinds #-} {-# Language ConstraintKinds #-}
{-# Language PatternSynonyms #-}
module HBS2.Net.Auth.Credentials module HBS2.Net.Auth.Credentials
( module HBS2.Net.Auth.Credentials ( module HBS2.Net.Auth.Credentials
) where ) where
@ -55,6 +56,9 @@ data KeyringEntry e =
} }
deriving stock (Generic) deriving stock (Generic)
pattern KeyringKeys :: forall {s} . PubKey 'Encrypt s -> PrivKey 'Encrypt s -> KeyringEntry s
pattern KeyringKeys a b <- KeyringEntry a b _
deriving stock instance (Eq (PubKey 'Encrypt e), Eq (PrivKey 'Encrypt e)) deriving stock instance (Eq (PubKey 'Encrypt e), Eq (PrivKey 'Encrypt e))
=> Eq (KeyringEntry e) => Eq (KeyringEntry e)
@ -72,6 +76,7 @@ makeLenses 'PeerCredentials
type ForHBS2Basic s = ( Signatures s type ForHBS2Basic s = ( Signatures s
, PrivKey 'Sign s ~ Sign.SecretKey , PrivKey 'Sign s ~ Sign.SecretKey
, PubKey 'Sign s ~ Sign.PublicKey , PubKey 'Sign s ~ Sign.PublicKey
, Eq (PubKey 'Encrypt HBS2Basic)
, IsEncoding (PubKey 'Encrypt s) , IsEncoding (PubKey 'Encrypt s)
, Eq (PubKey 'Encrypt HBS2Basic) , Eq (PubKey 'Encrypt HBS2Basic)
, s ~ HBS2Basic , s ~ HBS2Basic

View File

@ -11,6 +11,7 @@ module HBS2.Net.Auth.GroupKeySymm
import HBS2.Prelude.Plated import HBS2.Prelude.Plated
import HBS2.Base58 import HBS2.Base58
import HBS2.Data.Types.EncryptedBox import HBS2.Data.Types.EncryptedBox
import HBS2.Data.Types.SmallEncryptedBlock
import HBS2.Data.Types.Refs import HBS2.Data.Types.Refs
import HBS2.Hash import HBS2.Hash
import HBS2.Merkle import HBS2.Merkle
@ -57,7 +58,7 @@ import System.IO.Unsafe (unsafePerformIO)
import Data.Bits (xor) import Data.Bits (xor)
type GroupSecretAsymm = Key type GroupSecret = Key
-- NOTE: breaking-change -- NOTE: breaking-change
@ -69,7 +70,7 @@ type GroupSecretAsymm = Key
-- ключ может быть потерян. а что делать-то, с другой стороны? -- ключ может быть потерян. а что делать-то, с другой стороны?
data instance GroupKey 'Symm s = data instance GroupKey 'Symm s =
GroupKeySymm GroupKeySymm
{ recipients :: HashMap (PubKey 'Encrypt s) (EncryptedBox GroupSecretAsymm) { recipients :: HashMap (PubKey 'Encrypt s) (EncryptedBox GroupSecret)
} }
deriving stock (Generic) deriving stock (Generic)
@ -85,10 +86,10 @@ instance Serialise SK.Nonce
-- NOTE: hardcoded-hbs2-basic-auth-type -- NOTE: hardcoded-hbs2-basic-auth-type
data instance ToEncrypt 'Symm s LBS.ByteString = data instance ToEncrypt 'Symm s LBS.ByteString =
ToEncryptSymmBS ToEncryptSymmBS
{ toEncryptSecret :: GroupSecretAsymm { toEncryptSecret :: GroupSecret
, toEncryptGroupKey :: LoadedRef (GroupKey 'Symm s)
, toEncryptNonce :: BS.ByteString , toEncryptNonce :: BS.ByteString
, toEncryptData :: Stream (Of LBS.ByteString) IO () , toEncryptData :: Stream (Of LBS.ByteString) IO ()
, toEncryptGroupKey :: GroupKey 'Symm s
, toEncryptMeta :: AnnMetaData , toEncryptMeta :: AnnMetaData
, toEncryptOpts :: Maybe EncryptGroupNaClSymmOpts , toEncryptOpts :: Maybe EncryptGroupNaClSymmOpts
} }
@ -98,7 +99,7 @@ type ForGroupKeySymm s = ( Eq (PubKey 'Encrypt s)
, PubKey 'Encrypt s ~ AK.PublicKey , PubKey 'Encrypt s ~ AK.PublicKey
, PrivKey 'Encrypt s ~ AK.SecretKey , PrivKey 'Encrypt s ~ AK.SecretKey
, Serialise (PubKey 'Encrypt s) , Serialise (PubKey 'Encrypt s)
, Serialise GroupSecretAsymm , Serialise GroupSecret
, Serialise SK.Nonce , Serialise SK.Nonce
, FromStringMaybe (PubKey 'Encrypt s) , FromStringMaybe (PubKey 'Encrypt s)
) )
@ -139,8 +140,9 @@ instance ( Serialise (GroupKey 'Symm s)
pretty (AsBase58 c) = pretty (AsBase58 c) =
pretty . B8.unpack . toBase58 . LBS.toStrict . serialise $ c pretty . B8.unpack . toBase58 . LBS.toStrict . serialise $ c
generateGroupKey :: forall s m . (ForGroupKeySymm s, MonadIO m) generateGroupKey :: forall s m . (ForGroupKeySymm s, MonadIO m)
=> Maybe GroupSecretAsymm => Maybe GroupSecret
-> [PubKey 'Encrypt s] -> [PubKey 'Encrypt s]
-> m (GroupKey 'Symm s) -> m (GroupKey 'Symm s)
@ -152,11 +154,30 @@ generateGroupKey mbk pks = GroupKeySymm <$> create
box <- liftIO $ AK.boxSeal pk (LBS.toStrict $ serialise sk) <&> EncryptedBox box <- liftIO $ AK.boxSeal pk (LBS.toStrict $ serialise sk) <&> EncryptedBox
pure (pk, box) pure (pk, box)
generateGroupKeyPure :: forall s nonce . (ForGroupKeySymm s, NonceFrom SK.Nonce nonce)
=> GroupSecret
-> nonce
-> [PubKey 'Encrypt s]
-> GroupKey 'Symm s
generateGroupKeyPure sec nonce pks = GroupKeySymm gk0
where
nonce0 = nonceFrom @SK.Nonce nonce
gk0 = undefined
-- gk0 = [ AK.box
-- HashMap.fromList <$> do
-- sk <- maybe1 mbk (liftIO SK.newKey) pure
-- forM pks $ \pk -> do
-- box <- liftIO $ AK.boxSeal pk (LBS.toStrict $ serialise sk) <&> EncryptedBox
-- pure (pk, box)
lookupGroupKey :: ForGroupKeySymm s lookupGroupKey :: ForGroupKeySymm s
=> PrivKey 'Encrypt s => PrivKey 'Encrypt s
-> PubKey 'Encrypt s -> PubKey 'Encrypt s
-> GroupKey 'Symm s -> GroupKey 'Symm s
-> Maybe GroupSecretAsymm -> Maybe GroupSecret
lookupGroupKey sk pk gk = runIdentity $ runMaybeT do lookupGroupKey sk pk gk = runIdentity $ runMaybeT do
(EncryptedBox bs) <- MaybeT $ pure $ HashMap.lookup pk (recipients gk) (EncryptedBox bs) <- MaybeT $ pure $ HashMap.lookup pk (recipients gk)
@ -216,7 +237,7 @@ instance ( MonadIO m
let nonce0 = nonceFrom @SK.Nonce (toEncryptNonce source) let nonce0 = nonceFrom @SK.Nonce (toEncryptNonce source)
gkh <- writeAsMerkle sto (serialise gk) <&> HashRef gkh <- either pure (\k -> HashRef <$> writeAsMerkle sto (serialise k)) gk
let prk = HKDF.extractSkip @_ @HbSyncHash (Saltine.encode key) let prk = HKDF.extractSkip @_ @HbSyncHash (Saltine.encode key)
@ -351,3 +372,74 @@ instance ( MonadIO m
pure (keys, gk, nonceS, tree) pure (keys, gk, nonceS, tree)
encryptBlock :: ( MonadIO m
, Storage sto h ByteString m
, ForGroupKeySymm s
, Serialise t
, h ~ HbSync
)
=> sto
-> GroupSecret
-> LoadedRef (GroupKey 'Symm s)
-> Maybe BS.ByteString -- ^ nonce
-> t
-> m (SmallEncryptedBlock t)
encryptBlock sto gks gk mnonce x = do
nonceS <- maybe (liftIO AK.newNonce <&> Saltine.encode) pure mnonce
let nonce0 = nonceFrom @SK.Nonce nonceS
gkh <- either pure (\k -> HashRef <$> writeAsMerkle sto (serialise k)) gk
let prk = HKDF.extractSkip @_ @HbSyncHash (Saltine.encode gks)
let key0 = HKDF.expand prk nonceS typicalKeyLength & Saltine.decode & fromJust
let encrypted = SK.secretbox key0 nonce0 (serialise x & LBS.toStrict)
pure $ SmallEncryptedBlock gkh nonceS (EncryptedBox encrypted)
decryptBlock :: forall t s sto h m . ( MonadIO m
, MonadError OperationError m
, Storage sto h ByteString m
, ForGroupKeySymm s
, h ~ HbSync
, Serialise t
)
=> sto
-> [KeyringEntry s]
-> SmallEncryptedBlock t
-> m t
decryptBlock sto keys (SmallEncryptedBlock{..}) = do
gkbs <- readFromMerkle sto (SimpleKey (fromHashRef sebGK0))
gk <- either (const $ throwError (GroupKeyNotFound 1)) pure (deserialiseOrFail @(GroupKey 'Symm s) gkbs)
let gksec' = [ lookupGroupKey sk pk gk | KeyringKeys pk sk <- keys ] & catMaybes & headMay
gksec <- maybe1 gksec' (throwError (GroupKeyNotFound 2)) pure
let prk = HKDF.extractSkip @_ @HbSyncHash (Saltine.encode gksec)
let key0 = HKDF.expand prk sebNonce typicalKeyLength & Saltine.decode & fromJust
let nonce0 = nonceFrom @SK.Nonce sebNonce
let unboxed = SK.secretboxOpen key0 nonce0 (unEncryptedBox sebBox)
lbs <- maybe1 unboxed (throwError DecryptionError) (pure . LBS.fromStrict)
either (const $ throwError UnsupportedFormat) pure (deserialiseOrFail lbs)
deriveGroupSecret :: NonceFrom SK.Nonce n => n -> BS.ByteString -> GroupSecret
deriveGroupSecret n bs = key0
where
nonceS = nonceFrom @SK.Nonce n & Saltine.encode & hashObject @HbSync & fromHbSyncHash
prk = HKDF.extractSkip @_ @HbSyncHash bs
key0 = HKDF.expand prk nonceS typicalKeyLength & Saltine.decode & fromJust

View File

@ -12,6 +12,8 @@ import Data.Hashable hiding (Hashed)
newtype AnyRefKey t s = AnyRefKey (PubKey 'Sign s) newtype AnyRefKey t s = AnyRefKey (PubKey 'Sign s)
instance RefMetaData (AnyRefKey t s)
deriving stock instance IsRefPubKey s => Eq (AnyRefKey n s) deriving stock instance IsRefPubKey s => Eq (AnyRefKey n s)
instance (IsRefPubKey s) => Hashable (AnyRefKey t s) where instance (IsRefPubKey s) => Hashable (AnyRefKey t s) where

View File

@ -124,6 +124,8 @@ instance Expires (SessionKey L4Proto (RefChanHeadBlock L4Proto)) where
newtype RefChanHeadKey s = RefChanHeadKey (PubKey 'Sign s) newtype RefChanHeadKey s = RefChanHeadKey (PubKey 'Sign s)
instance RefMetaData (RefChanHeadKey s)
deriving stock instance IsRefPubKey s => Eq (RefChanHeadKey s) deriving stock instance IsRefPubKey s => Eq (RefChanHeadKey s)
instance IsRefPubKey s => Hashable (RefChanHeadKey s) where instance IsRefPubKey s => Hashable (RefChanHeadKey s) where
@ -147,6 +149,8 @@ instance Pretty (AsBase58 (PubKey 'Sign s )) => Pretty (RefChanHeadKey s) where
newtype RefChanLogKey s = RefChanLogKey { fromRefChanLogKey :: PubKey 'Sign s } newtype RefChanLogKey s = RefChanLogKey { fromRefChanLogKey :: PubKey 'Sign s }
instance RefMetaData (RefChanLogKey s)
deriving stock instance IsRefPubKey s => Eq (RefChanLogKey s) deriving stock instance IsRefPubKey s => Eq (RefChanLogKey s)
instance IsRefPubKey s => Hashable (RefChanLogKey s) where instance IsRefPubKey s => Hashable (RefChanLogKey s) where

View File

@ -27,6 +27,8 @@ import Lens.Micro.Platform
newtype RefLogKey s = RefLogKey { fromRefLogKey :: PubKey 'Sign s } newtype RefLogKey s = RefLogKey { fromRefLogKey :: PubKey 'Sign s }
deriving stock Generic deriving stock Generic
instance RefMetaData (RefLogKey s)
instance Serialise (PubKey 'Sign s) => Serialise (RefLogKey s) instance Serialise (PubKey 'Sign s) => Serialise (RefLogKey s)
deriving stock instance IsRefPubKey s => Eq (RefLogKey s) deriving stock instance IsRefPubKey s => Eq (RefLogKey s)

View File

@ -1,8 +1,12 @@
module HBS2.OrDie where module HBS2.OrDie
( module HBS2.OrDie
) where
import Data.Kind import Data.Kind
import Control.Monad.IO.Class import Control.Monad.IO.Class
import System.Exit import System.Exit
import Prettyprinter
import UnliftIO
class OrDie m a where class OrDie m a where
type family OrDieResult a :: Type type family OrDieResult a :: Type
@ -28,3 +32,31 @@ instance MonadIO m => OrDie m ExitCode where
orDie mv err = mv >>= \case orDie mv err = mv >>= \case
ExitSuccess -> pure () ExitSuccess -> pure ()
ExitFailure{} -> liftIO $ die err ExitFailure{} -> liftIO $ die err
-- TODO: move-to-library
class OrThrow a where
type family OrThrowResult a :: Type
orThrow :: forall e m . (MonadIO m, Exception e) => e -> a -> m (OrThrowResult a)
instance OrThrow (Maybe a) where
type instance OrThrowResult (Maybe a) = a
orThrow e a = case a of
Nothing -> throwIO e
Just x -> pure x
instance OrThrow (Either b a) where
type instance OrThrowResult (Either b a) = a
orThrow e a = case a of
Left{} -> throwIO e
Right x -> pure x
orThrowUser :: (OrThrow a1, MonadIO m)
=> Doc ann
-> a1
-> m (OrThrowResult a1)
orThrowUser p = orThrow (userError (show p))

View File

@ -1,13 +1,16 @@
{-# Language FunctionalDependencies #-} {-# Language FunctionalDependencies #-}
{-# Language DefaultSignatures #-}
module HBS2.Storage where module HBS2.Storage where
import HBS2.Hash
import HBS2.Prelude.Plated import HBS2.Prelude.Plated
import HBS2.Hash
import HBS2.Data.Types.Refs (RefMetaData(..))
import Data.Kind import Data.Kind
import Lens.Micro.Platform import Lens.Micro.Platform
import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy (ByteString)
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
import Data.Word
import Codec.Serialise() import Codec.Serialise()
@ -29,6 +32,12 @@ newtype Size = Size Integer
deriving newtype (Eq,Ord,Enum,Num,Real,Integral,Hashable,Pretty,Serialise) deriving newtype (Eq,Ord,Enum,Num,Real,Integral,Hashable,Pretty,Serialise)
deriving stock (Show) deriving stock (Show)
data ExpiredAfter a = ExpiredAfter Word64 a
deriving stock (Generic)
instance Serialise a => Serialise (ExpiredAfter a)
class ( Monad m class ( Monad m
, IsKey h , IsKey h
, Hashed h block , Hashed h block
@ -46,11 +55,11 @@ class ( Monad m
hasBlock :: a -> Key h -> m (Maybe Integer) hasBlock :: a -> Key h -> m (Maybe Integer)
updateRef :: Hashed h k => a -> k -> Key h -> m () updateRef :: (Hashed h k, RefMetaData k) => a -> k -> Key h -> m ()
getRef :: (Hashed h k, Pretty k) => a -> k -> m (Maybe (Key h)) getRef :: (Hashed h k, Pretty k, RefMetaData k) => a -> k -> m (Maybe (Key h))
delRef :: Hashed h k => a -> k -> m () delRef :: (Hashed h k, RefMetaData k) => a -> k -> m ()
data AnyStorage = forall zu . ( Storage zu HbSync ByteString IO data AnyStorage = forall zu . ( Storage zu HbSync ByteString IO
@ -59,10 +68,14 @@ data AnyStorage = forall zu . ( Storage zu HbSync ByteString IO
class HasStorage m where class HasStorage m where
getStorage :: m AnyStorage getStorage :: m AnyStorage
instance (Monad m, HasStorage m) => HasStorage (MaybeT m) where instance (Monad m, HasStorage m) => HasStorage (MaybeT m) where
getStorage = lift getStorage getStorage = lift getStorage
instance Hashed h a => Hashed h (ExpiredAfter a) where
hashObject (ExpiredAfter _ a) = hashObject a
instance RefMetaData a => RefMetaData (ExpiredAfter a) where
refMetaData (ExpiredAfter t x) = [("expires", show t)] <> refMetaData x
instance (IsKey HbSync, MonadIO m) => Storage AnyStorage HbSync ByteString m where instance (IsKey HbSync, MonadIO m) => Storage AnyStorage HbSync ByteString m where
putBlock (AnyStorage s) = liftIO . putBlock s putBlock (AnyStorage s) = liftIO . putBlock s

View File

@ -22,7 +22,6 @@ class (MonadIO m, Storage storage hash (ToBlockW s) m) => MerkleWriter s hash st
type family ToBlockW s :: Type type family ToBlockW s :: Type
writeAsMerkle :: storage -> s -> m (Hash hash) writeAsMerkle :: storage -> s -> m (Hash hash)
class (MonadIO m, Storage storage h (ToBlockR s) m) => MerkleReader s storage h m where class (MonadIO m, Storage storage h (ToBlockR s) m) => MerkleReader s storage h m where
data family TreeKey s :: Type data family TreeKey s :: Type
type family ToBlockR s :: Type type family ToBlockR s :: Type

View File

@ -453,7 +453,13 @@ storeObjectRPC True repo meta bs = do
& LBS.toStrict & LBS.toStrict
let bsStream = readChunkedBS bs defBlockSize let bsStream = readChunkedBS bs defBlockSize
let source = ToEncryptSymmBS gks nonce bsStream gk0 (ShortMetadata txt) Nothing
let source = ToEncryptSymmBS gks
(Left gkh0 :: LoadedRef (GroupKey 'Symm HBS2Basic))
nonce
bsStream
(ShortMetadata txt)
Nothing
h <- runExceptT (writeAsMerkle sto source) >>= either (const cantWriteMerkle) pure h <- runExceptT (writeAsMerkle sto source) >>= either (const cantWriteMerkle) pure

View File

@ -50,6 +50,10 @@ import UnliftIO (MonadUnliftIO(..),async,race)
data PeerBrainsDb data PeerBrainsDb
-- TODO: refactor-brains
-- переключить на db-pipe
-- асинки - в runContT
-- FIXME: move-that-orphans-somewhere -- FIXME: move-that-orphans-somewhere
instance ToField HashRef where instance ToField HashRef where

View File

@ -1,6 +1,7 @@
{-# Language TemplateHaskell #-} {-# Language TemplateHaskell #-}
module CLI.Common where module CLI.Common where
import HBS2.Clock
import HBS2.Net.Messaging.Unix import HBS2.Net.Messaging.Unix
import HBS2.Net.Proto import HBS2.Net.Proto
import HBS2.Net.Proto.Service import HBS2.Net.Proto.Service
@ -33,4 +34,15 @@ withMyRPC o m = do
let soname = getRpcSocketName conf let soname = getRpcSocketName conf
withRPC2 @api @UNIX soname m withRPC2 @api @UNIX soname m
withRPCMessaging :: MonadIO m => RPCOpt -> (MessagingUnix -> m ()) -> m ()
withRPCMessaging o action = do
conf <- peerConfigRead (view rpcOptConf o)
let soname = getRpcSocketName conf
client1 <- newMessagingUnix False 1.0 soname
m1 <- liftIO $ async $ runMessagingUnix client1
link m1
action client1
pause @'Seconds 0.05
cancel m1

View File

@ -2,15 +2,26 @@ module CLI.RefChan where
import HBS2.Prelude.Plated import HBS2.Prelude.Plated
import HBS2.Hash
import HBS2.Clock
import HBS2.Net.Auth.Credentials import HBS2.Net.Auth.Credentials
import HBS2.Net.Auth.Credentials.Sigil
import HBS2.Net.Proto.Definition() import HBS2.Net.Proto.Definition()
import HBS2.Net.Proto.RefChan import HBS2.Net.Proto.RefChan
import HBS2.Net.Proto.Types import HBS2.Net.Proto.Types
import HBS2.Data.Types.Refs
import HBS2.Merkle
import HBS2.Actors.Peer.Types
import HBS2.Data.Types.SignedBox import HBS2.Data.Types.SignedBox
import HBS2.Net.Proto.Service import HBS2.Net.Proto.Service
import HBS2.Data.Detect import HBS2.Data.Detect
import HBS2.Storage import HBS2.Storage
import HBS2.Storage.Operations.ByteString
import HBS2.Data.Types.Refs import HBS2.Data.Types.Refs
import HBS2.Net.Messaging.Unix
import HBS2.Net.Auth.GroupKeySymm
import HBS2.KeyMan.Keys.Direct
import HBS2.OrDie import HBS2.OrDie
@ -22,13 +33,27 @@ import HBS2.Peer.RefChanNotifyLog
import CLI.Common import CLI.Common
import RPC2() import RPC2()
import Options.Applicative import HBS2.System.Logger.Simple hiding (info)
import Data.ByteString qualified as BS import HBS2.System.Logger.Simple qualified as Log
import Data.ByteString.Lazy qualified as LBS
import Lens.Micro.Platform import Control.Monad.Cont
import Data.Maybe import Control.Monad.Reader
import System.Exit
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
import Data.ByteString.Lazy qualified as LBS
import Data.ByteString qualified as BS
import Data.HashSet qualified as HashSet
import Data.Maybe
import Lens.Micro.Platform
import Options.Applicative
import System.Exit
import Data.Time.Clock (getCurrentTime)
import Data.Time.Clock.POSIX (getPOSIXTime)
import Control.Monad.Except (runExceptT)
import Data.Word
import Codec.Serialise
import UnliftIO
import Streaming.Prelude qualified as S
pRefChan :: Parser (IO ()) pRefChan :: Parser (IO ())
pRefChan = hsubparser ( command "head" (info pRefChanHead (progDesc "head commands" )) pRefChan = hsubparser ( command "head" (info pRefChanHead (progDesc "head commands" ))
@ -36,6 +61,7 @@ pRefChan = hsubparser ( command "head" (info pRefChanHead (progDesc "head comm
<> command "notify" (info pRefChanNotify (progDesc "post notify message")) <> command "notify" (info pRefChanNotify (progDesc "post notify message"))
<> command "fetch" (info pRefChanFetch (progDesc "fetch and sync refchan value")) <> command "fetch" (info pRefChanFetch (progDesc "fetch and sync refchan value"))
<> command "get" (info pRefChanGet (progDesc "get refchan value")) <> command "get" (info pRefChanGet (progDesc "get refchan value"))
<> command "gk" (info pRefChanGK (progDesc "generate a group key"))
) )
@ -138,19 +164,150 @@ pRefChanNotify =
<> command "tail" (info pRefChanNotifyLogTail (progDesc "output last messages")) <> command "tail" (info pRefChanNotifyLogTail (progDesc "output last messages"))
) )
data EncMethod = EncryptWithSigil FilePath
| NoEncryption
pRefChanNotifyPost :: Parser (IO ()) pRefChanNotifyPost :: Parser (IO ())
pRefChanNotifyPost = do pRefChanNotifyPost = do
opts <- pRpcCommon opts <- pRpcCommon
kra <- strOption (long "author" <> short 'a' <> help "author credentials")
fn <- optional $ strOption (long "file" <> short 'f' <> help "file")
puk <- argument pRefChanId (metavar "REFCHAH-REF")
pure $ withMyRPC @RefChanAPI opts $ \caller -> do
sc <- BS.readFile kra
creds <- pure (parseCredentials @(Encryption L4Proto) (AsCredFile sc)) `orDie` "bad keyring file"
lbs <- maybe1 fn LBS.getContents LBS.readFile
let box = makeSignedBox @L4Proto @BS.ByteString (view peerSignPk creds) (view peerSignSk creds) (LBS.toStrict lbs)
void $ callService @RpcRefChanNotify caller (puk, box)
si <- strOption (long "sigil" <> short 's' <> help "sigil file")
fn <- optional $ strOption (long "file" <> short 'f' <> help "file")
encrypt <- encryptOpt
-- EncMethod <$> optional (switch (long "encrypt" <> help "encrypt transaction"))
-- <*> optional (strOption (long "sigil" <> help "using sigil"))
puk <- argument pRefChanId (metavar "REFCHAH-REF")
pure $ flip runContT pure do
client <- ContT $ withRPCMessaging opts
self <- runReaderT (ownPeer @UNIX) client
refChanAPI <- makeServiceCaller @RefChanAPI self
storageAPI <- makeServiceCaller @StorageAPI self
let endpoints = [ Endpoint @UNIX refChanAPI
, Endpoint @UNIX storageAPI
]
void $ ContT $ bracket (async $ runReaderT (runServiceClientMulti endpoints) client) cancel
-- caller <- ContT $ withMyRPC @RefChanAPI opts
sigil <- liftIO $ (BS.readFile si <&> parseSerialisableFromBase58 @(Sigil L4Proto))
`orDie` "parse sigil failed"
(auPk, sd) <- pure (unboxSignedBox0 @(SigilData L4Proto) (sigilData sigil))
>>= orThrowUser "malformed sigil/bad signature"
keys <- liftIO $ runKeymanClient do
creds <- loadCredentials auPk >>= orThrowUser "can't load credentials"
encKey <- loadKeyRingEntry (sigilDataEncKey sd)
pure (creds,encKey)
let creds = view _1 keys
lbs <- liftIO $ maybe1 fn LBS.getContents LBS.readFile
let sto = AnyStorage (StorageClient storageAPI)
ss <- if not encrypt then do
pure lbs
else do
-- итак нам тут нужно знать:
-- 2. старый gk или новый
-- 2.1 если старый - то где взять
-- 2.2 стоит обновить gk или нет
-- 2.3 стоит сохранять gk или нет
--
-- допустим так: ключ равно: голова + эпоха + enc SigilData
--
-- тогда: 1) где его хранить? в кеймане или в стейте?
--
-- + в кеймане, допустим?
-- в кеймане может быть блокировка sqlite, нехорошо
-- можно сохранять в hbs2:
-- 1. групповой ключ и так там сохраняется
-- 2. ссылок не должно быть много, если
-- ссылка ~ hash(канал, ключ пользователя)
-- 3. обновление ключа: если явно сказано!
-- иначе -- берём существующий
-- 4. если голова поменялась -- можем
-- удалить ссылку?
kre@(KeyringKeys pk sk) <- orThrowUser "encryption key not found for given sigil" (view _2 keys)
let kreHash = hashObject @HbSync (serialise kre)
hh <- callService @RpcRefChanHeadGet refChanAPI puk
>>= orThrowUser "RPC error"
>>= orThrowUser "refchan head not available"
hd <- getRefChanHead @L4Proto sto (RefChanHeadKey puk)
>>= orThrowUser "head block not found"
let rcpts = view refChanHeadReaders hd
when ( HashSet.null rcpts ) do
throwIO (userError "empty recipients list -- encryption is not possible")
notice $ "refchan head" <+> pretty hh
-- FIXME: key-rotation-factor-hardcode
-- около раза в месяц. может, нормально и так
t <- liftIO $ getPOSIXTime <&> round
let gkkey0 = SomeRefKey (hh,kreHash)
notice $ "gkkey0" <+> pretty (hashObject @HbSync gkkey0)
mgk <- runMaybeT do
gkv <- toMPlus =<< getRef sto gkkey0
notice $ "FOUND REF VAL" <+> pretty gkv
gks <- runExceptT (readFromMerkle sto (SimpleKey gkv))
>>= toMPlus
gk <- deserialiseOrFail @(GroupKey 'Symm HBS2Basic) gks
& toMPlus
notice $ "found GK0" <+> pretty gkv
pure gk
gk <- case mgk of
Just x -> pure x
Nothing -> do
gknew <- generateGroupKey @HBS2Basic Nothing (HashSet.toList rcpts)
gkh <- writeAsMerkle sto (serialise gknew)
-- FIXME: key-expiration-hardcode
let gkkey1 = ExpiredAfter (t+1209600) gkkey0
notice $ "UPDATE REF" <+> pretty (hashObject @HbSync gkkey1) <+> pretty gkh
updateRef sto gkkey1 gkh
notice $ "generated! GK0" <+> pretty gkh
pure gknew
gks <- orThrowUser "can't decrypt group key" $ lookupGroupKey sk pk gk
-- FIXME: use-deterministic-nonce
lift $ encryptBlock sto gks (Right gk) Nothing lbs <&> serialise
let box = makeSignedBox @L4Proto @BS.ByteString (view peerSignPk creds) (view peerSignSk creds) (LBS.toStrict ss)
void $ callService @RpcRefChanNotify refChanAPI (puk, box)
where
encryptOpt = do
optional (switch (long "encrypt" <> help "post encrypted")) <&> fromMaybe False
-- noEncryption = pure NoEncryption
-- encryptWithSigil = EncryptWithSigil <$> strOption (long "sigil" <> help "sigil file")
pRefChanId :: ReadM (RefChanId L4Proto) pRefChanId :: ReadM (RefChanId L4Proto)
pRefChanId = maybeReader (fromStringMay @(RefChanId L4Proto)) pRefChanId = maybeReader (fromStringMay @(RefChanId L4Proto))
@ -204,3 +361,32 @@ pRefChanFetch = do
void $ callService @RpcRefChanFetch caller href void $ callService @RpcRefChanFetch caller href
pRefChanGK :: Parser (IO ())
pRefChanGK = do
opts <- pRpcCommon
puk <- argument pRefChanId (metavar "REFCHAH-REF")
pure $ flip runContT pure do
client <- ContT $ withRPCMessaging opts
self <- runReaderT (ownPeer @UNIX) client
refChanAPI <- makeServiceCaller @RefChanAPI self
storageAPI <- makeServiceCaller @StorageAPI self
let endpoints = [ Endpoint @UNIX refChanAPI
, Endpoint @UNIX storageAPI
]
void $ ContT $ bracket (async $ runReaderT (runServiceClientMulti endpoints) client) cancel
let sto = AnyStorage (StorageClient storageAPI)
hd <- getRefChanHead @L4Proto sto (RefChanHeadKey puk)
>>= orThrowUser "head block not found"
let readers = view refChanHeadReaders' hd
gk <- generateGroupKey @HBS2Basic Nothing (HashSet.toList readers)
liftIO $ print $ pretty (AsGroupKeyFile gk)

View File

@ -18,7 +18,7 @@ common warnings
common common-deps common common-deps
build-depends: build-depends:
base, hbs2-core, hbs2-storage-simple base, hbs2-core, hbs2-storage-simple, hbs2-keyman
, aeson , aeson
, async , async
, bytestring , bytestring
@ -198,7 +198,7 @@ executable hbs2-peer
, CLI.RefChan , CLI.RefChan
-- other-extensions: -- other-extensions:
build-depends: base, hbs2-peer build-depends: base, hbs2-peer, hbs2-keyman
hs-source-dirs: app hs-source-dirs: app

View File

@ -1,6 +1,8 @@
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
module HBS2.Peer.RPC.API.Storage where module HBS2.Peer.RPC.API.Storage where
import HBS2.Prelude
import HBS2.Data.Types.Refs
import HBS2.Actors.Peer import HBS2.Actors.Peer
import HBS2.Net.Proto.Service import HBS2.Net.Proto.Service
import HBS2.Net.Messaging.Unix import HBS2.Net.Messaging.Unix
@ -8,6 +10,7 @@ import HBS2.Peer.RPC.Internal.Types
import HBS2.Storage import HBS2.Storage
import HBS2.Data.Types.Refs (HashRef(..),RefAlias(..)) import HBS2.Data.Types.Refs (HashRef(..),RefAlias(..))
import Control.Applicative
import Control.Monad.Reader import Control.Monad.Reader
import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy (ByteString)
import Codec.Serialise import Codec.Serialise
@ -36,10 +39,9 @@ type StorageAPI = '[ RpcStorageHasBlock
instance HasProtocol UNIX (ServiceProto StorageAPI UNIX) where instance HasProtocol UNIX (ServiceProto StorageAPI UNIX) where
type instance ProtocolId (ServiceProto StorageAPI UNIX) = 0xDA2374610001 type instance ProtocolId (ServiceProto StorageAPI UNIX) = 0xDA2374610001
type instance Encoded UNIX = ByteString type instance Encoded UNIX = ByteString
decode = either (const Nothing) Just . deserialiseOrFail decode lbs = eitherToMaybe $ deserialiseOrFail lbs
encode = serialise encode = serialise
instance (Monad m) instance (Monad m)
=> HasRpcContext StorageAPI RPC2Context (ResponseM UNIX (ReaderT RPC2Context m)) where => HasRpcContext StorageAPI RPC2Context (ResponseM UNIX (ReaderT RPC2Context m)) where
getRpcContext = lift ask getRpcContext = lift ask
@ -65,12 +67,12 @@ type instance Output RpcStorageDelBlock = ()
type instance Input RpcStorageGetChunk = (HashRef, Offset, Size) type instance Input RpcStorageGetChunk = (HashRef, Offset, Size)
type instance Output RpcStorageGetChunk = Maybe ByteString type instance Output RpcStorageGetChunk = Maybe ByteString
type instance Input RpcStorageGetRef = RefAlias type instance Input RpcStorageGetRef = RefAlias2
type instance Output RpcStorageGetRef = Maybe HashRef type instance Output RpcStorageGetRef = Maybe HashRef
type instance Input RpcStorageUpdateRef = (RefAlias, HashRef) type instance Input RpcStorageUpdateRef = (RefAlias2, HashRef)
type instance Output RpcStorageUpdateRef = () type instance Output RpcStorageUpdateRef = ()
type instance Input RpcStorageDelRef = RefAlias type instance Input RpcStorageDelRef = RefAlias2
type instance Output RpcStorageDelRef = () type instance Output RpcStorageDelRef = ()

View File

@ -8,7 +8,7 @@ module HBS2.Peer.RPC.Client.StorageClient
import HBS2.Prelude.Plated import HBS2.Prelude.Plated
import HBS2.Hash import HBS2.Hash
import HBS2.Data.Types.Refs (HashRef(..),refAlias) import HBS2.Data.Types.Refs (HashRef(..),refAlias,refMetaData)
import HBS2.Net.Proto.Service import HBS2.Net.Proto.Service
import HBS2.Storage import HBS2.Storage
@ -56,6 +56,7 @@ instance ( MonadIO m
void $ callService @RpcStorageDelBlock (fromStorageClient s) (HashRef h) void $ callService @RpcStorageDelBlock (fromStorageClient s) (HashRef h)
updateRef s ref v = liftIO do updateRef s ref v = liftIO do
notice $ "metadata!" <+> pretty (refMetaData ref)
void $ callService @RpcStorageUpdateRef (fromStorageClient s) (refAlias ref, HashRef v) void $ callService @RpcStorageUpdateRef (fromStorageClient s) (refAlias ref, HashRef v)
getRef s ref = liftIO do getRef s ref = liftIO do

View File

@ -9,7 +9,7 @@ module HBS2.Peer.RPC.Internal.Storage
import HBS2.Prelude.Plated import HBS2.Prelude.Plated
import HBS2.Actors.Peer.Types import HBS2.Actors.Peer.Types
import HBS2.Data.Types.Refs (HashRef(..)) import HBS2.Data.Types.Refs (HashRef(..),refMetaData)
import HBS2.Storage import HBS2.Storage
import HBS2.Peer.RPC.Class import HBS2.Peer.RPC.Class
import HBS2.Peer.RPC.API.Storage import HBS2.Peer.RPC.API.Storage
@ -73,7 +73,7 @@ instance (StorageContext m) => HandleMethod m RpcStorageGetRef where
instance (StorageContext m) => HandleMethod m RpcStorageUpdateRef where instance (StorageContext m) => HandleMethod m RpcStorageUpdateRef where
handleMethod (ref, val) = do handleMethod (ref, val) = do
debug $ "rpc.storage.updateRef" <+> pretty ref debug $ "rpc.storage.updateRef" <+> pretty ref <+> pretty (refMetaData ref)
sto <- getStorage sto <- getStorage
liftIO $ updateRef sto ref (fromHashRef val) liftIO $ updateRef sto ref (fromHashRef val)

View File

@ -1,7 +1,9 @@
{-# Language TemplateHaskell #-}
module Main where module Main where
import HBS2.Prelude import HBS2.Prelude
import HBS2.Hash import HBS2.Hash
import HBS2.Data.Types.Refs
import HBS2.Storage import HBS2.Storage
import HBS2.Storage.Simple import HBS2.Storage.Simple
@ -17,13 +19,22 @@ import Control.Monad (replicateM)
import Data.ByteString.Lazy qualified as LBS import Data.ByteString.Lazy qualified as LBS
import Data.Word (Word8) import Data.Word (Word8)
import Data.Function
import Text.InterpolatedString.Perl6 (qc) import Text.InterpolatedString.Perl6 (qc)
import Control.Monad import Control.Monad
import UnliftIO import UnliftIO
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HashMap
import Data.Map (Map)
import Data.Map qualified as Map
import Streaming.Prelude (Of,Stream) import Streaming.Prelude (Of,Stream)
import Streaming.Prelude qualified as S import Streaming.Prelude qualified as S
import Control.Concurrent.STM (retry,flushTQueue)
import Codec.Serialise
-- Генерация одного случайного байта -- Генерация одного случайного байта
randomByte :: IO Word8 randomByte :: IO Word8
randomByte = randomRIO (0, 255) randomByte = randomRIO (0, 255)
@ -41,6 +52,9 @@ randomByteStrings n len = replicateM_ n $ do
bs <- liftIO $ randomByteString len bs <- liftIO $ randomByteString len
S.yield bs S.yield bs
-- chunkStream :: Monad m => Int -> Stream (Of LBS.ByteString) m r -> Stream (Of [LBS.ByteString]) m r
-- chunkStream n = S.mapsM (sequence . take n) . S.chunksOf n
main :: IO () main :: IO ()
main = do main = do
(ns:ss:pref:_) <- getArgs (ns:ss:pref:_) <- getArgs
@ -49,8 +63,13 @@ main = do
let s = readDef @Int 256 ss let s = readDef @Int 256 ss
let p = pref let p = pref
let bss = randomByteStrings n s -- let bss = randomByteStrings n s
let bss2 = randomByteStrings n s let bss2 = randomByteStrings n s
let bss3 = randomByteStrings n s
-- let bss41 = randomByteStrings (n `div` 2) s
-- let bss42 = randomByteStrings (n`div` 2) s
-- let bss43 = randomByteStrings (n`div`4) s
-- let bss44 = randomByteStrings (n`div`4) s
let path = pref </> ".test-storage" let path = pref </> ".test-storage"
@ -72,8 +91,8 @@ main = do
print $ "preparing to write" <+> pretty n <+> "chunks" print $ "preparing to write" <+> pretty n <+> "chunks"
timeItNamed "write chunks test" do -- timeItNamed "write chunks test" do
S.mapM_ (enqueueBlock storage) bss -- S.mapM_ (enqueueBlock storage) bss
timeItNamed "write chunks to sqlite test" do timeItNamed "write chunks to sqlite test" do
withDB env $ transactional do withDB env $ transactional do
@ -81,3 +100,89 @@ main = do
let h = hashObject @HbSync bs & pretty & show let h = hashObject @HbSync bs & pretty & show
insert [qc|insert into wtf (hash,val) values(?,?)|] (h,bs) insert [qc|insert into wtf (hash,val) values(?,?)|] (h,bs)
timeItNamed "write chunks to log" do
fh <- openFile (path </> "lsm") AppendMode
flip S.mapM_ bss3 $ \bs -> do
let h = hashObject @HbSync bs & pretty & show
LBS.hPut fh (serialise (h,bs))
hClose fh
timeItNamed "write chunks to log 2" do
buf <- newIORef (mempty, 0 :: Int)
fh <- openFile (path </> "lsm2") AppendMode
flip S.mapM_ bss3 $ \bs -> do
let h = hashObject @HbSync bs & pretty & show
num <- atomicModifyIORef buf (\(chunks,sz) -> ((serialise (h,bs) : chunks,sz+1),sz+1))
when (num >= 16) do
w <- atomicModifyIORef buf (\(chunks,_) -> ((mempty,0),chunks))
LBS.hPut fh (mconcat w)
(w,_) <- readIORef buf
LBS.hPut fh (mconcat w)
hClose fh
timeItNamed "write chunks to LSM-mock" do
if n*s > 1073741824 then do
print "too much"
else do
let k = 6
let batch = 100
rem <- newTVarIO n
quit <- newTVarIO False
out <- newTQueueIO
queue <- newTQueueIO
w1 <- async do
fix \next -> do
r <- readTVarIO rem
when (r > 0) do
b <- S.toList_ (randomByteStrings (min r batch) s)
atomically $ do
writeTQueue queue b
modifyTVar rem (+ (-batch))
next
atomically $ writeTVar quit True
as <- forM [1..k] \j -> async do
mem <- newIORef mempty
-- mem <- newTVarIO mempty
fix \next -> do
b <- atomically $ do
q <- readTVar quit
if q
then return Nothing
else (Just <$> readTQueue queue)
`orElse` (
readTVar quit >>= \z -> if z then pure Nothing else retry
)
case b of
Nothing -> pure ()
Just bss -> do
new <- for bss $ \bs -> do
let h = hashObject @HbSync bs & HashRef
pure $ (h, bs)
modifyIORef' mem (HashMap.union (HashMap.fromList new))
-- atomically $ modifyTVar mem (Map.union (Map.fromList new))
next
co <- readIORef mem
-- co <- readTVarIO mem
atomically $ writeTQueue out co
mapM_ wait (w1:as)
result <- atomically $ flushTQueue out
h <- openFile (path </> "lsm3") WriteMode
LBS.hPut h (serialise result)
hClose h

View File

@ -76,6 +76,8 @@ library
, stm , stm
, stm-chans , stm-chans
, streaming , streaming
, serialise
, time
, transformers , transformers
, uniplate , uniplate
, unordered-containers , unordered-containers
@ -136,7 +138,7 @@ executable hbs2-storage-simple-benchmarks
-- -fno-warn-unused-binds -- -fno-warn-unused-binds
-threaded -threaded
-rtsopts -rtsopts
"-with-rtsopts=-N4 -A64m -AL256m -I0" "-with-rtsopts=-N8 -A64m -AL256m -I0"
main-is: Main.hs main-is: Main.hs
-- other-modules: -- other-modules:
@ -172,6 +174,7 @@ executable hbs2-storage-simple-benchmarks
, stm , stm
, unliftio , unliftio
, network-byte-order , network-byte-order
, unordered-containers
hs-source-dirs: benchmarks hs-source-dirs: benchmarks
default-language: Haskell2010 default-language: Haskell2010

View File

@ -10,6 +10,7 @@ module HBS2.Storage.Simple
import HBS2.Clock import HBS2.Clock
import HBS2.Hash import HBS2.Hash
import HBS2.Prelude.Plated import HBS2.Prelude.Plated
import HBS2.Data.Types.Refs (refMetaData)
import HBS2.Storage import HBS2.Storage
import HBS2.Base58 import HBS2.Base58
@ -27,6 +28,9 @@ import Data.ByteString (ByteString)
import Data.Foldable import Data.Foldable
import Data.List qualified as L import Data.List qualified as L
import Data.Maybe import Data.Maybe
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Either
import Lens.Micro.Platform import Lens.Micro.Platform
import Prettyprinter import Prettyprinter
import System.Directory import System.Directory
@ -36,6 +40,7 @@ import System.IO.Error
import System.IO.Temp import System.IO.Temp
import System.AtomicWrite.Writer.LazyByteString qualified as AwLBS import System.AtomicWrite.Writer.LazyByteString qualified as AwLBS
import System.AtomicWrite.Writer.ByteString qualified as AwBS import System.AtomicWrite.Writer.ByteString qualified as AwBS
import Data.Time.Clock.POSIX (getPOSIXTime)
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
@ -48,6 +53,7 @@ import Control.Concurrent.STM.TBMQueue qualified as TBMQ
import Control.Concurrent.STM.TBMQueue (TBMQueue) import Control.Concurrent.STM.TBMQueue (TBMQueue)
import Control.Concurrent.STM.TVar qualified as TV import Control.Concurrent.STM.TVar qualified as TV
import Codec.Serialise
-- NOTE: random accessing files in a git-like storage -- NOTE: random accessing files in a git-like storage
@ -350,17 +356,30 @@ simpleWriteLinkRawRef :: forall h . ( IsSimpleStorageKey h
, ToByteString (AsBase58 (Hash h)) , ToByteString (AsBase58 (Hash h))
) )
=> SimpleStorage h => SimpleStorage h
-> [(String, String)]
-> Hash h -> Hash h
-> Hash h -> Hash h
-> IO () -> IO ()
simpleWriteLinkRawRef ss h ref = do simpleWriteLinkRawRef ss meta h ref = do
let fnr = simpleRefFileName ss h let fnr = simpleRefFileName ss h
void $ spawnAndWait ss $ do void $ spawnAndWait ss $ do
AwBS.atomicWriteFile fnr (toByteString (AsBase58 ref)) AwBS.atomicWriteFile fnr (toByteString (AsBase58 ref))
`catchAny` \_ -> do `catchAny` \_ -> do
err $ "simpleWriteLinkRawRef" <+> pretty h <+> pretty ref <+> pretty fnr err $ "simpleWriteLinkRawRef" <+> pretty h <+> pretty ref <+> pretty fnr
unless (null meta) do
let metaname = fnr `addExtension` "metadata"
meta0 <- try @IOError (LBS.readFile metaname)
<&> fromRight mempty
<&> deserialiseOrFail @(Map String String)
<&> fromRight mempty
let meta1 = meta0 <> Map.fromList meta
r <- try @IOError $ AwBS.atomicWriteFile metaname (LBS.toStrict $ serialise meta1)
case r of
Right{} -> pure ()
Left e -> err $ "simpleWriteLinkRawRef" <+> viaShow e
simpleReadLinkRaw :: forall r h . ( IsKey h, Hashed h r, Pretty r) simpleReadLinkRaw :: forall r h . ( IsKey h, Hashed h r, Pretty r)
=> SimpleStorage h => SimpleStorage h
-> r -> r
@ -370,14 +389,43 @@ simpleReadLinkRaw ss ref = do
let hash = hashObject @h ref let hash = hashObject @h ref
let fn = simpleRefFileName ss hash let fn = simpleRefFileName ss hash
rs <- spawnAndWait ss $ do rs <- spawnAndWait ss $ do
meta <- try @IOError (LBS.readFile (fn `addExtension` "metadata"))
>>= \case
Left{} -> pure mempty
Right sm -> pure $ deserialiseOrFail @(Map String String) sm & fromRight mempty
deleted <- runMaybeT do
ts <- toMPlus $ Map.lookup "expires" meta
t0 <- toMPlus $ readMay @Int ts
now <- liftIO $ getPOSIXTime <&> round
if now <= t0 then do
pure False
else do
liftIO $ simpleDelRef ss hash
pure True
if deleted == Just True then do
pure Nothing
else do
-- FIXME: log-this-situation -- FIXME: log-this-situation
(Just <$> LBS.readFile fn) `catchAny` \e -> do (Just <$> LBS.readFile fn) `catchAny` \e -> do
-- TODO: update-stats-instead-of-spamming
err $ "simpleReadLinkRaw" <+> pretty ref <+> pretty fn <+> viaShow e err $ "simpleReadLinkRaw" <+> pretty ref <+> pretty fn <+> viaShow e
pure Nothing pure Nothing
pure $ fromMaybe Nothing rs pure $ fromMaybe Nothing rs
simpleDelRef :: IsKey h => SimpleStorage h -> Hash h -> IO ()
simpleDelRef ss hash = do
let fn = simpleRefFileName ss hash
here <- doesFileExist fn
when here (removeFile fn)
let meta = fn `addExtension` "metadata"
mhere <- doesFileExist meta
when mhere (removeFile meta)
simpleReadLinkVal :: ( IsKey h simpleReadLinkVal :: ( IsKey h
, IsSimpleStorageKey h , IsSimpleStorageKey h
, Hashed h LBS.ByteString , Hashed h LBS.ByteString
@ -416,8 +464,9 @@ instance ( MonadIO m, IsKey hash
updateRef ss ref v = do updateRef ss ref v = do
let refHash = hashObject @hash ref let refHash = hashObject @hash ref
let meta = refMetaData ref
debug $ "updateRef:" <+> pretty refHash debug $ "updateRef:" <+> pretty refHash
void $ liftIO $ simpleWriteLinkRawRef ss refHash v void $ liftIO $ simpleWriteLinkRawRef ss meta refHash v
getRef ss ref = do getRef ss ref = do
runMaybeT do runMaybeT do
@ -435,8 +484,7 @@ instance ( MonadIO m, IsKey hash
delRef ss ref = do delRef ss ref = do
let refHash = hashObject @hash ref let refHash = hashObject @hash ref
let fn = simpleRefFileName ss refHash
void $ liftIO $ spawnAndWait ss $ do void $ liftIO $ spawnAndWait ss $ do
here <- doesFileExist fn simpleDelRef ss refHash
when here (removeFile fn)

View File

@ -5,6 +5,7 @@ import HBS2.Data.Detect
import HBS2.Data.Types import HBS2.Data.Types
import HBS2.Data.Types.EncryptedBox import HBS2.Data.Types.EncryptedBox
import HBS2.Data.Types.SignedBox import HBS2.Data.Types.SignedBox
import HBS2.Data.KeyRing as KeyRing
import HBS2.Defaults import HBS2.Defaults
import HBS2.Merkle import HBS2.Merkle
import HBS2.Net.Proto.Types import HBS2.Net.Proto.Types
@ -380,7 +381,7 @@ runStore opts ss = runResourceT do
let segments = readChunked fh (fromIntegral defBlockSize) let segments = readChunked fh (fromIntegral defBlockSize)
let source = ToEncryptSymmBS gks nonce segments gk NoMetaData Nothing let source = ToEncryptSymmBS gks (Right gk) nonce segments NoMetaData Nothing
r <- runExceptT $ writeAsMerkle ss source r <- runExceptT $ writeAsMerkle ss source
@ -536,6 +537,7 @@ main = join . customExecParser (prefs showHelpOnError) $
<> command "fsck" (info pFsck (progDesc "check storage constistency")) <> command "fsck" (info pFsck (progDesc "check storage constistency"))
<> command "deps" (info pDeps (progDesc "print dependencies")) <> command "deps" (info pDeps (progDesc "print dependencies"))
<> command "del" (info pDel (progDesc "del block")) <> command "del" (info pDel (progDesc "del block"))
<> command "keyring" (info pKeyRing (progDesc "keyring commands"))
<> command "keyring-new" (info pNewKey (progDesc "generates a new keyring")) <> command "keyring-new" (info pNewKey (progDesc "generates a new keyring"))
<> command "keyring-list" (info pKeyList (progDesc "list public keys from keyring")) <> command "keyring-list" (info pKeyList (progDesc "list public keys from keyring"))
<> command "keyring-key-add" (info pKeyAdd (progDesc "adds a new keypair into the keyring")) <> command "keyring-key-add" (info pKeyAdd (progDesc "adds a new keypair into the keyring"))
@ -674,6 +676,16 @@ main = join . customExecParser (prefs showHelpOnError) $
f <- strArgument ( metavar "KEYRING-FILE" ) f <- strArgument ( metavar "KEYRING-FILE" )
pure (runKeyDel s f) pure (runKeyDel s f)
pKeyRing = hsubparser ( command "find" (info pKeyRingFind (progDesc "find keyring"))
)
pKeyRingFind = do
spk <- option pPubKey ( long "sign-key" <> short 's' <> help "sign-key" )
masks <- many (strArgument (metavar "PATHS"))
pure do
krf <- KeyRing.findKeyRing masks spk
print $ vcat (fmap pretty krf)
pReflog = hsubparser ( command "get" (info pRefLogGet (progDesc "get reflog root") ) ) pReflog = hsubparser ( command "get" (info pRefLogGet (progDesc "get reflog root") ) )
-- FIXME: only-for-hbs2-basic-encryption -- FIXME: only-for-hbs2-basic-encryption
@ -872,6 +884,8 @@ main = join . customExecParser (prefs showHelpOnError) $
phref = maybeReader fromStringMay phref = maybeReader fromStringMay
pPubKey = maybeReader (fromStringMay @(PubKey 'Sign HBS2Basic))