Hyperledger Fabric从源码分析系统链码

系统链码简介

在 Fabric 中,有一些链码是比较特殊的,叫系统链码。它们作为peer进程的一部分运行,而不是像用户链码一样运行在独立的 docker 容器中。因此它们有更高的权限来访问 peer 中的资源来实现用户链码难以实现或者不能实现的功能。

在 Fabric 1.4 版本之前,总共有五种系统链码,它们分别是:

  • Life Cycle System Chaincode (LSCC):生命周期系统链码
  • Configuration System Chaincode (CSCC):配置系统链码
  • Query System Chaincode (QSCC):查询系统链码
  • Endorser System Chaincode (ESCC):背书系统链码
  • Validator System Chaincode (VSCC):验证系统链码

在我学习的过程中都是按照 Fabric 1.4 版本的源码及资料学习的,在 1.4 版本中,官方文档明确表示,当前现有只支持 LSCC、CSCC、QSCC 三种链码,而 ESCC 与 VSCC 则是被可插拔背书和验证方法所取代,具体的描述文档可以参考官方文档——可插拔交易背书与交易验证,主要是是因为在某些情况下,用户需要自定义背书与验证的规则。

和用户链码不同的是,系统链码不使用 SDK 或 CLI 的提案安装和实例化。它在 peer 节点启动的时候注册和部署。

本篇文章将从源码角度,分析系统链码是如何在 peer 节点启动的时候就注册和部署的,并且它们主要对外主要提供了哪些方法。

注:本篇文章会着重分析 1.4 版本中支持的三种链码,对于 ESCC 与 VSCC 只会简单带过。

1. peer 节点如何完成系统链码的注册与部署

peer 节点是以 docker 容器的形式存在的,在容器启动是会执行 peer node start命令来启动 peer 节点,因此我们顺藤摸瓜就可以找到 peer 节点启动时的源码文件 —— peer/node/start.go

我把有关系统链码的部分贴出来,别的部分暂时先不关心。start 命令的主要入口函数就是 serve 函数。

func serve(args []string) error {
  //.................
  
	// Initialize chaincode service
	chaincodeSupport, ccp, sccp, packageProvider := startChaincodeServer(peerHost, aclProvider, pr, opsSystem)
	
  //.................
  
	// deploy system chaincodes
	sccp.DeploySysCCs("", ccp)
  
  //.................
}

在 serve 函数主要就是调用了一个 startChaincodeServer 函数,创建了我们关心的两个对象 ccp (ChainCodeProvider,链码提供者)和 sccp (system ChainCodeProvider,系统链码提供者),之后调用 sccp.DeploySysCCs 方法来部署系统链码。因此系统链码的安装和部署就完成了,我们主要关心的是创建了一个 sccp 对象,之后调用了 sccp 的 sccp.DeploySysCCs 方法部署系统链码。那么我们就先来关心一下 startChaincodeServer 函数中创建系统链码的部分。

// startChaincodeServer will finish chaincode related initialization, including:
// 1) setup local chaincode install path
// 2) create chaincode specific tls CA
// 3) start the chaincode specific gRPC listening service
func startChaincodeServer(
	peerHost string,
	aclProvider aclmgmt.ACLProvider,
	pr *platforms.Registry,
	ops *operations.System,
) (*chaincode.ChaincodeSupport, ccprovider.ChaincodeProvider, *scc.Provider, *persistence.PackageProvider) {
   //.................
  
  // 调用registerChaincodeSupport注册
chaincodeSupport, ccp, sccp := registerChaincodeSupport(
		ccSrv,
		ccEndpoint,
		ca,
		packageProvider,
		aclProvider,
		pr,
		lifecycleSCC,
		ops,
	)
}

//NOTE - when we implement JOIN we will no longer pass the chainID as param
//The chaincode support will come up without registering system chaincodes
//which will be registered only during join phase.
func registerChaincodeSupport(
	grpcServer *comm.GRPCServer,
	ccEndpoint string,
	ca tlsgen.CA,
	packageProvider *persistence.PackageProvider,
	aclProvider aclmgmt.ACLProvider,
	pr *platforms.Registry,
	lifecycleSCC *lifecycle.SCC,
	ops *operations.System,
) (*chaincode.ChaincodeSupport, ccprovider.ChaincodeProvider, *scc.Provider) {
  sccp := scc.NewProvider(peer.Default, peer.DefaultSupport, ipRegistry)
 	// 创建LSCC,CSCC,QSCC
  lsccInst := lscc.New(sccp, aclProvider, pr)
  // 创建chaincodeSupport
  chaincodeSupport := chaincode.NewChaincodeSupport(
		chaincode.GlobalConfig(),
		ccEndpoint,
		userRunsCC,
		ca.CertBytes(),
		authenticator,
		packageProvider,
		lsccInst,
		aclProvider,
		container.NewVMController(
			map[string]container.VMProvider{
				dockercontroller.ContainerType: dockerProvider,
				inproccontroller.ContainerType: ipRegistry,
			},
		),
		sccp,
		pr,
		peer.DefaultSupport,
		ops.Provider,
	)
	ipRegistry.ChaincodeSupport = chaincodeSupport
  // 创建chaincodeProvider
	ccp := chaincode.NewProvider(chaincodeSupport)
	
  // 创建 CSCC 与 QSCC
  csccInst := cscc.New(ccp, sccp, aclProvider)
	qsccInst := qscc.New(aclProvider)
  
  //Now that chaincode is initialized, register all system chaincodes.
  // CreatePluginSysCCs creates all of the system chaincodes which are compiled into fabric
	sccs := scc.CreatePluginSysCCs(sccp)
	for _, cc := range append([]scc.SelfDescribingSysCC{lsccInst, csccInst, qsccInst, lifecycleSCC}, sccs...) {
    // sccp 注册给定的系统链码
		sccp.RegisterSysCC(cc)
	}
}

