Skip to content

Commit

Permalink
db and vault support for generic version
Browse files Browse the repository at this point in the history
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Sep 24, 2024
1 parent 5cf5b83 commit 706b425
Show file tree
Hide file tree
Showing 23 changed files with 575 additions and 408 deletions.
49 changes: 49 additions & 0 deletions platform/common/core/generic/vault/fver/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package fver

import (
"bytes"
"encoding/binary"

"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
)

var zeroVersion = []byte{0, 0, 0, 0, 0, 0, 0, 0}

func IsEqual(a, b driver.RawVersion) bool {
if bytes.Equal(a, b) {
return true
}
if len(a) == 0 && bytes.Equal(zeroVersion, b) {
return true
}
if len(b) == 0 && bytes.Equal(zeroVersion, a) {
return true
}
return false
}

func ToBytes(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
}

func FromBytes(data []byte) (driver.BlockNum, driver.TxNum, error) {
if len(data) == 0 {
return 0, 0, nil
}
if len(data) != 8 {
return 0, 0, errors.Errorf("block number must be 8 bytes, but got %d", len(data))
}
Block := driver.BlockNum(binary.BigEndian.Uint32(data[:4]))
TxNum := driver.TxNum(binary.BigEndian.Uint32(data[4:]))
return Block, TxNum, nil
}
232 changes: 120 additions & 112 deletions platform/common/core/generic/vault/helpers.go

Large diffs are not rendered by default.

27 changes: 14 additions & 13 deletions platform/common/core/generic/vault/interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ package vault
import (
"sync"

"github.com/hyperledger-labs/fabric-smart-client/platform/common/core/generic/vault/fver"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/hash"
"github.com/pkg/errors"
)

type VersionedQueryExecutor interface {
GetStateMetadata(namespace, key string) (map[string][]byte, uint64, uint64, error)
GetStateMetadata(namespace, key string) (driver.Metadata, driver.RawVersion, error)
GetState(namespace, key string) (VersionedValue, error)
Done()
}
Expand Down Expand Up @@ -89,9 +90,8 @@ func (i *Interceptor[V]) IsValid() error {
if err != nil {
return err
}

if vv.Block != v.Block || vv.TxNum != v.TxNum {
return errors.Errorf("invalid read: vault at version %s:%s %d:%d, read-write set at version %d:%d", ns, k, vv.Block, vv.TxNum, v.Block, v.TxNum)
if !fver.IsEqual(v, vv.Version) {
return errors.Errorf("invalid read: vault at fver %s:%s [%v], read-write set at fver [%v]", ns, k, vv, v)
}
}
}
Expand Down Expand Up @@ -236,20 +236,20 @@ func (i *Interceptor[V]) GetStateMetadata(namespace, key string, opts ...driver.
i.RUnlock()
return nil, errors.New("this instance is write only")
}
val, block, txnum, err := i.QE.GetStateMetadata(namespace, key)
val, vaultVersion, err := i.QE.GetStateMetadata(namespace, key)
if err != nil {
i.RUnlock()
return nil, err
}
i.RUnlock()

