Fabric1.4源码解析:链码实例化过程

2023-11-01

之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程。其实这几个过程内容已经很相似了,都是涉及到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源码解析:客户端安装链码这篇文章。
总的来说,整个流程共有以下几部分:

  1. 根据用户执行实例化链码的命令启动全过程
  2. 获取需要实例化链码的基本信息
  3. 创建ChaincodeDeploymentSpec结构体.
  4. 获取用于对Proposal进行签名的Creator
  5. 创建ProposalProposalHeader定义为ENDORSER_TRANSACTION,表示是一个需要背书的交易。
  6. 由之前获取的Creator进行签名操作。
  7. Peer节点调用ProcessProposal()方法进行处理,该方法的解析在这里。这是一个很重要的方法。
  8. 接收到由Peer节点处理完成所返回的Response消息后发送到Orderer节点。
  9. Orderer节点接收到消息后进行排序操作,如果是SOLO模式则由Orderer节点生成区块,最后将区块广播至Peer节点,
  10. Peer节点接收到区块消息后验证有效性,最后更新账本数据。

最后附上参考链接:1.传送门
2.传送门

转载于:https://www.cnblogs.com/cbkj-xd/p/11149791.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Fabric1.4源码解析:链码实例化过程 的相关文章

随机推荐

  • 【STM32】HAL库——ADC

    前期准备 STM32CubeMX STM32RCT6核心板 IDE Keil MDK ARM STM32CubeMX部分 1 配置时钟 选择STM32F103RCTx系列芯片 配置时钟的同时会自动配置IO口引脚 将HCLK设置为最大频率72
  • Altera FPGA PCIE 例程仿真

    由于刚开始学PCIE接口 所以按照官方给的例程进行仿真操作 下面主要介绍下仿真的具体步骤 该例子是采用Cyclone V器件进行仿真 PCIE为gen1X4 的 Quartus II 版本号为15 0 Modelsim为ModelsimSE
  • 小米商城网页制作大全-完结篇

    时隔多日 小米商城网页基本完成 跳转的第二页面没有做 在这过程中遇到了很多小而细的问题 例如浏览器兼容性 字符图标不显示 动画效果不起作用等 抽时间整理一下 再继续完善 效果图如下 实际右侧固定栏只有个人中心 购物车 联系客服 回到顶部四项
  • 【websocket定义和使用】

    文章目录 前言 一 websocket定义 2 websocket使用 总结 前言 websocket就是服务端和客户端建立长连接的一种方式 多在直播 弹幕 聊天业务中使用 具体的自己百度吧 一 websocket定义 代码如下 示例 fu
  • docker 服务编排

    一 docker 服务编排 微服务的应用系统中一般包含若干个微服务 每个微服务一般都会部署多个实例 如果每个微服务都要手动启停 维护的工作量会很大 要从dockerfile build image 或者去 dockerhub 拉取image
  • Flutter 指针事件原理&点击穿透

    隔离的这14天 慢慢的研究了Flutter的指针事件 在这个过程中 又重新梳理了一下Element和Render Tree的形成过程 这篇文章 主要对指针事件在Fluter中如何下发到各个组件的过程进行梳理 指针是指针 手势是手势 手势是指
  • 软件工程课件

    软件工程 考点概述 软件工程概述 能力成度模型 能力成熟度模型集成 软件过程模型 逆向工程 软件需求 需求获取 数据流图 需求定义 考点概述 重点章节 软件工程概述 之前老版教程的 之前考过 能力成度模型 记忆 能力等级 和 特点 能力成熟
  • (读书笔记)python数据处理-(python读取csv、excel文件)

    文章目录 python读取csv文件 python解析excel文件 1 查看工作表中的sheet名 2 查看工作表指定sheet的内容 3 查看sheet中每个元素 4 将提取信息以字典形式展示 python 判断excel文件是否存在
  • Java使用RabbitMQ

    一 简介 rabbitMQ是什么 怎么用 怎么安装 网上文档一大把 请自行百度 本文给出的代码是rabbitMQ的fanout交换机模式 最原生的java代码 如果需要使用其他模式的rabbitMQ 请自行更改相应部分代码 二 代码 rab
  • 浅学Oracles数据库

    一 Oracle数据库中的数据类型 1 1 关于mysql数据库中的数据类型 int 整数型 bigint 长整型 float 单精度浮点型 double 双精度浮点型 char 字符型 长度不可变 varchar 字符型 长度可变 dat
  • C 函数参数传递一级指针和二级指针的区别

    文章目录 一 概念 二 函数参数为一级指针例子 1 程序一 指针类型为基本数据 2 程序二 参数为结构体 不是指针类型 3 程序三 参数类型为结构体指针 三 函数参数为二级指针例子 1 程序四 二级指针类型为基本数据类型 2 程序五 二级指
  • Element UI DatePicker 监听年月切换按钮并获取变更

    需求 在每切换一次年月时调用接口获取数据 传参为当前切换成的年月 需要监听DatePicker是否显示 用input获得焦点时触发的focus事件 element自带 并绑定4个切换按钮的click事件 html
  • Vue3 优雅地监听 localStorage 变化

    最近在研究框架 也仔细用了Vue3一些功能 今天分享一次我的实践 Vue3如何监听localStorage的变化 为什么要这样做 原生的localStorage只能监听同源地址下不同页面的localStorage变化 作为单页面应用 显然不
  • Tip of the Week #10: Splitting Strings, not Hairs

    Tip of the Week 10 Splitting Strings not Hairs Originally published as totw 10 on 2012 08 16 By Greg Miller jgm google c
  • 前端面试题及答案(字节跳动)(一)

    目录 垂直居中 左侧固定 右侧自适应 如何判断一个值是数组 bigint 最大安全整数 如何判断某个字符串以 abc 开头 进程和线程的区别 tcp 与 udp 跨域问题的几种解决方案 option 预请求 跨域的同时携带 cookie 用
  • LeetCode 处理用时最长的那个任务的员工

    解题思路 把第一个一维数组的两个元素分别定义为最大值和id 之后遍历进行判断 class Solution public int hardestWorker int n int logs int max logs 0 1 int id lo
  • sql注入知识---堆叠注入

    MySQL手注之堆叠注入详解 一 堆叠注入产生原因 二 使用条件 三 堆叠注入语句 1 查看数据库 2 查看表格 3 查看列 4 查看数据 四 16进制类型 绕过 五 堆叠应用 一 堆叠注入产生原因 平常我们注入时都是通过对原来sql语句传
  • 数组元素交叉排列的算法题(a1 a2 a3 .. an b1 b2 b3 .. bn -->a 1 b1, a2 b2, a3 b3, .. an bn ) 概论思想(perfect shuffle 算法)

    perfect shuffle 算法 今天又发现一个关于完美洗牌的算法 这个比较简单一些 由 microsoft的Peiyush Jain提出 原论文 A Simple In Place Algorithm for In Shuffle P
  • 沪胶期货全称(沪胶期货全称叫什么)

    什么是黄金期货 上海黄金交易所和上海期货交易所是一家单位吗 1 以前的期货市场上没有黄金期货 黄金期货是在08年1月9日刚刚开始的 2 上海黄金交易所和上海期货交易所不是同一个单位 上海期交所只做各种期货交易 上海金交所只做黄金现货交易 3
  • Fabric1.4源码解析:链码实例化过程

    之前说完了链码的安装过程 接下来说一下链码的实例化过程好了 再然后是链码的调用过程 其实这几个过程内容已经很相似了 都是涉及到Proposal 不过整体流程还是要说一下的 同样 切入点仍然是fabric peer main go文件中的ma