github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/committer/txvalidator/v20/validator.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package txvalidator 8 9 import ( 10 "context" 11 "fmt" 12 "time" 13 14 "github.com/golang/protobuf/proto" 15 "github.com/hyperledger/fabric-protos-go/common" 16 mspprotos "github.com/hyperledger/fabric-protos-go/msp" 17 "github.com/hyperledger/fabric-protos-go/peer" 18 "github.com/hyperledger/fabric/bccsp" 19 "github.com/hyperledger/fabric/common/channelconfig" 20 "github.com/hyperledger/fabric/common/configtx" 21 commonerrors "github.com/hyperledger/fabric/common/errors" 22 "github.com/hyperledger/fabric/common/flogging" 23 "github.com/hyperledger/fabric/common/policies" 24 "github.com/hyperledger/fabric/core/committer/txvalidator/plugin" 25 "github.com/hyperledger/fabric/core/committer/txvalidator/v20/plugindispatcher" 26 "github.com/hyperledger/fabric/core/common/validation" 27 "github.com/hyperledger/fabric/core/ledger" 28 ledgerUtil "github.com/hyperledger/fabric/core/ledger/util" 29 "github.com/hyperledger/fabric/msp" 30 "github.com/hyperledger/fabric/protoutil" 31 "github.com/pkg/errors" 32 ) 33 34 // Semaphore provides to the validator means for synchronisation 35 type Semaphore interface { 36 // Acquire implements semaphore-like acquire semantics 37 Acquire(ctx context.Context) error 38 39 // Release implements semaphore-like release semantics 40 Release() 41 } 42 43 // ChannelResources provides access to channel artefacts or 44 // functions to interact with them 45 type ChannelResources interface { 46 // MSPManager returns the MSP manager for this channel 47 MSPManager() msp.MSPManager 48 49 // Apply attempts to apply a configtx to become the new config 50 Apply(configtx *common.ConfigEnvelope) error 51 52 // GetMSPIDs returns the IDs for the application MSPs 53 // that have been defined in the channel 54 GetMSPIDs() []string 55 56 // Capabilities defines the capabilities for the application portion of this channel 57 Capabilities() channelconfig.ApplicationCapabilities 58 } 59 60 // LedgerResources provides access to ledger artefacts or 61 // functions to interact with them 62 type LedgerResources interface { 63 // GetTransactionByID retrieves a transaction by id 64 GetTransactionByID(txID string) (*peer.ProcessedTransaction, error) 65 66 // NewQueryExecutor gives handle to a query executor. 67 // A client can obtain more than one 'QueryExecutor's for parallel execution. 68 // Any synchronization should be performed at the implementation level if required 69 NewQueryExecutor() (ledger.QueryExecutor, error) 70 } 71 72 // Dispatcher is an interface to decouple tx validator 73 // and plugin dispatcher 74 type Dispatcher interface { 75 // Dispatch invokes the appropriate validation plugin for the supplied transaction in the block 76 Dispatch(seq int, payload *common.Payload, envBytes []byte, block *common.Block) (error, peer.TxValidationCode) 77 } 78 79 //go:generate mockery -dir . -name ChannelResources -case underscore -output mocks/ 80 //go:generate mockery -dir . -name LedgerResources -case underscore -output mocks/ 81 //go:generate mockery -dir . -name Dispatcher -case underscore -output mocks/ 82 83 //go:generate mockery -dir . -name QueryExecutor -case underscore -output mocks/ 84 85 // QueryExecutor is the local interface that used to generate mocks for foreign interface. 86 type QueryExecutor interface { 87 ledger.QueryExecutor 88 } 89 90 //go:generate mockery -dir . -name ChannelPolicyManagerGetter -case underscore -output mocks/ 91 92 // ChannelPolicyManagerGetter is the local interface that used to generate mocks for foreign interface. 93 type ChannelPolicyManagerGetter interface { 94 policies.ChannelPolicyManagerGetter 95 } 96 97 //go:generate mockery -dir . -name PolicyManager -case underscore -output mocks/ 98 99 type PolicyManager interface { 100 policies.Manager 101 } 102 103 //go:generate mockery -dir plugindispatcher/ -name CollectionResources -case underscore -output mocks/ 104 105 // TxValidator is the implementation of Validator interface, keeps 106 // reference to the ledger to enable tx simulation 107 // and execution of plugins 108 type TxValidator struct { 109 ChannelID string 110 Semaphore Semaphore 111 ChannelResources ChannelResources 112 LedgerResources LedgerResources 113 Dispatcher Dispatcher 114 CryptoProvider bccsp.BCCSP 115 } 116 117 var logger = flogging.MustGetLogger("committer.txvalidator") 118 119 type blockValidationRequest struct { 120 block *common.Block 121 d []byte 122 tIdx int 123 } 124 125 type blockValidationResult struct { 126 tIdx int 127 validationCode peer.TxValidationCode 128 err error 129 txid string 130 } 131 132 // NewTxValidator creates new transactions validator 133 func NewTxValidator( 134 channelID string, 135 sem Semaphore, 136 cr ChannelResources, 137 ler LedgerResources, 138 lcr plugindispatcher.LifecycleResources, 139 cor plugindispatcher.CollectionResources, 140 pm plugin.Mapper, 141 channelPolicyManagerGetter policies.ChannelPolicyManagerGetter, 142 cryptoProvider bccsp.BCCSP, 143 ) *TxValidator { 144 // Encapsulates interface implementation 145 pluginValidator := plugindispatcher.NewPluginValidator(pm, ler, &dynamicDeserializer{cr: cr}, &dynamicCapabilities{cr: cr}, channelPolicyManagerGetter, cor) 146 return &TxValidator{ 147 ChannelID: channelID, 148 Semaphore: sem, 149 ChannelResources: cr, 150 LedgerResources: ler, 151 Dispatcher: plugindispatcher.New(channelID, cr, ler, lcr, pluginValidator), 152 CryptoProvider: cryptoProvider, 153 } 154 } 155 156 func (v *TxValidator) chainExists(chain string) bool { 157 // TODO: implement this function! 158 return true 159 } 160 161 // Validate performs the validation of a block. The validation 162 // of each transaction in the block is performed in parallel. 163 // The approach is as follows: the committer thread starts the 164 // tx validation function in a goroutine (using a semaphore to cap 165 // the number of concurrent validating goroutines). The committer 166 // thread then reads results of validation (in orderer of completion 167 // of the goroutines) from the results channel. The goroutines 168 // perform the validation of the txs in the block and enqueue the 169 // validation result in the results channel. A few note-worthy facts: 170 // 1) to keep the approach simple, the committer thread enqueues 171 // all transactions in the block and then moves on to reading the 172 // results. 173 // 2) for parallel validation to work, it is important that the 174 // validation function does not change the state of the system. 175 // Otherwise the order in which validation is perform matters 176 // and we have to resort to sequential validation (or some locking). 177 // This is currently true, because the only function that affects 178 // state is when a config transaction is received, but they are 179 // guaranteed to be alone in the block. If/when this assumption 180 // is violated, this code must be changed. 181 func (v *TxValidator) Validate(block *common.Block) error { 182 var err error 183 var errPos int 184 185 startValidation := time.Now() // timer to log Validate block duration 186 logger.Debugf("[%s] START Block Validation for block [%d]", v.ChannelID, block.Header.Number) 187 188 // Initialize trans as valid here, then set invalidation reason code upon invalidation below 189 txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data)) 190 // array of txids 191 txidArray := make([]string, len(block.Data.Data)) 192 193 results := make(chan *blockValidationResult) 194 go func() { 195 for tIdx, d := range block.Data.Data { 196 // ensure that we don't have too many concurrent validation workers 197 v.Semaphore.Acquire(context.Background()) 198 199 go func(index int, data []byte) { 200 defer v.Semaphore.Release() 201 202 v.validateTx(&blockValidationRequest{ 203 d: data, 204 block: block, 205 tIdx: index, 206 }, results) 207 }(tIdx, d) 208 } 209 }() 210 211 logger.Debugf("expecting %d block validation responses", len(block.Data.Data)) 212 213 // now we read responses in the order in which they come back 214 for i := 0; i < len(block.Data.Data); i++ { 215 res := <-results 216 217 if res.err != nil { 218 // if there is an error, we buffer its value, wait for 219 // all workers to complete validation and then return 220 // the error from the first tx in this block that returned an error 221 logger.Debugf("got terminal error %s for idx %d", res.err, res.tIdx) 222 223 if err == nil || res.tIdx < errPos { 224 err = res.err 225 errPos = res.tIdx 226 } 227 } else { 228 // if there was no error, we set the txsfltr and we set the 229 // txsChaincodeNames and txsUpgradedChaincodes maps 230 logger.Debugf("got result for idx %d, code %d", res.tIdx, res.validationCode) 231 232 txsfltr.SetFlag(res.tIdx, res.validationCode) 233 234 if res.validationCode == peer.TxValidationCode_VALID { 235 txidArray[res.tIdx] = res.txid 236 } 237 } 238 } 239 240 // if we're here, all workers have completed the validation. 241 // If there was an error we return the error from the first 242 // tx in this block that returned an error 243 if err != nil { 244 return err 245 } 246 247 // we mark invalid any transaction that has a txid 248 // which is equal to that of a previous tx in this block 249 markTXIdDuplicates(txidArray, txsfltr) 250 251 // make sure no transaction has skipped validation 252 err = v.allValidated(txsfltr, block) 253 if err != nil { 254 return err 255 } 256 257 // Initialize metadata structure 258 protoutil.InitBlockMetadata(block) 259 260 block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr 261 262 elapsedValidation := time.Since(startValidation) / time.Millisecond // duration in ms 263 logger.Infof("[%s] Validated block [%d] in %dms", v.ChannelID, block.Header.Number, elapsedValidation) 264 265 return nil 266 } 267 268 // allValidated returns error if some of the validation flags have not been set 269 // during validation 270 func (v *TxValidator) allValidated(txsfltr ledgerUtil.TxValidationFlags, block *common.Block) error { 271 for id, f := range txsfltr { 272 if peer.TxValidationCode(f) == peer.TxValidationCode_NOT_VALIDATED { 273 return errors.Errorf("transaction %d in block %d has skipped validation", id, block.Header.Number) 274 } 275 } 276 277 return nil 278 } 279 280 func markTXIdDuplicates(txids []string, txsfltr ledgerUtil.TxValidationFlags) { 281 txidMap := make(map[string]struct{}) 282 283 for id, txid := range txids { 284 if txid == "" { 285 continue 286 } 287 288 _, in := txidMap[txid] 289 if in { 290 logger.Error("Duplicate txid", txid, "found, skipping") 291 txsfltr.SetFlag(id, peer.TxValidationCode_DUPLICATE_TXID) 292 } else { 293 txidMap[txid] = struct{}{} 294 } 295 } 296 } 297 298 func (v *TxValidator) validateTx(req *blockValidationRequest, results chan<- *blockValidationResult) { 299 block := req.block 300 d := req.d 301 tIdx := req.tIdx 302 txID := "" 303 304 if d == nil { 305 results <- &blockValidationResult{ 306 tIdx: tIdx, 307 } 308 return 309 } 310 311 if env, err := protoutil.GetEnvelopeFromBlock(d); err != nil { 312 logger.Warningf("Error getting tx from block: %+v", err) 313 results <- &blockValidationResult{ 314 tIdx: tIdx, 315 validationCode: peer.TxValidationCode_INVALID_OTHER_REASON, 316 } 317 return 318 } else if env != nil { 319 // validate the transaction: here we check that the transaction 320 // is properly formed, properly signed and that the security 321 // chain binding proposal to endorsements to tx holds. We do 322 // NOT check the validity of endorsements, though. That's a 323 // job for the validation plugins 324 logger.Debugf("[%s] validateTx starts for block %p env %p txn %d", v.ChannelID, block, env, tIdx) 325 defer logger.Debugf("[%s] validateTx completes for block %p env %p txn %d", v.ChannelID, block, env, tIdx) 326 var payload *common.Payload 327 var err error 328 var txResult peer.TxValidationCode 329 330 if payload, txResult = validation.ValidateTransaction(env, v.CryptoProvider); txResult != peer.TxValidationCode_VALID { 331 logger.Errorf("Invalid transaction with index %d", tIdx) 332 results <- &blockValidationResult{ 333 tIdx: tIdx, 334 validationCode: txResult, 335 } 336 return 337 } 338 339 chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader) 340 if err != nil { 341 logger.Warningf("Could not unmarshal channel header, err %s, skipping", err) 342 results <- &blockValidationResult{ 343 tIdx: tIdx, 344 validationCode: peer.TxValidationCode_INVALID_OTHER_REASON, 345 } 346 return 347 } 348 349 channel := chdr.ChannelId 350 logger.Debugf("Transaction is for channel %s", channel) 351 352 if !v.chainExists(channel) { 353 logger.Errorf("Dropping transaction for non-existent channel %s", channel) 354 results <- &blockValidationResult{ 355 tIdx: tIdx, 356 validationCode: peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND, 357 } 358 return 359 } 360 361 if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { 362 363 txID = chdr.TxId 364 365 // Check duplicate transactions 366 erroneousResultEntry := v.checkTxIdDupsLedger(tIdx, chdr, v.LedgerResources) 367 if erroneousResultEntry != nil { 368 results <- erroneousResultEntry 369 return 370 } 371 372 // Validate tx with plugins 373 logger.Debug("Validating transaction with plugins") 374 err, cde := v.Dispatcher.Dispatch(tIdx, payload, d, block) 375 if err != nil { 376 logger.Errorf("Dispatch for transaction txId = %s returned error: %s", txID, err) 377 switch err.(type) { 378 case *commonerrors.VSCCExecutionFailureError: 379 results <- &blockValidationResult{ 380 tIdx: tIdx, 381 err: err, 382 } 383 return 384 case *commonerrors.VSCCInfoLookupFailureError: 385 results <- &blockValidationResult{ 386 tIdx: tIdx, 387 err: err, 388 } 389 return 390 default: 391 results <- &blockValidationResult{ 392 tIdx: tIdx, 393 validationCode: cde, 394 } 395 return 396 } 397 } 398 } else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG { 399 configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data) 400 if err != nil { 401 err = errors.WithMessage(err, "error unmarshalling config which passed initial validity checks") 402 logger.Criticalf("%+v", err) 403 results <- &blockValidationResult{ 404 tIdx: tIdx, 405 err: err, 406 } 407 return 408 } 409 410 if err := v.ChannelResources.Apply(configEnvelope); err != nil { 411 err = errors.WithMessage(err, "error validating config which passed initial validity checks") 412 logger.Criticalf("%+v", err) 413 results <- &blockValidationResult{ 414 tIdx: tIdx, 415 err: err, 416 } 417 return 418 } 419 logger.Debugf("config transaction received for chain %s", channel) 420 } else { 421 logger.Warningf("Unknown transaction type [%s] in block number [%d] transaction index [%d]", 422 common.HeaderType(chdr.Type), block.Header.Number, tIdx) 423 results <- &blockValidationResult{ 424 tIdx: tIdx, 425 validationCode: peer.TxValidationCode_UNKNOWN_TX_TYPE, 426 } 427 return 428 } 429 430 if _, err := proto.Marshal(env); err != nil { 431 logger.Warningf("Cannot marshal transaction: %s", err) 432 results <- &blockValidationResult{ 433 tIdx: tIdx, 434 validationCode: peer.TxValidationCode_MARSHAL_TX_ERROR, 435 } 436 return 437 } 438 // Succeeded to pass down here, transaction is valid 439 results <- &blockValidationResult{ 440 tIdx: tIdx, 441 validationCode: peer.TxValidationCode_VALID, 442 txid: txID, 443 } 444 return 445 } else { 446 logger.Warning("Nil tx from block") 447 results <- &blockValidationResult{ 448 tIdx: tIdx, 449 validationCode: peer.TxValidationCode_NIL_ENVELOPE, 450 } 451 return 452 } 453 } 454 455 // CheckTxIdDupsLedger returns a vlockValidationResult enhanced with the respective 456 // error codes if and only if there is transaction with the same transaction identifier 457 // in the ledger or no decision can be made for whether such transaction exists; 458 // the function returns nil if it has ensured that there is no such duplicate, such 459 // that its consumer can proceed with the transaction processing 460 func (v *TxValidator) checkTxIdDupsLedger(tIdx int, chdr *common.ChannelHeader, ldgr LedgerResources) *blockValidationResult { 461 462 // Retrieve the transaction identifier of the input header 463 txID := chdr.TxId 464 465 // Look for a transaction with the same identifier inside the ledger 466 _, err := ldgr.GetTransactionByID(txID) 467 468 switch err.(type) { 469 case nil: 470 // invalid case, returned error is nil. It means that there is already a tx in the ledger with the same id 471 logger.Error("Duplicate transaction found, ", txID, ", skipping") 472 return &blockValidationResult{ 473 tIdx: tIdx, 474 validationCode: peer.TxValidationCode_DUPLICATE_TXID, 475 } 476 case ledger.NotFoundInIndexErr: 477 // valid case, returned error is of type NotFoundInIndexErr. 478 // It means that no tx with the same id is found in the ledger 479 return nil 480 default: 481 // invalid case, returned error is not of type NotFoundInIndexErr. 482 // It means that we could not verify whether a tx with the supplied id is in the ledger 483 logger.Errorf("Ledger failure while attempting to detect duplicate status for txid %s: %s", txID, err) 484 return &blockValidationResult{ 485 tIdx: tIdx, 486 err: err, 487 } 488 } 489 } 490 491 // generateCCKey generates a unique identifier for chaincode in specific channel 492 func (v *TxValidator) generateCCKey(ccName, chainID string) string { 493 return fmt.Sprintf("%s/%s", ccName, chainID) 494 } 495 496 type dynamicDeserializer struct { 497 cr ChannelResources 498 } 499 500 func (ds *dynamicDeserializer) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) { 501 return ds.cr.MSPManager().DeserializeIdentity(serializedIdentity) 502 } 503 504 func (ds *dynamicDeserializer) IsWellFormed(identity *mspprotos.SerializedIdentity) error { 505 return ds.cr.MSPManager().IsWellFormed(identity) 506 } 507 508 type dynamicCapabilities struct { 509 cr ChannelResources 510 } 511 512 func (ds *dynamicCapabilities) ACLs() bool { 513 return ds.cr.Capabilities().ACLs() 514 } 515 516 func (ds *dynamicCapabilities) CollectionUpgrade() bool { 517 return ds.cr.Capabilities().CollectionUpgrade() 518 } 519 520 func (ds *dynamicCapabilities) ForbidDuplicateTXIdInBlock() bool { 521 return ds.cr.Capabilities().ForbidDuplicateTXIdInBlock() 522 } 523 524 func (ds *dynamicCapabilities) KeyLevelEndorsement() bool { 525 return ds.cr.Capabilities().KeyLevelEndorsement() 526 } 527 528 func (ds *dynamicCapabilities) MetadataLifecycle() bool { 529 // This capability no longer exists and should not be referenced in validation anyway 530 return false 531 } 532 533 func (ds *dynamicCapabilities) PrivateChannelData() bool { 534 return ds.cr.Capabilities().PrivateChannelData() 535 } 536 537 func (ds *dynamicCapabilities) StorePvtDataOfInvalidTx() bool { 538 return ds.cr.Capabilities().StorePvtDataOfInvalidTx() 539 } 540 541 func (ds *dynamicCapabilities) Supported() error { 542 return ds.cr.Capabilities().Supported() 543 } 544 545 func (ds *dynamicCapabilities) V1_1Validation() bool { 546 return ds.cr.Capabilities().V1_1Validation() 547 } 548 549 func (ds *dynamicCapabilities) V1_2Validation() bool { 550 return ds.cr.Capabilities().V1_2Validation() 551 } 552 553 func (ds *dynamicCapabilities) V1_3Validation() bool { 554 return ds.cr.Capabilities().V1_3Validation() 555 } 556 557 func (ds *dynamicCapabilities) V2_0Validation() bool { 558 return ds.cr.Capabilities().V2_0Validation() 559 }