fabric 2.0, system chaincode

fabric 2.0, system chaincode

  • All builtinSCCs
  • cscc
  • qscc
  • lscc
  • _lifecycle
    • Supported methods:
    • Data Structure
  • sys cc deployment

All builtinSCCs

	builtinSCCs := map[string]struct{}{
		"lscc":       {},
		"qscc":       {},
		"cscc":       {},
		"_lifecycle": {},
	}

BuiltinSCCs are special system chaincodes, differentiated from other (plugin) system chaincodes. These chaincodes do not need to be initialized in ‘_lifecycle’ and may be invoked without a channel context. It is expected that ‘_lifecycle’ will eventually be the only builtin SCCs. Note, this field should only be used on endorsement side, never in validation as it might change.

All system chaincodes have to implement:

  • SelfDescribingSysCC
  • Chaincode
// Chaincode interface must be implemented by all chaincodes. The fabric runs
// the transactions by calling these functions as specified.
type Chaincode interface {
	// Init is called during Instantiate transaction after the chaincode container
	// has been established for the first time, allowing the chaincode to
	// initialize its internal data
	Init(stub ChaincodeStubInterface) pb.Response

	// Invoke is called to update or query the ledger in a proposal transaction.
	// Updated state variables are not committed to the ledger until the
	// transaction is committed.
	Invoke(stub ChaincodeStubInterface) pb.Response
}

type SelfDescribingSysCC interface {
	//Unique name of the system chaincode
	Name() string
	// Chaincode returns the underlying chaincode
	Chaincode() shim.Chaincode
}

_lifecycle is new in fabric 2.0. It is used to manage CC in a new way.

cscc

// PeerConfiger implements the configuration handler for the peer. For every
// configuration transaction coming in from the ordering service, the
// committer calls this system chaincode to process the transaction.
type PeerConfiger struct {
	policyChecker          policy.PolicyChecker
	configMgr              config.Manager
	aclProvider            aclmgmt.ACLProvider
	deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider
	legacyLifecycle        plugindispatcher.LifecycleResources
	newLifecycle           plugindispatcher.CollectionAndLifecycleResources
	peer                   *peer.Peer
	bccsp                  bccsp.BCCSP
}

// Invoke is called for the following:
// # to process joining a chain (called by app as a transaction proposal)
// # to get the current configuration block (called by app)
// # to update the configuration block (called by committer)
// Peer calls this function with 2 arguments:
// # args[0] is the function name, which must be JoinChain, GetConfigBlock or
// UpdateConfigBlock
// # args[1] is a configuration Block if args[0] is JoinChain or
// UpdateConfigBlock; otherwise it is the chain id
// TODO: Improve the scc interface to avoid marshal/unmarshal args
func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	args := stub.GetArgs()

	if len(args) < 1 {
		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
	}

	fname := string(args[0])

	if fname != GetChannels && len(args) < 2 {
		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
	}

	cnflogger.Debugf("Invoke function: %s", fname)

	// Handle ACL:
	// 1. get the signed proposal
	sp, err := stub.GetSignedProposal()
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err))
	}

	name, err := protoutil.InvokedChaincodeName(sp.ProposalBytes)
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed to identify the called chaincode: %s", err))
	}

	if name != e.Name() {
		return shim.Error(fmt.Sprintf("Rejecting invoke of CSCC from another chaincode, original invocation for '%s'", name))
	}

	return e.InvokeNoShim(args, sp)
}

qscc

// LedgerQuerier implements the ledger query functions, including:
// - GetChainInfo returns BlockchainInfo
// - GetBlockByNumber returns a block
// - GetBlockByHash returns a block
// - GetTransactionByID returns a transaction
type LedgerQuerier struct {
	aclProvider aclmgmt.ACLProvider
	ledgers     LedgerGetter
}

