From 64596337086314b817ad19e724669a9d1f14a188 Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Thu, 28 Nov 2024 14:27:18 +0100 Subject: [PATCH] This commit addresses #698 Signed-off-by: Angelo De Caro --- .../common/core/generic/vault/inspector.go | 4 + .../common/core/generic/vault/interceptor.go | 5 ++ .../core/generic/vault/interceptor_test.go | 80 +++++++------------ .../common/core/generic/vault/mocks/mocks.go | 47 +++++++++++ platform/common/core/generic/vault/version.go | 8 +- platform/common/driver/rwset.go | 3 + 6 files changed, 90 insertions(+), 57 deletions(-) create mode 100644 platform/common/core/generic/vault/mocks/mocks.go diff --git a/platform/common/core/generic/vault/inspector.go b/platform/common/core/generic/vault/inspector.go index 949607734..497867349 100644 --- a/platform/common/core/generic/vault/inspector.go +++ b/platform/common/core/generic/vault/inspector.go @@ -45,6 +45,10 @@ func (i *Inspector) SetState(driver.Namespace, driver.PKey, driver.RawValue) err panic("programming error: the rwset inspector is read-only") } +func (i *Inspector) AddReadAt(ns driver.Namespace, key string, version Version) error { + panic("programming error: the rwset inspector is read-only") +} + func (i *Inspector) GetState(namespace driver.Namespace, key driver.PKey, _ ...driver.GetStateOpt) (driver.RawValue, error) { return i.Rws.WriteSet.Get(namespace, key), nil } diff --git a/platform/common/core/generic/vault/interceptor.go b/platform/common/core/generic/vault/interceptor.go index 223e051e9..fe6a9db15 100644 --- a/platform/common/core/generic/vault/interceptor.go +++ b/platform/common/core/generic/vault/interceptor.go @@ -116,6 +116,11 @@ func (i *Interceptor[V]) Clear(ns string) error { return nil } +func (i *Interceptor[V]) AddReadAt(ns driver.Namespace, key string, version Version) error { + i.Rws.ReadSet.Add(ns, key, version) + return nil +} + func (i *Interceptor[V]) GetReadKeyAt(ns string, pos int) (string, error) { if i.IsClosed() { return "", errors.New("this instance was closed") diff --git a/platform/common/core/generic/vault/interceptor_test.go b/platform/common/core/generic/vault/interceptor_test.go index 4d4888f3c..ac768e9b5 100644 --- a/platform/common/core/generic/vault/interceptor_test.go +++ b/platform/common/core/generic/vault/interceptor_test.go @@ -10,76 +10,40 @@ import ( "sync" "testing" + "github.com/hyperledger-labs/fabric-smart-client/platform/common/core/generic/vault/mocks" "github.com/hyperledger-labs/fabric-smart-client/platform/common/driver" - "github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/collections" - "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/assert" "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging" + "github.com/stretchr/testify/assert" ) -func newMockQE() mockQE { - return mockQE{ - State: VersionedValue{ - Raw: []byte("raw"), - Version: blockTxIndexToBytes(1, 1), - }, - Metadata: map[string][]byte{ - "md": []byte("meta"), - }, - } -} - -type mockQE struct { - State VersionedValue - Metadata map[string][]byte -} - -func (qe mockQE) GetStateMetadata(driver.Namespace, driver.PKey) (driver.Metadata, driver.RawVersion, error) { - return qe.Metadata, blockTxIndexToBytes(1, 1), nil -} -func (qe mockQE) GetState(driver.Namespace, driver.PKey) (VersionedValue, error) { - return qe.State, nil -} -func (qe mockQE) Done() { -} - -type mockTXIDStoreReader struct { -} - -func (m mockTXIDStoreReader) Iterator(interface{}) (collections.Iterator[*driver.ByNum[int]], error) { - panic("not implemented") -} -func (m mockTXIDStoreReader) Get(txID driver.TxID) (int, string, error) { - return 1, txID, nil -} - func TestConcurrency(t *testing.T) { - qe := newMockQE() - idsr := mockTXIDStoreReader{} + qe := mocks.MockQE{} + idsr := mocks.MockTXIDStoreReader{} i := newInterceptor(flogging.MustGetLogger("interceptor_test"), qe, idsr, "1") s, err := i.GetState("ns", "key") - assert.NoError(err) - assert.Equal(qe.State.Raw, s, "with no opts, getstate should return the FromStorage value (query executor)") + assert.NoError(t, err) + assert.Equal(t, qe.State.Raw, s, "with no opts, getstate should return the FromStorage value (query executor)") md, err := i.GetStateMetadata("ns", "key") - assert.NoError(err) - assert.Equal(qe.Metadata, md, "with no opts, GetStateMetadata should return the FromStorage value (query executor)") + assert.NoError(t, err) + assert.Equal(t, qe.Metadata, md, "with no opts, GetStateMetadata should return the FromStorage value (query executor)") s, err = i.GetState("ns", "key", driver.FromBoth) - assert.NoError(err) - assert.Equal(qe.State.Raw, s, "FromBoth should fallback to FromStorage with empty rwset") + assert.NoError(t, err) + assert.Equal(t, qe.State.Raw, s, "FromBoth should fallback to FromStorage with empty rwset") md, err = i.GetStateMetadata("ns", "key", driver.FromBoth) - assert.NoError(err) - assert.Equal(qe.Metadata, md, "FromBoth should fallback to FromStorage with empty rwset") + assert.NoError(t, err) + assert.Equal(t, qe.Metadata, md, "FromBoth should fallback to FromStorage with empty rwset") s, err = i.GetState("ns", "key", driver.FromIntermediate) - assert.NoError(err) - assert.Equal([]byte(nil), s, "FromIntermediate should return empty result from empty rwset") + assert.NoError(t, err) + assert.Equal(t, []byte(nil), s, "FromIntermediate should return empty result from empty rwset") md, err = i.GetStateMetadata("ns", "key", driver.FromIntermediate) - assert.NoError(err) - assert.True(md == nil, "FromIntermediate should return empty result from empty rwset") + assert.NoError(t, err) + assert.True(t, md == nil, "FromIntermediate should return empty result from empty rwset") // Done in parallel wg := sync.WaitGroup{} @@ -94,5 +58,15 @@ func TestConcurrency(t *testing.T) { wg.Wait() _, err = i.GetState("ns", "key") - assert.Error(err, "this instance was closed") + assert.Error(t, err, "this instance was closed") +} + +func TestAddReadAt(t *testing.T) { + qe := mocks.MockQE{} + idsr := mocks.MockTXIDStoreReader{} + i := newInterceptor(flogging.MustGetLogger("interceptor_test"), qe, idsr, "1") + + assert.NoError(t, i.AddReadAt("ns", "key", []byte("version"))) + assert.Len(t, i.RWs().Reads, 1) + assert.Equal(t, []byte("version"), i.RWs().Reads["ns"]["key"]) } diff --git a/platform/common/core/generic/vault/mocks/mocks.go b/platform/common/core/generic/vault/mocks/mocks.go new file mode 100644 index 000000000..dca94c4c0 --- /dev/null +++ b/platform/common/core/generic/vault/mocks/mocks.go @@ -0,0 +1,47 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package mocks + +import ( + "encoding/binary" + + "github.com/hyperledger-labs/fabric-smart-client/platform/common/driver" + "github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/collections" +) + +type MockQE struct { + State driver.VersionedValue + Metadata map[string][]byte +} + +func (qe MockQE) GetStateMetadata(driver.Namespace, driver.PKey) (driver.Metadata, driver.RawVersion, error) { + return qe.Metadata, blockTxIndexToBytes(1, 1), nil +} + +func (qe MockQE) GetState(driver.Namespace, driver.PKey) (driver.VersionedValue, error) { + return qe.State, nil +} + +func (qe MockQE) Done() { +} + +type MockTXIDStoreReader struct { +} + +func (m MockTXIDStoreReader) Iterator(interface{}) (collections.Iterator[*driver.ByNum[int]], error) { + panic("not implemented") +} +func (m MockTXIDStoreReader) Get(txID driver.TxID) (int, string, error) { + return 1, txID, nil +} + +func blockTxIndexToBytes(Block driver.BlockNum, TxNum driver.TxNum) []byte { + buf := make([]byte, 8) + binary.BigEndian.PutUint32(buf[:4], uint32(Block)) + binary.BigEndian.PutUint32(buf[4:], uint32(TxNum)) + return buf +} diff --git a/platform/common/core/generic/vault/version.go b/platform/common/core/generic/vault/version.go index 85eed3d57..7e129b4e7 100644 --- a/platform/common/core/generic/vault/version.go +++ b/platform/common/core/generic/vault/version.go @@ -28,7 +28,7 @@ type BlockTxIndexVersionBuilder struct{} func (b *BlockTxIndexVersionBuilder) VersionedValues(rws *ReadWriteSet, ns driver2.Namespace, writes NamespaceWrites, block driver2.BlockNum, indexInBloc driver2.TxNum) (map[driver2.PKey]VersionedValue, error) { vals := make(map[driver2.PKey]driver.VersionedValue, len(writes)) for pkey, val := range writes { - vals[pkey] = driver.VersionedValue{Raw: val, Version: blockTxIndexToBytes(block, indexInBloc)} + vals[pkey] = driver.VersionedValue{Raw: val, Version: BlockTxIndexToBytes(block, indexInBloc)} } return vals, nil } @@ -36,7 +36,7 @@ func (b *BlockTxIndexVersionBuilder) VersionedValues(rws *ReadWriteSet, ns drive func (b *BlockTxIndexVersionBuilder) VersionedMetaValues(rws *ReadWriteSet, ns driver2.Namespace, writes KeyedMetaWrites, block driver2.BlockNum, indexInBloc driver2.TxNum) (map[driver2.PKey]driver2.VersionedMetadataValue, error) { vals := make(map[driver2.PKey]driver2.VersionedMetadataValue, len(writes)) for pkey, val := range writes { - vals[pkey] = driver2.VersionedMetadataValue{Metadata: val, Version: blockTxIndexToBytes(block, indexInBloc)} + vals[pkey] = driver2.VersionedMetadataValue{Metadata: val, Version: BlockTxIndexToBytes(block, indexInBloc)} } return vals, nil } @@ -57,10 +57,10 @@ func (m BlockTxIndexVersionMarshaller) FromBytes(data Version) (driver2.BlockNum } func (m BlockTxIndexVersionMarshaller) ToBytes(bn driver2.BlockNum, txn driver2.TxNum) Version { - return blockTxIndexToBytes(bn, txn) + return BlockTxIndexToBytes(bn, txn) } -func blockTxIndexToBytes(Block driver2.BlockNum, TxNum driver2.TxNum) []byte { +func BlockTxIndexToBytes(Block driver2.BlockNum, TxNum driver2.TxNum) []byte { buf := make([]byte, 8) binary.BigEndian.PutUint32(buf[:4], uint32(Block)) binary.BigEndian.PutUint32(buf[4:], uint32(TxNum)) diff --git a/platform/common/driver/rwset.go b/platform/common/driver/rwset.go index 365ca7dee..2023e69f2 100644 --- a/platform/common/driver/rwset.go +++ b/platform/common/driver/rwset.go @@ -27,6 +27,9 @@ type RWSet interface { // Clear remove the passed namespace from this rwset Clear(ns Namespace) error + // AddReadAt adds a read dependency for the given namespace and key at the given version + AddReadAt(ns Namespace, key string, version RawVersion) error + // SetState sets the given value for the given namespace and key. SetState(namespace Namespace, key PKey, value RawValue) error