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