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