// Invoke is called with args[0] contains the query function name, args[1]
// contains the chain ID, which is temporary for now until it is part of stub.
// Each function requires additional parameters as described below:
// # GetChainInfo: Return a BlockchainInfo object marshalled in bytes
// # GetBlockByNumber: Return the block specified by block number in args[2]
// # GetBlockByHash: Return the block specified by block hash in args[2]
// # GetTransactionByID: Return the transaction specified by ID in args[2]
func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	args := stub.GetArgs()

	if len(args) < 2 {
		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
	}

	fname := string(args[0])
	cid := string(args[1])

	sp, err := stub.GetSignedProposal()
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub, %s: %s", cid, err))
	}

	name, err := protoutil.InvokedChaincodeName(sp.ProposalBytes)
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed to identify the called chaincode: %s", err))
	}

	if name != e.Name() {
		return shim.Error(fmt.Sprintf("Rejecting invoke of QSCC from another chaincode because of potential for deadlocks, original invocation for '%s'", name))
	}

	if fname != GetChainInfo && len(args) < 3 {
		return shim.Error(fmt.Sprintf("missing 3rd argument for %s", fname))
	}

	targetLedger := e.ledgers.GetLedger(cid)
	if targetLedger == nil {
		return shim.Error(fmt.Sprintf("Invalid chain ID, %s", cid))
	}

	qscclogger.Debugf("Invoke function: %s on chain: %s", fname, cid)

	// Handle ACL:
	res := getACLResource(fname)
	if err = e.aclProvider.CheckACL(res, cid, sp); err != nil {
		return shim.Error(fmt.Sprintf("access denied for [%s][%s]: [%s]", fname, cid, err))
	}

	switch fname {
	case GetTransactionByID:
		return getTransactionByID(targetLedger, args[2])
	case GetBlockByNumber:
		return getBlockByNumber(targetLedger, args[2])
	case GetBlockByHash:
		return getBlockByHash(targetLedger, args[2])
	case GetChainInfo:
		return getChainInfo(targetLedger)
	case GetBlockByTxID:
		return getBlockByTxID(targetLedger, args[2])
	}

	return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
}

lscc

Legacy lifecycle manager, used by v1.4 and earlier.

