63 Star 183 Fork 3

Gitee 极速下载/hyperledger-fabric

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/hyperledger/fabric
克隆/下载
validator.go 30.27 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
/*
Copyright IBM Corp. 2016 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package txvalidator
import (
"fmt"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/configtx"
"github.com/hyperledger/fabric/common/errors"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/resourcesconfig"
coreUtil "github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/sysccprovider"
"github.com/hyperledger/fabric/core/common/validation"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"
"golang.org/x/net/context"
)
// Support provides all of the needed to evaluate the VSCC
type Support interface {
// Acquire implements semaphore-like acquire semantics
Acquire(ctx context.Context, n int64) error
// Release implements semaphore-like release semantics
Release(n int64)
// Ledger returns the ledger associated with this validator
Ledger() ledger.PeerLedger
// MSPManager returns the MSP manager for this chain
MSPManager() msp.MSPManager
// Apply attempts to apply a configtx to become the new config
Apply(configtx *common.ConfigEnvelope) error
// GetMSPIDs returns the IDs for the application MSPs
// that have been defined in the channel
GetMSPIDs(cid string) []string
// Capabilities defines the capabilities for the application portion of this channel
Capabilities() channelconfig.ApplicationCapabilities
// ChaincodeByName returns the definition (and whether they exist)
// for a chaincode in a specific channel
ChaincodeByName(chainname, ccname string) (resourcesconfig.ChaincodeDefinition, bool)
}
//Validator interface which defines API to validate block transactions
// and return the bit array mask indicating invalid transactions which
// didn't pass validation.
type Validator interface {
Validate(block *common.Block) error
}
// private interface to decouple tx validator
// and vscc execution, in order to increase
// testability of txValidator
type vsccValidator interface {
VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) (error, peer.TxValidationCode)
}
// vsccValidator implementation which used to call
// vscc chaincode and validate block transactions
type vsccValidatorImpl struct {
support Support
ccprovider ccprovider.ChaincodeProvider
sccprovider sysccprovider.SystemChaincodeProvider
}
// implementation of Validator interface, keeps
// reference to the ledger to enable tx simulation
// and execution of vscc
type txValidator struct {
support Support
vscc vsccValidator
}
var logger *logging.Logger // package-level logger
func init() {
// Init logger with module name
logger = flogging.MustGetLogger("txvalidator")
}
type blockValidationRequest struct {
block *common.Block
d []byte
tIdx int
v *txValidator
}
type blockValidationResult struct {
tIdx int
validationCode peer.TxValidationCode
txsChaincodeName *sysccprovider.ChaincodeInstance
txsUpgradedChaincode *sysccprovider.ChaincodeInstance
err error
txid string
}
// NewTxValidator creates new transactions validator
func NewTxValidator(support Support) Validator {
// Encapsulates interface implementation
return &txValidator{support,
&vsccValidatorImpl{
support: support,
ccprovider: ccprovider.GetChaincodeProvider(),
sccprovider: sysccprovider.GetSystemChaincodeProvider()}}
}
func (v *txValidator) chainExists(chain string) bool {
// TODO: implement this function!
return true
}
// Validate performs the validation of a block. The validation
// of each transaction in the block is performed in parallel.
// The approach is as follows: the committer thread starts the
// tx validation function in a goroutine (using a semaphore to cap
// the number of concurrent validating goroutines). The committer
// thread then reads results of validation (in orderer of completion
// of the goroutines) from the results channel. The goroutines
// perform the validation of the txs in the block and enqueue the
// validation result in the results channel. A few note-worthy facts:
// 1) to keep the approach simple, the committer thread enqueues
// all transactions in the block and then moves on to reading the
// results.
// 2) for parallel validation to work, it is important that the
// validation function does not change the state of the system.
// Otherwise the order in which validation is perform matters
// and we have to resort to sequential validation (or some locking).
// This is currently true, because the only function that affects
// state is when a config transaction is received, but they are
// guaranteed to be alone in the block. If/when this assumption
// is violated, this code must be changed.
func (v *txValidator) Validate(block *common.Block) error {
var err error
var errPos int
logger.Debug("START Block Validation")
defer logger.Debug("END Block Validation")
// Initialize trans as valid here, then set invalidation reason code upon invalidation below
txsfltr := ledgerUtil.NewTxValidationFlags(len(block.Data.Data))
// txsChaincodeNames records all the invoked chaincodes by tx in a block
txsChaincodeNames := make(map[int]*sysccprovider.ChaincodeInstance)
// upgradedChaincodes records all the chaincodes that are upgraded in a block
txsUpgradedChaincodes := make(map[int]*sysccprovider.ChaincodeInstance)
// array of txids
txidArray := make([]string, len(block.Data.Data))
results := make(chan *blockValidationResult)
go func() {
for tIdx, d := range block.Data.Data {
tIdxLcl := tIdx
dLcl := d
// ensure that we don't have too many concurrent validation workers
v.support.Acquire(context.Background(), 1)
go func() {
defer v.support.Release(1)
validateTx(&blockValidationRequest{
d: dLcl,
block: block,
tIdx: tIdxLcl,
v: v,
}, results)
}()
}
}()
logger.Debugf("expecting %d block validation responses", len(block.Data.Data))
// now we read responses in the order in which they come back
for i := 0; i < len(block.Data.Data); i++ {
res := <-results
if res.err != nil {
// if there is an error, we buffer its value, wait for
// all workers to complete validation and then return
// the error from the first tx in this block that returned an error
logger.Debugf("got terminal error %s for idx %d", res.err, res.tIdx)
if err == nil || res.tIdx < errPos {
err = res.err
errPos = res.tIdx
}
} else {
// if there was no error, we set the txsfltr and we set the
// txsChaincodeNames and txsUpgradedChaincodes maps
logger.Debugf("got result for idx %d, code %d", res.tIdx, res.validationCode)
txsfltr.SetFlag(res.tIdx, res.validationCode)
if res.validationCode == peer.TxValidationCode_VALID {
if res.txsChaincodeName != nil {
txsChaincodeNames[res.tIdx] = res.txsChaincodeName
}
if res.txsUpgradedChaincode != nil {
txsUpgradedChaincodes[res.tIdx] = res.txsUpgradedChaincode
}
txidArray[res.tIdx] = res.txid
}
}
}
// if we're here, all workers have completed the validation.
// If there was an error we return the error from the first
// tx in this block that returned an error
if err != nil {
return err
}
// if we operate with this capability, we mark invalid any transaction that has a txid
// which is equal to that of a previous tx in this block
if v.support.Capabilities().ForbidDuplicateTXIdInBlock() {
markTXIdDuplicates(txidArray, txsfltr)
}
// if we're here, all workers have completed validation and
// no error was reported; we set the tx filter and return
// success
txsfltr = v.invalidTXsForUpgradeCC(txsChaincodeNames, txsUpgradedChaincodes, txsfltr)
// Initialize metadata structure
utils.InitBlockMetadata(block)
block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr
return nil
}
func markTXIdDuplicates(txids []string, txsfltr ledgerUtil.TxValidationFlags) {
txidMap := make(map[string]struct{})
for id, txid := range txids {
if txid == "" {
continue
}
_, in := txidMap[txid]
if in {
logger.Error("Duplicate txid", txid, "found, skipping")
txsfltr.SetFlag(id, peer.TxValidationCode_DUPLICATE_TXID)
} else {
txidMap[txid] = struct{}{}
}
}
}
func validateTx(req *blockValidationRequest, results chan<- *blockValidationResult) {
block := req.block
d := req.d
tIdx := req.tIdx
v := req.v
txID := ""
if d == nil {
results <- &blockValidationResult{
tIdx: tIdx,
}
return
}
if env, err := utils.GetEnvelopeFromBlock(d); err != nil {
logger.Warningf("Error getting tx from block(%s)", err)
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_INVALID_OTHER_REASON,
}
return
} else if env != nil {
// validate the transaction: here we check that the transaction
// is properly formed, properly signed and that the security
// chain binding proposal to endorsements to tx holds. We do
// NOT check the validity of endorsements, though. That's a
// job for VSCC below
logger.Debugf("validateTx starts for block %p env %p txn %d", block, env, tIdx)
defer logger.Debugf("validateTx completes for block %p env %p txn %d", block, env, tIdx)
var payload *common.Payload
var err error
var txResult peer.TxValidationCode
var txsChaincodeName *sysccprovider.ChaincodeInstance
var txsUpgradedChaincode *sysccprovider.ChaincodeInstance
if payload, txResult = validation.ValidateTransaction(env, v.support.Capabilities()); txResult != peer.TxValidationCode_VALID {
logger.Errorf("Invalid transaction with index %d", tIdx)
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: txResult,
}
return
}
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
logger.Warningf("Could not unmarshal channel header, err %s, skipping", err)
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_INVALID_OTHER_REASON,
}
return
}
channel := chdr.ChannelId
logger.Debugf("Transaction is for chain %s", channel)
if !v.chainExists(channel) {
logger.Errorf("Dropping transaction for non-existent chain %s", channel)
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_TARGET_CHAIN_NOT_FOUND,
}
return
}
if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION {
// Check duplicate transactions
txID = chdr.TxId
if _, err := v.support.Ledger().GetTransactionByID(txID); err == nil {
logger.Error("Duplicate transaction found, ", txID, ", skipping")
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_DUPLICATE_TXID,
}
return
}
// Validate tx with vscc and policy
logger.Debug("Validating transaction vscc tx validate")
err, cde := v.vscc.VSCCValidateTx(payload, d, env)
if err != nil {
logger.Errorf("VSCCValidateTx for transaction txId = %s returned error %s", txID, err)
switch err.(type) {
case *errors.VSCCExecutionFailureError:
results <- &blockValidationResult{
tIdx: tIdx,
err: err,
}
return
case *errors.VSCCInfoLookupFailureError:
results <- &blockValidationResult{
tIdx: tIdx,
err: err,
}
return
default:
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: cde,
}
return
}
}
invokeCC, upgradeCC, err := v.getTxCCInstance(payload)
if err != nil {
logger.Errorf("Get chaincode instance from transaction txId = %s returned error %s", txID, err)
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_INVALID_OTHER_REASON,
}
return
}
txsChaincodeName = invokeCC
if upgradeCC != nil {
logger.Infof("Find chaincode upgrade transaction for chaincode %s on chain %s with new version %s", upgradeCC.ChaincodeName, upgradeCC.ChainID, upgradeCC.ChaincodeVersion)
txsUpgradedChaincode = upgradeCC
}
} else if common.HeaderType(chdr.Type) == common.HeaderType_CONFIG {
configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data)
if err != nil {
err := fmt.Errorf("Error unmarshaling config which passed initial validity checks: %s", err)
logger.Critical(err)
results <- &blockValidationResult{
tIdx: tIdx,
err: err,
}
return
}
if err := v.support.Apply(configEnvelope); err != nil {
err := fmt.Errorf("Error validating config which passed initial validity checks: %s", err)
logger.Critical(err)
results <- &blockValidationResult{
tIdx: tIdx,
err: err,
}
return
}
logger.Debugf("config transaction received for chain %s", channel)
} else if common.HeaderType(chdr.Type) == common.HeaderType_PEER_RESOURCE_UPDATE {
// FIXME: in the context of FAB-7341, we should introduce validation
// for this kind of transaction here. For now we just ignore this
// type of transaction and delegate its validation to other components
results <- &blockValidationResult{
tIdx: tIdx,
err: nil,
}
return
} else {
logger.Warningf("Unknown transaction type [%s] in block number [%d] transaction index [%d]",
common.HeaderType(chdr.Type), block.Header.Number, tIdx)
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_UNKNOWN_TX_TYPE,
}
return
}
if _, err := proto.Marshal(env); err != nil {
logger.Warningf("Cannot marshal transaction due to %s", err)
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_MARSHAL_TX_ERROR,
}
return
}
// Succeeded to pass down here, transaction is valid
results <- &blockValidationResult{
tIdx: tIdx,
txsChaincodeName: txsChaincodeName,
txsUpgradedChaincode: txsUpgradedChaincode,
validationCode: peer.TxValidationCode_VALID,
txid: txID,
}
return
} else {
logger.Warning("Nil tx from block")
results <- &blockValidationResult{
tIdx: tIdx,
validationCode: peer.TxValidationCode_NIL_ENVELOPE,
}
return
}
}
// generateCCKey generates a unique identifier for chaincode in specific chain
func (v *txValidator) generateCCKey(ccName, chainID string) string {
return fmt.Sprintf("%s/%s", ccName, chainID)
}
// invalidTXsForUpgradeCC invalid all txs that should be invalided because of chaincode upgrade txs
func (v *txValidator) invalidTXsForUpgradeCC(txsChaincodeNames map[int]*sysccprovider.ChaincodeInstance, txsUpgradedChaincodes map[int]*sysccprovider.ChaincodeInstance, txsfltr ledgerUtil.TxValidationFlags) ledgerUtil.TxValidationFlags {
if len(txsUpgradedChaincodes) == 0 {
return txsfltr
}
// Invalid former cc upgrade txs if there're two or more txs upgrade the same cc
finalValidUpgradeTXs := make(map[string]int)
upgradedChaincodes := make(map[string]*sysccprovider.ChaincodeInstance)
for tIdx, cc := range txsUpgradedChaincodes {
if cc == nil {
continue
}
upgradedCCKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
if finalIdx, exist := finalValidUpgradeTXs[upgradedCCKey]; !exist {
finalValidUpgradeTXs[upgradedCCKey] = tIdx
upgradedChaincodes[upgradedCCKey] = cc
} else if finalIdx < tIdx {
logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", finalIdx)
txsfltr.SetFlag(finalIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT)
// record latter cc upgrade tx info
finalValidUpgradeTXs[upgradedCCKey] = tIdx
upgradedChaincodes[upgradedCCKey] = cc
} else {
logger.Infof("Invalid transaction with index %d: chaincode was upgraded by latter tx", tIdx)
txsfltr.SetFlag(tIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT)
}
}
// invalid txs which invoke the upgraded chaincodes
for tIdx, cc := range txsChaincodeNames {
if cc == nil {
continue
}
ccKey := v.generateCCKey(cc.ChaincodeName, cc.ChainID)
if _, exist := upgradedChaincodes[ccKey]; exist {
if txsfltr.IsValid(tIdx) {
logger.Infof("Invalid transaction with index %d: chaincode was upgraded in the same block", tIdx)
txsfltr.SetFlag(tIdx, peer.TxValidationCode_CHAINCODE_VERSION_CONFLICT)
}
}
}
return txsfltr
}
func (v *txValidator) getTxCCInstance(payload *common.Payload) (invokeCCIns, upgradeCCIns *sysccprovider.ChaincodeInstance, err error) {
// This is duplicated unpacking work, but make test easier.
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return nil, nil, err
}
// Chain ID
chainID := chdr.ChannelId // it is guaranteed to be an existing channel by now
// ChaincodeID
hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
if err != nil {
return nil, nil, err
}
invokeCC := hdrExt.ChaincodeId
invokeIns := &sysccprovider.ChaincodeInstance{ChainID: chainID, ChaincodeName: invokeCC.Name, ChaincodeVersion: invokeCC.Version}
// Transaction
tx, err := utils.GetTransaction(payload.Data)
if err != nil {
logger.Errorf("GetTransaction failed: %s", err)
return invokeIns, nil, nil
}
// ChaincodeActionPayload
cap, err := utils.GetChaincodeActionPayload(tx.Actions[0].Payload)
if err != nil {
logger.Errorf("GetChaincodeActionPayload failed: %s", err)
return invokeIns, nil, nil
}
// ChaincodeProposalPayload
cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
if err != nil {
logger.Errorf("GetChaincodeProposalPayload failed: %s", err)
return invokeIns, nil, nil
}
// ChaincodeInvocationSpec
cis := &peer.ChaincodeInvocationSpec{}
err = proto.Unmarshal(cpp.Input, cis)
if err != nil {
logger.Errorf("GetChaincodeInvokeSpec failed: %s", err)
return invokeIns, nil, nil
}
if invokeCC.Name == "lscc" {
if string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade" {
upgradeIns, err := v.getUpgradeTxInstance(chainID, cis.ChaincodeSpec.Input.Args[2])
if err != nil {
return invokeIns, nil, nil
}
return invokeIns, upgradeIns, nil
}
}
return invokeIns, nil, nil
}
func (v *txValidator) getUpgradeTxInstance(chainID string, cdsBytes []byte) (*sysccprovider.ChaincodeInstance, error) {
cds, err := utils.GetChaincodeDeploymentSpec(cdsBytes)
if err != nil {
return nil, err
}
return &sysccprovider.ChaincodeInstance{
ChainID: chainID,
ChaincodeName: cds.ChaincodeSpec.ChaincodeId.Name,
ChaincodeVersion: cds.ChaincodeSpec.ChaincodeId.Version,
}, nil
}
// GetInfoForValidate gets the ChaincodeInstance(with latest version) of tx, vscc and policy from lscc
func (v *vsccValidatorImpl) GetInfoForValidate(txid, chID, ccID string) (*sysccprovider.ChaincodeInstance, *sysccprovider.ChaincodeInstance, []byte, error) {
cc := &sysccprovider.ChaincodeInstance{ChainID: chID}
vscc := &sysccprovider.ChaincodeInstance{ChainID: chID}
var policy []byte
var err error
if !sysccprovider.GetSystemChaincodeProvider().IsSysCC(ccID) {
// when we are validating a chaincode that is not a
// system CC, we need to ask the CC to give us the name
// of VSCC and of the policy that should be used
// obtain name of the VSCC and the policy
cd, err := v.getCDataForCC(chID, ccID)
if err != nil {
msg := fmt.Sprintf("Unable to get chaincode data from ledger for txid %s, due to %s", txid, err)
logger.Errorf(msg)
return nil, nil, nil, err
}
cc.ChaincodeName = cd.CCName()
cc.ChaincodeVersion = cd.CCVersion()
vscc.ChaincodeName, policy = cd.Validation()
} else {
// when we are validating a system CC, we use the default
// VSCC and a default policy that requires one signature
// from any of the members of the channel
cc.ChaincodeName = ccID
cc.ChaincodeVersion = coreUtil.GetSysCCVersion()
vscc.ChaincodeName = "vscc"
p := cauthdsl.SignedByAnyMember(v.support.GetMSPIDs(chID))
policy, err = utils.Marshal(p)
if err != nil {
return nil, nil, nil, err
}
}
// Get vscc version
vscc.ChaincodeVersion = coreUtil.GetSysCCVersion()
return cc, vscc, policy, nil
}
// txWritesToNamespace returns true if the supplied NsRwSet
// performs a ledger write
func (v *vsccValidatorImpl) txWritesToNamespace(ns *rwsetutil.NsRwSet) bool {
// check for public writes first
if ns.KvRwSet != nil && len(ns.KvRwSet.Writes) > 0 {
return true
}
// do not look at collection data if we don't support that capability
if !v.support.Capabilities().PrivateChannelData() {
return false
}
// check for private writes for all collections
for _, c := range ns.CollHashedRwSets {
if c.HashedRwSet != nil && len(c.HashedRwSet.HashedWrites) > 0 {
return true
}
}
return false
}
func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte, env *common.Envelope) (error, peer.TxValidationCode) {
logger.Debugf("VSCCValidateTx starts for env %p envbytes %p", env, envBytes)
defer logger.Debugf("VSCCValidateTx completes for env %p envbytes %p", env, envBytes)
// get header extensions so we have the chaincode ID
hdrExt, err := utils.GetChaincodeHeaderExtension(payload.Header)
if err != nil {
return err, peer.TxValidationCode_BAD_HEADER_EXTENSION
}
// get channel header
chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return err, peer.TxValidationCode_BAD_CHANNEL_HEADER
}
/* obtain the list of namespaces we're writing stuff to;
at first, we establish a few facts about this invocation:
1) which namespaces does it write to?
2) does it write to LSCC's namespace?
3) does it write to any cc that cannot be invoked? */
wrNamespace := []string{}
writesToLSCC := false
writesToNonInvokableSCC := false
respPayload, err := utils.GetActionFromEnvelope(envBytes)
if err != nil {
return fmt.Errorf("GetActionFromEnvelope failed, error %s", err), peer.TxValidationCode_BAD_RESPONSE_PAYLOAD
}
txRWSet := &rwsetutil.TxRwSet{}
if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil {
return fmt.Errorf("txRWSet.FromProtoBytes failed, error %s", err), peer.TxValidationCode_BAD_RWSET
}
for _, ns := range txRWSet.NsRwSets {
if v.txWritesToNamespace(ns) {
wrNamespace = append(wrNamespace, ns.NameSpace)
if !writesToLSCC && ns.NameSpace == "lscc" {
writesToLSCC = true
}
if !writesToNonInvokableSCC && v.sccprovider.IsSysCCAndNotInvokableCC2CC(ns.NameSpace) {
writesToNonInvokableSCC = true
}
if !writesToNonInvokableSCC && v.sccprovider.IsSysCCAndNotInvokableExternal(ns.NameSpace) {
writesToNonInvokableSCC = true
}
}
}
// get name and version of the cc we invoked
ccID := hdrExt.ChaincodeId.Name
ccVer := respPayload.ChaincodeId.Version
// sanity check on ccID
if ccID == "" {
err := fmt.Errorf("invalid chaincode ID")
logger.Errorf("%s", err)
return err, peer.TxValidationCode_INVALID_OTHER_REASON
}
if ccID != respPayload.ChaincodeId.Name {
err := fmt.Errorf("inconsistent ccid info (%s/%s)", ccID, respPayload.ChaincodeId.Name)
logger.Errorf("%s", err)
return err, peer.TxValidationCode_INVALID_OTHER_REASON
}
// sanity check on ccver
if ccVer == "" {
err := fmt.Errorf("invalid chaincode version")
logger.Errorf("%s", err)
return err, peer.TxValidationCode_INVALID_OTHER_REASON
}
// we've gathered all the info required to proceed to validation;
// validation will behave differently depending on the type of
// chaincode (system vs. application)
if !v.sccprovider.IsSysCC(ccID) {
// if we're here, we know this is an invocation of an application chaincode;
// first of all, we make sure that:
// 1) we don't write to LSCC - an application chaincode is free to invoke LSCC
// for instance to get information about itself or another chaincode; however
// these legitimate invocations only ready from LSCC's namespace; currently
// only two functions of LSCC write to its namespace: deploy and upgrade and
// neither should be used by an application chaincode
if writesToLSCC {
return fmt.Errorf("Chaincode %s attempted to write to the namespace of LSCC", ccID),
peer.TxValidationCode_ILLEGAL_WRITESET
}
// 2) we don't write to the namespace of a chaincode that we cannot invoke - if
// the chaincode cannot be invoked in the first place, there's no legitimate
// way in which a transaction has a write set that writes to it; additionally
// we don't have any means of verifying whether the transaction had the rights
// to perform that write operation because in v1, system chaincodes do not have
// any endorsement policies to speak of. So if the chaincode can't be invoked
// it can't be written to by an invocation of an application chaincode
if writesToNonInvokableSCC {
return fmt.Errorf("Chaincode %s attempted to write to the namespace of a system chaincode that cannot be invoked", ccID),
peer.TxValidationCode_ILLEGAL_WRITESET
}
// validate *EACH* read write set according to its chaincode's endorsement policy
for _, ns := range wrNamespace {
// Get latest chaincode version, vscc and validate policy
txcc, vscc, policy, err := v.GetInfoForValidate(chdr.TxId, chdr.ChannelId, ns)
if err != nil {
logger.Errorf("GetInfoForValidate for txId = %s returned error %s", chdr.TxId, err)
return err, peer.TxValidationCode_INVALID_OTHER_REASON
}
// if the namespace corresponds to the cc that was originally
// invoked, we check that the version of the cc that was
// invoked corresponds to the version that lscc has returned
if ns == ccID && txcc.ChaincodeVersion != ccVer {
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)
logger.Errorf(err.Error())
return err, peer.TxValidationCode_EXPIRED_CHAINCODE
}
// do VSCC validation
if err = v.VSCCValidateTxForCC(envBytes, chdr.TxId, chdr.ChannelId, vscc.ChaincodeName, vscc.ChaincodeVersion, policy); err != nil {
switch err.(type) {
case *errors.VSCCEndorsementPolicyError:
return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE
default:
return err, peer.TxValidationCode_INVALID_OTHER_REASON
}
}
}
} else {
// make sure that we can invoke this system chaincode - if the chaincode
// cannot be invoked through a proposal to this peer, we have to drop the
// transaction; if we didn't, we wouldn't know how to decide whether it's
// valid or not because in v1, system chaincodes have no endorsement policy
if v.sccprovider.IsSysCCAndNotInvokableExternal(ccID) {
return fmt.Errorf("Committing an invocation of cc %s is illegal", ccID),
peer.TxValidationCode_ILLEGAL_WRITESET
}
// Get latest chaincode version, vscc and validate policy
_, vscc, policy, err := v.GetInfoForValidate(chdr.TxId, chdr.ChannelId, ccID)
if err != nil {
logger.Errorf("GetInfoForValidate for txId = %s returned error %s", chdr.TxId, err)
return err, peer.TxValidationCode_INVALID_OTHER_REASON
}
// validate the transaction as an invocation of this system chaincode;
// vscc will have to do custom validation for this system chaincode
// currently, VSCC does custom validation for LSCC only; if an hlf
// user creates a new system chaincode which is invokable from the outside
// they have to modify VSCC to provide appropriate validation
if err = v.VSCCValidateTxForCC(envBytes, chdr.TxId, vscc.ChainID, vscc.ChaincodeName, vscc.ChaincodeVersion, policy); err != nil {
switch err.(type) {
case *errors.VSCCEndorsementPolicyError:
return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE
default:
return err, peer.TxValidationCode_INVALID_OTHER_REASON
}
}
}
return nil, peer.TxValidationCode_VALID
}
func (v *vsccValidatorImpl) VSCCValidateTxForCC(envBytes []byte, txid, chid, vsccName, vsccVer string, policy []byte) error {
logger.Debugf("VSCCValidateTxForCC starts for envbytes %p", envBytes)
defer logger.Debugf("VSCCValidateTxForCC completes for envbytes %p", envBytes)
ctxt, txsim, err := v.ccprovider.GetContext(v.support.Ledger(), txid)
if err != nil {
msg := fmt.Sprintf("Cannot obtain context for txid=%s, err %s", txid, err)
logger.Errorf(msg)
return &errors.VSCCExecutionFailureError{msg}
}
defer txsim.Done()
// build arguments for VSCC invocation
// args[0] - function name (not used now)
// args[1] - serialized Envelope
// args[2] - serialized policy
args := [][]byte{[]byte(""), envBytes, policy}
// get context to invoke VSCC
vscctxid := coreUtil.GenerateUUID()
cccid := v.ccprovider.GetCCContext(chid, vsccName, vsccVer, vscctxid, true, nil, nil)
// invoke VSCC
logger.Debug("Invoking VSCC txid", txid, "chaindID", chid)
res, _, err := v.ccprovider.ExecuteChaincode(ctxt, cccid, args)
if err != nil {
msg := fmt.Sprintf("Invoke VSCC failed for transaction txid=%s, error %s", txid, err)
logger.Errorf(msg)
return &errors.VSCCExecutionFailureError{msg}
}
if res.Status != shim.OK {
logger.Errorf("VSCC check failed for transaction txid=%s, error %s", txid, res.Message)
return &errors.VSCCEndorsementPolicyError{fmt.Sprintf("%s", res.Message)}
}
return nil
}
func (v *vsccValidatorImpl) getCDataForCC(chid, ccid string) (resourcesconfig.ChaincodeDefinition, error) {
l := v.support.Ledger()
if l == nil {
return nil, fmt.Errorf("nil ledger instance")
}
qe, err := l.NewQueryExecutor()
if err != nil {
return nil, fmt.Errorf("Could not retrieve QueryExecutor, error %s", err)
}
defer qe.Done()
bytes, err := qe.GetState("lscc", ccid)
if err != nil {
return nil, &errors.VSCCInfoLookupFailureError{fmt.Sprintf("Could not retrieve state for chaincode %s, error %s", ccid, err)}
}
if bytes == nil {
return nil, fmt.Errorf("lscc's state for [%s] not found.", ccid)
}
cd := &ccprovider.ChaincodeData{}
err = proto.Unmarshal(bytes, cd)
if err != nil {
return nil, fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err)
}
if cd.Vscc == "" {
return nil, fmt.Errorf("lscc's state for [%s] is invalid, vscc field must be set.", ccid)
}
if len(cd.Policy) == 0 {
return nil, fmt.Errorf("lscc's state for [%s] is invalid, policy field must be set.", ccid)
}
return cd, err
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mirrors/hyperledger-fabric.git
git@gitee.com:mirrors/hyperledger-fabric.git
mirrors
hyperledger-fabric
hyperledger-fabric
v1.1.0-alpha

搜索帮助

0d507c66 1850385 C8b1a773 1850385