Fabric1.4源码解析:链码实例化过程
之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程。其实这几个过程内容已经很相似了,都是涉及到Proposal
,不过整体流程还是要说一下的。
同样,切入点仍然是fabric/peer/main.go
文件中的main()
方法:
#这一句定义了关于通过Peer节点操作链码的命令
mainCmd.AddCommand(chaincode.Cmd(nil))
然后是fabric/peer/chaincode/chaincode.go
文件中的Cmd()
方法,这里则是具体的操作链码的命令,其中就有对链码进行实例化的命令:
chaincodeCmd.AddCommand(instantiateCmd(cf))
最后调用到了fabric/peer/chaincode/instantiate.go
文件中的第57行的instantiate()
方法。也就是说,当我们在Peer
节点执行以下命令时,最终会到这个方法:
#以官方的实例化链码的方法为例
peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
接下来就看一下instantiate()
方法:
#首先获取要实例化的链码的信息
spec, err := getChaincodeSpec(cmd)
if err != nil {
return nil, err
}
getChaincodeSpec()
方法在peer/chaincode/common.go
文件中第69行:
#将用户实例化链码所执行的命令作为参数传入进去
func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
#定义一个ChaincodeSpec结构体
spec := &pb.ChaincodeSpec{}
====================ChaincodeSpec===========================
type ChaincodeSpec struct {
#Type表示链码的类型 有GOLANG,NODE,CAR,JAVA,UNDEFINED五种类型
Type ChaincodeSpec_Type `protobuf:"varint,1,opt,name=type,proto3,enum=protos.ChaincodeSpec_Type" json:"type,omitempty"`
#ChaincodeId也是一个结构体,定义了链码的路径信息,链码的名称以及版本信息
ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"`
#ChaincodeInput结构体中定义链码的功能以及函数参数信息
Input *ChaincodeInput `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"`
Timeout int32 `protobuf:"varint,4,opt,name=timeout,proto3" json:"timeout,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
====================ChaincodeSpec===========================
#对用户输入的命令进行检查
if err := checkChaincodeCmdParams(cmd); err != nil {
// unset usage silence because it's a command line usage error
cmd.SilenceUsage = false
return spec, err
}
#定义ChaincodeInput结构体,就是上面说过的那个
input := &pb.ChaincodeInput{}
if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
return spec, errors.Wrap(err, "chaincode argument error")
}
chaincodeLang = strings.ToUpper(chaincodeLang)
#最后将创建的ChaincodeSpec结构体返回
spec = &pb.ChaincodeSpec{
Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion},
Input: input,
}
return spec, nil
}
看一下checkChaincodeCmdParams()
方法做了哪些工作,在219行:
func checkChaincodeCmdParams(cmd *cobra.Command) error {
#检查用户输入的链码名称是否为空字符串
if chaincodeName == common.UndefinedParamValue {
return errors.Errorf("must supply value for %s name parameter", chainFuncName)
}
#调用的方法是否为instantiate,install,upgrade,package其中的一个
if cmd.Name() == instantiateCmdName || cmd.Name() == installCmdName ||
cmd.Name() == upgradeCmdName || cmd.Name() == packageCmdName {
if chaincodeVersion == common.UndefinedParamValue {
return errors.Errorf("chaincode version is not provided for %s", cmd.Name())
}
if escc != common.UndefinedParamValue {
logger.Infof("Using escc %s", escc)
} else {
logger.Info("Using default escc")
escc = "escc"
}
if vscc != common.UndefinedParamValue {
logger.Infof("Using vscc %s", vscc)
} else {
logger.Info("Using default vscc")
vscc = "vscc"
}
if policy != common.UndefinedParamValue {
#获取定义的策略,就比如 OR ('Org1MSP.member','Org2MSP.member')这条信息是否有误
p, err := cauthdsl.FromString(policy)
if err != nil {
return errors.Errorf("invalid policy %s", policy)
}
policyMarshalled = putils.MarshalOrPanic(p)
}
#如果定义了配置文件,则从配置文件中读取配置信息
if collectionsConfigFile != common.UndefinedParamValue {
var err error
collectionConfigBytes, err = getCollectionConfigFromFile(collectionsConfigFile)
if err != nil {
return errors.WithMessage(err, fmt.Sprintf("invalid collection configuration in file %s", collectionsConfigFile))
}
}
}
#对用户传入的实例化参数比如:-c '{"Args":["init","a","100","b","200"]}'
if chaincodeCtorJSON != "{}" {
...
}
return nil
}
回到instantiate()
方法:
cds, err := getChaincodeDeploymentSpec(spec, false)
if err != nil {
return nil, fmt.Errorf("error getting chaincode code %s: %s", chaincodeName, err)
}
获取ChaincodeDeploymentSpec
这个结构体:
type ChaincodeDeploymentSpec struct {
#这个是之前获取到的结构体
ChaincodeSpec *ChaincodeSpec `protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec,proto3" json:"chaincode_spec,omitempty"`
#链码数据
CodePackage []byte `protobuf:"bytes,3,opt,name=code_package,json=codePackage,proto3" json:"code_package,omitempty"`
#链码的运行环境,有两种,Docker容器或者直接在系统中运行
ExecEnv ChaincodeDeploymentSpec_ExecutionEnvironment `protobuf:"varint,4,opt,name=exec_env,json=execEnv,proto3,enum=protos.ChaincodeDeploymentSpec_ExecutionEnvironment" json:"exec_env,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
看一下如何获取ChaincodeDeploymentSpec
结构体:
#定义了ChaincodeDeploymentSpec中的CodePackage
var codePackageBytes []byte
#判断是否为开发模式
if chaincode.IsDevMode() == false && crtPkg {
var err error
#如果不是则检查链码是否为空,以及路径是否正确
if err = checkSpec(spec); err != nil {
return nil, err
}
#将链码转换为Byte数据
codePackageBytes, err = container.GetChaincodePackageBytes(platformRegistry, spec)
...
}
#构造chaincodeDeploymentSpec并返回
chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}
return chaincodeDeploymentSpec, nil
回到instantiate()
方法:
#获取一全个签名者,需要对创建实例化链码的Proposal进行签名
creator, err := cf.Signer.Serialize()
if err != nil {
return nil, fmt.Errorf("error serializing identity for %s: %s", cf.Signer.GetIdentifier(), err)
}
#要创建用于实例化链码的Proposal了
prop, _, err := utils.CreateDeployProposalFromCDS(channelID, cds, creator, policyMarshalled, []byte(escc), []byte(vscc), collectionConfigBytes)
if err != nil {
return nil, fmt.Errorf("error creating proposal %s: %s", chainFuncName, err)
}
看一下CreateDeployProposalFromCDS()
方法,看名字了解到是根据chaincodeDeploymentSpec
创建用于部署链码的Proposal
:
func CreateDeployProposalFromCDS(
#通道Id
chainID string,
cds *peer.ChaincodeDeploymentSpec,
#签名者
creator []byte,
#具体的策略
policy []byte,
#endorser system chaincode
escc []byte,
#Verification System ChainCode
vscc []byte,
collectionConfig []byte) (*peer.Proposal, string, error) {
#下面的两个方法调用的是同一个,只是传入的参数不同,点进去
if collectionConfig == nil {
return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc)
}
return createProposalFromCDS(chainID, cds, creator, "deploy", policy, escc, vscc, collectionConfig)
}
该方法在538行,接下来的部分与客户端安装链码所执行的流程基本是相同的,只有下面的一部分不同:
#对于实例化链码来说,执行的是deploy与upgrade这两部分,而安装链码则是install这部分,差异就在于ChaincodeInput结构体内的参数不同
case "deploy":
fallthrough
case "upgrade":
cds, ok := msg.(*peer.ChaincodeDeploymentSpec)
if !ok || cds == nil {
return nil, "", errors.New("invalid message for creating lifecycle chaincode proposal")
}
Args := [][]byte{[]byte(propType), []byte(chainID), b}
Args = append(Args, args...)
ccinp = &peer.ChaincodeInput{Args: Args}
case "install":
ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}}
}
// wrap the deployment in an invocation spec to lscc...
lsccSpec := &peer.ChaincodeInvocationSpec{
ChaincodeSpec: &peer.ChaincodeSpec{
Type: peer.ChaincodeSpec_GOLANG,
ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
Input: ccinp,
},
}
剩下的部分就不再重复看了,可以参考Fabric1.4源码解析:客户端安装链码这篇文章。
总的来说,整个流程共有以下几部分:
- 根据用户执行实例化链码的命令启动全过程
- 获取需要实例化链码的基本信息
- 创建
ChaincodeDeploymentSpec
结构体. - 获取用于对
Proposal
进行签名的Creator
。 - 创建
Proposal
,Proposal
的Header
定义为ENDORSER_TRANSACTION
,表示是一个需要背书的交易。 - 由之前获取的
Creator
进行签名操作。 - 由
Peer
节点调用ProcessProposal()
方法进行处理,该方法的解析在这里。这是一个很重要的方法。 - 接收到由
Peer
节点处理完成所返回的Response
消息后发送到Orderer
节点。 Orderer
节点接收到消息后进行排序操作,如果是SOLO
模式则由Orderer
节点生成区块,最后将区块广播至Peer
节点,Peer
节点接收到区块消息后验证有效性,最后更新账本数据。
Fabric1.4源码解析:链码实例化过程的更多相关文章
- Fabric1.4源码解析: 链码容器启动过程
想写点东西记录一下最近看的一些Fabric源码,本文使用的是fabric1.4的版本,所以对于其他版本的fabric,内容可能会有所不同. 本文想针对Fabric中链码容器的启动过程进行源码的解析.这 ...
- Fabric1.4源码解析:客户端安装链码
看了看客户端安装链码的部分,感觉还是比较简单的,所以在这里记录一下. 还是先给出安装链码所使用的命令好了,这里就使用官方的安装链码的一个例子: #-n 指定mycc是由用户定义 ...
- Fabric1.4源码解析:Peer节点加入通道
又开始新的阅读了,这次看的是Peer节点加入通道的过程.其实每次看源码都会有好多没有看懂的地方,不过相信只要坚持下去,保持记录,还是有很多收获的. 对于Peer节点加入通道这一 ...
- Fabric1.4源码解析:客户端创建通道过程
在使用Fabric创建通道的时候,通常我们执行一条命令完成,这篇文章就解析一下执行这条命令后Fabric源码中执行的流程. peer channel create -o orderer.example ...
- Fabric1.4源码解析:Peer节点背书提案过程
以前从来没有写过博客,从这段时间开始才开始写一些自己的博客,之前总觉得写一篇博客要耗费大量的时间,而且写的还是自己已经学会的,觉得没什么必要.但是当开始用博客记录下来的时候,才发现有些学会的地方只是自 ...
- jQuery源码解析对象实例化与jQuery原型及整体构建模型分析(一)
//源码剖析都基于jQuery-2.0.3版本,主要考虑到兼容IE 一.关于jQuery对象实例化的逻辑: 整个jQuery程序被包裹在一个匿名自执行行数内: (function(window,und ...
- mybatis源码解析7---MappedStatement初始化过程
上一篇我们了解到了MappedStatement类就是mapper.xml中的一个sql语句,而Configuration初始化的时候会加载所有的mapper接口类,而本篇再分析下是如何将mapper ...
- Fabric1.4源码解析:Peer节点启动过程
看一下Peer节点的启动过程,通常在Fabric网络中,Peer节点的启动方式有两种,通过Docker容器启动,或者是通过执行命令直接启动. 一般情况下,我们都是执行docker-compose -f ...
- SuperSocket源码解析之启动过程
一 简介 这里主要说明从配置系统引导启动SuperScoekt作为应用程序,且以控制台程序方式启动 二 启动过程 2.1 配置解析 从读取配置文件开始,直接拿到一个SocketServiceConfi ...
随机推荐
- 我的Spring之旅(二):为请求加入參数
1.前言 在上一篇我的Spring之旅(一)中,我们仅仅是利用不带參数的请求返回一个网页或一段json,在实际的B/S.C/S网络交互中,请求中须要自己定义的參数.本篇将简单地为之前的请求加入參数. ...
- QT下的几种透明效果(QPalette背景白色,窗口设置setWindowOpacity,QPainter使用Clear模式绘图)
1.窗口整体透明,但是窗体上的控件不透明. 通过设置窗体的背景色来实现,将背景色设置为全透. QPalette pal = palette(); pal.setColor(QPalette::B ...
- VS中实时获取SVN的版本号并写入到AssemblyInfo.cs中(C#)
原文:VS中实时获取SVN的版本号并写入到AssemblyInfo.cs中(C#) 在开发项目时,需要知道当前发布的到底是哪个版本,比较好的方式就是获取SVN的版本来作为项目的版本.项目版本一般由主版 ...
- XF 滑块和步进控件
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http:/ ...
- 图像滤镜艺术---流行艺术风滤镜特效PS实现
原文:图像滤镜艺术---流行艺术风滤镜特效PS实现 今天,本人给大家介绍一款新滤镜:流行艺术风效果,先看下效果吧! 原图 流行艺术风效果图 上面的这款滤镜效果是不是很赞,呵呵,按照本人以往的逻辑,我会 ...
- C#数字图像处理时注意图像的未用区域
原文:C#数字图像处理时注意图像的未用区域 图1. 被锁定图像像素数组基本布局 如图1所示,数组的宽度并不一定等于图像像素数组的宽度,还有一部分未用区域.这是为了提高效率,系统要确定每 ...
- 微信小程序把玩(七)数据绑定
原文:微信小程序把玩(七)数据绑定 数据绑定有一部分前几个看着还行,后面的几个可能有几个不理解,界面展示的数据有的也因为条件没法显示.看不懂的可以先记着,后面真正用到时就会明白,反正我是这样想的.这里 ...
- oracle 12c连接pdb
12c中,如何连接pluggable database: 使用默认的service连接pdb,创建pdb之后,在监听中自动添加以pdb为名的service: 用户在cluster中创建service, ...
- Android零基础入门第61节:滚动视图ScrollView
原文:Android零基础入门第61节:滚动视图ScrollView 前面几期学习了ProgressBar系列组件.ViewAnimator系列组件.Picker系列组件和时间日期系列组件,接下来几期 ...
- HUSTOJ的Windows版评判内核(限制内存使用)
HUSTOJ的Windows版评判内核(一) 作者:游蓝海 个人主页:http://blog.csdn.net/you_lan_hai 2013.4.9 注:最新版本项目地址:https://gith ...