// 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. It is initialized to be the peer instance
	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
}
// Invoke implements lifecycle functions "deploy", "start", "stop", "upgrade".
// Deploy's arguments -  {[]byte("deploy"), []byte(), }
//
// Invoke also implements some query-like functions
// Get chaincode arguments -  {[]byte("getid"), []byte(), []byte()}
func (lscc *SCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	args := stub.GetArgs()
	...

_lifecycle

It uses Dispatcher to invoke the underlying commands. As below call stack:

github.com/hyperledger/fabric/core/chaincode/lifecycle.(*Invocation).QueryInstalledChaincodes at scc.go:321
runtime.call64 at asm_amd64.s:540
reflect.Value.call at value.go:460
reflect.Value.Call at value.go:321
github.com/hyperledger/fabric/core/dispatcher.(*Dispatcher).Dispatch at dispatcher.go:66
github.com/hyperledger/fabric/core/chaincode/lifecycle.(*SCC).Invoke at scc.go:207

Supported methods:


// SCCFunctions provides a backing implementation with concrete arguments
// for each of the SCC functions
type SCCFunctions interface {
	// InstallChaincode persists a chaincode definition to disk
	InstallChaincode([]byte) (*chaincode.InstalledChaincode, error)

	// QueryInstalledChaincode returns metadata for the chaincode with the supplied package ID.
	QueryInstalledChaincode(packageID string) (*chaincode.InstalledChaincode, error)

	// GetInstalledChaincodePackage returns the chaincode package
	// installed on the peer as bytes.
	GetInstalledChaincodePackage(packageID string) ([]byte, error)

	// QueryInstalledChaincodes returns the currently installed chaincodes
	QueryInstalledChaincodes() []*chaincode.InstalledChaincode

	// ApproveChaincodeDefinitionForOrg records a chaincode definition into this org's implicit collection.
	ApproveChaincodeDefinitionForOrg(chname, ccname string, cd *ChaincodeDefinition, packageID string, publicState ReadableState, orgState ReadWritableState) error

	// CheckCommitReadiness returns a map containing the orgs
	// whose orgStates were supplied and whether or not they have approved
	// the specified definition.
	CheckCommitReadiness(chname, ccname string, cd *ChaincodeDefinition, publicState ReadWritableState, orgStates []OpaqueState) (map[string]bool, error)

	// CommitChaincodeDefinition records a new chaincode definition into the
	// public state and returns a map containing the orgs whose orgStates
	// were supplied and whether or not they have approved the definition.
	CommitChaincodeDefinition(chname, ccname string, cd *ChaincodeDefinition, publicState ReadWritableState, orgStates []OpaqueState) (map[string]bool, error)

	// QueryChaincodeDefinition returns a chaincode definition from the public
	// state.
	QueryChaincodeDefinition(name string, publicState ReadableState) (*ChaincodeDefinition, error)

	// QueryOrgApprovals returns a map containing the orgs whose orgStates were
	// supplied and whether or not they have approved a chaincode definition with
	// the specified parameters.
	QueryOrgApprovals(name string, cd *ChaincodeDefinition, orgStates []OpaqueState) (map[string]bool, error)

	// QueryNamespaceDefinitions returns all defined namespaces
	QueryNamespaceDefinitions(publicState RangeableState) (map[string]string, error)
}

Data Structure

// SCC implements the required methods to satisfy the chaincode interface.
// It routes the invocation calls to the backing implementations.
type SCC struct {
	OrgMSPID string

	ACLProvider aclmgmt.ACLProvider

	ChannelConfigSource ChannelConfigSource

	DeployedCCInfoProvider ledger.DeployedChaincodeInfoProvider
	QueryExecutorProvider  QueryExecutorProvider

	// Functions provides the backing implementation of lifecycle.
	Functions SCCFunctions

	// Dispatcher handles the rote protobuf boilerplate for unmarshaling/marshaling
	// the inputs and outputs of the SCC functions.
	Dispatcher *dispatcher.Dispatcher
}


// Invoke takes chaincode invocation arguments and routes them to the correct
// underlying lifecycle operation.  All functions take a single argument of
// type marshaled lb.Args and return a marshaled lb.Result
func (scc *SCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	args := stub.GetArgs()
	if len(args) == 0 {
		return shim.Error("lifecycle scc must be invoked with arguments")
	}

	if len(args) != 2 {
		return shim.Error(fmt.Sprintf("lifecycle scc operations require exactly two arguments but received %d", len(args)))
	}

	var ac channelconfig.Application
	var channelID string
	if channelID = stub.GetChannelID(); channelID != "" {
		channelConfig := scc.ChannelConfigSource.GetStableChannelConfig(channelID)
		if channelConfig == nil {
			return shim.Error(fmt.Sprintf("could not get channelconfig for channel '%s'", channelID))
		}
		var ok bool
		ac, ok = channelConfig.ApplicationConfig()
		if !ok {
			return shim.Error(fmt.Sprintf("could not get application config for channel '%s'", channelID))
		}
		if !ac.Capabilities().LifecycleV20() {
			return shim.Error(fmt.Sprintf("cannot use new lifecycle for channel '%s' as it does not have the required capabilities enabled", channelID))
		}
	}

	// Handle ACL:
	sp, err := stub.GetSignedProposal()
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err))
	}

	err = scc.ACLProvider.CheckACL(fmt.Sprintf("%s/%s", LifecycleNamespace, args[0]), stub.GetChannelID(), sp)
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed to authorize invocation due to failed ACL check: %s", err))
	}

	outputBytes, err := scc.Dispatcher.Dispatch(
		args[1],
		string(args[0]),
		&Invocation{
			ChannelID:         channelID,
			ApplicationConfig: ac,
			SCC:               scc,
			Stub:              stub,
		},
	)
	if err != nil {
		switch err.(type) {
		case ErrNamespaceNotDefined, persistence.CodePackageNotFoundErr:
			return pb.Response{
				Status:  404,
				Message: err.Error(),
			}
		default:
			return shim.Error(fmt.Sprintf("failed to invoke backing implementation of '%s': %s", string(args[0]), err.Error()))
		}
	}

	return shim.Success(outputBytes)
}

// SCC dispatcher

// Dispatcher is used to handle the boilerplate proto tasks of unmarshaling inputs and remarshaling outputs
// so that the receiver may focus on the implementation details rather than the proto hassles.
type Dispatcher struct {
	// Protobuf should pass through to Google Protobuf in production paths
	Protobuf Protobuf
}

