github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/hashcheck_pvtdata.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package kvledger
     8  
     9  import (
    10  	"bytes"
    11  
    12  	"github.com/hechain20/hechain/common/ledger/blkstorage"
    13  	"github.com/hechain20/hechain/common/util"
    14  	"github.com/hechain20/hechain/core/ledger"
    15  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil"
    16  	"github.com/hechain20/hechain/core/ledger/pvtdatastorage"
    17  	"github.com/hechain20/hechain/protoutil"
    18  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    19  )
    20  
    21  // constructValidAndInvalidPvtData computes the valid pvt data and hash mismatch list
    22  // from a received pvt data list of old blocks. The reconciled data elements that belong
    23  // to a block which is not available in the blockstore (i.e., the block is prior to or equal
    24  // to the lastBlockInBootSnapshot), the boot KV hashes in the private data store are used for
    25  // verifying the hashes. Further, in this case, the write-set of a collection is trimmed in order
    26  // to remove the key-values that were not present in the snapshot (most likely because, they
    27  //	were over-written in a later version)
    28  func constructValidAndInvalidPvtData(
    29  	reconciledPvtdata []*ledger.ReconciledPvtdata,
    30  	blockStore *blkstorage.BlockStore,
    31  	pvtdataStore *pvtdatastorage.Store,
    32  	lastBlockInBootSnapshot uint64,
    33  ) (
    34  	map[uint64][]*ledger.TxPvtData, []*ledger.PvtdataHashMismatch, error,
    35  ) {
    36  	// for each block, for each transaction, verify the hash of pvtRwSet
    37  	// present in the received data txPvtData.
    38  	// On a mismatch, add an entry to hashMismatch list.
    39  	// On a match, add the pvtData to the validPvtData list
    40  	validPvtData := make(map[uint64][]*ledger.TxPvtData)
    41  	var invalidPvtData []*ledger.PvtdataHashMismatch
    42  
    43  	for _, pvtdata := range reconciledPvtdata {
    44  		var validData []*ledger.TxPvtData
    45  		var invalidData []*ledger.PvtdataHashMismatch
    46  		var err error
    47  
    48  		if pvtdata.BlockNum <= lastBlockInBootSnapshot {
    49  			validData, invalidData, err = verifyHashesViaBootKVHashes(pvtdata, pvtdataStore)
    50  		} else {
    51  			validData, invalidData, err = verifyHashesFromBlockStore(pvtdata, blockStore)
    52  		}
    53  		if err != nil {
    54  			return nil, nil, err
    55  		}
    56  		if len(validData) > 0 {
    57  			validPvtData[pvtdata.BlockNum] = validData
    58  		}
    59  		invalidPvtData = append(invalidPvtData, invalidData...)
    60  	} // for each block's pvtData
    61  	return validPvtData, invalidPvtData, nil
    62  }
    63  
    64  func verifyHashesFromBlockStore(reconciledPvtdata *ledger.ReconciledPvtdata, blockStore *blkstorage.BlockStore) (
    65  	[]*ledger.TxPvtData, []*ledger.PvtdataHashMismatch, error,
    66  ) {
    67  	var validPvtData []*ledger.TxPvtData
    68  	var invalidPvtData []*ledger.PvtdataHashMismatch
    69  	for _, txPvtData := range reconciledPvtdata.WriteSets {
    70  		// (1) retrieve the txrwset from the blockstore
    71  		logger.Debugf("Retrieving rwset of blockNum:[%d], txNum:[%d]", reconciledPvtdata.BlockNum, txPvtData.SeqInBlock)
    72  		txRWSet, err := retrieveRwsetForTx(reconciledPvtdata.BlockNum, txPvtData.SeqInBlock, blockStore)
    73  		if err != nil {
    74  			return nil, nil, err
    75  		}
    76  
    77  		// (2) validate passed pvtData against the pvtData hash in the tx rwset.
    78  		logger.Debugf("Constructing valid and invalid pvtData using rwset of blockNum:[%d], txNum:[%d]",
    79  			reconciledPvtdata.BlockNum, txPvtData.SeqInBlock)
    80  		validData, invalidData := findValidAndInvalidTxPvtData(txPvtData, txRWSet, reconciledPvtdata.BlockNum)
    81  
    82  		// (3) append validData to validPvtDataPvt list of this block and
    83  		// invalidData to invalidPvtData list
    84  		if validData != nil {
    85  			validPvtData = append(validPvtData, validData)
    86  		}
    87  		invalidPvtData = append(invalidPvtData, invalidData...)
    88  	} // for each tx's pvtData
    89  	return validPvtData, invalidPvtData, nil
    90  }
    91  
    92  func retrieveRwsetForTx(blkNum uint64, txNum uint64, blockStore *blkstorage.BlockStore) (*rwsetutil.TxRwSet, error) {
    93  	// retrieve the txEnvelope from the block store so that the hash of
    94  	// the pvtData can be retrieved for comparison
    95  	txEnvelope, err := blockStore.RetrieveTxByBlockNumTranNum(blkNum, txNum)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	// retrieve pvtRWset hash from the txEnvelope
   100  	responsePayload, err := protoutil.GetActionFromEnvelopeMsg(txEnvelope)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	txRWSet := &rwsetutil.TxRwSet{}
   105  	if err := txRWSet.FromProtoBytes(responsePayload.Results); err != nil {
   106  		return nil, err
   107  	}
   108  	return txRWSet, nil
   109  }
   110  
   111  func findValidAndInvalidTxPvtData(txPvtData *ledger.TxPvtData, txRWSet *rwsetutil.TxRwSet, blkNum uint64) (
   112  	*ledger.TxPvtData, []*ledger.PvtdataHashMismatch,
   113  ) {
   114  	var invalidPvtData []*ledger.PvtdataHashMismatch
   115  	var toDeleteNsColl []*nsColl
   116  	// Compare the hash of pvtData with the hash present in the rwset to
   117  	// find valid and invalid pvt data
   118  	for _, nsRwset := range txPvtData.WriteSet.NsPvtRwset {
   119  		txNum := txPvtData.SeqInBlock
   120  		invalidData, invalidNsColl := findInvalidNsPvtData(nsRwset, txRWSet, blkNum, txNum)
   121  		invalidPvtData = append(invalidPvtData, invalidData...)
   122  		toDeleteNsColl = append(toDeleteNsColl, invalidNsColl...)
   123  	}
   124  	for _, nsColl := range toDeleteNsColl {
   125  		removeCollFromTxPvtReadWriteSet(txPvtData.WriteSet, nsColl.ns, nsColl.coll)
   126  	}
   127  	if len(txPvtData.WriteSet.NsPvtRwset) == 0 {
   128  		// denotes that all namespaces had
   129  		// invalid pvt data
   130  		return nil, invalidPvtData
   131  	}
   132  	return txPvtData, invalidPvtData
   133  }
   134  
   135  // Remove removes the rwset for the given <ns, coll> tuple. If after this removal,
   136  // there are no more collection in the namespace <ns>, the whole namespace entry is removed
   137  func removeCollFromTxPvtReadWriteSet(p *rwset.TxPvtReadWriteSet, ns, coll string) {
   138  	for i := 0; i < len(p.NsPvtRwset); i++ {
   139  		n := p.NsPvtRwset[i]
   140  		if n.Namespace != ns {
   141  			continue
   142  		}
   143  		removeCollFromNsPvtWriteSet(n, coll)
   144  		if len(n.CollectionPvtRwset) == 0 {
   145  			p.NsPvtRwset = append(p.NsPvtRwset[:i], p.NsPvtRwset[i+1:]...)
   146  		}
   147  		return
   148  	}
   149  }
   150  
   151  func removeCollFromNsPvtWriteSet(n *rwset.NsPvtReadWriteSet, collName string) {
   152  	for i := 0; i < len(n.CollectionPvtRwset); i++ {
   153  		c := n.CollectionPvtRwset[i]
   154  		if c.CollectionName != collName {
   155  			continue
   156  		}
   157  		n.CollectionPvtRwset = append(n.CollectionPvtRwset[:i], n.CollectionPvtRwset[i+1:]...)
   158  		return
   159  	}
   160  }
   161  
   162  type nsColl struct {
   163  	ns, coll string
   164  }
   165  
   166  func findInvalidNsPvtData(nsRwset *rwset.NsPvtReadWriteSet, txRWSet *rwsetutil.TxRwSet, blkNum, txNum uint64) (
   167  	[]*ledger.PvtdataHashMismatch, []*nsColl,
   168  ) {
   169  	var invalidPvtData []*ledger.PvtdataHashMismatch
   170  	var invalidNsColl []*nsColl
   171  
   172  	ns := nsRwset.Namespace
   173  	for _, collPvtRwset := range nsRwset.CollectionPvtRwset {
   174  		coll := collPvtRwset.CollectionName
   175  		rwsetHash := txRWSet.GetPvtDataHash(ns, coll)
   176  		if rwsetHash == nil {
   177  			logger.Warningf("namespace: %s collection: %s was not accessed by txNum %d in BlkNum %d. "+
   178  				"Unnecessary pvtdata has been passed", ns, coll, txNum, blkNum)
   179  			invalidNsColl = append(invalidNsColl, &nsColl{ns, coll})
   180  			continue
   181  		}
   182  
   183  		if !bytes.Equal(util.ComputeSHA256(collPvtRwset.Rwset), rwsetHash) {
   184  			invalidPvtData = append(invalidPvtData, &ledger.PvtdataHashMismatch{
   185  				BlockNum:   blkNum,
   186  				TxNum:      txNum,
   187  				Namespace:  ns,
   188  				Collection: coll,
   189  			})
   190  			invalidNsColl = append(invalidNsColl, &nsColl{ns, coll})
   191  		}
   192  	}
   193  	return invalidPvtData, invalidNsColl
   194  }
   195  
   196  func verifyHashesViaBootKVHashes(reconciledPvtdata *ledger.ReconciledPvtdata, pvtdataStore *pvtdatastorage.Store) (
   197  	[]*ledger.TxPvtData, []*ledger.PvtdataHashMismatch, error,
   198  ) {
   199  	var validPvtData []*ledger.TxPvtData
   200  	var invalidPvtData []*ledger.PvtdataHashMismatch
   201  
   202  	blkNum := reconciledPvtdata.BlockNum
   203  
   204  	for txNum, txData := range reconciledPvtdata.WriteSets { // Tx loop
   205  		var toDeleteNsColl []*nsColl
   206  
   207  		reconTx, err := rwsetutil.TxPvtRwSetFromProtoMsg(txData.WriteSet)
   208  		if err != nil {
   209  			continue
   210  		}
   211  
   212  		for _, reconNS := range reconTx.NsPvtRwSet { // Ns Loop
   213  			for _, reconColl := range reconNS.CollPvtRwSets { // coll loop
   214  				if reconColl.KvRwSet == nil || len(reconColl.KvRwSet.Writes) == 0 {
   215  					toDeleteNsColl = append(toDeleteNsColl,
   216  						&nsColl{
   217  							ns:   reconNS.NameSpace,
   218  							coll: reconColl.CollectionName,
   219  						},
   220  					)
   221  					continue
   222  				}
   223  
   224  				expectedKVHashes, err := pvtdataStore.FetchBootKVHashes(blkNum, txNum, reconNS.NameSpace, reconColl.CollectionName)
   225  				if err != nil {
   226  					return nil, nil, err
   227  				}
   228  				if len(expectedKVHashes) == 0 {
   229  					toDeleteNsColl = append(toDeleteNsColl,
   230  						&nsColl{
   231  							ns:   reconNS.NameSpace,
   232  							coll: reconColl.CollectionName,
   233  						},
   234  					)
   235  					continue
   236  				}
   237  
   238  				anyKVMismatch := false
   239  				numKVsRecieved := 0
   240  				keysVisited := map[string]struct{}{}
   241  
   242  				for _, reconKV := range reconColl.KvRwSet.Writes {
   243  					_, ok := keysVisited[reconKV.Key]
   244  					if ok {
   245  						anyKVMismatch = true
   246  						break
   247  					}
   248  					keysVisited[reconKV.Key] = struct{}{}
   249  
   250  					reconKeyHash := util.ComputeSHA256([]byte(reconKV.Key))
   251  					reconValHash := util.ComputeSHA256(reconKV.Value)
   252  
   253  					expectedValHash, ok := expectedKVHashes[string(reconKeyHash)]
   254  					if ok {
   255  						numKVsRecieved++
   256  						if !bytes.Equal(expectedValHash, reconValHash) {
   257  							anyKVMismatch = true
   258  							break
   259  						}
   260  					}
   261  				}
   262  
   263  				if anyKVMismatch || numKVsRecieved < len(expectedKVHashes) {
   264  					invalidPvtData = append(invalidPvtData,
   265  						&ledger.PvtdataHashMismatch{
   266  							BlockNum:   blkNum,
   267  							TxNum:      txNum,
   268  							Namespace:  reconNS.NameSpace,
   269  							Collection: reconColl.CollectionName,
   270  						},
   271  					)
   272  					toDeleteNsColl = append(toDeleteNsColl,
   273  						&nsColl{
   274  							ns:   reconNS.NameSpace,
   275  							coll: reconColl.CollectionName,
   276  						},
   277  					)
   278  					continue
   279  				}
   280  			} // end coll loop
   281  		} // end Ns loop
   282  
   283  		for _, nsColl := range toDeleteNsColl {
   284  			removeCollFromTxPvtReadWriteSet(txData.WriteSet, nsColl.ns, nsColl.coll)
   285  		}
   286  
   287  		if len(txData.WriteSet.NsPvtRwset) > 0 {
   288  			validPvtData = append(validPvtData, txData)
   289  		}
   290  	} // end Tx loop
   291  	return validPvtData, invalidPvtData, nil
   292  }