github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/committer/txvalidator/validator.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package txvalidator 18 19 import ( 20 "fmt" 21 22 "github.com/golang/protobuf/proto" 23 "github.com/hyperledger/fabric/common/configtx" 24 "github.com/hyperledger/fabric/common/flogging" 25 coreUtil "github.com/hyperledger/fabric/common/util" 26 "github.com/hyperledger/fabric/core/chaincode/shim" 27 "github.com/hyperledger/fabric/core/common/ccprovider" 28 "github.com/hyperledger/fabric/core/common/sysccprovider" 29 "github.com/hyperledger/fabric/core/common/validation" 30 "github.com/hyperledger/fabric/core/ledger" 31 ledgerUtil "github.com/hyperledger/fabric/core/ledger/util" 32 "github.com/hyperledger/fabric/msp" 33 34 "github.com/hyperledger/fabric/protos/common" 35 "github.com/hyperledger/fabric/protos/peer" 36 "github.com/hyperledger/fabric/protos/utils" 37 "github.com/op/go-logging" 38 39 "errors" 40 41 "github.com/hyperledger/fabric/common/cauthdsl" 42 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 43 ) 44 45 // Support provides all of the needed to evaluate the VSCC 46 type Support interface { 47 // Ledger returns the ledger associated with this validator 48 Ledger() ledger.PeerLedger 49 50 // MSPManager returns the MSP manager for this chain 51 MSPManager() msp.MSPManager 52 53 // Apply attempts to apply a configtx to become the new config 54 Apply(configtx *common.ConfigEnvelope) error 55 56 // GetMSPIDs returns the IDs for the application MSPs 57 // that have been defined in the channel 58 GetMSPIDs(cid string) []string 59 } 60 61 //Validator interface which defines API to validate block transactions 62 // and return the bit array mask indicating invalid transactions which 63 // didn't pass validation. 64 type Validator interface { 65 Validate(block *common.Block) error 66 } 67 68 // private interface to decouple tx validator 69 // and vscc execution, in order to increase 70 // testability of txValidator 71 type vsccValidator interface { 72 VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) (error, peer.TxValidationCode) 73 } 74 75 // vsccValidator implementation which used to call 76 // vscc chaincode and validate block transactions 77 type vsccValidatorImpl struct { 78 support Support 79 ccprovider ccprovider.ChaincodeProvider 80 sccprovider sysccprovider.SystemChaincodeProvider 81 } 82 83 // implementation of Validator interface, keeps 84 // reference to the ledger to enable tx simulation 85 // and execution of vscc 86 type txValidator struct { 87 support Support 88 vscc vsccValidator 89 } 90 91 // VSCCInfoLookupFailureError error to indicate inability 92 // to obtain VSCC information from LCCC 93 type VSCCInfoLookupFailureError struct { 94 reason string 95 } 96 97 // Error returns reasons which lead to the failure 98 func (e VSCCInfoLookupFailureError) Error() string { 99 return e.reason 100 } 101 102 // VSCCEndorsementPolicyError error to mark transaction 103 // failed endrosement policy check 104 type VSCCEndorsementPolicyError struct { 105 reason string 106 } 107 108 // Error returns reasons which lead to the failure 109 func (e VSCCEndorsementPolicyError) Error() string { 110 return e.reason 111 } 112 113 // VSCCExecutionFailureError error to indicate 114 // failure during attempt of executing VSCC 115 // endorsement policy check 116 type VSCCExecutionFailureError struct { 117 reason string 118 } 119 120 // Error returns reasons which lead to the failure 121 func (e VSCCExecutionFailureError) Error() string { 122 return e.reason 123 } 124 125 var logger *logging.Logger // package-level logger 126 127 func init() { 128 // Init logger with module name 129 logger = flogging.MustGetLogger("txvalidator") 130 } 131 132 // NewTxValidator creates new transactions validator 133 func NewTxValidator(support Support) Validator { 134 // Encapsulates interface implementation 135 return &txValidator{support, 136 &vsccValidatorImpl{ 137 support: support, 138 ccprovider: ccprovider.GetChaincodeProvider(), 139 sccprovider: sysccprovider.GetSystemChaincodeProvider()}} 140 } 141 142 func (v *txValidator) chainExists(chain string) bool { 143 // TODO: implement this function! 144 return true 145 } 146 147 func (v *txValidator) Validate(block *common.Block) error { 148 logger.Debug("START Block Validation") 149 defer logger.Debug("END Block Validation") 150 // Initialize trans as valid here, then set invalidation reason code upon invalidation below 151 txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data)) 152 // txsChaincodeNames records all the invoked chaincodes by tx in a block 153 txsChaincodeNames := make(map[int]*sysccprovider.ChaincodeInstance) 154 // upgradedChaincodes records all the chaincodes that are upgrded in a block 155 txsUpgradedChaincodes := make(map[int]*sysccprovider.ChaincodeInstance) 156 for tIdx, d := range block.Data.Data { 157 if d != nil { 158 if env, err := utils.GetEnvelopeFromBlock(d); err != nil { 159 logger.Warningf("Error getting tx from block(%s)", err) 160 txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON) 161 } else if env != nil { 162 // validate the transaction: here we check that the transaction 163 // is properly formed, properly signed and that the security 164 // chain binding proposal to endorsements to tx holds. We do 165 // NOT check the validity of endorsements, though. That's a 166 // job for VSCC below 167 logger.Debug("Validating transaction peer.ValidateTransaction()") 168 var payload *common.Payload 169 var err error 170 var txResult peer.TxValidationCode 171 172 if payload, txResult = validation.ValidateTransaction(env); txResult != peer.TxValidationCode_VALID { 173 logger.Errorf("Invalid transaction with index %d", tIdx) 174 txsfltr.SetFlag(tIdx, txResult) 175 continue 176 } 177 178 chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) 179 if err != nil { 180 logger.Warningf("Could not unmarshal channel header, err %s, skipping", err) 181 txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON) 182 continue 183 } 184 185 channel := chdr.ChannelId 186 logger.Debugf("Transaction is for chain %s", channel) 187 188 if !v.chainExists(channel) { 189 logger.Errorf("Dropping transaction for non-existent chain %s", channel) 190 txsfltr.SetFlag(tIdx, peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND) 191 continue 192 } 193 194 if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { 195 // Check duplicate transactions 196 txID := chdr.TxId 197 if _, err := v.support.Ledger().GetTransactionByID(txID); err == nil { 198 logger.Error("Duplicate transaction found, ", txID, ", skipping") 199 txsfltr.SetFlag(tIdx, peer.TxValidationCode_DUPLICATE_TXID) 200 continue 201 } 202 203 // Validate tx with vscc and policy 204 logger.Debug("Validating transaction vscc tx validate") 205 err, cde := v.vscc.VSCCValidateTx(payload, d, env) 206 if err != nil { 207 txID := txID 208 logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err) 209 switch err.(type) { 210 case *VSCCExecutionFailureError: 211 return err 212 case *VSCCInfoLookupFailureError: 213 return err 214 default: 215 txsfltr.SetFlag(tIdx, cde) 216 continue 217 } 218 } 219 220 invokeCC, upgradeCC, err := v.getTxCCInstance(payload) 221 if err != nil { 222 logger.Errorf("Get chaincode instance from transaction txId = %s returned error %s", txID, err) 223 txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON) 224 continue 225 } 226 txsChaincodeNames[tIdx] = invokeCC 227 if upgradeCC != nil { 228 logger.Infof("Find chaincode upgrade transaction for chaincode %s on chain %s with new version %s", upgradeCC.ChaincodeName, upgradeCC.ChainID, upgradeCC.ChaincodeVersion) 229 txsUpgradedChaincodes[tIdx] = upgradeCC 230 } 231 } else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG { 232 configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data) 233 if err != nil { 234 err := fmt.Errorf("Error unmarshaling config which passed initial validity checks: %s", err) 235 logger.Critical(err) 236 return err 237 } 238 239 if err := v.support.Apply(configEnvelope); err != nil { 240 err := fmt.Errorf("Error validating config which passed initial validity checks: %s", err) 241 logger.Critical(err) 242 return err 243 } 244 logger.Debugf("config transaction received for chain %s", channel) 245 } else { 246 logger.Warningf("Unknown transaction type [%s] in block number [%d] transaction index [%d]", 247 common.HeaderType(chdr.Type), block.Header.Number, tIdx) 248 txsfltr.SetFlag(tIdx, peer.TxValidationCode_UNKNOWN_TX_TYPE) 249 continue 250 } 251 252 if _, err := proto.Marshal(env); err != nil { 253 logger.Warningf("Cannot marshal transaction due to %s", err) 254 txsfltr.SetFlag(tIdx, peer.TxValidationCode_MARSHAL_TX_ERROR) 255 continue 256 } 257 // Succeeded to pass down here, transaction is valid 258 txsfltr.SetFlag(tIdx, peer.TxValidationCode_VALID) 259 } else { 260 logger.Warning("Nil tx from block") 261 txsfltr.SetFlag(tIdx, peer.TxValidationCode_NIL_ENVELOPE) 262 } 263 } 264 } 265 266 txsfltr = v.invalidTXsForUpgradeCC(txsChaincodeNames, txsUpgradedChaincodes, txsfltr) 267 268 // Initialize metadata structure 269 utils.InitBlockMetadata(block) 270 271 block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr 272 273 return nil 274 } 275 276 // generateCCKey generates a unique identifier for chaincode in specific chain 277 func (v *txValidator) generateCCKey(ccName, chainID string) string { 278 return fmt.Sprintf("%s/%s", ccName, chainID) 279 } 280 281 // invalidTXsForUpgradeCC invalid all txs that should be invalided because of chaincode upgrade txs 282 func (v *txValidator) invalidTXsForUpgradeCC(txsChaincodeNames map[int]*sysccprovider.ChaincodeInstance, txsUpgradedChaincodes map[int]*sysccprovider.ChaincodeInstance, txsfltr ledgerUtil.TxValidationFlags) ledgerUtil.TxValidationFlags { 283 if len(txsUpgradedChaincodes) == 0 { 284 return txsfltr 285 } 286 287 // Invalid former cc upgrade txs if there're two or more txs upgrade the same cc 288 finalValidUpgradeTXs := make(map[string]int) 289 upgradedChaincodes := make(map[string]*sysccprovider.ChaincodeInstance) 290 for tIdx, cc := range txsUpgradedChaincodes { 291 if cc == nil { 292 continue 293 } 294 upgradedCCKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID) 295 296 if finalIdx, exist := finalValidUpgradeTXs[upgradedCCKey]; !exist { 297 finalValidUpgradeTXs[upgradedCCKey] = tIdx 298 upgradedChaincodes[upgradedCCKey] = cc 299 } else if finalIdx < tIdx { 300 logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", finalIdx) 301 txsfltr.SetFlag(finalIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT) 302 303 // record latter cc upgrade tx info 304 finalValidUpgradeTXs[upgradedCCKey] = tIdx 305 upgradedChaincodes[upgradedCCKey] = cc 306 } else { 307 logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", tIdx) 308 txsfltr.SetFlag(tIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT) 309 } 310 } 311 312 // invalid txs which invoke the upgraded chaincodes 313 for tIdx, cc := range txsChaincodeNames { 314 if cc == nil { 315 continue 316 } 317 ccKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID) 318 if _, exist := upgradedChaincodes[ccKey]; exist { 319 if txsfltr.IsValid(tIdx) { 320 logger.Infof("Invalid transaction with index %d: chaincode was upgraded in the same block", tIdx) 321 txsfltr.SetFlag(tIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT) 322 } 323 } 324 } 325 326 return txsfltr 327 } 328 329 func (v *txValidator) getTxCCInstance(payload *common.Payload) (invokeCCIns, upgradeCCIns *sysccprovider.ChaincodeInstance, err error) { 330 // This is duplicated unpacking work, but make test easier. 331 chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) 332 if err != nil { 333 return nil, nil, err 334 } 335 336 // Chain ID 337 chainID := chdr.ChannelId // it is guaranteed to be an existing channel by now 338 339 // ChaincodeID 340 hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header) 341 if err != nil { 342 return nil, nil, err 343 } 344 invokeCC := hdrExt.ChaincodeId 345 invokeIns := &sysccprovider.ChaincodeInstance{ChainID: chainID, ChaincodeName: invokeCC.Name, ChaincodeVersion: invokeCC.Version} 346 347 // Transaction 348 tx, err := utils.GetTransaction(payload.Data) 349 if err != nil { 350 logger.Errorf("GetTransaction failed: %s", err) 351 return invokeIns, nil, nil 352 } 353 354 // ChaincodeActionPayload 355 cap, err := utils.GetChaincodeActionPayload(tx.Actions[0].Payload) 356 if err != nil { 357 logger.Errorf("GetChaincodeActionPayload failed: %s", err) 358 return invokeIns, nil, nil 359 } 360 361 // ChaincodeProposalPayload 362 cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload) 363 if err != nil { 364 logger.Errorf("GetChaincodeProposalPayload failed: %s", err) 365 return invokeIns, nil, nil 366 } 367 368 // ChaincodeInvocationSpec 369 cis := &peer.ChaincodeInvocationSpec{} 370 err = proto.Unmarshal(cpp.Input, cis) 371 if err != nil { 372 logger.Errorf("GetChaincodeInvokeSpec failed: %s", err) 373 return invokeIns, nil, nil 374 } 375 376 if invokeCC.Name == "lscc" { 377 if string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade" { 378 upgradeIns, err := v.getUpgradeTxInstance(chainID, cis.ChaincodeSpec.Input.Args[2]) 379 if err != nil { 380 return invokeIns, nil, nil 381 } 382 return invokeIns, upgradeIns, nil 383 } 384 } 385 386 return invokeIns, nil, nil 387 } 388 389 func (v *txValidator) getUpgradeTxInstance(chainID string, cdsBytes []byte) (*sysccprovider.ChaincodeInstance, error) { 390 cds, err := utils.GetChaincodeDeploymentSpec(cdsBytes) 391 if err != nil { 392 return nil, err 393 } 394 395 return &sysccprovider.ChaincodeInstance{ 396 ChainID: chainID, 397 ChaincodeName: cds.ChaincodeSpec.ChaincodeId.Name, 398 ChaincodeVersion: cds.ChaincodeSpec.ChaincodeId.Version, 399 }, nil 400 } 401 402 // GetInfoForValidate gets the ChaincodeInstance(with latest version) of tx, vscc and policy from lscc 403 func (v *vsccValidatorImpl) GetInfoForValidate(txid, chID, ccID string) (*sysccprovider.ChaincodeInstance, *sysccprovider.ChaincodeInstance, []byte, error) { 404 cc := &sysccprovider.ChaincodeInstance{ChainID: chID} 405 vscc := &sysccprovider.ChaincodeInstance{ChainID: chID} 406 var policy []byte 407 var err error 408 if ccID != "lscc" { 409 // when we are validating any chaincode other than 410 // LSCC, we need to ask LSCC to give us the name 411 // of VSCC and of the policy that should be used 412 413 // obtain name of the VSCC and the policy from LSCC 414 cd, err := v.getCDataForCC(ccID) 415 if err != nil { 416 msg := fmt.Sprintf("Unable to get chaincode data from ledger for txid %s, due to %s", txid, err) 417 logger.Errorf(msg) 418 return nil, nil, nil, err 419 } 420 cc.ChaincodeName = cd.Name 421 cc.ChaincodeVersion = cd.Version 422 vscc.ChaincodeName = cd.Vscc 423 policy = cd.Policy 424 } else { 425 // when we are validating LSCC, we use the default 426 // VSCC and a default policy that requires one signature 427 // from any of the members of the channel 428 cc.ChaincodeName = "lscc" 429 cc.ChaincodeVersion = coreUtil.GetSysCCVersion() 430 vscc.ChaincodeName = "vscc" 431 p := cauthdsl.SignedByAnyMember(v.support.GetMSPIDs(chID)) 432 policy, err = utils.Marshal(p) 433 if err != nil { 434 return nil, nil, nil, err 435 } 436 } 437 438 // Get vscc version 439 vscc.ChaincodeVersion = coreUtil.GetSysCCVersion() 440 441 return cc, vscc, policy, nil 442 } 443 444 func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) (error, peer.TxValidationCode) { 445 // get header extensions so we have the chaincode ID 446 hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header) 447 if err != nil { 448 return err, peer.TxValidationCode_BAD_HEADER_EXTENSION 449 } 450 451 // get channel header 452 chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) 453 if err != nil { 454 return err, peer.TxValidationCode_BAD_CHANNEL_HEADER 455 } 456 457 /* obtain the list of namespaces we're writing stuff to; 458 at first, we establish a few facts about this invocation: 459 1) which namespaces does it write to? 460 2) does it write to LSCC's namespace? 461 3) does it write to any cc that cannot be invoked? */ 462 wrNamespace := []string{} 463 writesToLSCC := false 464 writesToNonInvokableSCC := false 465 respPayload, err := utils.GetActionFromEnvelope(envBytes) 466 if err != nil { 467 return fmt.Errorf("GetActionFromEnvelope failed, error %s", err), peer.TxValidationCode_BAD_RESPONSE_PAYLOAD 468 } 469 txRWSet := &rwsetutil.TxRwSet{} 470 if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil { 471 return fmt.Errorf("txRWSet.FromProtoBytes failed, error %s", err), peer.TxValidationCode_BAD_RWSET 472 } 473 for _, ns := range txRWSet.NsRwSets { 474 if len(ns.KvRwSet.Writes) > 0 { 475 wrNamespace = append(wrNamespace, ns.NameSpace) 476 477 if !writesToLSCC && ns.NameSpace == "lscc" { 478 writesToLSCC = true 479 } 480 481 if !writesToNonInvokableSCC && v.sccprovider.IsSysCCAndNotInvokableCC2CC(ns.NameSpace) { 482 writesToNonInvokableSCC = true 483 } 484 485 if !writesToNonInvokableSCC && v.sccprovider.IsSysCCAndNotInvokableExternal(ns.NameSpace) { 486 writesToNonInvokableSCC = true 487 } 488 } 489 } 490 491 // Verify the header extension and response payload contain the ChaincodeId 492 if hdrExt.ChaincodeId == nil { 493 return errors.New("nil ChaincodeId in header extension"), peer.TxValidationCode_INVALID_OTHER_REASON 494 } 495 496 if respPayload.ChaincodeId == nil { 497 return errors.New("nil ChaincodeId in ChaincodeAction"), peer.TxValidationCode_INVALID_OTHER_REASON 498 } 499 500 // get name and version of the cc we invoked 501 ccID := hdrExt.ChaincodeId.Name 502 ccVer := respPayload.ChaincodeId.Version 503 504 // sanity check on ccID 505 if ccID == "" { 506 err := fmt.Errorf("invalid chaincode ID") 507 logger.Errorf("%s", err) 508 return err, peer.TxValidationCode_INVALID_OTHER_REASON 509 } 510 if ccID != respPayload.ChaincodeId.Name { 511 err := fmt.Errorf("inconsistent ccid info (%s/%s)", ccID, respPayload.ChaincodeId.Name) 512 logger.Errorf("%s", err) 513 return err, peer.TxValidationCode_INVALID_OTHER_REASON 514 } 515 // sanity check on ccver 516 if ccVer == "" { 517 err := fmt.Errorf("invalid chaincode version") 518 logger.Errorf("%s", err) 519 return err, peer.TxValidationCode_INVALID_OTHER_REASON 520 } 521 522 // we've gathered all the info required to proceed to validation; 523 // validation will behave differently depending on the type of 524 // chaincode (system vs. application) 525 526 if !v.sccprovider.IsSysCC(ccID) { 527 // if we're here, we know this is an invocation of an application chaincode; 528 // first of all, we make sure that: 529 // 1) we don't write to LSCC - an application chaincode is free to invoke LSCC 530 // for instance to get information about itself or another chaincode; however 531 // these legitimate invocations only ready from LSCC's namespace; currently 532 // only two functions of LSCC write to its namespace: deploy and upgrade and 533 // neither should be used by an application chaincode 534 if writesToLSCC { 535 return fmt.Errorf("Chaincode %s attempted to write to the namespace of LSCC", ccID), 536 peer.TxValidationCode_ILLEGAL_WRITESET 537 } 538 // 2) we don't write to the namespace of a chaincode that we cannot invoke - if 539 // the chaincode cannot be invoked in the first place, there's no legitimate 540 // way in which a transaction has a write set that writes to it; additionally 541 // we don't have any means of verifying whether the transaction had the rights 542 // to perform that write operation because in v1, system chaincodes do not have 543 // any endorsement policies to speak of. So if the chaincode can't be invoked 544 // it can't be written to by an invocation of an application chaincode 545 if writesToNonInvokableSCC { 546 return fmt.Errorf("Chaincode %s attempted to write to the namespace of a system chaincode that cannot be invoked", ccID), 547 peer.TxValidationCode_ILLEGAL_WRITESET 548 } 549 550 // validate *EACH* read write set according to its chaincode's endorsement policy 551 for _, ns := range wrNamespace { 552 // Get latest chaincode version, vscc and validate policy 553 txcc, vscc, policy, err := v.GetInfoForValidate(chdr.TxId, chdr.ChannelId, ns) 554 if err != nil { 555 logger.Errorf("GetInfoForValidate for txId = %s returned error %s", chdr.TxId, err) 556 return err, peer.TxValidationCode_INVALID_OTHER_REASON 557 } 558 559 // if the namespace corresponds to the cc that was originally 560 // invoked, we check that the version of the cc that was 561 // invoked corresponds to the version that lscc has returned 562 if ns == ccID && txcc.ChaincodeVersion != ccVer { 563 err := fmt.Errorf("Chaincode %s:%s/%s didn't match %s:%s/%s in lscc", ccID, ccVer, chdr.ChannelId, txcc.ChaincodeName, txcc.ChaincodeVersion, chdr.ChannelId) 564 logger.Errorf(err.Error()) 565 return err, peer.TxValidationCode_EXPIRED_CHAINCODE 566 } 567 568 // do VSCC validation 569 if err = v.VSCCValidateTxForCC(envBytes, chdr.TxId, chdr.ChannelId, vscc.ChaincodeName, vscc.ChaincodeVersion, policy); err != nil { 570 switch err.(type) { 571 case *VSCCEndorsementPolicyError: 572 return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE 573 default: 574 return err, peer.TxValidationCode_INVALID_OTHER_REASON 575 } 576 } 577 } 578 } else { 579 // make sure that we can invoke this system chaincode - if the chaincode 580 // cannot be invoked through a proposal to this peer, we have to drop the 581 // transaction; if we didn't, we wouldn't know how to decide whether it's 582 // valid or not because in v1, system chaincodes have no endorsement policy 583 if v.sccprovider.IsSysCCAndNotInvokableExternal(ccID) { 584 return fmt.Errorf("Committing an invocation of cc %s is illegal", ccID), 585 peer.TxValidationCode_ILLEGAL_WRITESET 586 } 587 588 // Get latest chaincode version, vscc and validate policy 589 _, vscc, policy, err := v.GetInfoForValidate(chdr.TxId, chdr.ChannelId, ccID) 590 if err != nil { 591 logger.Errorf("GetInfoForValidate for txId = %s returned error %s", chdr.TxId, err) 592 return err, peer.TxValidationCode_INVALID_OTHER_REASON 593 } 594 595 // validate the transaction as an invocation of this system chaincode; 596 // vscc will have to do custom validation for this system chaincode 597 // currently, VSCC does custom validation for LSCC only; if an hlf 598 // user creates a new system chaincode which is invokable from the outside 599 // they have to modify VSCC to provide appropriate validation 600 if err = v.VSCCValidateTxForCC(envBytes, chdr.TxId, vscc.ChainID, vscc.ChaincodeName, vscc.ChaincodeVersion, policy); err != nil { 601 switch err.(type) { 602 case *VSCCEndorsementPolicyError: 603 return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE 604 default: 605 return err, peer.TxValidationCode_INVALID_OTHER_REASON 606 } 607 } 608 } 609 610 return nil, peer.TxValidationCode_VALID 611 } 612 613 func (v *vsccValidatorImpl) VSCCValidateTxForCC(envBytes []byte, txid, chid, vsccName, vsccVer string, policy []byte) error { 614 ctxt, err := v.ccprovider.GetContext(v.support.Ledger()) 615 if err != nil { 616 msg := fmt.Sprintf("Cannot obtain context for txid=%s, err %s", txid, err) 617 logger.Errorf(msg) 618 return &VSCCExecutionFailureError{msg} 619 } 620 defer v.ccprovider.ReleaseContext() 621 622 // build arguments for VSCC invocation 623 // args[0] - function name (not used now) 624 // args[1] - serialized Envelope 625 // args[2] - serialized policy 626 args := [][]byte{[]byte(""), envBytes, policy} 627 628 // get context to invoke VSCC 629 vscctxid := coreUtil.GenerateUUID() 630 cccid := v.ccprovider.GetCCContext(chid, vsccName, vsccVer, vscctxid, true, nil, nil) 631 632 // invoke VSCC 633 logger.Debug("Invoking VSCC txid", txid, "chaindID", chid) 634 res, _, err := v.ccprovider.ExecuteChaincode(ctxt, cccid, args) 635 if err != nil { 636 msg := fmt.Sprintf("Invoke VSCC failed for transaction txid=%s, error %s", txid, err) 637 logger.Errorf(msg) 638 return &VSCCExecutionFailureError{msg} 639 } 640 if res.Status != shim.OK { 641 logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, res.Message) 642 return &VSCCEndorsementPolicyError{fmt.Sprintf("%s", res.Message)} 643 } 644 645 return nil 646 } 647 648 func (v *vsccValidatorImpl) getCDataForCC(ccid string) (*ccprovider.ChaincodeData, error) { 649 l := v.support.Ledger() 650 if l == nil { 651 return nil, fmt.Errorf("nil ledger instance") 652 } 653 654 qe, err := l.NewQueryExecutor() 655 if err != nil { 656 return nil, fmt.Errorf("Could not retrieve QueryExecutor, error %s", err) 657 } 658 defer qe.Done() 659 660 bytes, err := qe.GetState("lscc", ccid) 661 if err != nil { 662 return nil, &VSCCInfoLookupFailureError{fmt.Sprintf("Could not retrieve state for chaincode %s, error %s", ccid, err)} 663 } 664 665 if bytes == nil { 666 return nil, fmt.Errorf("lscc's state for [%s] not found.", ccid) 667 } 668 669 cd := &ccprovider.ChaincodeData{} 670 err = proto.Unmarshal(bytes, cd) 671 if err != nil { 672 return nil, fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err) 673 } 674 675 if cd.Vscc == "" { 676 return nil, fmt.Errorf("lscc's state for [%s] is invalid, vscc field must be set.", ccid) 677 } 678 679 if len(cd.Policy) == 0 { 680 return nil, fmt.Errorf("lscc's state for [%s] is invalid, policy field must be set.", ccid) 681 } 682 683 return cd, err 684 }