// Dispatch deserializes the input bytes to the correct type for the method in the receiver, then
// if successful, marshals the output message to bytes and returns it.  On error, it simply returns
// the error.  The method on the receiver must take a single parameter which is a concrete proto
// message type and it should return a proto message and error.
func (d *Dispatcher) Dispatch(inputBytes []byte, methodName string, receiver interface{}) ([]byte, error) {
	method := reflect.ValueOf(receiver).MethodByName(methodName)

	if method == (reflect.Value{}) {
		return nil, errors.Errorf("receiver %T.%s does not exist", receiver, methodName)
	}

	if method.Type().NumIn() != 1 {
		return nil, errors.Errorf("receiver %T.%s has %d parameters but expected 1", receiver, methodName, method.Type().NumIn())
	}

	inputType := method.Type().In(0)
	if inputType.Kind() != reflect.Ptr {
		return nil, errors.Errorf("receiver %T.%s does not accept a pointer as its argument", receiver, methodName)
	}

	if method.Type().NumOut() != 2 {
		return nil, errors.Errorf("receiver %T.%s returns %d values but expected 2", receiver, methodName, method.Type().NumOut())
	}

	if !method.Type().Out(0).Implements(reflect.TypeOf((*proto.Message)(nil)).Elem()) {
		return nil, errors.Errorf("receiver %T.%s does not return a an implementor of proto.Message as its first return value", receiver, methodName)
	}

	if !method.Type().Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
		return nil, errors.Errorf("receiver %T.%s does not return an error as its second return value", receiver, methodName)
	}

	inputValue := reflect.New(inputType.Elem())
	inputMsg, ok := inputValue.Interface().(proto.Message)
	if !ok {
		return nil, errors.Errorf("receiver %T.%s does not accept a proto.Message as its argument, it is '%T'", receiver, methodName, inputValue.Interface())
	}

	err := d.Protobuf.Unmarshal(inputBytes, inputMsg)
	if err != nil {
		return nil, errors.WithMessagef(err, "could not decode input arg for %T.%s", receiver, methodName)
	}

	outputVals := method.Call([]reflect.Value{inputValue})

	if !outputVals[1].IsNil() {
		return nil, outputVals[1].Interface().(error)
	}

	if outputVals[0].IsNil() {
		return nil, errors.Errorf("receiver %T.%s returned (nil, nil) which is not allowed", receiver, methodName)
	}

	outputMsg := outputVals[0].Interface().(proto.Message)

	resultBytes, err := d.Protobuf.Marshal(outputMsg)
	if err != nil {
		return nil, errors.WithMessagef(err, "failed to marshal result for %T.%s", receiver, methodName)
	}

	return resultBytes, nil
}


sys cc deployment

Similar with user cc, sys cc are deployed as two parts:

  • handler
  • shim

The difference is sys cc are deployed via inProcStream, using ‘go chan’ to simulate the gRPC. Fundementally, inProcStream has two ‘go chan’ for sending and receiving.

// DeploySysCC is the hook for system chaincodes where system chaincodes are registered with the fabric.
// This call directly registers the chaincode with the chaincode handler and bypasses the other usercc constructs.
func DeploySysCC(sysCC SelfDescribingSysCC, chaincodeStreamHandler ChaincodeStreamHandler) {
	sysccLogger.Infof("deploying system chaincode '%s'", sysCC.Name())

	ccid := ChaincodeID(sysCC.Name())
	done := chaincodeStreamHandler.LaunchInProc(ccid)

	peerRcvCCSend := make(chan *pb.ChaincodeMessage)
	ccRcvPeerSend := make(chan *pb.ChaincodeMessage)

	go func() {
		stream := newInProcStream(peerRcvCCSend, ccRcvPeerSend)
		defer stream.CloseSend()

		sysccLogger.Debugf("starting chaincode-support stream for  %s", ccid)
		err := chaincodeStreamHandler.HandleChaincodeStream(stream)
		sysccLogger.Criticalf("shim stream ended with err: %v", err)
	}()

	go func(sysCC SelfDescribingSysCC) {
		stream := newInProcStream(ccRcvPeerSend, peerRcvCCSend)
		defer stream.CloseSend()

		sysccLogger.Debugf("chaincode started for %s", ccid)
		err := shim.StartInProc(ccid, stream, sysCC.Chaincode())
		sysccLogger.Criticalf("system chaincode ended with err: %v", err)
	}(sysCC)
	<-done
}

你可能感兴趣的:(fabric)