Fetch the repository succeeded.
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package ccprovider
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/core/ledger"
pb "github.com/hyperledger/fabric/protos/peer"
)
var ccproviderLogger = flogging.MustGetLogger("ccprovider")
var chaincodeInstallPath string
// CCPackage encapsulates a chaincode package which can be
// raw ChaincodeDeploymentSpec
// SignedChaincodeDeploymentSpec
// Attempt to keep the interface at a level with minimal
// interface for possible generalization.
type CCPackage interface {
//InitFromBuffer initialize the package from bytes
InitFromBuffer(buf []byte) (*ChaincodeData, error)
// InitFromFS gets the chaincode from the filesystem (includes the raw bytes too)
InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error)
// PutChaincodeToFS writes the chaincode to the filesystem
PutChaincodeToFS() error
// GetDepSpec gets the ChaincodeDeploymentSpec from the package
GetDepSpec() *pb.ChaincodeDeploymentSpec
// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
GetDepSpecBytes() []byte
// ValidateCC validates and returns the chaincode deployment spec corresponding to
// ChaincodeData. The validation is based on the metadata from ChaincodeData
// One use of this method is to validate the chaincode before launching
ValidateCC(ccdata *ChaincodeData) error
// GetPackageObject gets the object as a proto.Message
GetPackageObject() proto.Message
// GetChaincodeData gets the ChaincodeData
GetChaincodeData() *ChaincodeData
// GetId gets the fingerprint of the chaincode based on package computation
GetId() []byte
}
// SetChaincodesPath sets the chaincode path for this peer
func SetChaincodesPath(path string) {
if s, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
if err := os.Mkdir(path, 0755); err != nil {
panic(fmt.Sprintf("Could not create chaincodes install path: %s", err))
}
} else {
panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err))
}
} else if !s.IsDir() {
panic(fmt.Errorf("chaincode path exists but not a dir: %s", path))
}
chaincodeInstallPath = path
}
func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) {
return GetChaincodePackageFromPath(ccname, ccversion, chaincodeInstallPath)
}
// GetChaincodePackage returns the chaincode package from the file system
func GetChaincodePackageFromPath(ccname string, ccversion string, ccInstallPath string) ([]byte, error) {
path := fmt.Sprintf("%s/%s.%s", ccInstallPath, ccname, ccversion)
var ccbytes []byte
var err error
if ccbytes, err = ioutil.ReadFile(path); err != nil {
return nil, err
}
return ccbytes, nil
}
// ChaincodePackageExists returns whether the chaincode package exists in the file system
func ChaincodePackageExists(ccname string, ccversion string) (bool, error) {
path := filepath.Join(chaincodeInstallPath, ccname+"."+ccversion)
_, err := os.Stat(path)
if err == nil {
// chaincodepackage already exists
return true, nil
}
return false, err
}
type CCCacheSupport interface {
// GetChaincode is needed by the cache to get chaincode data
GetChaincode(ccname string, ccversion string) (CCPackage, error)
}
// CCInfoFSImpl provides the implementation for CC on the FS and the access to it
// It implements CCCacheSupport
type CCInfoFSImpl struct{}
// GetChaincodeFromFS this is a wrapper for hiding package implementation.
// It calls GetChaincodeFromPath with the chaincodeInstallPath
func (cifs *CCInfoFSImpl) GetChaincode(ccname string, ccversion string) (CCPackage, error) {
return cifs.GetChaincodeFromPath(ccname, ccversion, chaincodeInstallPath)
}
// GetChaincodeFromPath this is a wrapper for hiding package implementation.
func (*CCInfoFSImpl) GetChaincodeFromPath(ccname string, ccversion string, path string) (CCPackage, error) {
// try raw CDS
cccdspack := &CDSPackage{}
_, _, err := cccdspack.InitFromPath(ccname, ccversion, path)
if err != nil {
// try signed CDS
ccscdspack := &SignedCDSPackage{}
_, _, err = ccscdspack.InitFromPath(ccname, ccversion, path)
if err != nil {
return nil, err
}
return ccscdspack, nil
}
return cccdspack, nil
}
// PutChaincodeIntoFS is a wrapper for putting raw ChaincodeDeploymentSpec
//using CDSPackage. This is only used in UTs
func (*CCInfoFSImpl) PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error) {
buf, err := proto.Marshal(depSpec)
if err != nil {
return nil, err
}
cccdspack := &CDSPackage{}
if _, err := cccdspack.InitFromBuffer(buf); err != nil {
return nil, err
}
err = cccdspack.PutChaincodeToFS()
if err != nil {
return nil, err
}
return cccdspack, nil
}
// The following lines create the cache of CCPackage data that sits
// on top of the file system and avoids a trip to the file system
// every time. The cache is disabled by default and only enabled
// if EnableCCInfoCache is called. This is an unfortunate hack
// required by some legacy tests that remove chaincode packages
// from the file system as a means of simulating particular test
// conditions. This way of testing is incompatible with the
// immutable nature of chaincode packages that is assumed by hlf v1
// and implemented by this cache. For this reason, tests are for now
// allowed to run with the cache disabled (unless they enable it)
// until a later time in which they are fixed. The peer process on
// the other hand requires the benefits of this cache and therefore
// enables it.
// TODO: (post v1) enable cache by default as soon as https://jira.hyperledger.org/browse/FAB-3785 is completed
// ccInfoFSStorageMgr is the storage manager used either by the cache or if the
// cache is bypassed
var ccInfoFSProvider = &CCInfoFSImpl{}
// ccInfoCache is the cache instance itself
var ccInfoCache = NewCCInfoCache(ccInfoFSProvider)
// ccInfoCacheEnabled keeps track of whether the cache is enable
// (it is disabled by default)
var ccInfoCacheEnabled bool
// EnableCCInfoCache can be called to enable the cache
func EnableCCInfoCache() {
ccInfoCacheEnabled = true
}
// GetChaincodeFromFS retrieves chaincode information from the file system
func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) {
return ccInfoFSProvider.GetChaincode(ccname, ccversion)
}
// PutChaincodeIntoFS puts chaincode information in the file system (and
// also in the cache to prime it) if the cache is enabled, or directly
// from the file system otherwise
func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
_, err := ccInfoFSProvider.PutChaincode(depSpec)
return err
}
// GetChaincodeData gets chaincode data from cache if there's one
func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) {
if ccInfoCacheEnabled {
ccproviderLogger.Debugf("Getting chaincode data for <%s, %s> from cache", ccname, ccversion)
return ccInfoCache.GetChaincodeData(ccname, ccversion)
}
if ccpack, err := ccInfoFSProvider.GetChaincode(ccname, ccversion); err != nil {
return nil, err
} else {
ccproviderLogger.Infof("Putting chaincode data for <%s, %s> into cache", ccname, ccversion)
return ccpack.GetChaincodeData(), nil
}
}
func CheckInstantiationPolicy(name, version string, cdLedger *ChaincodeData) error {
ccdata, err := GetChaincodeData(name, version)
if err != nil {
return err
}
// 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 ccdata.InstantiationPolicy != nil {
if !bytes.Equal(ccdata.InstantiationPolicy, cdLedger.InstantiationPolicy) {
return fmt.Errorf("Instantiation policy mismatch for cc %s/%s", name, version)
}
}
return nil
}
// GetCCPackage tries each known package implementation one by one
// till the right package is found
func GetCCPackage(buf []byte) (CCPackage, error) {
// try raw CDS
cccdspack := &CDSPackage{}
if _, err := cccdspack.InitFromBuffer(buf); err != nil {
// try signed CDS
ccscdspack := &SignedCDSPackage{}
if _, err := ccscdspack.InitFromBuffer(buf); err != nil {
return nil, err
}
return ccscdspack, nil
}
return cccdspack, nil
}
// GetInstalledChaincodes returns a map whose key is the chaincode id and
// value is the ChaincodeDeploymentSpec struct for that chaincodes that have
// been installed (but not necessarily instantiated) on the peer by searching
// the chaincode install path
func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
files, err := ioutil.ReadDir(chaincodeInstallPath)
if err != nil {
return nil, err
}
// array to store info for all chaincode entries from LSCC
var ccInfoArray []*pb.ChaincodeInfo
for _, file := range files {
// split at first period as chaincode versions can contain periods while
// chaincode names cannot
fileNameArray := strings.SplitN(file.Name(), ".", 2)
// check that length is 2 as expected, otherwise skip to next cc file
if len(fileNameArray) == 2 {
ccname := fileNameArray[0]
ccversion := fileNameArray[1]
ccpack, err := GetChaincodeFromFS(ccname, ccversion)
if err != nil {
// either chaincode on filesystem has been tampered with or
// a non-chaincode file has been found in the chaincodes directory
ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
continue
}
cdsfs := ccpack.GetDepSpec()
name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
if name != ccname || version != ccversion {
// chaincode name/version in the chaincode file name has been modified
// by an external entity
ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
continue
}
path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
// since this is just an installed chaincode these should be blank
input, escc, vscc := "", "", ""
ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc, Id: ccpack.GetId()}
// add this specific chaincode's metadata to the array of all chaincodes
ccInfoArray = append(ccInfoArray, ccInfo)
}
}
// add array with info about all instantiated chaincodes to the query
// response proto
cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
return cqr, nil
}
// CCContext pass this around instead of string of args
type CCContext struct {
// ChainID chain id
ChainID string
// Name chaincode name
Name string
// Version used to construct the chaincode image and register
Version string
// TxID is the transaction id for the proposal (if any)
TxID string
// Syscc is this a system chaincode
Syscc bool
// SignedProposal for this invoke (if any) this is kept here for access
// control and in case we need to pass something from this to the chaincode
SignedProposal *pb.SignedProposal
// Proposal for this invoke (if any) this is kept here just in case we need to
// pass something from this to the chaincode
Proposal *pb.Proposal
// canonicalName is not set but computed
canonicalName string
// this is additional data passed to the chaincode
ProposalDecorations map[string][]byte
}
// NewCCContext just construct a new struct with whatever args
func NewCCContext(cname, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext {
cccid := &CCContext{
ChainID: cname,
Name: name,
Version: version,
TxID: txid,
Syscc: syscc,
SignedProposal: signedProp,
Proposal: prop,
canonicalName: name + ":" + version,
ProposalDecorations: nil,
}
// The version CANNOT be empty. The chaincode namespace has to use version and chain name.
// Note that neither channel name nor version are stored on the ledger.
if version == "" {
panic(fmt.Sprintf("---empty version---(%s)", cccid))
}
ccproviderLogger.Debugf("NewCCCC(%s)", cccid)
return cccid
}
func (cccid *CCContext) String() string {
return fmt.Sprintf("chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p,canname=%s",
cccid.ChainID, cccid.Name, cccid.Version, cccid.TxID, cccid.Syscc, cccid.Proposal, cccid.canonicalName)
}
// GetCanonicalName returns the canonical name associated with the proposal context
func (cccid *CCContext) GetCanonicalName() string {
if cccid.canonicalName == "" {
panic(fmt.Sprintf("missing canonical name: %s", cccid))
}
return cccid.canonicalName
}
//-------- ChaincodeDefinition - interface for ChaincodeData ------
// ChaincodeDefinition describes all of the necessary information for a peer to decide whether to endorse
// a proposal and whether to validate a transaction, for a particular chaincode.
type ChaincodeDefinition interface {
// CCName returns the name of this chaincode (the name it was put in the ChaincodeRegistry with).
CCName() string
// Hash returns the hash of the chaincode.
Hash() []byte
// CCVersion returns the version of the chaincode.
CCVersion() string
// Validation returns how to validate transactions for this chaincode.
// The string returned is the name of the validation method (usually 'vscc')
// and the bytes returned are the argument to the validation (in the case of
// 'vscc', this is a marshaled pb.VSCCArgs message).
Validation() (string, []byte)
// Endorsement returns how to endorse proposals for this chaincode.
// The string returns is the name of the endorsement method (usually 'escc').
Endorsement() string
}
//-------- ChaincodeData is stored on the LSCC -------
// ChaincodeData defines the datastructure for chaincodes to be serialized by proto
// Type provides an additional check by directing to use a specific package after instantiation
// Data is Type specifc (see CDSPackage and SignedCDSPackage)
type ChaincodeData struct {
// Name of the chaincode
Name string `protobuf:"bytes,1,opt,name=name"`
// Version of the chaincode
Version string `protobuf:"bytes,2,opt,name=version"`
// Escc for the chaincode instance
Escc string `protobuf:"bytes,3,opt,name=escc"`
// Vscc for the chaincode instance
Vscc string `protobuf:"bytes,4,opt,name=vscc"`
// Policy endorsement policy for the chaincode instance
Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"`
// Data data specific to the package
Data []byte `protobuf:"bytes,6,opt,name=data,proto3"`
// Id of the chaincode that's the unique fingerprint for the CC This is not
// currently used anywhere but serves as a good eyecatcher
Id []byte `protobuf:"bytes,7,opt,name=id,proto3"`
// InstantiationPolicy for the chaincode
InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"`
}
// CCName returns the name of this chaincode (the name it was put in the ChaincodeRegistry with).
func (cd *ChaincodeData) CCName() string {
return cd.Name
}
// Hash returns the hash of the chaincode.
func (cd *ChaincodeData) Hash() []byte {
return cd.Id
}
// CCVersion returns the version of the chaincode.
func (cd *ChaincodeData) CCVersion() string {
return cd.Version
}
// Validation returns how to validate transactions for this chaincode.
// The string returned is the name of the validation method (usually 'vscc')
// and the bytes returned are the argument to the validation (in the case of
// 'vscc', this is a marshaled pb.VSCCArgs message).
func (cd *ChaincodeData) Validation() (string, []byte) {
return cd.Vscc, cd.Policy
}
// Endorsement returns how to endorse proposals for this chaincode.
// The string returns is the name of the endorsement method (usually 'escc').
func (cd *ChaincodeData) Endorsement() string {
return cd.Escc
}
// implement functions needed from proto.Message for proto's mar/unmarshal functions
// Reset resets
func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} }
// String converts to string
func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) }
// ProtoMessage just exists to make proto happy
func (*ChaincodeData) ProtoMessage() {}
// ChaincodeSpecGetter normalizes getting a chaincode spec from an
// ChaincodeInvocationSpec or a ChaincodeDeploymentSpec.
type ChaincodeSpecGetter interface {
GetChaincodeSpec() *pb.ChaincodeSpec
}
// ChaincodeProvider provides an abstraction layer that is
// used for different packages to interact with code in the
// chaincode package without importing it; more methods
// should be added below if necessary
type ChaincodeProvider interface {
// GetContext returns a ledger context and a tx simulator; it's the
// caller's responsability to release the simulator by calling its
// done method once it is no longer useful
GetContext(ledger ledger.PeerLedger, txid string) (context.Context, ledger.TxSimulator, error)
// ExecuteChaincode executes the chaincode given context and args
ExecuteChaincode(ctxt context.Context, cccid *CCContext, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error)
// Execute executes the chaincode given context and spec (invocation or deploy)
Execute(ctxt context.Context, cccid *CCContext, spec ChaincodeSpecGetter) (*pb.Response, *pb.ChaincodeEvent, error)
// Stop stops the chaincode given context and deployment spec
Stop(ctxt context.Context, cccid *CCContext, spec *pb.ChaincodeDeploymentSpec) error
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。