在 startChaincodeServer 函数 中调用了 registerChaincodeSupport 函数来创建 sccp 对象,在该函数中,分别创建了 LSCC,CSCC 与 QSCC,并在最后完成了系统链码的注册操作。具体注册的内容交给 sccp 的 RegisterSysCC 函数去做,本文就不再继续展开了,有兴趣的可以继续追踪源码下去阅读。

需要注意一点的是,在正式注册之前,还执行了一步 scc.CreatePluginSysCCs(sccp) 的操作,根据注释的意思是创建编译进 fabric 的系统链码。系统链码可以通过两种方式链接到 peer 节点:

  • 静态连接
  • 使用 GO 插件动态连接(plugin)

用户可以自定义一些系统插件,在这一步就会被注册并部署到系统插件中。具体的操作流程可以参考官方文档——系统链码插件

介绍完了 peer 节点是如何注册系统链码并进行部署的,下面来看看各个链码具体都做了一些什么内容。

2. Life Cycle System Chaincode (LSCC)

LSCC 主要用于管理链码的生命周期,主要包括以下几种行为:

  • 在 peer 上安装链码
  • 在通道上部署和是升级链码
  • 用户从运行中的链码获取信息

这一部分的源代码主要在这里—— core/scc/lscc/lscc.go

链码的具体调用是通过 Invoke 函数,并通过 Args 参数指定需要执行的函数。 LSCC 提供的主要函数都定义成了常量,我们来看下

const (
	// 安装链码,即 peer chaincode install 命令
	INSTALL = "install"

	// 部署链码,实例化链码,即 peer chaincode instantiate 命令
	DEPLOY = "deploy"

	// 升级链码,即 peer chaincode upgrade
	UPGRADE = "upgrade"

	// 获取合约ID
	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"
)

3. Configuration System Chaincode (CSCC)

CSCC 管理 peer 上通道相关的信息以及执行通道配置交易

这一部分的源代码主要在这里—— core/scc/cscc/configure.go

来看下它主要提供的方法:

const (
  // 让一个peer加入通道,即 peer channel join
	JoinChain                string = "JoinChain"
  // 获取给定通道的当前配置区块,即 peer channel fetch
	GetConfigBlock           string = "GetConfigBlock"
  // 获取peer当前所加入的通道,即 peer channel list
	GetChannels              string = "GetChannels"
  // 获取当前指定通道的配置信息
	GetConfigTree            string = "GetConfigTree"
  // 模拟执行config结构更新
	SimulateConfigTreeUpdate string = "SimulateConfigTreeUpdate"
)

如果要从一个通道添加或移除组织,必须获取通道配置(GetConfigTree)来进行修改,并调用 SimulateConfigTreeUpdate 模拟执行配置更新,必须获取 CSCC 的背书

4. Query System Chaincode (QSCC)

QSCC 将指定的方法暴露给用户,使得用户可以查询存储在账本(世界状态)中的信息。

这一部分的源代码主要在这里—— core/scc/qscc/query.go

来看下它主要提供的方法:

const (
  // 获取通道信息
	GetChainInfo       string = "GetChainInfo"
  // 通过区块高度查询区块
	GetBlockByNumber   string = "GetBlockByNumber"
  // 通过区块hash查询区块
	GetBlockByHash     string = "GetBlockByHash"
  // 通过交易ID查询交易
	GetTransactionByID string = "GetTransactionByID"
  // 通过交易ID查询区块
	GetBlockByTxID     string = "GetBlockByTxID"
)

5. Endorser System Chaincode (ESCC)

ESCC 被背书节点调用。背书节点在执行交易之后,将背书结果放在交易响应中。

Fabric 实现了默认的 DefaultEndorsementFactory 默认背书系统链码,具体实现在 core/handlers/endorsement/builtin/default_endorsement.go,根据官方文档说明,用户可以自定义背书链码—— 可插拔交易背书与交易验证

6. Validator System Chaincode (VSCC)

VSCC 被记账节点调用,来根据合约的背书策略验证每个交易的签名集合

Fabric 实现了默认的 DefaultValidation 默认验证系统链码,具体实现在 core/handlers/validation/builtin/default_validation.go,根据官方文档说明,用户可以自定义背书链码—— 可插拔交易背书与交易验证

猜你喜欢

转载自blog.csdn.net/lvyibin890/article/details/106522740