From 18321fe38ace02832a8231da225f677f7ecca13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierluca=20Bors=C3=B2?= Date: Fri, 4 Aug 2023 15:18:03 +0200 Subject: [PATCH 1/6] Improved binprefix tests --- core/store/hashtree/binprefix/binprefix.go | 2 +- core/store/hashtree/binprefix/binprefix_test.go | 13 +++++++++---- core/store/hashtree/binprefix/tree_test.go | 11 ++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/core/store/hashtree/binprefix/binprefix.go b/core/store/hashtree/binprefix/binprefix.go index c63bbcd24..de77dca39 100644 --- a/core/store/hashtree/binprefix/binprefix.go +++ b/core/store/hashtree/binprefix/binprefix.go @@ -4,7 +4,7 @@ // https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-melara.pdf // // The merkle tree is stored in-memory until it reaches a certain threshold of -// depth where it will write the nodes in disk. The leaf are always stored in +// depth where it will write the nodes in disk. A leaf is always stored on // disk because of the value it holds. // // Interior (Root) diff --git a/core/store/hashtree/binprefix/binprefix_test.go b/core/store/hashtree/binprefix/binprefix_test.go index 56cf3286e..701606786 100644 --- a/core/store/hashtree/binprefix/binprefix_test.go +++ b/core/store/hashtree/binprefix/binprefix_test.go @@ -3,6 +3,7 @@ package binprefix import ( "bytes" "crypto/rand" + "fmt" "os" "path/filepath" "testing" @@ -204,7 +205,7 @@ func TestMerkleTree_Get(t *testing.T) { require.Nil(t, value) _, err = tree.Get(make([]byte, MaxDepth+1)) - require.EqualError(t, err, "couldn't search key: mismatch key length 33 > 32") + require.EqualError(t, err, couldntError("search key")) tree.tx = wrongTx{} _, err = tree.Get([]byte{}) @@ -254,7 +255,7 @@ func TestMerkleTree_GetPath(t *testing.T) { require.NoError(t, err) _, err = tree.GetPath(make([]byte, MaxDepth+1)) - require.EqualError(t, err, "couldn't search key: mismatch key length 33 > 32") + require.EqualError(t, err, couldntError("search key")) } func TestMerkleTree_Stage(t *testing.T) { @@ -300,7 +301,7 @@ func TestWritableMerkleTree_Set(t *testing.T) { require.Equal(t, 1, tree.tree.Len()) err = tree.Set(make([]byte, MaxDepth+1), nil) - require.EqualError(t, err, "couldn't insert pair: mismatch key length 33 > 32") + require.EqualError(t, err, couldntError("insert pair")) } func TestWritableMerkleTree_Delete(t *testing.T) { @@ -310,7 +311,7 @@ func TestWritableMerkleTree_Delete(t *testing.T) { require.NoError(t, err) err = tree.Delete(make([]byte, MaxDepth+1)) - require.EqualError(t, err, "couldn't delete key: mismatch key length 33 > 32") + require.EqualError(t, err, couldntError("delete key")) } // ----------------------------------------------------------------------------- @@ -337,3 +338,7 @@ func (tx badTx) GetBucketOrCreate([]byte) (kv.Bucket, error) { type wrongTx struct { store.Transaction } + +func couldntError(op string) string { + return fmt.Sprintf("couldn't %v: %v", op, mismatchKeyLength()) +} diff --git a/core/store/hashtree/binprefix/tree_test.go b/core/store/hashtree/binprefix/tree_test.go index dcae30205..fc5ab4f88 100644 --- a/core/store/hashtree/binprefix/tree_test.go +++ b/core/store/hashtree/binprefix/tree_test.go @@ -1,6 +1,7 @@ package binprefix import ( + "fmt" "math" "math/big" "testing" @@ -57,7 +58,7 @@ func TestTree_Search(t *testing.T) { require.Equal(t, []byte("B"), value) _, err = tree.Search(make([]byte, MaxDepth+1), nil, &fakeBucket{}) - require.EqualError(t, err, "mismatch key length 33 > 32") + require.EqualError(t, err, mismatchKeyLength()) tree.root = fakeNode{err: fake.GetError()} _, err = tree.Search([]byte("A"), nil, nil) @@ -72,7 +73,7 @@ func TestTree_Insert(t *testing.T) { require.Equal(t, 1, tree.Len()) err = tree.Insert(make([]byte, MaxDepth+1), nil, &fakeBucket{}) - require.EqualError(t, err, "mismatch key length 33 > 32") + require.EqualError(t, err, mismatchKeyLength()) tree.root = fakeNode{err: fake.GetError()} err = tree.Insert([]byte("A"), []byte("B"), nil) @@ -146,7 +147,7 @@ func TestTree_Delete(t *testing.T) { require.IsType(t, (*EmptyNode)(nil), tree.root) err = tree.Delete(make([]byte, MaxDepth+1), &fakeBucket{}) - require.EqualError(t, err, "mismatch key length 33 > 32") + require.EqualError(t, err, mismatchKeyLength()) tree.root = fakeNode{err: fake.GetError()} err = tree.Delete([]byte("A"), nil) @@ -629,6 +630,10 @@ func TestNodeFactory_Deserialize(t *testing.T) { // ----------------------------------------------------------------------------- // Utility functions +func mismatchKeyLength() string { + return fmt.Sprintf("mismatch key length %v > %v", MaxDepth+1, MaxDepth) +} + func makeBucket(t *testing.T) *fakeBucket { emptyNode, err := NewEmptyNode(1, big.NewInt(0)).Serialize(testCtx) require.NoError(t, err) From 2175b9d78a055b242a6d06f7e457fb9960e1ac93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierluca=20Bors=C3=B2?= Date: Fri, 4 Aug 2023 15:20:07 +0200 Subject: [PATCH 2/6] Crypto now has a generic hash factory --- core/ordering/cosipbft/cosipbft.go | 35 ++++++++++++----- core/ordering/cosipbft/cosipbft_test.go | 16 ++++---- core/ordering/cosipbft/pbft/pbft.go | 9 ++++- core/ordering/cosipbft/pbft/pbft_test.go | 4 +- core/ordering/cosipbft/types/block.go | 4 +- core/ordering/cosipbft/types/chain.go | 8 ++-- core/ordering/pow/block.go | 2 +- core/ordering/pow/block_test.go | 6 +-- core/ordering/pow/pow.go | 2 +- core/store/hashtree/binprefix/binprefix.go | 2 +- .../hashtree/binprefix/binprefix_test.go | 4 +- core/store/hashtree/binprefix/disk_test.go | 7 ++-- core/store/hashtree/binprefix/tree.go | 33 ++++++++++++---- core/store/hashtree/binprefix/tree_test.go | 14 +++---- core/txn/signed/signed.go | 9 +++-- core/txn/signed/signed_test.go | 2 +- core/validation/simple/simple.go | 2 +- crypto/{mod.go => crypto.go} | 0 crypto/hash.go | 38 +++++++++++++++---- crypto/hash_test.go | 14 ++++++- mino/minogrpc/certs/mem.go | 2 +- 21 files changed, 146 insertions(+), 67 deletions(-) rename crypto/{mod.go => crypto.go} (100%) diff --git a/core/ordering/cosipbft/cosipbft.go b/core/ordering/cosipbft/cosipbft.go index 647877afa..bcf350691 100644 --- a/core/ordering/cosipbft/cosipbft.go +++ b/core/ordering/cosipbft/cosipbft.go @@ -159,7 +159,7 @@ type ServiceParam struct { // NewService starts a new ordering service. func NewService(param ServiceParam, opts ...ServiceOption) (*Service, error) { tmpl := serviceTemplate{ - hashFac: crypto.NewSha256Factory(), + hashFac: crypto.NewHashFactory(crypto.Sha256), genesis: blockstore.NewGenesisStore(), blocks: blockstore.NewInMemory(), } @@ -173,7 +173,8 @@ func NewService(param ServiceParam, opts ...ServiceOption) (*Service, error) { proc.blocks = tmpl.blocks proc.genesis = tmpl.genesis proc.pool = param.Pool - proc.rosterFac = authority.NewFactory(param.Mino.GetAddressFactory(), param.Cosi.GetPublicKeyFactory()) + proc.rosterFac = authority.NewFactory(param.Mino.GetAddressFactory(), + param.Cosi.GetPublicKeyFactory()) proc.tree = blockstore.NewTreeCache(param.Tree) proc.access = param.Access proc.logger = dela.Logger.With().Str("addr", param.Mino.GetAddress().String()).Logger() @@ -193,7 +194,8 @@ func NewService(param ServiceParam, opts ...ServiceOption) (*Service, error) { proc.pbftsm = pbft.NewStateMachine(pcparam) blockFac := types.NewBlockFactory(param.Validation.GetFactory()) - csFac := authority.NewChangeSetFactory(param.Mino.GetAddressFactory(), param.Cosi.GetPublicKeyFactory()) + csFac := authority.NewChangeSetFactory(param.Mino.GetAddressFactory(), + param.Cosi.GetPublicKeyFactory()) linkFac := types.NewLinkFactory(blockFac, param.Cosi.GetSignatureFactory(), csFac) chainFac := types.NewChainFactory(linkFac) @@ -491,15 +493,21 @@ func (s *Service) doRound(ctx context.Context) error { } if s.me.Equal(leader) { - s.logger.Debug().Msgf("Starting a leader round with a %.1f seconds timeout", timeout.Seconds()) + s.logger.Debug().Msgf("Starting a leader round with a %.1f seconds timeout", + timeout.Seconds()) return s.doLeaderRound(ctx, roster, timeout) } - s.logger.Debug().Msgf("Starting a follower round with a %.1f seconds timeout", timeout.Seconds()) + s.logger.Debug().Msgf("Starting a follower round with a %.1f seconds timeout", + timeout.Seconds()) return s.doFollowerRound(ctx, roster) } -func (s *Service) doLeaderRound(ctx context.Context, roster authority.Authority, timeout time.Duration) error { +func (s *Service) doLeaderRound( + ctx context.Context, + roster authority.Authority, + timeout time.Duration, +) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() @@ -507,7 +515,8 @@ func (s *Service) doLeaderRound(ctx context.Context, roster authority.Authority, // Send a synchronization to the roster so that they can learn about the // latest block of the chain. - err := s.sync.Sync(ctx, roster, blocksync.Config{MinHard: threshold.ByzantineThreshold(roster.Len())}) + err := s.sync.Sync(ctx, roster, + blocksync.Config{MinHard: threshold.ByzantineThreshold(roster.Len())}) if err != nil { return xerrors.Errorf("sync failed: %v", err) } @@ -556,7 +565,8 @@ func (s *Service) doFollowerRound(ctx context.Context, roster authority.Authorit for resp := range resps { _, err = resp.GetMessageOrError() if err != nil { - s.logger.Warn().Err(err).Str("to", resp.GetFrom().String()).Msg("view propagation failure") + s.logger.Warn().Err(err).Str("to", + resp.GetFrom().String()).Msg("view propagation failure") } } @@ -712,7 +722,11 @@ func (s *Service) prepareViews() map[mino.Address]types.ViewMessage { return msgs } -func (s *Service) prepareData(txs []txn.Transaction) (data validation.Result, id types.Digest, err error) { +func (s *Service) prepareData(txs []txn.Transaction) ( + data validation.Result, + id types.Digest, + err error, +) { var stageTree hashtree.StagingTree stageTree, err = s.tree.Get().Stage(func(snap store.Snapshot) error { @@ -747,7 +761,8 @@ func (s *Service) wakeUp(ctx context.Context, ro authority.Authority) error { return xerrors.Errorf("read genesis failed: %v", err) } - resps, err := s.rpc.Call(ctx, types.NewGenesisMessage(genesis), mino.NewAddresses(changeset.GetNewAddresses()...)) + resps, err := s.rpc.Call(ctx, types.NewGenesisMessage(genesis), + mino.NewAddresses(changeset.GetNewAddresses()...)) if err != nil { return xerrors.Errorf("rpc failed: %v", err) } diff --git a/core/ordering/cosipbft/cosipbft_test.go b/core/ordering/cosipbft/cosipbft_test.go index ac55338bb..c274e607a 100644 --- a/core/ordering/cosipbft/cosipbft_test.go +++ b/core/ordering/cosipbft/cosipbft_test.go @@ -260,7 +260,7 @@ func TestService_Setup(t *testing.T) { srvc := &Service{processor: newProcessor()} srvc.rpc = rpc - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.tree = blockstore.NewTreeCache(fakeTree{}) srvc.genesis = blockstore.NewGenesisStore() srvc.access = fakeAccess{} @@ -623,7 +623,7 @@ func TestService_DoPBFT(t *testing.T) { srvc.val = fakeValidation{} srvc.blocks = blockstore.NewInMemory() srvc.genesis = blockstore.NewGenesisStore() - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.pbftsm = fakeSM{} srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) srvc.actor = fakeCosiActor{} @@ -707,7 +707,7 @@ func TestService_FailPrepare_DoPBFT(t *testing.T) { srvc.tree = blockstore.NewTreeCache(fakeTree{}) srvc.pbftsm = fakeSM{err: fake.GetError()} srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.blocks = blockstore.NewInMemory() srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) @@ -725,7 +725,7 @@ func TestService_FailReadRoster_DoPBFT(t *testing.T) { srvc.tree = blockstore.NewTreeCache(fakeTree{err: fake.GetError()}) srvc.pbftsm = fakeSM{} srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.blocks = blockstore.NewInMemory() srvc.pool.Add(makeTx(t, 0, fake.NewSigner())) @@ -743,7 +743,7 @@ func TestService_FailPrepareSig_DoPBFT(t *testing.T) { srvc.tree = blockstore.NewTreeCache(fakeTree{}) srvc.pbftsm = fakeSM{} srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.blocks = blockstore.NewInMemory() srvc.actor = fakeCosiActor{err: fake.GetError()} srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) @@ -763,7 +763,7 @@ func TestService_FailCommitSign_DoPBFT(t *testing.T) { srvc.tree = blockstore.NewTreeCache(fakeTree{}) srvc.pbftsm = fakeSM{} srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.blocks = blockstore.NewInMemory() srvc.actor = fakeCosiActor{ err: fake.GetError(), @@ -786,7 +786,7 @@ func TestService_FailPropagation_DoPBFT(t *testing.T) { srvc.tree = blockstore.NewTreeCache(fakeTree{}) srvc.pbftsm = fakeSM{} srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.blocks = blockstore.NewInMemory() srvc.actor = fakeCosiActor{} srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) @@ -810,7 +810,7 @@ func TestService_FailWakeUp_DoPBFT(t *testing.T) { srvc.tree = blockstore.NewTreeCache(fakeTree{}) srvc.pbftsm = fakeSM{} srvc.pool = mem.NewPool() - srvc.hashFactory = crypto.NewSha256Factory() + srvc.hashFactory = crypto.NewHashFactory(crypto.Sha256) srvc.blocks = blockstore.NewInMemory() srvc.actor = fakeCosiActor{} srvc.rosterFac = authority.NewFactory(fake.AddressFactory{}, fake.PublicKeyFactory{}) diff --git a/core/ordering/cosipbft/pbft/pbft.go b/core/ordering/cosipbft/pbft/pbft.go index bf31fe5b4..f540cdaab 100644 --- a/core/ordering/cosipbft/pbft/pbft.go +++ b/core/ordering/cosipbft/pbft/pbft.go @@ -219,7 +219,7 @@ func NewStateMachine(param StateMachineParam) StateMachine { return &pbftsm{ logger: param.Logger, watcher: core.NewWatcher(), - hashFac: crypto.NewSha256Factory(), + hashFac: crypto.NewHashFactory(crypto.Sha256), val: param.Validation, verifierFac: param.VerifierFactory, signer: param.Signer, @@ -622,7 +622,12 @@ func (m *pbftsm) Watch(ctx context.Context) <-chan State { return ch } -func (m *pbftsm) verifyPrepare(tree hashtree.Tree, block types.Block, r *round, ro authority.Authority) error { +func (m *pbftsm) verifyPrepare( + tree hashtree.Tree, + block types.Block, + r *round, + ro authority.Authority, +) error { stageTree, err := tree.Stage(func(snap store.Snapshot) error { txs := block.GetTransactions() rejected := 0 diff --git a/core/ordering/cosipbft/pbft/pbft_test.go b/core/ordering/cosipbft/pbft/pbft_test.go index 230977df4..b75d1df0c 100644 --- a/core/ordering/cosipbft/pbft/pbft_test.go +++ b/core/ordering/cosipbft/pbft/pbft_test.go @@ -272,7 +272,7 @@ func TestStateMachine_FailReadRosterInStageTree_Prepare(t *testing.T) { }, genesis: blockstore.NewGenesisStore(), blocks: blockstore.NewInMemory(), - hashFac: crypto.NewSha256Factory(), + hashFac: crypto.NewHashFactory(crypto.Sha256), watcher: core.NewWatcher(), } @@ -564,7 +564,7 @@ func TestStateMachine_FailStoreBlock_Finalize(t *testing.T) { genesis: blockstore.NewGenesisStore(), blocks: badBlockStore{}, db: db, - hashFac: crypto.NewSha256Factory(), + hashFac: crypto.NewHashFactory(crypto.Sha256), round: round{ prepareSig: fake.Signature{}, tree: tree.(hashtree.StagingTree), diff --git a/core/ordering/cosipbft/types/block.go b/core/ordering/cosipbft/types/block.go index d7a591256..cacfd72fb 100644 --- a/core/ordering/cosipbft/types/block.go +++ b/core/ordering/cosipbft/types/block.go @@ -89,7 +89,7 @@ func NewGenesis(ro authority.Authority, opts ...GenesisOption) (Genesis, error) roster: ro, treeRoot: Digest{}, }, - hashFactory: crypto.NewSha256Factory(), + hashFactory: crypto.NewHashFactory(crypto.Sha256), } for _, opt := range opts { @@ -231,7 +231,7 @@ func NewBlock(data validation.Result, opts ...BlockOption) (Block, error) { data: data, treeRoot: Digest{}, }, - hashFactory: crypto.NewSha256Factory(), + hashFactory: crypto.NewHashFactory(crypto.Sha256), } for _, opt := range opts { diff --git a/core/ordering/cosipbft/types/chain.go b/core/ordering/cosipbft/types/chain.go index 20b486f6a..dd324f615 100644 --- a/core/ordering/cosipbft/types/chain.go +++ b/core/ordering/cosipbft/types/chain.go @@ -85,7 +85,7 @@ func NewForwardLink(from, to Digest, opts ...LinkOption) (Link, error) { to: to, changeset: authority.NewChangeSet(), }, - hashFac: crypto.NewSha256Factory(), + hashFac: crypto.NewHashFactory(crypto.Sha256), } for _, opt := range opts { @@ -230,8 +230,10 @@ type linkFac struct { } // NewLinkFactory creates a new block link factory. -func NewLinkFactory(blockFac serde.Factory, - sigFac crypto.SignatureFactory, csFac authority.ChangeSetFactory) LinkFactory { +func NewLinkFactory( + blockFac serde.Factory, + sigFac crypto.SignatureFactory, csFac authority.ChangeSetFactory, +) LinkFactory { return linkFac{ blockFac: blockFac, diff --git a/core/ordering/pow/block.go b/core/ordering/pow/block.go index 282a5d642..c0cccdc0f 100644 --- a/core/ordering/pow/block.go +++ b/core/ordering/pow/block.go @@ -71,7 +71,7 @@ func NewBlock(ctx context.Context, data validation.Result, opts ...BlockOption) Block: Block{ data: data, }, - hashFactory: crypto.NewSha256Factory(), + hashFactory: crypto.NewHashFactory(crypto.Sha256), difficulty: Difficulty, } diff --git a/core/ordering/pow/block_test.go b/core/ordering/pow/block_test.go index 108cd2d8d..6779c0552 100644 --- a/core/ordering/pow/block_test.go +++ b/core/ordering/pow/block_test.go @@ -33,11 +33,11 @@ func TestBlock_Prepare(t *testing.T) { ctx := context.Background() - err := block.prepare(ctx, crypto.NewSha256Factory(), 1) + err := block.prepare(ctx, crypto.NewHashFactory(crypto.Sha256), 1) require.NoError(t, err) require.Len(t, block.hash, 32) - err = block.prepare(ctx, crypto.NewSha256Factory(), 0) + err = block.prepare(ctx, crypto.NewHashFactory(crypto.Sha256), 0) require.NoError(t, err) require.Len(t, block.hash, 32) @@ -48,7 +48,7 @@ func TestBlock_Prepare(t *testing.T) { require.EqualError(t, err, fake.Err("failed to write root")) block.data = fakeData{err: fake.GetError()} - err = block.prepare(ctx, crypto.NewSha256Factory(), 0) + err = block.prepare(ctx, crypto.NewHashFactory(crypto.Sha256), 0) require.EqualError(t, err, fake.Err("failed to fingerprint data")) block.data = fakeData{} diff --git a/core/ordering/pow/pow.go b/core/ordering/pow/pow.go index 91c848745..266e1bec0 100644 --- a/core/ordering/pow/pow.go +++ b/core/ordering/pow/pow.go @@ -46,7 +46,7 @@ func NewService(pool pool.Pool, val validation.Service, trie hashtree.Tree) *Ser pool: pool, validation: val, epochs: []epoch{{store: trie}}, - hashFactory: crypto.NewSha256Factory(), + hashFactory: crypto.NewHashFactory(crypto.Sha256), difficulty: 1, watcher: core.NewWatcher(), } diff --git a/core/store/hashtree/binprefix/binprefix.go b/core/store/hashtree/binprefix/binprefix.go index de77dca39..eb9842774 100644 --- a/core/store/hashtree/binprefix/binprefix.go +++ b/core/store/hashtree/binprefix/binprefix.go @@ -65,7 +65,7 @@ func NewMerkleTree(db kv.DB, nonce Nonce) *MerkleTree { tree: NewTree(nonce), db: db, bucket: []byte("hashtree"), - hashFactory: crypto.NewSha256Factory(), + hashFactory: crypto.NewHashFactory(crypto.Sha256), } } diff --git a/core/store/hashtree/binprefix/binprefix_test.go b/core/store/hashtree/binprefix/binprefix_test.go index 701606786..0b7d4b38f 100644 --- a/core/store/hashtree/binprefix/binprefix_test.go +++ b/core/store/hashtree/binprefix/binprefix_test.go @@ -146,7 +146,7 @@ func TestMerkleTree_Random_IntegrationTest(t *testing.T) { require.NoError(t, err) require.Equal(t, value, path.GetValue()) - root, err := path.(Path).computeRoot(crypto.NewSha256Factory()) + root, err := path.(Path).computeRoot(crypto.NewHashFactory(crypto.Sha256)) require.NoError(t, err) require.Equal(t, tree.GetRoot(), root) } @@ -162,7 +162,7 @@ func TestMerkleTree_Random_IntegrationTest(t *testing.T) { require.NoError(t, err) require.Equal(t, value, path.GetValue()) - root, err := path.(Path).computeRoot(crypto.NewSha256Factory()) + root, err := path.(Path).computeRoot(crypto.NewHashFactory(crypto.Sha256)) require.NoError(t, err) require.Equal(t, newTree.GetRoot(), root) } diff --git a/core/store/hashtree/binprefix/disk_test.go b/core/store/hashtree/binprefix/disk_test.go index c0d2401b9..b0ac65f7b 100644 --- a/core/store/hashtree/binprefix/disk_test.go +++ b/core/store/hashtree/binprefix/disk_test.go @@ -119,7 +119,8 @@ func TestDiskNode_Prepare(t *testing.T) { bucket := newFakeBucket(node.prepareKey(big.NewInt(0)), data) - hash, err := node.Prepare([]byte{1}, big.NewInt(0), bucket, crypto.NewSha256Factory()) + hash, err := node.Prepare([]byte{1}, big.NewInt(0), bucket, + crypto.NewHashFactory(crypto.Sha256)) require.NoError(t, err) require.Len(t, hash, 32) require.Equal(t, hash, node.hash) @@ -129,7 +130,7 @@ func TestDiskNode_Prepare(t *testing.T) { require.EqualError(t, err, "failed to load node: prefix 0 (depth 0) not in database") bucket.errSet = fake.GetError() - _, err = node.Prepare([]byte{}, big.NewInt(0), bucket, crypto.NewSha256Factory()) + _, err = node.Prepare([]byte{}, big.NewInt(0), bucket, crypto.NewHashFactory(crypto.Sha256)) require.EqualError(t, err, fake.Err("failed to store node: failed to set key")) inter := NewInteriorNode(0, big.NewInt(0)) @@ -138,7 +139,7 @@ func TestDiskNode_Prepare(t *testing.T) { bucket = newFakeBucket(node.prepareKey(big.NewInt(0)), data) - _, err = node.Prepare([]byte{}, big.NewInt(0), bucket, crypto.NewSha256Factory()) + _, err = node.Prepare([]byte{}, big.NewInt(0), bucket, crypto.NewHashFactory(crypto.Sha256)) require.EqualError(t, err, "failed to load node: prefix 0 (depth 1) not in database") } diff --git a/core/store/hashtree/binprefix/tree.go b/core/store/hashtree/binprefix/tree.go index 29489969a..3879af47b 100644 --- a/core/store/hashtree/binprefix/tree.go +++ b/core/store/hashtree/binprefix/tree.go @@ -272,7 +272,13 @@ func (t *Tree) Persist(b kv.Bucket) error { }) } -func (t *Tree) toDisk(depth uint16, prefix *big.Int, node TreeNode, b kv.Bucket, clean, store bool) error { +func (t *Tree) toDisk( + depth uint16, + prefix *big.Int, + node TreeNode, + b kv.Bucket, + clean, store bool, +) error { disknode := NewDiskNode(depth, nil, t.context, t.factory) if clean { @@ -370,8 +376,10 @@ func (n *EmptyNode) Delete(key *big.Int, b kv.Bucket) (TreeNode, error) { // Prepare implements binprefix.TreeNode. It updates the hash of the node if not // already set and returns the digest. -func (n *EmptyNode) Prepare(nonce []byte, - prefix *big.Int, b kv.Bucket, fac crypto.HashFactory) ([]byte, error) { +func (n *EmptyNode) Prepare( + nonce []byte, + prefix *big.Int, b kv.Bucket, fac crypto.HashFactory, +) ([]byte, error) { if len(n.hash) > 0 { // Hash is already calculated so we can skip and return. @@ -452,7 +460,12 @@ func NewInteriorNode(depth uint16, prefix *big.Int) *InteriorNode { // NewInteriorNodeWithChildren creates a new interior node with the two given // children. -func NewInteriorNodeWithChildren(depth uint16, prefix *big.Int, hash []byte, left, right TreeNode) *InteriorNode { +func NewInteriorNodeWithChildren( + depth uint16, + prefix *big.Int, + hash []byte, + left, right TreeNode, +) *InteriorNode { return &InteriorNode{ depth: depth, prefix: prefix, @@ -577,8 +590,10 @@ func (n *InteriorNode) load(node TreeNode, key *big.Int, bit uint, b kv.Bucket) // Prepare implements binprefix.TreeNode. It updates the hash of the node if not // already set and returns the digest. -func (n *InteriorNode) Prepare(nonce []byte, - prefix *big.Int, b kv.Bucket, fac crypto.HashFactory) ([]byte, error) { +func (n *InteriorNode) Prepare( + nonce []byte, + prefix *big.Int, b kv.Bucket, fac crypto.HashFactory, +) ([]byte, error) { if len(n.hash) > 0 { // Hash is already calculated so we can skip and return. @@ -755,8 +770,10 @@ func (n *LeafNode) Delete(key *big.Int, b kv.Bucket) (TreeNode, error) { // Prepare implements binprefix.TreeNode. It updates the hash of the node if not // already set and returns the digest. -func (n *LeafNode) Prepare(nonce []byte, - prefix *big.Int, b kv.Bucket, fac crypto.HashFactory) ([]byte, error) { +func (n *LeafNode) Prepare( + nonce []byte, + prefix *big.Int, b kv.Bucket, fac crypto.HashFactory, +) ([]byte, error) { if len(n.hash) > 0 { // Hash is already calculated so we can skip and return. diff --git a/core/store/hashtree/binprefix/tree_test.go b/core/store/hashtree/binprefix/tree_test.go index fc5ab4f88..fecc85718 100644 --- a/core/store/hashtree/binprefix/tree_test.go +++ b/core/store/hashtree/binprefix/tree_test.go @@ -82,7 +82,7 @@ func TestTree_Insert(t *testing.T) { func TestTree_Insert_UpdateLeaf(t *testing.T) { bucket := &fakeBucket{} - hashFactory := crypto.NewSha256Factory() + hashFactory := crypto.NewHashFactory(crypto.Sha256) tree := NewTree(Nonce{}) for i := 0; i <= math.MaxUint8; i++ { @@ -248,8 +248,8 @@ func TestTree_Clone(t *testing.T) { clone := tree.Clone() require.Equal(t, tree.Len(), clone.Len()) - require.NoError(t, tree.CalculateRoot(crypto.NewSha256Factory(), &fakeBucket{})) - require.NoError(t, clone.CalculateRoot(crypto.NewSha256Factory(), &fakeBucket{})) + require.NoError(t, tree.CalculateRoot(crypto.NewHashFactory(crypto.Sha256), &fakeBucket{})) + require.NoError(t, clone.CalculateRoot(crypto.NewHashFactory(crypto.Sha256), &fakeBucket{})) require.Equal(t, tree.root.GetHash(), clone.root.GetHash()) } @@ -295,7 +295,7 @@ func TestEmptyNode_Delete(t *testing.T) { func TestEmptyNode_Prepare(t *testing.T) { node := NewEmptyNode(3, big.NewInt(0)) - fac := crypto.NewSha256Factory() + fac := crypto.NewHashFactory(crypto.Sha256) hash, err := node.Prepare([]byte("nonce"), new(big.Int), nil, fac) require.NoError(t, err) @@ -442,12 +442,12 @@ func TestInteriorNode_Prepare(t *testing.T) { node.hash = nil node.left = fakeNode{err: xerrors.New("bad node error")} - _, err = node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewSha256Factory()) + _, err = node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewHashFactory(crypto.Sha256)) require.EqualError(t, err, "bad node error") node.left = fakeNode{} node.right = fakeNode{err: xerrors.New("bad node error")} - _, err = node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewSha256Factory()) + _, err = node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewHashFactory(crypto.Sha256)) require.EqualError(t, err, "bad node error") node.right = fakeNode{} @@ -505,7 +505,7 @@ func TestLeafNode_GetHash(t *testing.T) { require.Empty(t, node.GetHash()) - _, err := node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewSha256Factory()) + _, err := node.Prepare([]byte{1}, big.NewInt(2), nil, crypto.NewHashFactory(crypto.Sha256)) require.NoError(t, err) require.Len(t, node.GetHash(), 32) } diff --git a/core/txn/signed/signed.go b/core/txn/signed/signed.go index 657878989..ef0d1d9b4 100644 --- a/core/txn/signed/signed.go +++ b/core/txn/signed/signed.go @@ -74,14 +74,17 @@ func WithHashFactory(f crypto.HashFactory) TransactionOption { } // NewTransaction creates a new transaction with the provided nonce. -func NewTransaction(nonce uint64, pk crypto.PublicKey, opts ...TransactionOption) (*Transaction, error) { +func NewTransaction(nonce uint64, pk crypto.PublicKey, opts ...TransactionOption) ( + *Transaction, + error, +) { tmpl := template{ Transaction: Transaction{ nonce: nonce, pubkey: pk, args: make(map[string][]byte), }, - hashFactory: crypto.NewSha256Factory(), + hashFactory: crypto.NewHashFactory(crypto.Sha256), } for _, opt := range opts { @@ -289,7 +292,7 @@ func NewManager(signer crypto.Signer, client Client) *TransactionManager { client: client, signer: signer, nonce: 0, - hashFac: crypto.NewSha256Factory(), + hashFac: crypto.NewHashFactory(crypto.Sha256), } } diff --git a/core/txn/signed/signed_test.go b/core/txn/signed/signed_test.go index d9465071a..b8c47227a 100644 --- a/core/txn/signed/signed_test.go +++ b/core/txn/signed/signed_test.go @@ -172,7 +172,7 @@ func TestManager_Make(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "failed to create tx: ") - mgr.hashFac = crypto.NewSha256Factory() + mgr.hashFac = crypto.NewHashFactory(crypto.Sha256) mgr.signer = fake.NewBadSigner() _, err = mgr.Make() require.EqualError(t, err, fake.Err("failed to sign: signer")) diff --git a/core/validation/simple/simple.go b/core/validation/simple/simple.go index 5f901a5bf..36433e3bc 100644 --- a/core/validation/simple/simple.go +++ b/core/validation/simple/simple.go @@ -32,7 +32,7 @@ func NewService(exec execution.Service, f txn.Factory) Service { return Service{ execution: exec, fac: NewResultFactory(f), - hashFac: crypto.NewSha256Factory(), + hashFac: crypto.NewHashFactory(crypto.Sha256), } } diff --git a/crypto/mod.go b/crypto/crypto.go similarity index 100% rename from crypto/mod.go rename to crypto/crypto.go diff --git a/crypto/hash.go b/crypto/hash.go index 9d8940ea2..bb586b100 100644 --- a/crypto/hash.go +++ b/crypto/hash.go @@ -7,19 +7,43 @@ package crypto import ( "crypto/sha256" "hash" + + "golang.org/x/crypto/sha3" +) + +type HashAlgorithm int + +const ( + Sha256 HashAlgorithm = iota + Sha3_224 ) -// Sha256Factory is a hash factory that is using SHA256. +// hashFactory is a hash factory that is using SHA algorithms. // // - implements crypto.HashFactory -type Sha256Factory struct{} +type hashFactory struct { + hashType HashAlgorithm +} // NewSha256Factory returns a new instance of the factory. -func NewSha256Factory() Sha256Factory { - return Sha256Factory{} +// Deprecated: use NewHashFactory instead. +func NewSha256Factory() hashFactory { + return hashFactory{Sha256} +} + +// NewHashFactory returns a new instance of the factory. +func NewHashFactory(a HashAlgorithm) hashFactory { + return hashFactory{a} } -// New implements crypto.HashFactory. It returns a new SHA256 instance. -func (f Sha256Factory) New() hash.Hash { - return sha256.New() +// New implements crypto.HashFactory. It returns a new Hash instance. +func (f hashFactory) New() hash.Hash { + switch f.hashType { + case Sha256: + return sha256.New() + case Sha3_224: + return sha3.New224() + default: + panic("unknown hash type") + } } diff --git a/crypto/hash_test.go b/crypto/hash_test.go index bf17ea122..194e4c28b 100644 --- a/crypto/hash_test.go +++ b/crypto/hash_test.go @@ -7,6 +7,18 @@ import ( ) func TestSha256Factory_New(t *testing.T) { - factory := NewSha256Factory() + factory := NewHashFactory(Sha256) require.NotNil(t, factory.New()) } + +func TestSha3224Factory_New(t *testing.T) { + factory := NewHashFactory(Sha3_224) + require.NotNil(t, factory.New()) +} + +func TestUnsupportedPanics(t *testing.T) { + require.Panics(t, func() { + factory := NewHashFactory(3) + factory.New() + }) +} diff --git a/mino/minogrpc/certs/mem.go b/mino/minogrpc/certs/mem.go index 942e6b4c1..e460e2c8a 100644 --- a/mino/minogrpc/certs/mem.go +++ b/mino/minogrpc/certs/mem.go @@ -28,7 +28,7 @@ type InMemoryStore struct { func NewInMemoryStore() *InMemoryStore { return &InMemoryStore{ certs: &sync.Map{}, - hashFactory: crypto.NewSha256Factory(), + hashFactory: crypto.NewHashFactory(crypto.Sha256), } } From 972309e2ec6e9b99350a9cf00ac648cb94bc620c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierluca=20Bors=C3=B2?= Date: Fri, 4 Aug 2023 17:28:18 +0200 Subject: [PATCH 3/6] Added prefix for value contract storage --- contracts/value/value.go | 25 ++++++++++++++++++++----- contracts/value/value_test.go | 12 ++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/contracts/value/value.go b/contracts/value/value.go index b0fb0a8aa..8cc6ea71c 100644 --- a/contracts/value/value.go +++ b/contracts/value/value.go @@ -44,6 +44,9 @@ const ( // credentialAllCommand defines the credential command that is allowed to // perform all commands. credentialAllCommand = "all" + + // contractKeyPrefix is used to prefix keys in the K/V store. + contractKeyPrefix = "VAL" ) // Command defines a type of command for the value contract @@ -172,14 +175,17 @@ func (c valueCommand) write(snap store.Snapshot, step execution.Step) error { return xerrors.Errorf("'%s' not found in tx arg", ValueArg) } - err := snap.Set(key, value) + snapKey := prefix(key) + err := snap.Set(snapKey, value) if err != nil { return xerrors.Errorf("failed to set value: %v", err) } c.index[string(key)] = struct{}{} - dela.Logger.Info().Str("contract", ContractName).Msgf("setting %x=%s", key, value) + dela.Logger.Info(). + Str("contract", ContractName). + Msgf("setting value %x=%s", snapKey, value) return nil } @@ -191,7 +197,7 @@ func (c valueCommand) read(snap store.Snapshot, step execution.Step) error { return xerrors.Errorf("'%s' not found in tx arg", KeyArg) } - val, err := snap.Get(key) + val, err := snap.Get(prefix(key)) if err != nil { return xerrors.Errorf("failed to get key '%s': %v", key, err) } @@ -208,13 +214,18 @@ func (c valueCommand) delete(snap store.Snapshot, step execution.Step) error { return xerrors.Errorf("'%s' not found in tx arg", KeyArg) } - err := snap.Delete(key) + snapKey := prefix(key) + err := snap.Delete(snapKey) if err != nil { return xerrors.Errorf("failed to delete key '%x': %v", key, err) } delete(c.index, string(key)) + dela.Logger.Info(). + Str("contract", ContractName). + Msgf("deleting value %x", snapKey) + return nil } @@ -223,7 +234,7 @@ func (c valueCommand) list(snap store.Snapshot) error { res := []string{} for k := range c.index { - v, err := snap.Get([]byte(k)) + v, err := snap.Get(prefix([]byte(k))) if err != nil { return xerrors.Errorf("failed to get key '%s': %v", k, err) } @@ -247,3 +258,7 @@ func (h infoLog) Write(p []byte) (int, error) { return len(p), nil } + +func prefix(key []byte) []byte { + return append([]byte(contractKeyPrefix), key...) +} diff --git a/contracts/value/value_test.go b/contracts/value/value_test.go index 00b3ed75a..c358793cd 100644 --- a/contracts/value/value_test.go +++ b/contracts/value/value_test.go @@ -76,7 +76,7 @@ func TestCommand_Write(t *testing.T) { _, found = contract.index["dummy"] require.True(t, found) - res, err := snap.Get([]byte("dummy")) + res, err := snap.Get(prefix([]byte("dummy"))) require.NoError(t, err) require.Equal(t, "value", string(res)) } @@ -98,7 +98,7 @@ func TestCommand_Read(t *testing.T) { require.EqualError(t, err, fake.Err("failed to get key 'dummy'")) snap := fake.NewSnapshot() - snap.Set(key, []byte("value")) + snap.Set(prefix(key), []byte("value")) buf := &bytes.Buffer{} cmd.Contract.printer = buf @@ -127,13 +127,13 @@ func TestCommand_Delete(t *testing.T) { require.EqualError(t, err, fake.Err("failed to delete key '"+keyHex+"'")) snap := fake.NewSnapshot() - snap.Set(key, []byte("value")) + snap.Set(prefix(key), []byte("value")) contract.index[keyStr] = struct{}{} err = cmd.delete(snap, makeStep(t, KeyArg, keyStr)) require.NoError(t, err) - res, err := snap.Get(key) + res, err := snap.Get(prefix(key)) require.Nil(t, err) require.Nil(t, res) @@ -158,8 +158,8 @@ func TestCommand_List(t *testing.T) { } snap := fake.NewSnapshot() - snap.Set([]byte(key1), []byte("value1")) - snap.Set([]byte(key2), []byte("value2")) + snap.Set(prefix([]byte(key1)), []byte("value1")) + snap.Set(prefix([]byte(key2)), []byte("value2")) err := cmd.list(snap) require.NoError(t, err) From d0c8112585a97d995a7c037a2bada5d198eebe5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierluca=20Bors=C3=B2?= Date: Fri, 4 Aug 2023 18:29:34 +0200 Subject: [PATCH 4/6] Updated go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index def79b507..1a0070bfc 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/urfave/cli/v2 v2.2.0 go.dedis.ch/kyber/v3 v3.0.14 go.etcd.io/bbolt v1.3.5 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 golang.org/x/net v0.6.0 golang.org/x/tools v0.6.0 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 @@ -43,7 +44,6 @@ require ( go.dedis.ch/fixbuf v1.0.3 // indirect go.dedis.ch/protobuf v1.0.11 // indirect go.uber.org/atomic v1.7.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect From 99f2c2558a72cb22af3c6a58d684048c43212c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierluca=20Bors=C3=B2?= Date: Fri, 4 Aug 2023 19:10:08 +0200 Subject: [PATCH 5/6] Fixed tests --- contracts/value/value.go | 2 +- test/integration_test.go | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/contracts/value/value.go b/contracts/value/value.go index 8cc6ea71c..6e1a6b831 100644 --- a/contracts/value/value.go +++ b/contracts/value/value.go @@ -46,7 +46,7 @@ const ( credentialAllCommand = "all" // contractKeyPrefix is used to prefix keys in the K/V store. - contractKeyPrefix = "VAL" + contractKeyPrefix = "VALU" // intentionally 4 bytes only, not a typo! ) // Command defines a type of command for the value contract diff --git a/test/integration_test.go b/test/integration_test.go index e4882e708..3a1441eef 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -87,7 +87,7 @@ func getTest[T require.TestingT](numNode, numTx int) func(t T) { require.NoError(t, err) for i := 0; i < numTx; i++ { - key := make([]byte, 32) + key := make([]byte, 28) // only 224 bits are available _, err = rand.Read(key) require.NoError(t, err) @@ -102,7 +102,7 @@ func getTest[T require.TestingT](numNode, numTx int) func(t T) { err = addAndWait(t, timeout, manager, nodes[0].(cosiDelaNode), args...) require.NoError(t, err) - proof, err := nodes[0].GetOrdering().GetProof(key) + proof, err := nodes[0].GetOrdering().GetProof(append([]byte("VALU"), key...)) require.NoError(t, err) require.Equal(t, []byte("value1"), proof.GetValue()) } @@ -112,7 +112,13 @@ func getTest[T require.TestingT](numNode, numTx int) func(t T) { // ----------------------------------------------------------------------------- // Utility functions -func addAndWait(t require.TestingT, to time.Duration, manager txn.Manager, node cosiDelaNode, args ...txn.Arg) error { +func addAndWait( + t require.TestingT, + to time.Duration, + manager txn.Manager, + node cosiDelaNode, + args ...txn.Arg, +) error { manager.Sync() tx, err := manager.Make(args...) From ceea23f4929c4d75cf47bfb16619183f5d5c2056 Mon Sep 17 00:00:00 2001 From: Jean Viaene Date: Mon, 7 Aug 2023 12:07:07 +0200 Subject: [PATCH 6/6] Keep deprecated version for backward compatibility. --- crypto/hash_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crypto/hash_test.go b/crypto/hash_test.go index 194e4c28b..9e61bcbd5 100644 --- a/crypto/hash_test.go +++ b/crypto/hash_test.go @@ -6,6 +6,11 @@ import ( "github.com/stretchr/testify/require" ) +func TestSha256Factory_NewDeprecated(t *testing.T) { + factory := NewSha256Factory() + require.NotNil(t, factory.New()) +} + func TestSha256Factory_New(t *testing.T) { factory := NewHashFactory(Sha256) require.NotNil(t, factory.New())