1 Star 0 Fork 0

妥協 / fabric

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
lscc.go 37.83 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package lscc
import (
"bytes"
"fmt"
"regexp"
"sync"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-chaincode-go/shim"
mb "github.com/hyperledger/fabric-protos-go/msp"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/common/channelconfig"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/policydsl"
"github.com/hyperledger/fabric/core/aclmgmt"
"github.com/hyperledger/fabric/core/aclmgmt/resources"
"github.com/hyperledger/fabric/core/chaincode/lifecycle"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/privdata"
"github.com/hyperledger/fabric/core/common/sysccprovider"
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/externalbuilder"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/cceventmgmt"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/policy"
"github.com/hyperledger/fabric/core/scc"
"github.com/hyperledger/fabric/internal/ccmetadata"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
)
// The lifecycle system chaincode manages chaincodes deployed
// on this peer. It manages chaincodes via Invoke proposals.
// "Args":["deploy",<ChaincodeDeploymentSpec>]
// "Args":["upgrade",<ChaincodeDeploymentSpec>]
// "Args":["stop",<ChaincodeInvocationSpec>]
// "Args":["start",<ChaincodeInvocationSpec>]
var (
logger = flogging.MustGetLogger("lscc")
// NOTE these regular expressions should stay in sync with those defined in
// core/chaincode/lifecycle/scc.go until LSCC has been removed.
ChaincodeNameRegExp = regexp.MustCompile("^[a-zA-Z0-9]+([-_][a-zA-Z0-9]+)*$")
ChaincodeVersionRegExp = regexp.MustCompile("^[A-Za-z0-9_.+-]+$")
)
const (
// chaincode lifecycle commands
// INSTALL install command
INSTALL = "install"
// DEPLOY deploy command
DEPLOY = "deploy"
// UPGRADE upgrade chaincode
UPGRADE = "upgrade"
// CCEXISTS get chaincode
CCEXISTS = "getid"
// CHAINCODEEXISTS get chaincode alias
CHAINCODEEXISTS = "ChaincodeExists"
// GETDEPSPEC get ChaincodeDeploymentSpec
GETDEPSPEC = "getdepspec"
// GETDEPLOYMENTSPEC get ChaincodeDeploymentSpec alias
GETDEPLOYMENTSPEC = "GetDeploymentSpec"
// GETCCDATA get ChaincodeData
GETCCDATA = "getccdata"
// GETCHAINCODEDATA get ChaincodeData alias
GETCHAINCODEDATA = "GetChaincodeData"
// GETCHAINCODES gets the instantiated chaincodes on a channel
GETCHAINCODES = "getchaincodes"
// GETCHAINCODESALIAS gets the instantiated chaincodes on a channel
GETCHAINCODESALIAS = "GetChaincodes"
// GETINSTALLEDCHAINCODES gets the installed chaincodes on a peer
GETINSTALLEDCHAINCODES = "getinstalledchaincodes"
// GETINSTALLEDCHAINCODESALIAS gets the installed chaincodes on a peer
GETINSTALLEDCHAINCODESALIAS = "GetInstalledChaincodes"
// GETCOLLECTIONSCONFIG gets the collections config for a chaincode
GETCOLLECTIONSCONFIG = "GetCollectionsConfig"
// GETCOLLECTIONSCONFIGALIAS gets the collections config for a chaincode
GETCOLLECTIONSCONFIGALIAS = "getcollectionsconfig"
)
// FilesystemSupport contains functions that LSCC requires to execute its tasks
type FilesystemSupport interface {
// PutChaincodeToLocalStorage stores the supplied chaincode
// package to local storage (i.e. the file system)
PutChaincodeToLocalStorage(ccprovider.CCPackage) error
// GetChaincodeFromLocalStorage retrieves the chaincode package
// for the requested chaincode, specified by name and version
GetChaincodeFromLocalStorage(ccNameVersion string) (ccprovider.CCPackage, error)
// GetChaincodesFromLocalStorage returns an array of all chaincode
// data that have previously been persisted to local storage
GetChaincodesFromLocalStorage() (*pb.ChaincodeQueryResponse, error)
// GetInstantiationPolicy returns the instantiation policy for the
// supplied chaincode (or the channel's default if none was specified)
GetInstantiationPolicy(channel string, ccpack ccprovider.CCPackage) ([]byte, error)
// CheckInstantiationPolicy checks whether the supplied signed proposal
// complies with the supplied instantiation policy
CheckInstantiationPolicy(signedProposal *pb.SignedProposal, chainName string, instantiationPolicy []byte) error
}
type ChaincodeBuilder interface {
Build(ccid string) error
}
// MSPsIDGetter is used to get the MSP IDs for a channel.
type MSPIDsGetter func(string) []string
//---------- the LSCC -----------------
// SCC implements chaincode lifecycle and policies around it
type SCC struct {
// aclProvider is responsible for access control evaluation
ACLProvider aclmgmt.ACLProvider
BuiltinSCCs scc.BuiltinSCCs
// SCCProvider is the interface which is passed into system chaincodes
// to access other parts of the system
SCCProvider sysccprovider.SystemChaincodeProvider
// PolicyChecker is the interface used to perform
// access control
PolicyChecker policy.PolicyChecker
// Support provides the implementation of several
// static functions
Support FilesystemSupport
GetMSPIDs MSPIDsGetter
BuildRegistry *container.BuildRegistry
ChaincodeBuilder ChaincodeBuilder
EbMetadataProvider *externalbuilder.MetadataProvider
// BCCSP instance
BCCSP bccsp.BCCSP
PackageCache PackageCache
}
// PeerShim adapts the peer instance for use with LSCC by providing methods
// previously provided by the scc provider. If the lscc code weren't all getting
// deleted soon, it would probably be worth rewriting it to use these APIs directly
// rather that go through this shim, but it will be gone soon.
type PeerShim struct {
Peer *peer.Peer
}
// GetQueryExecutorForLedger returns a query executor for the specified channel
func (p *PeerShim) GetQueryExecutorForLedger(cid string) (ledger.QueryExecutor, error) {
l := p.Peer.GetLedger(cid)
if l == nil {
return nil, fmt.Errorf("Could not retrieve ledger for channel %s", cid)
}
return l.NewQueryExecutor()
}
// GetApplicationConfig returns the configtxapplication.SharedConfig for the channel
// and whether the Application config exists
func (p *PeerShim) GetApplicationConfig(cid string) (channelconfig.Application, bool) {
return p.Peer.GetApplicationConfig(cid)
}
// Returns the policy manager associated to the passed channel
// and whether the policy manager exists
func (p *PeerShim) PolicyManager(channelID string) (policies.Manager, bool) {
m := p.Peer.GetPolicyManager(channelID)
return m, (m != nil)
}
func (lscc *SCC) Name() string { return "lscc" }
func (lscc *SCC) Chaincode() shim.Chaincode { return lscc }
type PackageCache struct {
Mutex sync.RWMutex
ValidatedPackages map[string]*ccprovider.ChaincodeData
}
type LegacySecurity struct {
Support FilesystemSupport
PackageCache *PackageCache
}
func (ls *LegacySecurity) SecurityCheckLegacyChaincode(cd *ccprovider.ChaincodeData) error {
ccid := cd.ChaincodeID()
ls.PackageCache.Mutex.RLock()
fsData, ok := ls.PackageCache.ValidatedPackages[ccid]
ls.PackageCache.Mutex.RUnlock()
if !ok {
ls.PackageCache.Mutex.Lock()
defer ls.PackageCache.Mutex.Unlock()
fsData, ok = ls.PackageCache.ValidatedPackages[ccid]
if !ok {
ccpack, err := ls.Support.GetChaincodeFromLocalStorage(cd.ChaincodeID())
if err != nil {
return InvalidDeploymentSpecErr(err.Error())
}
// This is 'the big security check', though it's no clear what's being accomplished
// here. Basically, it seems to try to verify that the chaincode defintion matches
// what's on the filesystem, which, might include instanatiation policy, but it's
// not obvious from the code, and was being checked separately, so we check it
// explicitly below.
if err = ccpack.ValidateCC(cd); err != nil {
return InvalidCCOnFSError(err.Error())
}
if ls.PackageCache.ValidatedPackages == nil {
ls.PackageCache.ValidatedPackages = map[string]*ccprovider.ChaincodeData{}
}
fsData = ccpack.GetChaincodeData()
ls.PackageCache.ValidatedPackages[ccid] = fsData
}
}
// we have the info from the fs, check that the policy
// matches the one on the file system if one was specified;
// this check is required because the admin of this peer
// might have specified instantiation policies for their
// chaincode, for example to make sure that the chaincode
// is only instantiated on certain channels; a malicious
// peer on the other hand might have created a deploy
// transaction that attempts to bypass the instantiation
// policy. This check is there to ensure that this will not
// happen, i.e. that the peer will refuse to invoke the
// chaincode under these conditions. More info on
// https://jira.hyperledger.org/browse/FAB-3156
if fsData.InstantiationPolicy != nil {
if !bytes.Equal(fsData.InstantiationPolicy, cd.InstantiationPolicy) {
return fmt.Errorf("Instantiation policy mismatch for cc %s", cd.ChaincodeID())
}
}
return nil
}
func (lscc *SCC) ChaincodeEndorsementInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*lifecycle.ChaincodeEndorsementInfo, error) {
chaincodeDataBytes, err := qe.GetState("lscc", chaincodeName)
if err != nil {
return nil, errors.Wrapf(err, "could not retrieve state for chaincode %s", chaincodeName)
}
if chaincodeDataBytes == nil {
return nil, errors.Errorf("chaincode %s not found", chaincodeName)
}
chaincodeData := &ccprovider.ChaincodeData{}
err = proto.Unmarshal(chaincodeDataBytes, chaincodeData)
if err != nil {
return nil, errors.Wrapf(err, "chaincode %s has bad definition", chaincodeName)
}
ls := &LegacySecurity{
Support: lscc.Support,
PackageCache: &lscc.PackageCache,
}
err = ls.SecurityCheckLegacyChaincode(chaincodeData)
if err != nil {
return nil, errors.WithMessage(err, "failed security checks")
}
return &lifecycle.ChaincodeEndorsementInfo{
Version: chaincodeData.Version,
EndorsementPlugin: chaincodeData.Escc,
ChaincodeID: chaincodeData.Name + ":" + chaincodeData.Version,
}, nil
}
// ValidationInfo returns name&arguments of the validation plugin for the supplied chaincode.
// The function returns two types of errors, unexpected errors and validation errors. The
// reason for this is that this function is to be called from the validation code, which
// needs to tell apart the two types of error to halt processing on the channel if the
// unexpected error is not nil and mark the transaction as invalid if the validation error
// is not nil.
func (lscc *SCC) ValidationInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (plugin string, args []byte, unexpectedErr error, validationErr error) {
chaincodeDataBytes, err := qe.GetState("lscc", chaincodeName)
if err != nil {
// failure to access the ledger is clearly an unexpected
// error since we expect the ledger to be reachable
unexpectedErr = errors.Wrapf(err, "could not retrieve state for chaincode %s", chaincodeName)
return
}
if chaincodeDataBytes == nil {
// no chaincode definition is a validation error since
// we're trying to retrieve chaincode definitions for a non-existent chaincode
validationErr = errors.Errorf("chaincode %s not found", chaincodeName)
return
}
chaincodeData := &ccprovider.ChaincodeData{}
err = proto.Unmarshal(chaincodeDataBytes, chaincodeData)
if err != nil {
// this kind of data corruption is unexpected since our code
// always marshals ChaincodeData into these keys
unexpectedErr = errors.Wrapf(err, "chaincode %s has bad definition", chaincodeName)
return
}
plugin = chaincodeData.Vscc
args = chaincodeData.Policy
return
}
//create the chaincode on the given chain
func (lscc *SCC) putChaincodeData(stub shim.ChaincodeStubInterface, cd *ccprovider.ChaincodeData) error {
cdbytes, err := proto.Marshal(cd)
if err != nil {
return err
}
if cdbytes == nil {
return MarshallErr(cd.Name)
}
err = stub.PutState(cd.Name, cdbytes)
return err
}
// checkCollectionMemberPolicy checks whether the supplied collection configuration
// complies to the given msp configuration and performs semantic validation.
// Channel config may change afterwards (i.e., after endorsement or commit of this transaction).
// Fabric will deal with the situation where some collection configs are no longer meaningful.
// Therefore, the use of channel config for verifying during endorsement is more
// towards catching manual errors in the config as oppose to any attempt of serializability.
func checkCollectionMemberPolicy(collectionConfig *pb.CollectionConfig, mspmgr msp.MSPManager) error {
if mspmgr == nil {
return fmt.Errorf("msp manager not set")
}
msps, err := mspmgr.GetMSPs()
if err != nil {
return errors.Wrapf(err, "error getting channel msp")
}
if collectionConfig == nil {
return fmt.Errorf("collection configuration is not set")
}
coll := collectionConfig.GetStaticCollectionConfig()
if coll == nil {
return fmt.Errorf("collection configuration is empty")
}
if coll.MemberOrgsPolicy == nil {
return fmt.Errorf("collection member policy is not set")
}
if coll.MemberOrgsPolicy.GetSignaturePolicy() == nil {
return fmt.Errorf("collection member org policy is empty")
}
// make sure that the orgs listed are actually part of the channel
// check all principals in the signature policy
for _, principal := range coll.MemberOrgsPolicy.GetSignaturePolicy().Identities {
found := false
var orgID string
// the member org policy only supports certain principal types
switch principal.PrincipalClassification {
case mb.MSPPrincipal_ROLE:
msprole := &mb.MSPRole{}
err := proto.Unmarshal(principal.Principal, msprole)
if err != nil {
return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identities", coll.GetName())
}
orgID = msprole.MspIdentifier
// the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack
for mspid := range msps {
if mspid == orgID {
found = true
break
}
}
case mb.MSPPrincipal_ORGANIZATION_UNIT:
mspou := &mb.OrganizationUnit{}
err := proto.Unmarshal(principal.Principal, mspou)
if err != nil {
return errors.Wrapf(err, "collection-name: %s -- cannot unmarshal identities", coll.GetName())
}
orgID = mspou.MspIdentifier
// the msp map is indexed using msp IDs - this behavior is implementation specific, making the following check a bit of a hack
for mspid := range msps {
if mspid == orgID {
found = true
break
}
}
case mb.MSPPrincipal_IDENTITY:
orgID = "identity principal"
for _, msp := range msps {
_, err := msp.DeserializeIdentity(principal.Principal)
if err == nil {
found = true
break
}
}
default:
return fmt.Errorf("collection-name: %s -- principal type %v is not supported", coll.GetName(), principal.PrincipalClassification)
}
if !found {
logger.Warningf("collection-name: %s collection member %s is not part of the channel", coll.GetName(), orgID)
}
}
// Call the constructor for SignaturePolicyEnvelope evaluators to perform extra semantic validation.
// Among other things, this validation catches any out-of-range references to the identities array.
policyProvider := &cauthdsl.EnvelopeBasedPolicyProvider{Deserializer: mspmgr}
if _, err := policyProvider.NewPolicy(coll.MemberOrgsPolicy.GetSignaturePolicy()); err != nil {
logger.Errorf("Invalid member org policy for collection '%s', error: %s", coll.Name, err)
return errors.WithMessage(err, fmt.Sprintf("invalid member org policy for collection '%s'", coll.Name))
}
return nil
}
// putChaincodeCollectionData adds collection data for the chaincode
func (lscc *SCC) putChaincodeCollectionData(stub shim.ChaincodeStubInterface, cd *ccprovider.ChaincodeData, collectionConfigBytes []byte) error {
if cd == nil {
return errors.New("nil ChaincodeData")
}
if len(collectionConfigBytes) == 0 {
logger.Debug("No collection configuration specified")
return nil
}
collections := &pb.CollectionConfigPackage{}
err := proto.Unmarshal(collectionConfigBytes, collections)
if err != nil {
return errors.Errorf("invalid collection configuration supplied for chaincode %s:%s", cd.Name, cd.Version)
}
mspmgr := mgmt.GetManagerForChain(stub.GetChannelID())
if mspmgr == nil {
return fmt.Errorf("could not get MSP manager for channel %s", stub.GetChannelID())
}
for _, collectionConfig := range collections.Config {
err = checkCollectionMemberPolicy(collectionConfig, mspmgr)
if err != nil {
return errors.Wrapf(err, "collection member policy check failed")
}
}
key := privdata.BuildCollectionKVSKey(cd.Name)
err = stub.PutState(key, collectionConfigBytes)
if err != nil {
return errors.WithMessagef(err, "error putting collection for chaincode %s:%s", cd.Name, cd.Version)
}
return nil
}
// getChaincodeCollectionData retrieve collections config.
func (lscc *SCC) getChaincodeCollectionData(stub shim.ChaincodeStubInterface, chaincodeName string) pb.Response {
key := privdata.BuildCollectionKVSKey(chaincodeName)
collectionsConfigBytes, err := stub.GetState(key)
if err != nil {
return shim.Error(err.Error())
}
if len(collectionsConfigBytes) == 0 {
return shim.Error(fmt.Sprintf("collections config not defined for chaincode %s", chaincodeName))
}
return shim.Success(collectionsConfigBytes)
}
//checks for existence of chaincode on the given channel
func (lscc *SCC) getCCInstance(stub shim.ChaincodeStubInterface, ccname string) ([]byte, error) {
cdbytes, err := stub.GetState(ccname)
if err != nil {
return nil, TXNotFoundErr(err.Error())
}
if cdbytes == nil {
return nil, NotFoundErr(ccname)
}
return cdbytes, nil
}
//gets the cd out of the bytes
func (lscc *SCC) getChaincodeData(ccname string, cdbytes []byte) (*ccprovider.ChaincodeData, error) {
cd := &ccprovider.ChaincodeData{}
err := proto.Unmarshal(cdbytes, cd)
if err != nil {
return nil, MarshallErr(ccname)
}
//this should not happen but still a sanity check is not a bad thing
if cd.Name != ccname {
return nil, ChaincodeMismatchErr(fmt.Sprintf("%s!=%s", ccname, cd.Name))
}
return cd, nil
}
//checks for existence of chaincode on the given chain
func (lscc *SCC) getCCCode(ccname string, cdbytes []byte) (*pb.ChaincodeDeploymentSpec, []byte, error) {
cd, err := lscc.getChaincodeData(ccname, cdbytes)
if err != nil {
return nil, nil, err
}
ccpack, err := lscc.Support.GetChaincodeFromLocalStorage(cd.ChaincodeID())
if err != nil {
return nil, nil, InvalidDeploymentSpecErr(err.Error())
}
//this is the big test and the reason every launch should go through
//getChaincode call. We validate the chaincode entry against the
//the chaincode in FS
if err = ccpack.ValidateCC(cd); err != nil {
return nil, nil, InvalidCCOnFSError(err.Error())
}
//these are guaranteed to be non-nil because we got a valid ccpack
depspec := ccpack.GetDepSpec()
depspecbytes := ccpack.GetDepSpecBytes()
return depspec, depspecbytes, nil
}
// getChaincodes returns all chaincodes instantiated on this LSCC's channel
func (lscc *SCC) getChaincodes(stub shim.ChaincodeStubInterface) pb.Response {
// get all rows from LSCC
itr, err := stub.GetStateByRange("", "")
if err != nil {
return shim.Error(err.Error())
}
defer itr.Close()
// array to store metadata for all chaincode entries from LSCC
var ccInfoArray []*pb.ChaincodeInfo
for itr.HasNext() {
response, err := itr.Next()
if err != nil {
return shim.Error(err.Error())
}
// CollectionConfig isn't ChaincodeData
if privdata.IsCollectionConfigKey(response.Key) {
continue
}
ccdata := &ccprovider.ChaincodeData{}
if err = proto.Unmarshal(response.Value, ccdata); err != nil {
return shim.Error(err.Error())
}
var path string
var input string
// if chaincode is not installed on the system we won't have
// data beyond name and version
ccpack, err := lscc.Support.GetChaincodeFromLocalStorage(ccdata.ChaincodeID())
if err == nil {
path = ccpack.GetDepSpec().GetChaincodeSpec().ChaincodeId.Path
input = ccpack.GetDepSpec().GetChaincodeSpec().Input.String()
}
// add this specific chaincode's metadata to the array of all chaincodes
ccInfo := &pb.ChaincodeInfo{Name: ccdata.Name, Version: ccdata.Version, Path: path, Input: input, Escc: ccdata.Escc, Vscc: ccdata.Vscc}
ccInfoArray = append(ccInfoArray, ccInfo)
}
// add array with info about all instantiated chaincodes to the query
// response proto
cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
cqrbytes, err := proto.Marshal(cqr)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(cqrbytes)
}
// getInstalledChaincodes returns all chaincodes installed on the peer
func (lscc *SCC) getInstalledChaincodes() pb.Response {
// get chaincode query response proto which contains information about all
// installed chaincodes
cqr, err := lscc.Support.GetChaincodesFromLocalStorage()
if err != nil {
return shim.Error(err.Error())
}
cqrbytes, err := proto.Marshal(cqr)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(cqrbytes)
}
// check validity of channel name
func (lscc *SCC) isValidChannelName(channel string) bool {
// TODO we probably need more checks
if channel == "" {
return false
}
return true
}
// isValidChaincodeName checks the validity of chaincode name. Chaincode names
// should never be blank and should only consist of alphanumerics, '_', and '-'
func (lscc *SCC) isValidChaincodeName(chaincodeName string) error {
if !ChaincodeNameRegExp.MatchString(chaincodeName) {
return InvalidChaincodeNameErr(chaincodeName)
}
return nil
}
// isValidChaincodeVersion checks the validity of chaincode version. Versions
// should never be blank and should only consist of alphanumerics, '_', '-',
// '+', and '.'
func (lscc *SCC) isValidChaincodeVersion(chaincodeName string, version string) error {
if !ChaincodeVersionRegExp.MatchString(version) {
return InvalidVersionErr(version)
}
return nil
}
func isValidStatedbArtifactsTar(statedbArtifactsTar []byte) error {
// Extract the metadata files from the archive
// Passing an empty string for the databaseType will validate all artifacts in
// the archive
archiveFiles, err := ccprovider.ExtractFileEntries(statedbArtifactsTar, "")
if err != nil {
return err
}
// iterate through the files and validate
for _, archiveDirectoryFiles := range archiveFiles {
for _, fileEntry := range archiveDirectoryFiles {
indexData := fileEntry.FileContent
// Validation is based on the passed file name, e.g. META-INF/statedb/couchdb/indexes/indexname.json
err = ccmetadata.ValidateMetadataFile(fileEntry.FileHeader.Name, indexData)
if err != nil {
return err
}
}
}
return nil
}
// executeInstall implements the "install" Invoke transaction
func (lscc *SCC) executeInstall(stub shim.ChaincodeStubInterface, ccbytes []byte) error {
ccpack, err := ccprovider.GetCCPackage(ccbytes, lscc.BCCSP)
if err != nil {
return err
}
cds := ccpack.GetDepSpec()
if cds == nil {
return fmt.Errorf("nil deployment spec from the CC package")
}
if err = lscc.isValidChaincodeName(cds.ChaincodeSpec.ChaincodeId.Name); err != nil {
return err
}
if err = lscc.isValidChaincodeVersion(cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version); err != nil {
return err
}
if lscc.BuiltinSCCs.IsSysCC(cds.ChaincodeSpec.ChaincodeId.Name) {
return errors.Errorf("cannot install: %s is the name of a system chaincode", cds.ChaincodeSpec.ChaincodeId.Name)
}
// We must put the chaincode package on the filesystem prior to building it.
// This creates a race for endorsements coming in and executing the chaincode
// prior to indexes being installed, but, such is life, and this case is already
// present when an already installed chaincode is instantiated.
if err = lscc.Support.PutChaincodeToLocalStorage(ccpack); err != nil {
return err
}
ccid := ccpack.GetChaincodeData().Name + ":" + ccpack.GetChaincodeData().Version
buildStatus, building := lscc.BuildRegistry.BuildStatus(ccid)
if !building {
err := lscc.ChaincodeBuilder.Build(ccid)
buildStatus.Notify(err)
}
<-buildStatus.Done()
if err := buildStatus.Err(); err != nil {
return errors.WithMessage(err, "could not build chaincode")
}
md, err := lscc.EbMetadataProvider.PackageMetadata(ccid)
if err != nil {
return errors.WithMessage(err, "external builder release metadata found, but could not be packaged")
}
if md == nil {
// Get any statedb artifacts from the chaincode package, e.g. couchdb index definitions
md, err = ccprovider.ExtractStatedbArtifactsFromCCPackage(ccpack)
if err != nil {
return err
}
}
if err = isValidStatedbArtifactsTar(md); err != nil {
return InvalidStatedbArtifactsErr(err.Error())
}
chaincodeDefinition := &cceventmgmt.ChaincodeDefinition{
Name: ccpack.GetChaincodeData().Name,
Version: ccpack.GetChaincodeData().Version,
Hash: ccpack.GetId()} // Note - The chaincode 'id' is the hash of chaincode's (CodeHash || MetaDataHash), aka fingerprint
// HandleChaincodeInstall will apply any statedb artifacts (e.g. couchdb indexes) to
// any channel's statedb where the chaincode is already instantiated
// Note - this step is done prior to PutChaincodeToLocalStorage() since this step is idempotent and harmless until endorsements start,
// that is, if there are errors deploying the indexes the chaincode install can safely be re-attempted later.
err = cceventmgmt.GetMgr().HandleChaincodeInstall(chaincodeDefinition, md)
defer func() {
cceventmgmt.GetMgr().ChaincodeInstallDone(err == nil)
}()
if err != nil {
return err
}
logger.Infof("Installed Chaincode [%s] Version [%s] to peer", ccpack.GetChaincodeData().Name, ccpack.GetChaincodeData().Version)
return nil
}
// executeDeployOrUpgrade routes the code path either to executeDeploy or executeUpgrade
// depending on its function argument
func (lscc *SCC) executeDeployOrUpgrade(
stub shim.ChaincodeStubInterface,
chainname string,
cds *pb.ChaincodeDeploymentSpec,
policy, escc, vscc, collectionConfigBytes []byte,
function string,
) (*ccprovider.ChaincodeData, error) {
chaincodeName := cds.ChaincodeSpec.ChaincodeId.Name
chaincodeVersion := cds.ChaincodeSpec.ChaincodeId.Version
if err := lscc.isValidChaincodeName(chaincodeName); err != nil {
return nil, err
}
if err := lscc.isValidChaincodeVersion(chaincodeName, chaincodeVersion); err != nil {
return nil, err
}
chaincodeNameVersion := chaincodeName + ":" + chaincodeVersion
ccpack, err := lscc.Support.GetChaincodeFromLocalStorage(chaincodeNameVersion)
if err != nil {
retErrMsg := fmt.Sprintf("cannot get package for chaincode (%s)", chaincodeNameVersion)
logger.Errorf("%s-err:%s", retErrMsg, err)
return nil, fmt.Errorf("%s", retErrMsg)
}
cd := ccpack.GetChaincodeData()
switch function {
case DEPLOY:
return lscc.executeDeploy(stub, chainname, cds, policy, escc, vscc, cd, ccpack, collectionConfigBytes)
case UPGRADE:
return lscc.executeUpgrade(stub, chainname, cds, policy, escc, vscc, cd, ccpack, collectionConfigBytes)
default:
logger.Panicf("Programming error, unexpected function '%s'", function)
panic("") // unreachable code
}
}
// executeDeploy implements the "instantiate" Invoke transaction
func (lscc *SCC) executeDeploy(
stub shim.ChaincodeStubInterface,
chainname string,
cds *pb.ChaincodeDeploymentSpec,
policy []byte,
escc []byte,
vscc []byte,
cdfs *ccprovider.ChaincodeData,
ccpackfs ccprovider.CCPackage,
collectionConfigBytes []byte,
) (*ccprovider.ChaincodeData, error) {
//just test for existence of the chaincode in the LSCC
chaincodeName := cds.ChaincodeSpec.ChaincodeId.Name
_, err := lscc.getCCInstance(stub, chaincodeName)
if err == nil {
return nil, ExistsErr(chaincodeName)
}
//retain chaincode specific data and fill channel specific ones
cdfs.Escc = string(escc)
cdfs.Vscc = string(vscc)
cdfs.Policy = policy
// retrieve and evaluate instantiation policy
cdfs.InstantiationPolicy, err = lscc.Support.GetInstantiationPolicy(chainname, ccpackfs)
if err != nil {
return nil, err
}
// get the signed instantiation proposal
signedProp, err := stub.GetSignedProposal()
if err != nil {
return nil, err
}
err = lscc.Support.CheckInstantiationPolicy(signedProp, chainname, cdfs.InstantiationPolicy)
if err != nil {
return nil, err
}
err = lscc.putChaincodeData(stub, cdfs)
if err != nil {
return nil, err
}
err = lscc.putChaincodeCollectionData(stub, cdfs, collectionConfigBytes)
if err != nil {
return nil, err
}
return cdfs, nil
}
// executeUpgrade implements the "upgrade" Invoke transaction.
func (lscc *SCC) executeUpgrade(stub shim.ChaincodeStubInterface, chainName string, cds *pb.ChaincodeDeploymentSpec, policy []byte, escc []byte, vscc []byte, cdfs *ccprovider.ChaincodeData, ccpackfs ccprovider.CCPackage, collectionConfigBytes []byte) (*ccprovider.ChaincodeData, error) {
chaincodeName := cds.ChaincodeSpec.ChaincodeId.Name
// check for existence of chaincode instance only (it has to exist on the channel)
// we dont care about the old chaincode on the FS. In particular, user may even
// have deleted it
cdbytes, _ := lscc.getCCInstance(stub, chaincodeName)
if cdbytes == nil {
return nil, NotFoundErr(chaincodeName)
}
//we need the cd to compare the version
cdLedger, err := lscc.getChaincodeData(chaincodeName, cdbytes)
if err != nil {
return nil, err
}
//do not upgrade if same version
if cdLedger.Version == cds.ChaincodeSpec.ChaincodeId.Version {
return nil, IdenticalVersionErr(chaincodeName)
}
//do not upgrade if instantiation policy is violated
if cdLedger.InstantiationPolicy == nil {
return nil, InstantiationPolicyMissing("")
}
// get the signed instantiation proposal
signedProp, err := stub.GetSignedProposal()
if err != nil {
return nil, err
}
err = lscc.Support.CheckInstantiationPolicy(signedProp, chainName, cdLedger.InstantiationPolicy)
if err != nil {
return nil, err
}
//retain chaincode specific data and fill channel specific ones
cdfs.Escc = string(escc)
cdfs.Vscc = string(vscc)
cdfs.Policy = policy
// retrieve and evaluate new instantiation policy
cdfs.InstantiationPolicy, err = lscc.Support.GetInstantiationPolicy(chainName, ccpackfs)
if err != nil {
return nil, err
}
err = lscc.Support.CheckInstantiationPolicy(signedProp, chainName, cdfs.InstantiationPolicy)
if err != nil {
return nil, err
}
err = lscc.putChaincodeData(stub, cdfs)
if err != nil {
return nil, err
}
ac, exists := lscc.SCCProvider.GetApplicationConfig(chainName)
if !exists {
logger.Panicf("programming error, non-existent appplication config for channel '%s'", chainName)
}
if ac.Capabilities().CollectionUpgrade() {
err = lscc.putChaincodeCollectionData(stub, cdfs, collectionConfigBytes)
if err != nil {
return nil, err
}
} else {
if collectionConfigBytes != nil {
return nil, errors.New(CollectionsConfigUpgradesNotAllowed("").Error())
}
}
lifecycleEvent := &pb.LifecycleEvent{ChaincodeName: chaincodeName}
lifecycleEventBytes := protoutil.MarshalOrPanic(lifecycleEvent)
stub.SetEvent(UPGRADE, lifecycleEventBytes)
return cdfs, nil
}
//-------------- the chaincode stub interface implementation ----------
//Init is mostly useless for SCC
func (lscc *SCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}
// Invoke implements lifecycle functions "deploy", "start", "stop", "upgrade".
// Deploy's arguments - {[]byte("deploy"), []byte(<chainname>), <unmarshalled pb.ChaincodeDeploymentSpec>}
//
// Invoke also implements some query-like functions
// Get chaincode arguments - {[]byte("getid"), []byte(<chainname>), []byte(<chaincodename>)}
func (lscc *SCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
args := stub.GetArgs()
if len(args) < 1 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
function := string(args[0])
// Handle ACL:
// 1. get the signed proposal
sp, err := stub.GetSignedProposal()
if err != nil {
return shim.Error(fmt.Sprintf("Failed retrieving signed proposal on executing %s with error %s", function, err))
}
switch function {
case INSTALL:
if len(args) < 2 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
// 2. check install policy
if err = lscc.ACLProvider.CheckACL(resources.Lscc_Install, "", sp); err != nil {
return shim.Error(fmt.Sprintf("access denied for [%s]: %s", function, err))
}
depSpec := args[1]
err := lscc.executeInstall(stub, depSpec)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte("OK"))
case DEPLOY, UPGRADE:
// we expect a minimum of 3 arguments, the function
// name, the chain name and deployment spec
if len(args) < 3 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
// channel the chaincode should be associated with. It
// should be created with a register call
channel := string(args[1])
if !lscc.isValidChannelName(channel) {
return shim.Error(InvalidChannelNameErr(channel).Error())
}
ac, exists := lscc.SCCProvider.GetApplicationConfig(channel)
if !exists {
logger.Panicf("programming error, non-existent appplication config for channel '%s'", channel)
}
if ac.Capabilities().LifecycleV20() {
return shim.Error(fmt.Sprintf("Channel '%s' has been migrated to the new lifecycle, LSCC is now read-only", channel))
}
// the maximum number of arguments depends on the capability of the channel
if !ac.Capabilities().PrivateChannelData() && len(args) > 6 {
return shim.Error(PrivateChannelDataNotAvailable("").Error())
}
if ac.Capabilities().PrivateChannelData() && len(args) > 7 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
depSpec := args[2]
cds := &pb.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(depSpec, cds)
if err != nil {
return shim.Error(fmt.Sprintf("error unmarshaling ChaincodeDeploymentSpec: %s", err))
}
// optional arguments here (they can each be nil and may or may not be present)
// args[3] is a marshalled SignaturePolicyEnvelope representing the endorsement policy
// args[4] is the name of escc
// args[5] is the name of vscc
// args[6] is a marshalled CollectionConfigPackage struct
var EP []byte
if len(args) > 3 && len(args[3]) > 0 {
EP = args[3]
} else {
mspIDs := lscc.GetMSPIDs(channel)
p := policydsl.SignedByAnyMember(mspIDs)
EP, err = protoutil.Marshal(p)
if err != nil {
return shim.Error(err.Error())
}
}
var escc []byte
if len(args) > 4 && len(args[4]) > 0 {
escc = args[4]
} else {
escc = []byte("escc")
}
var vscc []byte
if len(args) > 5 && len(args[5]) > 0 {
vscc = args[5]
} else {
vscc = []byte("vscc")
}
var collectionsConfig []byte
// we proceed with a non-nil collection configuration only if
// we Support the PrivateChannelData capability
if ac.Capabilities().PrivateChannelData() && len(args) > 6 {
collectionsConfig = args[6]
}
cd, err := lscc.executeDeployOrUpgrade(stub, channel, cds, EP, escc, vscc, collectionsConfig, function)
if err != nil {
return shim.Error(err.Error())
}
cdbytes, err := proto.Marshal(cd)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(cdbytes)
case CCEXISTS, CHAINCODEEXISTS, GETDEPSPEC, GETDEPLOYMENTSPEC, GETCCDATA, GETCHAINCODEDATA:
if len(args) != 3 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
channel := string(args[1])
ccname := string(args[2])
// 2. check policy for ACL resource
var resource string
switch function {
case CCEXISTS, CHAINCODEEXISTS:
resource = resources.Lscc_ChaincodeExists
case GETDEPSPEC, GETDEPLOYMENTSPEC:
resource = resources.Lscc_GetDeploymentSpec
case GETCCDATA, GETCHAINCODEDATA:
resource = resources.Lscc_GetChaincodeData
}
if err = lscc.ACLProvider.CheckACL(resource, channel, sp); err != nil {
return shim.Error(fmt.Sprintf("access denied for [%s][%s]: %s", function, channel, err))
}
cdbytes, err := lscc.getCCInstance(stub, ccname)
if err != nil {
logger.Errorf("error getting chaincode %s on channel [%s]: %s", ccname, channel, err)
return shim.Error(err.Error())
}
switch function {
case CCEXISTS, CHAINCODEEXISTS:
cd, err := lscc.getChaincodeData(ccname, cdbytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte(cd.Name))
case GETCCDATA, GETCHAINCODEDATA:
return shim.Success(cdbytes)
case GETDEPSPEC, GETDEPLOYMENTSPEC:
_, depspecbytes, err := lscc.getCCCode(ccname, cdbytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(depspecbytes)
default:
panic("unreachable")
}
case GETCHAINCODES, GETCHAINCODESALIAS:
if len(args) != 1 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
if err = lscc.ACLProvider.CheckACL(resources.Lscc_GetInstantiatedChaincodes, stub.GetChannelID(), sp); err != nil {
return shim.Error(fmt.Sprintf("access denied for [%s][%s]: %s", function, stub.GetChannelID(), err))
}
return lscc.getChaincodes(stub)
case GETINSTALLEDCHAINCODES, GETINSTALLEDCHAINCODESALIAS:
if len(args) != 1 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
// 2. check Lscc_GetInstalledChaincodes policy
if err = lscc.ACLProvider.CheckACL(resources.Lscc_GetInstalledChaincodes, "", sp); err != nil {
return shim.Error(fmt.Sprintf("access denied for [%s]: %s", function, err))
}
return lscc.getInstalledChaincodes()
case GETCOLLECTIONSCONFIG, GETCOLLECTIONSCONFIGALIAS:
if len(args) != 2 {
return shim.Error(InvalidArgsLenErr(len(args)).Error())
}
chaincodeName := string(args[1])
logger.Debugf("GetCollectionsConfig, chaincodeName:%s, start to check ACL for current identity policy", chaincodeName)
if err = lscc.ACLProvider.CheckACL(resources.Lscc_GetCollectionsConfig, stub.GetChannelID(), sp); err != nil {
logger.Debugf("ACL Check Failed for channel:%s, chaincode:%s", stub.GetChannelID(), chaincodeName)
return shim.Error(fmt.Sprintf("access denied for [%s]: %s", function, err))
}
return lscc.getChaincodeCollectionData(stub, chaincodeName)
}
return shim.Error(InvalidFunctionErr(function).Error())
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/liurenhao/fabric.git
git@gitee.com:liurenhao/fabric.git
liurenhao
fabric
fabric
v2.1.0

搜索帮助

344bd9b3 5694891 D2dac590 5694891