Skip to content

Commit

Permalink
[FAB-5630] SideDB - RWSetBuilder enhancements
Browse files Browse the repository at this point in the history
This CR enhance the read-write set builder for capturing the
additional information during simulation of a transaction

In addition to the public read-erite set for channel scoped
data, this CR allows to capture the hashed read-write set and
private read-write set for the private data.

Change-Id: I438692d7af0255d589adf8f21cda51a3f00d7418
Signed-off-by: manish <manish.sethi@gmail.com>
  • Loading branch information
manish-sethi committed Aug 8, 2017
1 parent 6e9e042 commit d9e0004
Show file tree
Hide file tree
Showing 7 changed files with 901 additions and 112 deletions.
309 changes: 248 additions & 61 deletions core/ledger/kvledger/txmgmt/rwsetutil/rwset_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,45 @@ package rwsetutil

import (
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
"github.com/hyperledger/fabric/core/ledger/util"
"github.com/hyperledger/fabric/protos/ledger/rwset"
"github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset"
)

var logger = flogging.MustGetLogger("rwsetutil")

type nsRWs struct {
readMap map[string]*kvrwset.KVRead //for mvcc validation
writeMap map[string]*kvrwset.KVWrite
rangeQueriesMap map[rangeQueryKey]*kvrwset.RangeQueryInfo //for phantom read validation
rangeQueriesKeys []rangeQueryKey
// RWSetBuilder helps building the read-write set
type RWSetBuilder struct {
pubRwBuilderMap map[string]*nsPubRwBuilder
pvtRwBuilderMap map[string]*nsPvtRwBuilder
}

func newNsRWs() *nsRWs {
return &nsRWs{make(map[string]*kvrwset.KVRead),
make(map[string]*kvrwset.KVWrite),
make(map[rangeQueryKey]*kvrwset.RangeQueryInfo), nil}
type nsPubRwBuilder struct {
namespace string
readMap map[string]*kvrwset.KVRead //for mvcc validation
writeMap map[string]*kvrwset.KVWrite
rangeQueriesMap map[rangeQueryKey]*kvrwset.RangeQueryInfo //for phantom read validation
rangeQueriesKeys []rangeQueryKey
collHashRwBuilder map[string]*collHashRwBuilder
}

type collHashRwBuilder struct {
collName string
readMap map[string]*kvrwset.KVReadHash
writeMap map[string]*kvrwset.KVWriteHash
pvtDataHash []byte
}

type nsPvtRwBuilder struct {
namespace string
collPvtRwBuilders map[string]*collPvtRwBuilder
}

type collPvtRwBuilder struct {
collectionName string
writeMap map[string]*kvrwset.KVWrite
}

type rangeQueryKey struct {
Expand All @@ -44,80 +65,246 @@ type rangeQueryKey struct {
itrExhausted bool
}

// RWSetBuilder helps building the read-write set
type RWSetBuilder struct {
rwMap map[string]*nsRWs
}

// NewRWSetBuilder constructs a new instance of RWSetBuilder
func NewRWSetBuilder() *RWSetBuilder {
return &RWSetBuilder{make(map[string]*nsRWs)}
return &RWSetBuilder{make(map[string]*nsPubRwBuilder), make(map[string]*nsPvtRwBuilder)}
}

// AddToReadSet adds a key and corresponding version to the read-set
func (rws *RWSetBuilder) AddToReadSet(ns string, key string, version *version.Height) {
nsRWs := rws.getOrCreateNsRW(ns)
nsRWs.readMap[key] = NewKVRead(key, version)
func (b *RWSetBuilder) AddToReadSet(ns string, key string, version *version.Height) {
nsPubRwBuilder := b.getOrCreateNsPubRwBuilder(ns)
nsPubRwBuilder.readMap[key] = NewKVRead(key, version)
}

// AddToWriteSet adds a key and value to the write-set
func (rws *RWSetBuilder) AddToWriteSet(ns string, key string, value []byte) {
nsRWs := rws.getOrCreateNsRW(ns)
nsRWs.writeMap[key] = newKVWrite(key, value)
func (b *RWSetBuilder) AddToWriteSet(ns string, key string, value []byte) {
nsPubRwBuilder := b.getOrCreateNsPubRwBuilder(ns)
nsPubRwBuilder.writeMap[key] = newKVWrite(key, value)
}

// AddToRangeQuerySet adds a range query info for performing phantom read validation
func (rws *RWSetBuilder) AddToRangeQuerySet(ns string, rqi *kvrwset.RangeQueryInfo) {
nsRWs := rws.getOrCreateNsRW(ns)
func (b *RWSetBuilder) AddToRangeQuerySet(ns string, rqi *kvrwset.RangeQueryInfo) {
nsPubRwBuilder := b.getOrCreateNsPubRwBuilder(ns)
key := rangeQueryKey{rqi.StartKey, rqi.EndKey, rqi.ItrExhausted}
_, ok := nsRWs.rangeQueriesMap[key]
_, ok := nsPubRwBuilder.rangeQueriesMap[key]
if !ok {
nsRWs.rangeQueriesMap[key] = rqi
nsRWs.rangeQueriesKeys = append(nsRWs.rangeQueriesKeys, key)
nsPubRwBuilder.rangeQueriesMap[key] = rqi
nsPubRwBuilder.rangeQueriesKeys = append(nsPubRwBuilder.rangeQueriesKeys, key)
}
}

// GetTxReadWriteSet returns the read-write set in the form that can be serialized
func (rws *RWSetBuilder) GetTxReadWriteSet() *TxRwSet {
txRWSet := &TxRwSet{}
sortedNamespaces := util.GetSortedKeys(rws.rwMap)
for _, ns := range sortedNamespaces {
//Get namespace specific read-writes
nsReadWriteMap := rws.rwMap[ns]
// AddToHashedReadSet adds a key and corresponding version to the hashed read-set
func (b *RWSetBuilder) AddToHashedReadSet(ns string, coll string, key string, version *version.Height) error {
kvReadHash, err := newPvtKVReadHash(key, version)
if err != nil {
return err
}
b.getOrCreateCollHashedRwBuilder(ns, coll).readMap[key] = kvReadHash
return nil
}

//add read set
var reads []*kvrwset.KVRead
sortedReadKeys := util.GetSortedKeys(nsReadWriteMap.readMap)
for _, key := range sortedReadKeys {
reads = append(reads, nsReadWriteMap.readMap[key])
}
// AddToPvtAndHashedWriteSet adds a key and value to the private and hashed write-set
func (b *RWSetBuilder) AddToPvtAndHashedWriteSet(ns string, coll string, key string, value []byte) error {
kvWrite, kvWriteHash, err := newPvtKVWriteAndHash(key, value)
if err != nil {
return err
}
b.getOrCreateCollPvtRwBuilder(ns, coll).writeMap[key] = kvWrite
b.getOrCreateCollHashedRwBuilder(ns, coll).writeMap[key] = kvWriteHash
return nil
}

//add write set
var writes []*kvrwset.KVWrite
sortedWriteKeys := util.GetSortedKeys(nsReadWriteMap.writeMap)
for _, key := range sortedWriteKeys {
writes = append(writes, nsReadWriteMap.writeMap[key])
}
// GetTxSimulationResults returns the proto bytes of public rwset
// (public data + hashes of private data) and the private rwset for the transaction
func (b *RWSetBuilder) GetTxSimulationResults() (*ledger.TxSimulationResults, error) {
pvtData := b.getTxPvtReadWriteSet()
var err error

//add range query info
var rangeQueriesInfo []*kvrwset.RangeQueryInfo
rangeQueriesMap := nsReadWriteMap.rangeQueriesMap
for _, key := range nsReadWriteMap.rangeQueriesKeys {
rangeQueriesInfo = append(rangeQueriesInfo, rangeQueriesMap[key])
var pubDataProto *rwset.TxReadWriteSet
var pvtDataProto *rwset.TxPvtReadWriteSet

// Populate the collection-level hashes into pub rwset and compute the proto bytes for pvt rwset
if pvtData != nil {
if pvtDataProto, err = pvtData.toProtoMsg(); err != nil {
return nil, err
}
for _, ns := range pvtDataProto.NsPvtRwset {
for _, coll := range ns.CollectionPvtRwset {
b.setPvtCollectionHash(ns.Namespace, coll.CollectionName, coll.Rwset)
}
}
kvRWs := &kvrwset.KVRWSet{Reads: reads, Writes: writes, RangeQueriesInfo: rangeQueriesInfo}
nsRWs := &NsRwSet{ns, kvRWs}
txRWSet.NsRwSets = append(txRWSet.NsRwSets, nsRWs)
}
return txRWSet
// Compute the proto bytes for pub rwset
pubSet := b.GetTxReadWriteSet()
if pubSet != nil {
if pubDataProto, err = b.GetTxReadWriteSet().toProtoMsg(); err != nil {
return nil, err
}
}
return &ledger.TxSimulationResults{
PubSimulationResults: pubDataProto,
PvtSimulationResults: pvtDataProto,
}, nil
}

func (b *RWSetBuilder) setPvtCollectionHash(ns string, coll string, pvtDataProto []byte) {
collHashedBuilder := b.getOrCreateCollHashedRwBuilder(ns, coll)
collHashedBuilder.pvtDataHash = util.ComputeHash(pvtDataProto)
}

// GetTxReadWriteSet returns the read-write set
// TODO make this function private once txmgr starts using new function `GetTxSimulationResults` introduced here
func (b *RWSetBuilder) GetTxReadWriteSet() *TxRwSet {
sortedNsPubBuilders := []*nsPubRwBuilder{}
util.GetValuesBySortedKeys(&(b.pubRwBuilderMap), &sortedNsPubBuilders)

var nsPubRwSets []*NsRwSet
for _, nsPubRwBuilder := range sortedNsPubBuilders {
nsPubRwSets = append(nsPubRwSets, nsPubRwBuilder.build())
}
return &TxRwSet{NsRwSets: nsPubRwSets}
}

func (rws *RWSetBuilder) getOrCreateNsRW(ns string) *nsRWs {
var nsRWs *nsRWs
var ok bool
if nsRWs, ok = rws.rwMap[ns]; !ok {
nsRWs = newNsRWs()
rws.rwMap[ns] = nsRWs
// getTxPvtReadWriteSet returns the private read-write set
func (b *RWSetBuilder) getTxPvtReadWriteSet() *TxPvtRwSet {
sortedNsPvtBuilders := []*nsPvtRwBuilder{}
util.GetValuesBySortedKeys(&(b.pvtRwBuilderMap), &sortedNsPvtBuilders)

var nsPvtRwSets []*NsPvtRwSet
for _, nsPvtRwBuilder := range sortedNsPvtBuilders {
nsPvtRwSets = append(nsPvtRwSets, nsPvtRwBuilder.build())
}
if len(nsPvtRwSets) == 0 {
return nil
}
return nsRWs
return &TxPvtRwSet{NsPvtRwSet: nsPvtRwSets}
}

func (b *nsPubRwBuilder) build() *NsRwSet {
var readSet []*kvrwset.KVRead
var writeSet []*kvrwset.KVWrite
var rangeQueriesInfo []*kvrwset.RangeQueryInfo
var collHashedRwSet []*CollHashedRwSet
//add read set
util.GetValuesBySortedKeys(&(b.readMap), &readSet)
//add write set
util.GetValuesBySortedKeys(&(b.writeMap), &writeSet)
//add range query info
for _, key := range b.rangeQueriesKeys {
rangeQueriesInfo = append(rangeQueriesInfo, b.rangeQueriesMap[key])
}
// add hashed rws for private collections
sortedCollBuilders := []*collHashRwBuilder{}
util.GetValuesBySortedKeys(&(b.collHashRwBuilder), &sortedCollBuilders)
for _, collBuilder := range sortedCollBuilders {
collHashedRwSet = append(collHashedRwSet, collBuilder.build())
}
return &NsRwSet{
NameSpace: b.namespace,
KvRwSet: &kvrwset.KVRWSet{Reads: readSet, Writes: writeSet, RangeQueriesInfo: rangeQueriesInfo},
CollHashedRwSets: collHashedRwSet,
}
}

func (b *nsPvtRwBuilder) build() *NsPvtRwSet {
sortedCollBuilders := []*collPvtRwBuilder{}
util.GetValuesBySortedKeys(&(b.collPvtRwBuilders), &sortedCollBuilders)

var collPvtRwSets []*CollPvtRwSet
for _, collBuilder := range sortedCollBuilders {
collPvtRwSets = append(collPvtRwSets, collBuilder.build())
}
return &NsPvtRwSet{NameSpace: b.namespace, CollPvtRwSets: collPvtRwSets}
}

func (b *collHashRwBuilder) build() *CollHashedRwSet {
var readSet []*kvrwset.KVReadHash
var writeSet []*kvrwset.KVWriteHash
util.GetValuesBySortedKeys(&(b.readMap), &readSet)
util.GetValuesBySortedKeys(&(b.writeMap), &writeSet)
return &CollHashedRwSet{
CollectionName: b.collName,
HashedRwSet: &kvrwset.HashedRWSet{
HashedReads: readSet,
HashedWrites: writeSet,
},
PvtRwSetHash: b.pvtDataHash,
}
}

func (b *collPvtRwBuilder) build() *CollPvtRwSet {
var writeSet []*kvrwset.KVWrite
util.GetValuesBySortedKeys(&(b.writeMap), &writeSet)
return &CollPvtRwSet{
CollectionName: b.collectionName,
KvRwSet: &kvrwset.KVRWSet{
Writes: writeSet,
},
}
}

func (b *RWSetBuilder) getOrCreateNsPubRwBuilder(ns string) *nsPubRwBuilder {
nsPubRwBuilder, ok := b.pubRwBuilderMap[ns]
if !ok {
nsPubRwBuilder = newNsPubRwBuilder(ns)
b.pubRwBuilderMap[ns] = nsPubRwBuilder
}
return nsPubRwBuilder
}

func (b *RWSetBuilder) getOrCreateNsPvtRwBuilder(ns string) *nsPvtRwBuilder {
nsPvtRwBuilder, ok := b.pvtRwBuilderMap[ns]
if !ok {
nsPvtRwBuilder = newNsPvtRwBuilder(ns)
b.pvtRwBuilderMap[ns] = nsPvtRwBuilder
}
return nsPvtRwBuilder
}

func (b *RWSetBuilder) getOrCreateCollHashedRwBuilder(ns string, coll string) *collHashRwBuilder {
nsPubRwBuilder := b.getOrCreateNsPubRwBuilder(ns)
collHashRwBuilder, ok := nsPubRwBuilder.collHashRwBuilder[coll]
if !ok {
collHashRwBuilder = newCollHashRwBuilder(coll)
nsPubRwBuilder.collHashRwBuilder[coll] = collHashRwBuilder
}
return collHashRwBuilder
}

func (b *RWSetBuilder) getOrCreateCollPvtRwBuilder(ns string, coll string) *collPvtRwBuilder {
nsPvtRwBuilder := b.getOrCreateNsPvtRwBuilder(ns)
collPvtRwBuilder, ok := nsPvtRwBuilder.collPvtRwBuilders[coll]
if !ok {
collPvtRwBuilder = newCollPvtRwBuilder(coll)
nsPvtRwBuilder.collPvtRwBuilders[coll] = collPvtRwBuilder
}
return collPvtRwBuilder
}

func newNsPubRwBuilder(namespace string) *nsPubRwBuilder {
return &nsPubRwBuilder{
namespace,
make(map[string]*kvrwset.KVRead),
make(map[string]*kvrwset.KVWrite),
make(map[rangeQueryKey]*kvrwset.RangeQueryInfo),
nil,
make(map[string]*collHashRwBuilder),
}
}

func newNsPvtRwBuilder(namespace string) *nsPvtRwBuilder {
return &nsPvtRwBuilder{namespace, make(map[string]*collPvtRwBuilder)}
}

func newCollHashRwBuilder(collName string) *collHashRwBuilder {
return &collHashRwBuilder{
collName,
make(map[string]*kvrwset.KVReadHash),
make(map[string]*kvrwset.KVWriteHash),
nil,
}
}

func newCollPvtRwBuilder(collName string) *collPvtRwBuilder {
return &collPvtRwBuilder{collName, make(map[string]*kvrwset.KVWrite)}
}
Loading

0 comments on commit d9e0004

Please sign in to comment.