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