1151 lines
38 KiB
Go
1151 lines
38 KiB
Go
/*
|
|
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/scc"
|
|
"github.com/hyperledger/fabric/internal/ccmetadata"
|
|
"github.com/hyperledger/fabric/msp"
|
|
"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
|
|
|
|
// IDMSPManagerGetters used to get the MSP Manager for a channel.
|
|
type MSPManagerGetter func(string) msp.MSPManager
|
|
|
|
//---------- 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
|
|
|
|
// Support provides the implementation of several
|
|
// static functions
|
|
Support FilesystemSupport
|
|
|
|
GetMSPIDs MSPIDsGetter
|
|
|
|
GetMSPManager MSPManagerGetter
|
|
|
|
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 definition matches
|
|
// what's on the filesystem, which, might include instantiation 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 := lscc.GetMSPManager(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
|
|
// 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
|
|
return channel != ""
|
|
}
|
|
|
|
// 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, "chaincode installed to peer but 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 unmarshalling 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())
|
|
}
|