github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/txmgr/update_batch_bytes.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package txmgr 8 9 import ( 10 "sort" 11 12 "github.com/golang/protobuf/proto" 13 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/privacyenabledstate" 14 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb" 15 "github.com/hechain20/hechain/core/ledger/util" 16 "github.com/pkg/errors" 17 ) 18 19 // deterministicBytesForPubAndHashUpdates constructs the bytes for a given UpdateBatch 20 // while constructing the bytes, it considers only public writes and hashed writes for 21 // the collections. For achieving the determinism, it constructs a slice of proto messages 22 // of type 'KVWrite'. In the slice, all the writes for a namespace "ns1" appear before 23 // the writes for another namespace "ns2" if "ns1" < "ns2" (lexicographically). Within a 24 // namespace, all the public writes appear before the collection writes. Like namespaces, 25 // the collections writes within a namespace appear in the order of lexicographical order. 26 // If an entry has the same namespace as its preceding entry, the namespace field is skipped. 27 // A Similar treatment is given to the repetitive entries for a collection within a namespace. 28 // For illustration, see the corresponding unit tests 29 func deterministicBytesForPubAndHashUpdates(u *privacyenabledstate.UpdateBatch) ([]byte, error) { 30 pubUpdates := u.PubUpdates 31 hashUpdates := u.HashUpdates.UpdateMap 32 namespaces := dedupAndSort( 33 pubUpdates.GetUpdatedNamespaces(), 34 hashUpdates.GetUpdatedNamespaces(), 35 ) 36 37 kvWrites := []*KVWrite{} 38 for _, ns := range namespaces { 39 if ns == "" { 40 // an empty namespace is used for persisting the channel config 41 // skipping the channel config from including into commit hash computation 42 // as this proto uses maps and hence is non deterministic 43 continue 44 } 45 kvs := genKVsFromNsUpdates(pubUpdates.GetUpdates(ns)) 46 collsForNs, ok := hashUpdates[ns] 47 if ok { 48 kvs = append(kvs, genKVsFromCollsUpdates(collsForNs)...) 49 } 50 kvs[0].Namespace = ns 51 kvWrites = append(kvWrites, kvs...) 52 } 53 updates := &Updates{ 54 Kvwrites: kvWrites, 55 } 56 57 batchBytes, err := proto.Marshal(updates) 58 return batchBytes, errors.Wrap(err, "error constructing deterministic bytes from update batch") 59 } 60 61 func genKVsFromNsUpdates(nsUpdates map[string]*statedb.VersionedValue) []*KVWrite { 62 return genKVs(nsUpdates) 63 } 64 65 func genKVsFromCollsUpdates(collsUpdates privacyenabledstate.NsBatch) []*KVWrite { 66 collNames := collsUpdates.GetCollectionNames() 67 sort.Strings(collNames) 68 collsKVWrites := []*KVWrite{} 69 for _, collName := range collNames { 70 collUpdates := collsUpdates.GetCollectionUpdates(collName) 71 kvs := genKVs(collUpdates) 72 kvs[0].Collection = collName 73 collsKVWrites = append(collsKVWrites, kvs...) 74 } 75 return collsKVWrites 76 } 77 78 func genKVs(updates map[string]*statedb.VersionedValue) []*KVWrite { 79 keys := util.GetSortedKeys(updates) 80 kvWrites := []*KVWrite{} 81 for _, key := range keys { 82 val := updates[key] 83 kvWrites = append( 84 kvWrites, 85 &KVWrite{ 86 Key: []byte(key), 87 Value: val.Value, 88 IsDelete: val.Value == nil, 89 VersionBytes: val.Version.ToBytes(), 90 }, 91 ) 92 } 93 return kvWrites 94 } 95 96 func dedupAndSort(a, b []string) []string { 97 m := map[string]struct{}{} 98 for _, entry := range a { 99 m[entry] = struct{}{} 100 } 101 for _, entry := range b { 102 m[entry] = struct{}{} 103 } 104 return util.GetSortedKeys(m) 105 }