github.com/ewagmig/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/privacyenabledstate/update_batch_bytes.go (about) 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 "sort" 11 12 "github.com/golang/protobuf/proto" 13 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 14 "github.com/hyperledger/fabric/core/ledger/util" 15 "github.com/pkg/errors" 16 ) 17 18 type UpdatesBytesBuilder struct { 19 } 20 21 // DeterministicBytesForPubAndHashUpdates constructs the bytes for a given UpdateBatch 22 // while constructing the bytes, it considers only public writes and hashed writes for 23 // the collections. For achieving the determinism, it constructs a slice of proto messages 24 // of type 'KVWriteProto'. In the slice, all the writes for a namespace "ns1" appear before 25 // the writes for another namespace "ns2" if "ns1" < "ns2" (lexicographically). Within a 26 // namespace, all the public writes appear before the collection writes. Like namespaces, 27 // the collections writes within a namespace appear in the order of lexicographical order. 28 // If an entry has the same namespace as its preceding entry, the namespace field is skipped. 29 // A Similar treatment is given to the repetitive entries for a collection within a namespace. 30 // For illustration, see the corresponding unit tests 31 func (bb *UpdatesBytesBuilder) DeterministicBytesForPubAndHashUpdates(u *UpdateBatch) ([]byte, error) { 32 pubUpdates := u.PubUpdates 33 hashUpdates := u.HashUpdates.UpdateMap 34 namespaces := dedupAndSort( 35 pubUpdates.GetUpdatedNamespaces(), 36 hashUpdates.getUpdatedNamespaces(), 37 ) 38 39 kvWritesProto := []*KVWriteProto{} 40 for _, ns := range namespaces { 41 if ns == "" { 42 // an empty namespace is used for persisting the channel config 43 // skipping the channel config from including into commit hash computation 44 // as this proto uses maps and hence is non deterministic 45 continue 46 } 47 p := bb.buildForKeys(pubUpdates.GetUpdates(ns)) 48 collsForNs, ok := hashUpdates[ns] 49 if ok { 50 p = append(p, bb.buildForColls(collsForNs)...) 51 } 52 p[0].Namespace = ns 53 kvWritesProto = append(kvWritesProto, p...) 54 } 55 batchProto := &KVWritesBatchProto{ 56 Kvwrites: kvWritesProto, 57 } 58 59 batchBytes, err := proto.Marshal(batchProto) 60 return batchBytes, errors.Wrap(err, "error constructing deterministic bytes from update batch") 61 } 62 63 func (bb *UpdatesBytesBuilder) buildForColls(colls nsBatch) []*KVWriteProto { 64 collNames := colls.GetCollectionNames() 65 sort.Strings(collNames) 66 collsProto := []*KVWriteProto{} 67 for _, collName := range collNames { 68 collUpdates := colls.getCollectionUpdates(collName) 69 p := bb.buildForKeys(collUpdates) 70 p[0].Collection = collName 71 collsProto = append(collsProto, p...) 72 } 73 return collsProto 74 } 75 76 func (bb *UpdatesBytesBuilder) buildForKeys(kv map[string]*statedb.VersionedValue) []*KVWriteProto { 77 keys := util.GetSortedKeys(kv) 78 p := []*KVWriteProto{} 79 for _, key := range keys { 80 val := kv[key] 81 p = append( 82 p, 83 &KVWriteProto{ 84 Key: []byte(key), 85 Value: val.Value, 86 IsDelete: val.Value == nil, 87 VersionBytes: val.Version.ToBytes(), 88 }, 89 ) 90 } 91 return p 92 } 93 94 func dedupAndSort(a, b []string) []string { 95 m := map[string]struct{}{} 96 for _, entry := range a { 97 m[entry] = struct{}{} 98 } 99 for _, entry := range b { 100 m[entry] = struct{}{} 101 } 102 return util.GetSortedKeys(m) 103 }