github.com/Hnampk/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  }