Skip to content

Commit 2dec57d

Browse files
committed
[FAB-4977] sidedb:statedb enhancements
This CR provides support for maintaining public, private, and hashed data This includes - An interface for managing the three categories of data - A default implementation that allows the use of either leveldb or couchdb. The default implementation uses a single logical db and uses different namespaces for different categories of data. Alternate implementation based on furture need or exploration should be easy to support. These may include using different dbs for different categories such as using leveldb for hashed data and using separate dbs in a couch instance for public and private data - If the underlying db does not support storing random bytes as key (for example couch supports onlu valid utf-8 bytes as key), the key for the hashed data is encoded using base64 Change-Id: Ia8ede4f4c0ab392119e59bb7f46e9c20062a411a Signed-off-by: manish <manish.sethi@gmail.com>
1 parent 5101b9e commit 2dec57d

File tree

9 files changed

+572
-8
lines changed

9 files changed

+572
-8
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package privacyenabledstate
8+
9+
import (
10+
"encoding/base64"
11+
"fmt"
12+
13+
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
14+
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb"
15+
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb"
16+
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
17+
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
18+
)
19+
20+
const (
21+
nsJoiner = "/"
22+
pvtDataPrefix = "p"
23+
hashDataPrefix = "h"
24+
)
25+
26+
// CommonStorageDBProvider implements interface DBProvider
27+
type CommonStorageDBProvider struct {
28+
statedb.VersionedDBProvider
29+
}
30+
31+
// NewCommonStorageDBProvider constructs an instance of DBProvider
32+
func NewCommonStorageDBProvider() (DBProvider, error) {
33+
var vdbProvider statedb.VersionedDBProvider
34+
var err error
35+
if ledgerconfig.IsCouchDBEnabled() {
36+
if vdbProvider, err = statecouchdb.NewVersionedDBProvider(); err != nil {
37+
return nil, err
38+
}
39+
} else {
40+
vdbProvider = stateleveldb.NewVersionedDBProvider()
41+
}
42+
return &CommonStorageDBProvider{vdbProvider}, nil
43+
}
44+
45+
// GetDBHandle implements function from interface DBProvider
46+
func (p *CommonStorageDBProvider) GetDBHandle(id string) (DB, error) {
47+
vdb, err := p.VersionedDBProvider.GetDBHandle(id)
48+
if err != nil {
49+
return nil, err
50+
}
51+
return NewCommonStorageDB(vdb, id)
52+
}
53+
54+
// Close implements function from interface DBProvider
55+
func (p *CommonStorageDBProvider) Close() {
56+
p.VersionedDBProvider.Close()
57+
}
58+
59+
// CommonStorageDB implements interface DB. This implementation uses a single database to maintain
60+
// both the public and private data
61+
type CommonStorageDB struct {
62+
statedb.VersionedDB
63+
}
64+
65+
// NewCommonStorageDB wraps a VersionedDB instance. The public data is managed directly by the wrapped versionedDB.
66+
// For managing the hashed data and private data, this implementation creates separate namespaces in the wrapped db
67+
func NewCommonStorageDB(vdb statedb.VersionedDB, ledgerid string) (DB, error) {
68+
return &CommonStorageDB{VersionedDB: vdb}, nil
69+
}
70+
71+
// GetPrivateData implements corresponding function in interface DB
72+
func (s *CommonStorageDB) GetPrivateData(namespace, collection, key string) (*statedb.VersionedValue, error) {
73+
return s.GetState(derivePvtDataNs(namespace, collection), key)
74+
}
75+
76+
// GetValueHash implements corresponding function in interface DB
77+
func (s *CommonStorageDB) GetValueHash(namespace, collection string, keyHash []byte) (*statedb.VersionedValue, error) {
78+
keyHashStr := string(keyHash)
79+
if !s.BytesKeySuppoted() {
80+
keyHashStr = base64.StdEncoding.EncodeToString(keyHash)
81+
}
82+
return s.GetState(deriveHashedDataNs(namespace, collection), keyHashStr)
83+
}
84+
85+
// GetPrivateDataMultipleKeys implements corresponding function in interface DB
86+
func (s *CommonStorageDB) GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([]*statedb.VersionedValue, error) {
87+
return s.GetStateMultipleKeys(derivePvtDataNs(namespace, collection), keys)
88+
}
89+
90+
// GetPrivateDataRangeScanIterator implements corresponding function in interface DB
91+
func (s *CommonStorageDB) GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (statedb.ResultsIterator, error) {
92+
return s.GetStateRangeScanIterator(derivePvtDataNs(namespace, collection), startKey, endKey)
93+
}
94+
95+
// ExecuteQueryOnPrivateData implements corresponding function in interface DB
96+
func (s CommonStorageDB) ExecuteQueryOnPrivateData(namespace, collection, query string) (statedb.ResultsIterator, error) {
97+
return s.ExecuteQuery(derivePvtDataNs(namespace, collection), query)
98+
}
99+
100+
// ApplyUpdates overrides the funciton in statedb.VersionedDB and throws appropriate error message
101+
// Otherwise, somewhere in the code, usage of this function could lead to updating only public data.
102+
func (s *CommonStorageDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error {
103+
return fmt.Errorf("This function should not be invoked on this type. Please invoke function 'ApplyPrivacyAwareUpdates'")
104+
}
105+
106+
// ApplyPrivacyAwareUpdates implements corresponding function in interface DB
107+
func (s *CommonStorageDB) ApplyPrivacyAwareUpdates(updates *UpdateBatch, height *version.Height) error {
108+
addPvtUpdates(updates.PubUpdates, updates.PvtUpdates)
109+
addHashedUpdates(updates.PubUpdates, updates.HashUpdates, !s.BytesKeySuppoted())
110+
return s.VersionedDB.ApplyUpdates(updates.PubUpdates.UpdateBatch, height)
111+
}
112+
113+
func derivePvtDataNs(namespace, collection string) string {
114+
return namespace + nsJoiner + pvtDataPrefix + collection
115+
}
116+
117+
func deriveHashedDataNs(namespace, collection string) string {
118+
return namespace + nsJoiner + hashDataPrefix + collection
119+
}
120+
121+
func addPvtUpdates(pubUpdateBatch *PubUpdateBatch, pvtUpdateBatch *PvtUpdateBatch) {
122+
for ns, nsBatch := range pvtUpdateBatch.UpdateMap {
123+
for _, coll := range nsBatch.GetCollectionNames() {
124+
for key, vv := range nsBatch.GetUpdates(coll) {
125+
pubUpdateBatch.Update(derivePvtDataNs(ns, coll), key, vv)
126+
}
127+
}
128+
}
129+
}
130+
131+
func addHashedUpdates(pubUpdateBatch *PubUpdateBatch, hashedUpdateBatch *HashedUpdateBatch, base64Key bool) {
132+
for ns, nsBatch := range hashedUpdateBatch.UpdateMap {
133+
for _, coll := range nsBatch.GetCollectionNames() {
134+
for key, vv := range nsBatch.GetUpdates(coll) {
135+
if base64Key {
136+
key = base64.StdEncoding.EncodeToString([]byte(key))
137+
}
138+
pubUpdateBatch.Update(deriveHashedDataNs(ns, coll), key, vv)
139+
}
140+
}
141+
}
142+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package privacyenabledstate
8+
9+
import (
10+
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
11+
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
12+
)
13+
14+
// DBProvider provides handle to a PvtVersionedDB
15+
type DBProvider interface {
16+
// GetDBHandle returns a handle to a PvtVersionedDB
17+
GetDBHandle(id string) (DB, error)
18+
// Close closes all the PvtVersionedDB instances and releases any resources held by VersionedDBProvider
19+
Close()
20+
}
21+
22+
// DB extends VersionedDB interface. This interface provides additional functions for managing private data state
23+
type DB interface {
24+
statedb.VersionedDB
25+
GetPrivateData(namespace, collection, key string) (*statedb.VersionedValue, error)
26+
GetValueHash(namespace, collection string, keyHash []byte) (*statedb.VersionedValue, error)
27+
GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([]*statedb.VersionedValue, error)
28+
GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (statedb.ResultsIterator, error)
29+
ExecuteQueryOnPrivateData(namespace, collection, query string) (statedb.ResultsIterator, error)
30+
ApplyPrivacyAwareUpdates(updates *UpdateBatch, height *version.Height) error
31+
}
32+
33+
// UpdateBatch encapsulates the updates to Public, Private, and Hashed data.
34+
// This is expected to contain a consistent set of updates
35+
type UpdateBatch struct {
36+
PubUpdates *PubUpdateBatch
37+
HashUpdates *HashedUpdateBatch
38+
PvtUpdates *PvtUpdateBatch
39+
}
40+
41+
// PubUpdateBatch contains update for the public data
42+
type PubUpdateBatch struct {
43+
*statedb.UpdateBatch
44+
}
45+
46+
// HashedUpdateBatch contains updates for the hashes of the private data
47+
type HashedUpdateBatch struct {
48+
UpdateMap
49+
}
50+
51+
// PvtUpdateBatch contains updates for the private data
52+
type PvtUpdateBatch struct {
53+
UpdateMap
54+
}
55+
56+
// UpdateMap maintains entries of tuple <Namespace, UpdatesForNamespace>
57+
type UpdateMap map[string]nsBatch
58+
59+
// nsBatch contains updates related to one namespace
60+
type nsBatch struct {
61+
*statedb.UpdateBatch
62+
}
63+
64+
// NewUpdateBatch creates and empty UpdateBatch
65+
func NewUpdateBatch() *UpdateBatch {
66+
return &UpdateBatch{NewPubUpdateBatch(), NewHashedUpdateBatch(), NewPvtUpdateBatch()}
67+
}
68+
69+
// NewPubUpdateBatch creates an empty PubUpdateBatch
70+
func NewPubUpdateBatch() *PubUpdateBatch {
71+
return &PubUpdateBatch{statedb.NewUpdateBatch()}
72+
}
73+
74+
// NewHashedUpdateBatch creates an empty HashedUpdateBatch
75+
func NewHashedUpdateBatch() *HashedUpdateBatch {
76+
return &HashedUpdateBatch{make(map[string]nsBatch)}
77+
}
78+
79+
// NewPvtUpdateBatch creates an empty PvtUpdateBatch
80+
func NewPvtUpdateBatch() *PvtUpdateBatch {
81+
return &PvtUpdateBatch{make(map[string]nsBatch)}
82+
}
83+
84+
// IsEmpty returns true if there exists any updates
85+
func (b UpdateMap) IsEmpty() bool {
86+
return len(b) == 0
87+
}
88+
89+
// Put sets the value in the batch for a given combination of namespace and collection name
90+
func (b UpdateMap) Put(ns, coll, key string, value []byte, version *version.Height) {
91+
b.getOrCreateNsBatch(ns).Put(coll, key, value, version)
92+
}
93+
94+
// Delete removes the entry from the batch for a given combination of namespace and collection name
95+
func (b UpdateMap) Delete(ns, coll, key string, version *version.Height) {
96+
b.getOrCreateNsBatch(ns).Delete(coll, key, version)
97+
}
98+
99+
// Get retrieves the value from the batch for a given combination of namespace and collection name
100+
func (b UpdateMap) Get(ns, coll, key string) *statedb.VersionedValue {
101+
nsPvtBatch, ok := b[ns]
102+
if !ok {
103+
return nil
104+
}
105+
return nsPvtBatch.Get(coll, key)
106+
}
107+
108+
func (nsb nsBatch) GetCollectionNames() []string {
109+
return nsb.GetUpdatedNamespaces()
110+
}
111+
112+
func (b UpdateMap) getOrCreateNsBatch(ns string) nsBatch {
113+
batch, ok := b[ns]
114+
if !ok {
115+
batch = nsBatch{statedb.NewUpdateBatch()}
116+
b[ns] = batch
117+
}
118+
return batch
119+
}
120+
121+
// Contains returns true if the given <ns,coll,keyHash> tuple is present in the batch
122+
func (h HashedUpdateBatch) Contains(ns, coll string, keyHash []byte) bool {
123+
nsBatch, ok := h.UpdateMap[ns]
124+
if !ok {
125+
return false
126+
}
127+
return nsBatch.Exists(coll, string(keyHash))
128+
}
129+
130+
// Put overrides the function in UpdateMap for allowing the key to be a []byte instead of a string
131+
func (h HashedUpdateBatch) Put(ns, coll string, key []byte, value []byte, version *version.Height) {
132+
h.UpdateMap.Put(ns, coll, string(key), value, version)
133+
}
134+
135+
// Delete overrides the function in UpdateMap for allowing the key to be a []byte instead of a string
136+
func (h HashedUpdateBatch) Delete(ns, coll string, key []byte, version *version.Height) {
137+
h.UpdateMap.Delete(ns, coll, string(key), version)
138+
}

0 commit comments

Comments
 (0)