github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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/validation" 29 "github.com/hyperledger/fabric/core/ledger" 30 ledgerUtil "github.com/hyperledger/fabric/core/ledger/util" 31 "github.com/hyperledger/fabric/msp" 32 33 "github.com/hyperledger/fabric/common/policies" 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 ) 41 42 // Support provides all of the needed to evaluate the VSCC 43 type Support interface { 44 // Ledger returns the ledger associated with this validator 45 Ledger() ledger.PeerLedger 46 47 // MSPManager returns the MSP manager for this chain 48 MSPManager() msp.MSPManager 49 50 // Apply attempts to apply a configtx to become the new config 51 Apply(configtx *common.ConfigEnvelope) error 52 53 // PolicyManager returns the policies.Manager for the channel 54 PolicyManager() policies.Manager 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 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 } 81 82 // implementation of Validator interface, keeps 83 // reference to the ledger to enable tx simulation 84 // and execution of vscc 85 type txValidator struct { 86 support Support 87 vscc vsccValidator 88 } 89 90 var logger *logging.Logger // package-level logger 91 92 func init() { 93 // Init logger with module name 94 logger = flogging.MustGetLogger("txvalidator") 95 } 96 97 // NewTxValidator creates new transactions validator 98 func NewTxValidator(support Support) Validator { 99 // Encapsulates interface implementation 100 return &txValidator{support, &vsccValidatorImpl{support: support, ccprovider: ccprovider.GetChaincodeProvider()}} 101 } 102 103 func (v *txValidator) chainExists(chain string) bool { 104 // TODO: implement this function! 105 return true 106 } 107 108 // ChaincodeInstance is unique identifier of chaincode instance 109 type ChaincodeInstance struct { 110 ChainID string 111 ChaincodeName string 112 ChaincodeVersion string 113 } 114 115 func (v *txValidator) Validate(block *common.Block) error { 116 logger.Debug("START Block Validation") 117 defer logger.Debug("END Block Validation") 118 // Initialize trans as valid here, then set invalidation reason code upon invalidation below 119 txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data)) 120 // txsChaincodeNames records all the invoked chaincodes by tx in a block 121 txsChaincodeNames := make(map[int]*ChaincodeInstance) 122 // upgradedChaincodes records all the chaincodes that are upgrded in a block 123 txsUpgradedChaincodes := make(map[int]*ChaincodeInstance) 124 for tIdx, d := range block.Data.Data { 125 if d != nil { 126 if env, err := utils.GetEnvelopeFromBlock(d); err != nil { 127 logger.Warningf("Error getting tx from block(%s)", err) 128 txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON) 129 } else if env != nil { 130 // validate the transaction: here we check that the transaction 131 // is properly formed, properly signed and that the security 132 // chain binding proposal to endorsements to tx holds. We do 133 // NOT check the validity of endorsements, though. That's a 134 // job for VSCC below 135 logger.Debug("Validating transaction peer.ValidateTransaction()") 136 var payload *common.Payload 137 var err error 138 var txResult peer.TxValidationCode 139 140 if payload, txResult = validation.ValidateTransaction(env); txResult != peer.TxValidationCode_VALID { 141 logger.Errorf("Invalid transaction with index %d, error %s", tIdx, err) 142 txsfltr.SetFlag(tIdx, txResult) 143 continue 144 } 145 146 chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) 147 if err != nil { 148 logger.Warning("Could not unmarshal channel header, err %s, skipping", err) 149 txsfltr.SetFlag(tIdx, peer.TxValidationCode_INVALID_OTHER_REASON) 150 continue 151 } 152 153 channel := chdr.ChannelId 154 logger.Debug("Transaction is for chain %s", channel) 155 156 if !v.chainExists(channel) { 157 logger.Errorf("Dropping transaction for non-existent chain %s", channel) 158 txsfltr.SetFlag(tIdx, peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND) 159 continue 160 } 161 162 if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { 163 // Check duplicate transactions 164 txID := chdr.TxId 165 if _, err := v.support.Ledger().GetTransactionByID(txID); err == nil { 166 logger.Error("Duplicate transaction found, ", txID, ", skipping") 167 txsfltr.SetFlag(tIdx, peer.TxValidationCode_DUPLICATE_TXID) 168 continue 169 } 170 171 //the payload is used to get headers 172 logger.Debug("Validating transaction vscc tx validate") 173 if err = v.vscc.VSCCValidateTx(payload, d, env); err != nil { 174 txID := txID 175 logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err) 176 txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE) 177 continue 178 } 179 180 invokeCC, upgradeCC, err := v.getTxCCInstance(payload) 181 if err != nil { 182 logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err) 183 txsfltr.SetFlag(tIdx, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE) 184 continue 185 } 186 txsChaincodeNames[tIdx] = invokeCC 187 if upgradeCC != nil { 188 logger.Infof("Find chaincode upgrade transaction for chaincode %s on chain %s with new version %s", upgradeCC.ChaincodeName, upgradeCC.ChainID, upgradeCC.ChaincodeVersion) 189 txsUpgradedChaincodes[tIdx] = upgradeCC 190 } 191 } else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG { 192 configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data) 193 if err != nil { 194 err := fmt.Errorf("Error unmarshaling config which passed initial validity checks: %s", err) 195 logger.Critical(err) 196 return err 197 } 198 199 if err := v.support.Apply(configEnvelope); err != nil { 200 err := fmt.Errorf("Error validating config which passed initial validity checks: %s", err) 201 logger.Critical(err) 202 return err 203 } 204 logger.Debugf("config transaction received for chain %s", channel) 205 } 206 207 if _, err := proto.Marshal(env); err != nil { 208 logger.Warningf("Cannot marshal transaction due to %s", err) 209 txsfltr.SetFlag(tIdx, peer.TxValidationCode_MARSHAL_TX_ERROR) 210 continue 211 } 212 // Succeeded to pass down here, transaction is valid 213 txsfltr.SetFlag(tIdx, peer.TxValidationCode_VALID) 214 } else { 215 logger.Warning("Nil tx from block") 216 txsfltr.SetFlag(tIdx, peer.TxValidationCode_NIL_ENVELOPE) 217 } 218 } 219 } 220 221 txsfltr = v.invalidTXsForUpgradeCC(txsChaincodeNames, txsUpgradedChaincodes, txsfltr) 222 223 // Initialize metadata structure 224 utils.InitBlockMetadata(block) 225 226 block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr 227 228 return nil 229 } 230 231 // generateCCKey generates a unique identifier for chaincode in specific chain 232 func (v *txValidator) generateCCKey(ccName, chainID string) string { 233 return fmt.Sprintf("%s/%s", ccName, chainID) 234 } 235 236 // invalidTXsForUpgradeCC invalid all txs that should be invalided because of chaincode upgrade txs 237 func (v *txValidator) invalidTXsForUpgradeCC(txsChaincodeNames map[int]*ChaincodeInstance, txsUpgradedChaincodes map[int]*ChaincodeInstance, txsfltr ledgerUtil.TxValidationFlags) ledgerUtil.TxValidationFlags { 238 if len(txsUpgradedChaincodes) == 0 { 239 return txsfltr 240 } 241 242 // Invalid former cc upgrade txs if there're two or more txs upgrade the same cc 243 finalValidUpgradeTXs := make(map[string]int) 244 upgradedChaincodes := make(map[string]*ChaincodeInstance) 245 for tIdx, cc := range txsUpgradedChaincodes { 246 if cc == nil { 247 continue 248 } 249 upgradedCCKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID) 250 251 if finalIdx, exist := finalValidUpgradeTXs[upgradedCCKey]; !exist { 252 finalValidUpgradeTXs[upgradedCCKey] = tIdx 253 upgradedChaincodes[upgradedCCKey] = cc 254 } else if finalIdx < tIdx { 255 logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", finalIdx) 256 txsfltr.SetFlag(finalIdx, peer.TxValidationCode_EXPIRED_CHAINCODE) 257 258 // record latter cc upgrade tx info 259 finalValidUpgradeTXs[upgradedCCKey] = tIdx 260 upgradedChaincodes[upgradedCCKey] = cc 261 } else { 262 logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", tIdx) 263 txsfltr.SetFlag(tIdx, peer.TxValidationCode_EXPIRED_CHAINCODE) 264 } 265 } 266 267 // invalid txs which invoke the upgraded chaincodes 268 for tIdx, cc := range txsChaincodeNames { 269 if cc == nil { 270 continue 271 } 272 ccKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID) 273 if _, exist := upgradedChaincodes[ccKey]; exist { 274 if txsfltr.IsValid(tIdx) { 275 logger.Infof("Invalid transaction with index %d: chaincode was upgraded in the same block", tIdx) 276 txsfltr.SetFlag(tIdx, peer.TxValidationCode_EXPIRED_CHAINCODE) 277 } 278 } 279 } 280 281 return txsfltr 282 } 283 284 func (v *txValidator) getTxCCInstance(payload *common.Payload) (invokeCCIns, upgradeCCIns *ChaincodeInstance, err error) { 285 // This is duplicated unpacking work, but make test easier. 286 chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) 287 if err != nil { 288 return nil, nil, err 289 } 290 291 // Chain ID 292 chainID := chdr.ChannelId 293 if chainID == "" { 294 err := fmt.Errorf("transaction header does not contain an chain ID") 295 logger.Errorf("%s", err) 296 return nil, nil, err 297 } 298 299 // ChaincodeID 300 hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header) 301 if err != nil { 302 return nil, nil, err 303 } 304 invokeCC := hdrExt.ChaincodeId 305 invokeIns := &ChaincodeInstance{ChainID: chainID, ChaincodeName: invokeCC.Name, ChaincodeVersion: invokeCC.Version} 306 307 // Transaction 308 tx, err := utils.GetTransaction(payload.Data) 309 if err != nil { 310 logger.Errorf("GetTransaction failed: %s", err) 311 return invokeIns, nil, nil 312 } 313 314 // ChaincodeActionPayload 315 cap, err := utils.GetChaincodeActionPayload(tx.Actions[0].Payload) 316 if err != nil { 317 logger.Errorf("GetChaincodeActionPayload failed: %s", err) 318 return invokeIns, nil, nil 319 } 320 321 // ChaincodeProposalPayload 322 cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload) 323 if err != nil { 324 logger.Errorf("GetChaincodeProposalPayload failed: %s", err) 325 return invokeIns, nil, nil 326 } 327 328 // ChaincodeInvocationSpec 329 cis := &peer.ChaincodeInvocationSpec{} 330 err = proto.Unmarshal(cpp.Input, cis) 331 if err != nil { 332 logger.Errorf("GetChaincodeInvokeSpec failed: %s", err) 333 return invokeIns, nil, nil 334 } 335 336 if invokeCC.Name == "lscc" { 337 if string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade" { 338 upgradeIns, err := v.getUpgradeTxInstance(chainID, cis.ChaincodeSpec.Input.Args[2]) 339 if err != nil { 340 return invokeIns, nil, nil 341 } 342 return invokeIns, upgradeIns, nil 343 } 344 } 345 346 return invokeIns, nil, nil 347 } 348 349 func (v *txValidator) getUpgradeTxInstance(chainID string, cdsBytes []byte) (*ChaincodeInstance, error) { 350 cds, err := utils.GetChaincodeDeploymentSpec(cdsBytes) 351 if err != nil { 352 return nil, err 353 } 354 355 return &ChaincodeInstance{ 356 ChainID: chainID, 357 ChaincodeName: cds.ChaincodeSpec.ChaincodeId.Name, 358 ChaincodeVersion: cds.ChaincodeSpec.ChaincodeId.Version, 359 }, nil 360 } 361 362 func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) error { 363 // get channel header 364 chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) 365 if err != nil { 366 return err 367 } 368 369 // Chain ID 370 chainID := chdr.ChannelId 371 if chainID == "" { 372 err := fmt.Errorf("transaction header does not contain an chain ID") 373 logger.Errorf("%s", err) 374 return err 375 } 376 377 // Get transaction id 378 txid := chdr.TxId 379 if txid == "" { 380 err := fmt.Errorf("transaction header does not contain transaction ID") 381 logger.Errorf("%s", err) 382 return err 383 } 384 385 ctxt, err := v.ccprovider.GetContext(v.support.Ledger()) 386 if err != nil { 387 logger.Errorf("Cannot obtain context for txid=%s, err %s", txid, err) 388 return err 389 } 390 defer v.ccprovider.ReleaseContext() 391 392 // get header extensions so we have the visibility field 393 hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header) 394 if err != nil { 395 return err 396 } 397 398 var vscc string 399 var policy []byte 400 if hdrExt.ChaincodeId.Name != "lscc" { 401 // when we are validating any chaincode other than 402 // LSCC, we need to ask LSCC to give us the name 403 // of VSCC and of the policy that should be used 404 405 // obtain name of the VSCC and the policy from LSCC 406 cd, err := v.getCDataForCC(hdrExt.ChaincodeId.Name) 407 if err != nil { 408 logger.Errorf("Unable to get chaincode data from ledger for txid %s, due to %s", txid, err) 409 return err 410 } 411 vscc = cd.Vscc 412 policy = cd.Policy 413 } else { 414 // when we are validating LSCC, we use the default 415 // VSCC and a default policy that requires one signature 416 // from any of the members of the channel 417 vscc = "vscc" 418 policy = cauthdsl.SignedByAnyMember(v.support.GetMSPIDs(chainID)) 419 } 420 421 // build arguments for VSCC invocation 422 // args[0] - function name (not used now) 423 // args[1] - serialized Envelope 424 // args[2] - serialized policy 425 args := [][]byte{[]byte(""), envBytes, policy} 426 427 vscctxid := coreUtil.GenerateUUID() 428 429 // Get chaincode version 430 version := coreUtil.GetSysCCVersion() 431 cccid := v.ccprovider.GetCCContext(chainID, vscc, version, vscctxid, true, nil, nil) 432 433 // invoke VSCC 434 logger.Debug("Invoking VSCC txid", txid, "chaindID", chainID) 435 res, _, err := v.ccprovider.ExecuteChaincode(ctxt, cccid, args) 436 if err != nil { 437 logger.Errorf("Invoke VSCC failed for transaction txid=%s, error %s", txid, err) 438 return err 439 } 440 if res.Status != shim.OK { 441 logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, res.Message) 442 return fmt.Errorf("%s", res.Message) 443 } 444 445 return nil 446 } 447 448 func (v *vsccValidatorImpl) getCDataForCC(ccid string) (*ccprovider.ChaincodeData, error) { 449 l := v.support.Ledger() 450 if l == nil { 451 return nil, fmt.Errorf("nil ledger instance") 452 } 453 454 qe, err := l.NewQueryExecutor() 455 if err != nil { 456 return nil, fmt.Errorf("Could not retrieve QueryExecutor, error %s", err) 457 } 458 defer qe.Done() 459 460 bytes, err := qe.GetState("lscc", ccid) 461 if err != nil { 462 return nil, fmt.Errorf("Could not retrieve state for chaincode %s, error %s", ccid, err) 463 } 464 465 if bytes == nil { 466 return nil, fmt.Errorf("lscc's state for [%s] not found.", ccid) 467 } 468 469 cd := &ccprovider.ChaincodeData{} 470 err = proto.Unmarshal(bytes, cd) 471 if err != nil { 472 return nil, fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err) 473 } 474 475 if cd.Vscc == "" { 476 return nil, fmt.Errorf("lscc's state for [%s] is invalid, vscc field must be set.", ccid) 477 } 478 479 if len(cd.Policy) == 0 { 480 return nil, fmt.Errorf("lscc's state for [%s] is invalid, policy field must be set.", ccid) 481 } 482 483 return cd, err 484 }