github.com/defanghe/fabric@v2.1.1+incompatible/core/common/validation/statebased/validator_keylevel.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package statebased 8 9 import ( 10 "sync" 11 12 "github.com/hyperledger/fabric-protos-go/common" 13 "github.com/hyperledger/fabric-protos-go/peer" 14 commonerrors "github.com/hyperledger/fabric/common/errors" 15 validation "github.com/hyperledger/fabric/core/handlers/validation/api/policies" 16 "github.com/hyperledger/fabric/core/ledger" 17 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 18 "github.com/hyperledger/fabric/protoutil" 19 "github.com/pkg/errors" 20 ) 21 22 type epEvaluator interface { 23 CheckCCEPIfNotChecked(cc, coll string, blockNum, txNum uint64, sd []*protoutil.SignedData) commonerrors.TxValidationError 24 CheckCCEPIfNoEPChecked(cc string, blockNum, txNum uint64, sd []*protoutil.SignedData) commonerrors.TxValidationError 25 SBEPChecked() 26 } 27 28 /**********************************************************************************************************/ 29 /**********************************************************************************************************/ 30 31 type baseEvaluator struct { 32 epEvaluator 33 vpmgr KeyLevelValidationParameterManager 34 policySupport validation.PolicyEvaluator 35 } 36 37 func (p *baseEvaluator) checkSBAndCCEP(cc, coll, key string, blockNum, txNum uint64, signatureSet []*protoutil.SignedData) commonerrors.TxValidationError { 38 // see if there is a key-level validation parameter for this key 39 vp, err := p.vpmgr.GetValidationParameterForKey(cc, coll, key, blockNum, txNum) 40 if err != nil { 41 // error handling for GetValidationParameterForKey follows this rationale: 42 switch err := errors.Cause(err).(type) { 43 // 1) if there is a conflict because validation params have been updated 44 // by another transaction in this block, we will get ValidationParameterUpdatedError. 45 // This should lead to invalidating the transaction by calling policyErr 46 case *ValidationParameterUpdatedError: 47 return policyErr(err) 48 // 2) if the ledger returns "determinstic" errors, that is, errors that 49 // every peer in the channel will also return (such as errors linked to 50 // an attempt to retrieve metadata from a non-defined collection) should be 51 // logged and ignored. The ledger will take the most appropriate action 52 // when performing its side of the validation. 53 case *ledger.CollConfigNotDefinedError, *ledger.InvalidCollNameError: 54 logger.Warningf(errors.WithMessage(err, "skipping key-level validation").Error()) 55 err = nil 56 // 3) any other type of error should return an execution failure which will 57 // lead to halting the processing on this channel. Note that any non-categorized 58 // deterministic error would be caught by the default and would lead to 59 // a processing halt. This would certainly be a bug, but - in the absence of a 60 // single, well-defined deterministic error returned by the ledger, it is 61 // best to err on the side of caution and rather halt processing (because a 62 // deterministic error is treated like an I/O one) rather than risking a fork 63 // (in case an I/O error is treated as a deterministic one). 64 default: 65 return &commonerrors.VSCCExecutionFailureError{ 66 Err: err, 67 } 68 } 69 } 70 71 // if no key-level validation parameter has been specified, the regular cc endorsement policy needs to hold 72 if len(vp) == 0 { 73 return p.CheckCCEPIfNotChecked(cc, coll, blockNum, txNum, signatureSet) 74 } 75 76 // validate against key-level vp 77 err = p.policySupport.Evaluate(vp, signatureSet) 78 if err != nil { 79 return policyErr(errors.Wrapf(err, "validation of key %s (coll'%s':ns'%s') in tx %d:%d failed", key, coll, cc, blockNum, txNum)) 80 } 81 82 p.SBEPChecked() 83 84 return nil 85 } 86 87 func (p *baseEvaluator) Evaluate(blockNum, txNum uint64, NsRwSets []*rwsetutil.NsRwSet, ns string, sd []*protoutil.SignedData) commonerrors.TxValidationError { 88 // iterate over all writes in the rwset 89 for _, nsRWSet := range NsRwSets { 90 // skip other namespaces 91 if nsRWSet.NameSpace != ns { 92 continue 93 } 94 95 // public writes 96 // we validate writes against key-level validation parameters 97 // if any are present or the chaincode-wide endorsement policy 98 for _, pubWrite := range nsRWSet.KvRwSet.Writes { 99 err := p.checkSBAndCCEP(ns, "", pubWrite.Key, blockNum, txNum, sd) 100 if err != nil { 101 return err 102 } 103 } 104 // public metadata writes 105 // we validate writes against key-level validation parameters 106 // if any are present or the chaincode-wide endorsement policy 107 for _, pubMdWrite := range nsRWSet.KvRwSet.MetadataWrites { 108 err := p.checkSBAndCCEP(ns, "", pubMdWrite.Key, blockNum, txNum, sd) 109 if err != nil { 110 return err 111 } 112 } 113 // writes in collections 114 // we validate writes against key-level validation parameters 115 // if any are present or the chaincode-wide endorsement policy 116 for _, collRWSet := range nsRWSet.CollHashedRwSets { 117 coll := collRWSet.CollectionName 118 for _, hashedWrite := range collRWSet.HashedRwSet.HashedWrites { 119 key := string(hashedWrite.KeyHash) 120 err := p.checkSBAndCCEP(ns, coll, key, blockNum, txNum, sd) 121 if err != nil { 122 return err 123 } 124 } 125 } 126 // metadata writes in collections 127 // we validate writes against key-level validation parameters 128 // if any are present or the chaincode-wide endorsement policy 129 for _, collRWSet := range nsRWSet.CollHashedRwSets { 130 coll := collRWSet.CollectionName 131 for _, hashedMdWrite := range collRWSet.HashedRwSet.MetadataWrites { 132 key := string(hashedMdWrite.KeyHash) 133 err := p.checkSBAndCCEP(ns, coll, key, blockNum, txNum, sd) 134 if err != nil { 135 return err 136 } 137 } 138 } 139 } 140 141 // we make sure that we check at least the CCEP to honour FAB-9473 142 return p.CheckCCEPIfNoEPChecked(ns, blockNum, txNum, sd) 143 } 144 145 /**********************************************************************************************************/ 146 /**********************************************************************************************************/ 147 148 // RWSetPolicyEvaluatorFactory is a factory for policy evaluators 149 type RWSetPolicyEvaluatorFactory interface { 150 // Evaluator returns a new policy evaluator 151 // given the supplied chaincode endorsement policy 152 Evaluator(ccEP []byte) RWSetPolicyEvaluator 153 } 154 155 // RWSetPolicyEvaluator provides means to evaluate transaction artefacts 156 type RWSetPolicyEvaluator interface { 157 Evaluate(blockNum, txNum uint64, NsRwSets []*rwsetutil.NsRwSet, ns string, sd []*protoutil.SignedData) commonerrors.TxValidationError 158 } 159 160 /**********************************************************************************************************/ 161 /**********************************************************************************************************/ 162 163 type blockDependency struct { 164 mutex sync.Mutex 165 blockNum uint64 166 txDepOnce []sync.Once 167 } 168 169 // KeyLevelValidator implements per-key level ep validation 170 type KeyLevelValidator struct { 171 vpmgr KeyLevelValidationParameterManager 172 blockDep blockDependency 173 pef RWSetPolicyEvaluatorFactory 174 } 175 176 func NewKeyLevelValidator(evaluator RWSetPolicyEvaluatorFactory, vpmgr KeyLevelValidationParameterManager) *KeyLevelValidator { 177 return &KeyLevelValidator{ 178 vpmgr: vpmgr, 179 blockDep: blockDependency{}, 180 pef: evaluator, 181 } 182 } 183 184 func (klv *KeyLevelValidator) extractDependenciesForTx(blockNum, txNum uint64, envelopeBytes []byte) { 185 env, err := protoutil.GetEnvelopeFromBlock(envelopeBytes) 186 if err != nil { 187 logger.Warningf("while executing GetEnvelopeFromBlock got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum) 188 return 189 } 190 191 payl, err := protoutil.UnmarshalPayload(env.Payload) 192 if err != nil { 193 logger.Warningf("while executing GetPayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum) 194 return 195 } 196 197 tx, err := protoutil.UnmarshalTransaction(payl.Data) 198 if err != nil { 199 logger.Warningf("while executing GetTransaction got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum) 200 return 201 } 202 203 cap, err := protoutil.UnmarshalChaincodeActionPayload(tx.Actions[0].Payload) 204 if err != nil { 205 logger.Warningf("while executing GetChaincodeActionPayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum) 206 return 207 } 208 209 pRespPayload, err := protoutil.UnmarshalProposalResponsePayload(cap.Action.ProposalResponsePayload) 210 if err != nil { 211 logger.Warningf("while executing GetProposalResponsePayload got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum) 212 return 213 } 214 215 respPayload, err := protoutil.UnmarshalChaincodeAction(pRespPayload.Extension) 216 if err != nil { 217 logger.Warningf("while executing GetChaincodeAction got error '%s', skipping tx at height (%d,%d)", err, blockNum, txNum) 218 return 219 } 220 221 klv.vpmgr.ExtractValidationParameterDependency(blockNum, txNum, respPayload.Results) 222 } 223 224 // PreValidate implements the function of the StateBasedValidator interface 225 func (klv *KeyLevelValidator) PreValidate(txNum uint64, block *common.Block) { 226 klv.blockDep.mutex.Lock() 227 if klv.blockDep.blockNum != block.Header.Number { 228 klv.blockDep.blockNum = block.Header.Number 229 klv.blockDep.txDepOnce = make([]sync.Once, len(block.Data.Data)) 230 } 231 klv.blockDep.mutex.Unlock() 232 233 for i := int64(txNum); i >= 0; i-- { 234 txPosition := uint64(i) 235 236 klv.blockDep.txDepOnce[i].Do( 237 func() { 238 klv.extractDependenciesForTx(block.Header.Number, txPosition, block.Data.Data[txPosition]) 239 }) 240 } 241 } 242 243 // Validate implements the function of the StateBasedValidator interface 244 func (klv *KeyLevelValidator) Validate(cc string, blockNum, txNum uint64, rwsetBytes, prp, ccEP []byte, endorsements []*peer.Endorsement) commonerrors.TxValidationError { 245 // construct signature set 246 signatureSet := []*protoutil.SignedData{} 247 for _, endorsement := range endorsements { 248 data := make([]byte, len(prp)+len(endorsement.Endorser)) 249 copy(data, prp) 250 copy(data[len(prp):], endorsement.Endorser) 251 252 signatureSet = append(signatureSet, &protoutil.SignedData{ 253 // set the data that is signed; concatenation of proposal response bytes and endorser ID 254 Data: data, 255 // set the identity that signs the message: it's the endorser 256 Identity: endorsement.Endorser, 257 // set the signature 258 Signature: endorsement.Signature}) 259 } 260 261 // construct the policy checker object 262 policyEvaluator := klv.pef.Evaluator(ccEP) 263 264 // unpack the rwset 265 rwset := &rwsetutil.TxRwSet{} 266 if err := rwset.FromProtoBytes(rwsetBytes); err != nil { 267 return policyErr(errors.WithMessagef(err, "txRWSet.FromProtoBytes failed on tx (%d,%d)", blockNum, txNum)) 268 } 269 270 // return the decision of the policy evaluator 271 return policyEvaluator.Evaluate(blockNum, txNum, rwset.NsRwSets, cc, signatureSet) 272 } 273 274 // PostValidate implements the function of the StateBasedValidator interface 275 func (klv *KeyLevelValidator) PostValidate(cc string, blockNum, txNum uint64, err error) { 276 klv.vpmgr.SetTxValidationResult(cc, blockNum, txNum, err) 277 } 278 279 func policyErr(err error) *commonerrors.VSCCEndorsementPolicyError { 280 return &commonerrors.VSCCEndorsementPolicyError{ 281 Err: err, 282 } 283 }