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  }