b, t, in := i.Rws.ReadSet.Get(namespace, key)
version, in := i.Rws.ReadSet.Get(namespace, key)
if in {
if b != block || t != txnum {
return nil, errors.Errorf("invalid metadata read: previous value returned at version %d:%d, current value at version %d:%d", b, t, block, txnum)
if !fver.IsEqual(version, vaultVersion) {
return nil, errors.Errorf("invalid metadata read: previous value returned at fver [%v], current value at fver [%v]", version, vaultVersion)
}
} else {
i.Rws.ReadSet.Add(namespace, key, block, txnum)
i.Rws.ReadSet.Add(namespace, key, vaultVersion)
}

return val, nil
Expand Down Expand Up @@ -297,14 +297,15 @@ func (i *Interceptor[V]) GetState(namespace driver.Namespace, key driver.PKey, o
return nil, err
}
i.RUnlock()
vaultVersion := vv.Version

b, t, in := i.Rws.ReadSet.Get(namespace, key)
version, in := i.Rws.ReadSet.Get(namespace, key)
if in {
if b != vv.Block || t != vv.TxNum {
return nil, errors.Errorf("invalid read [%s:%s]: previous value returned at version %d:%d, current value at version %d:%d", namespace, key, b, t, vv.Block, vv.TxNum)
if !fver.IsEqual(version, vaultVersion) {
return nil, errors.Errorf("invalid read [%s:%s]: previous value returned at fver [%v], current value at fver [%v]", namespace, key, version, vaultVersion)
}
} else {
i.Rws.ReadSet.Add(namespace, key, vv.Block, vv.TxNum)
i.Rws.ReadSet.Add(namespace, key, vaultVersion)
}

return vv.Raw, nil
Expand Down
10 changes: 5 additions & 5 deletions platform/common/core/generic/vault/interceptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"sync"
"testing"

"github.com/hyperledger-labs/fabric-smart-client/platform/common/core/generic/vault/fver"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/assert"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/flogging"
Expand All @@ -18,9 +19,8 @@ import (
func newMockQE() mockQE {
return mockQE{
State: VersionedValue{
Raw: []byte("raw"),
Block: 1,
TxNum: 1,
Raw: []byte("raw"),
Version: fver.ToBytes(1, 1),
},
Metadata: map[string][]byte{
"md": []byte("meta"),
Expand All @@ -33,8 +33,8 @@ type mockQE struct {
Metadata map[string][]byte
}

func (qe mockQE) GetStateMetadata(driver.Namespace, driver.PKey) (driver.Metadata, driver.BlockNum, driver.TxNum, error) {
return qe.Metadata, 1, 1, nil
func (qe mockQE) GetStateMetadata(driver.Namespace, driver.PKey) (driver.Metadata, driver.RawVersion, error) {
return qe.Metadata, fver.ToBytes(1, 1), nil
}
func (qe mockQE) GetState(driver.Namespace, driver.PKey) (VersionedValue, error) {
return qe.State, nil
Expand Down
20 changes: 15 additions & 5 deletions platform/common/core/generic/vault/queryexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ SPDX-License-Identifier: Apache-2.0

package vault

import "github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
import (
"github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
)

// this file contains all structs that perform DB access. They
// differ in terms of the results that they return. They are both
Expand All @@ -28,8 +30,12 @@ func (q *directQueryExecutor[V]) GetStateRangeScanIterator(namespace driver.Name
return q.vault.store.GetStateRangeScanIterator(namespace, startKey, endKey)
}

func (q *directQueryExecutor[V]) GetStateMetadata(namespace driver.Namespace, key driver.PKey) (driver.Metadata, driver.BlockNum, driver.TxNum, error) {
return q.vault.store.GetStateMetadata(namespace, key)
func (q *directQueryExecutor[V]) GetStateMetadata(namespace driver.Namespace, key driver.PKey) (driver.Metadata, driver.RawVersion, error) {
m, version, err := q.vault.store.GetStateMetadata(namespace, key)
if err != nil {
return nil, nil, err
}
return m, version, nil
}

func (q *directQueryExecutor[V]) Done() {
Expand All @@ -46,8 +52,12 @@ func (i *interceptorQueryExecutor[V]) Done() {
i.storeLock.RUnlock()
}

func (i *interceptorQueryExecutor[V]) GetStateMetadata(namespace driver.Namespace, key driver.PKey) (driver.Metadata, driver.BlockNum, driver.TxNum, error) {
return i.store.GetStateMetadata(namespace, key)
func (i *interceptorQueryExecutor[V]) GetStateMetadata(namespace driver.Namespace, key driver.PKey) (driver.Metadata, driver.RawVersion, error) {
m, version, err := i.store.GetStateMetadata(namespace, key)
if err != nil {
return nil, nil, err
}
return m, version, nil
}

func (i *interceptorQueryExecutor[V]) GetState(namespace driver.Namespace, key driver.PKey) (VersionedValue, error) {
Expand Down
23 changes: 10 additions & 13 deletions platform/common/core/generic/vault/rwset.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,13 @@ func (w *WriteSet) Clear(ns string) {
w.OrderedWrites[ns] = []string{}
}

type TxPosition struct {
Block driver.BlockNum
TxNum driver.TxNum
}
type Version = []byte

type NamespaceReads map[string]TxPosition
type NamespaceReads map[string]Version

func (r NamespaceReads) Equals(o NamespaceReads) error {
return entriesEqual(r, o, func(v, v2 TxPosition) bool {
return v.Block == v2.Block && v.TxNum == v2.TxNum
return entriesEqual(r, o, func(v, v2 Version) bool {
return bytes.Equal(v, v2)
})
}

Expand All @@ -164,22 +161,22 @@ type ReadSet struct {
OrderedReads map[string][]string
}

func (r *ReadSet) Add(ns driver.Namespace, key string, block driver.BlockNum, txnum driver.TxNum) {
func (r *ReadSet) Add(ns driver.Namespace, key string, version Version) {
nsMap, in := r.Reads[ns]
if !in {
nsMap = make(map[driver.Namespace]TxPosition)
nsMap = make(map[driver.Namespace]Version)

r.Reads[ns] = nsMap
r.OrderedReads[ns] = make([]string, 0, 8)
}

nsMap[key] = TxPosition{block, txnum}
nsMap[key] = version
r.OrderedReads[ns] = append(r.OrderedReads[ns], key)
}

func (r *ReadSet) Get(ns driver.Namespace, key string) (driver.BlockNum, driver.TxNum, bool) {
func (r *ReadSet) Get(ns driver.Namespace, key string) (Version, bool) {
entry, in := r.Reads[ns][key]
return entry.Block, entry.TxNum, in
return entry, in
}

func (r *ReadSet) GetAt(ns driver.Namespace, i int) (string, bool) {
Expand All @@ -192,7 +189,7 @@ func (r *ReadSet) GetAt(ns driver.Namespace, i int) (string, bool) {
}

func (r *ReadSet) Clear(ns driver.Namespace) {
r.Reads[ns] = map[string]TxPosition{}
r.Reads[ns] = map[string]Version{}
r.OrderedReads[ns] = []string{}
}

Expand Down
6 changes: 4 additions & 2 deletions platform/common/core/generic/vault/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"sync"

"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/core/generic/vault/fver"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/collections"
dbdriver "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/db/driver"
Expand Down Expand Up @@ -57,6 +58,7 @@ type NewInterceptorFunc[V driver.ValidationCode] func(logger Logger, qe Versione
type (
VersionedPersistence = dbdriver.VersionedPersistence
VersionedValue = dbdriver.VersionedValue
VersionedMetaData = dbdriver.VersionedMetaData
VersionedRead = dbdriver.VersionedRead
VersionedResultsIterator = dbdriver.VersionedResultsIterator
QueryExecutor = dbdriver.QueryExecutor
Expand Down Expand Up @@ -289,7 +291,7 @@ func (db *Vault[V]) storeMetaWrites(ctx context.Context, writes NamespaceKeyedMe
span := trace.SpanFromContext(ctx)
for ns, keyMap := range writes {
span.AddEvent("set_tx_metadata_state")
if errs := db.store.SetStateMetadatas(ns, keyMap, block, indexInBloc); len(errs) > 0 {
if errs := db.store.SetStateMetadatas(ns, keyMap, nil); len(errs) > 0 {
return db.discard(ns, block, indexInBloc, errs)
}
}
Expand All @@ -310,7 +312,7 @@ func (db *Vault[V]) storeWrites(ctx context.Context, writes Writes, block driver
func versionedValues(keyMap NamespaceWrites, block driver.BlockNum, indexInBloc driver.TxNum) map[driver.PKey]VersionedValue {
vals := make(map[driver.PKey]VersionedValue, len(keyMap))
for pkey, val := range keyMap {
vals[pkey] = VersionedValue{Raw: val, Block: block, TxNum: indexInBloc}
vals[pkey] = VersionedValue{Raw: val, Version: fver.ToBytes(block, indexInBloc)}
}
return vals
}
Expand Down
9 changes: 5 additions & 4 deletions platform/common/core/generic/vault/vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/core/generic/vault/db"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/core/generic/vault/fver"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/core/generic/vault/txidstore"
driver2 "github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/collections"
Expand Down Expand Up @@ -109,11 +110,11 @@ func (m *marshaller) Append(destination *ReadWriteSet, raw []byte, nss ...string
continue
}
for s, position := range reads {
b, t, in := destination.ReadSet.Get(ns, s)
if in && (b != position.Block || t != position.TxNum) {
return errors.Errorf("invalid read [%s:%s]: previous value returned at version %d:%d, current value at version %d:%d", ns, s, b, t, b, t)
v, in := destination.ReadSet.Get(ns, s)
if in && !fver.IsEqual(position, v) {
return errors.Errorf("invalid read [%s:%s]: previous value returned at fver [%v], current value at fver [%v]", ns, s, position, v)
}
destination.ReadSet.Add(ns, s, position.Block, position.TxNum)
destination.ReadSet.Add(ns, s, position)
}
}
destination.OrderedReads = source.OrderedReads
Expand Down
23 changes: 14 additions & 9 deletions platform/common/driver/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,29 @@ import (
)

type (
PKey = string
MKey = string
RawValue = []byte
Metadata = map[MKey][]byte
PKey = string
MKey = string
RawValue = []byte
Metadata = map[MKey][]byte
RawVersion = []byte
)

type VersionedRead struct {
Key PKey
Raw RawValue
Block BlockNum
TxNum TxNum
Key PKey
Raw RawValue
Version RawVersion
}

type VersionedMetadata struct {
Metadata Metadata
Version RawVersion
}

type VersionedResultsIterator = collections.Iterator[*VersionedRead]

type QueryExecutor interface {
GetState(namespace Namespace, key PKey) (RawValue, error)
GetStateMetadata(namespace Namespace, key PKey) (Metadata, BlockNum, TxNum, error)
GetStateMetadata(namespace Namespace, key PKey) (Metadata, RawVersion, error)
GetStateRangeScanIterator(namespace Namespace, startKey PKey, endKey PKey) (VersionedResultsIterator, error)
Done()
}
Expand Down
Loading

0 comments on commit 706b425

Please sign in to comment.