> For the complete documentation index, see [llms.txt](https://yeasy.gitbook.io/blockchain_guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yeasy.gitbook.io/blockchain_guide/10_fabric_op/chaincode.md).

# 管理链上代码

> **历史说明**：本节记录 Fabric 1.x 及未启用 `V2_0` 应用能力时的旧链码生命周期，包含 `peer chaincode install/instantiate/upgrade/list/package/signpackage` 和 LSCC（Lifecycle System Chaincode）内部流程。Fabric 2.5 LTS 和 Fabric 3.x 的生产网络应使用 `peer lifecycle chaincode` 命令族，详见 [管理链上代码（Lifecycle）](/blockchain_guide/10_fabric_op/chaincode_v2.md)。在启用 `V2_0` 应用能力的通道上，旧生命周期不能再用于安装、实例化或升级链码；迁移场景中，旧生命周期部署过的链码仍可被调用。Fabric-X 是另一套实现，不能直接套用本节的经典 Fabric 链码部署命令，也不是从 2.5/3.x 原地升级得到；版本选择和迁移边界见[升级矩阵](/blockchain_guide/10_fabric_op/upgrade.md)。

链上代码（Chaincode），简称链码，一般指的是用户编写的用来实现智能合约的应用代码。

链码被部署在 Peer 节点上，运行在独立的沙盒或外部链码服务中，并通过 gRPC 协议来与相应的 Peer 节点进行交互。链码被调用时，会按照预定逻辑根据当前账本状态来计算对账本的更新（读写集合）。

启动 Fabric 网络后，可以通过命令行或 SDK 进行链码调用。

*注：用户链码有别于系统链码（System Chaincode）。系统链码指的是 Fabric Peer 中负责系统配置、背书、验证等平台功能的代码逻辑，运行在 Peer 进程内，将在后续章节予以介绍。*

## 客户端链码操作命令

旧生命周期中，用户可以通过命令行方式来管理链码，支持的链码子命令包括 install、instantiate、invoke、query、upgrade、list、package、signpackage 等。大部分命令（除了 package、signpackage 外）的处理过程都是类似的，创建签名提案消息，发给 Peer 进行背书，获取 ProposalResponse 消息。

特别地，旧生命周期的 instantiate、upgrade 以及普通的 invoke 子命令还需要根据 ProposalResponse 消息创建交易，发送给 Orderer 进行排序后被全网接受。package、signpackage 子命令作为本地操作，无需跟 Peer 或 Orderer 打交道。

Fabric 2.5 LTS 和 Fabric 3.x 中，链码安装、组织批准、提交、查询定义和升级应使用 `peer lifecycle chaincode`；`peer chaincode invoke/query` 仍用于调用和查询已经提交的链码。

各个命令的功能总结如下表所示：

| 旧命令         | 发往组件         | 功能                                       |
| ----------- | ------------ | ---------------------------------------- |
| install     | Peer 节点      | 将链码信息打包并安装到 Peer（旧生命周期）                  |
| query       | Peer 节点      | 查询链码                                     |
| list        | Peer 节点      | 列出旧生命周期链码信息，包括某个 Peer 上安装过的链码或通道内实例化过的链码 |
| instantiate | Peer 节点和排序服务 | 在通道中实例化链码（旧生命周期）                         |
| invoke      | Peer 节点和排序服务 | 调用链码                                     |
| upgrade     | Peer 节点和排序服务 | 升级链码（旧生命周期）                              |
| package     | 本地操作         | 打包本地链码为部署 Spec 包（旧生命周期）                  |
| signpackage | 本地操作         | 为给定链码包添加签名（旧生命周期）                        |

可以通过 `peer chaincode <subcommand> --help` 来查看具体的命令使用说明。

这些操作管理了链码的整个生命周期，如下图所示。

![链码生命周期](/files/4bq2mN9jTkVuuzY2oouB)

旧生命周期中，用户需要将链码安装到 Peer 节点，之后可以在 Peer 所属的某个通道内实例化链码容器。此时链码处于运行状态，应用可以通过 invoke 或 query 来调用链码。链码在一定时间不被调用会处于空闲状态，可能被停止并在后续调用时重新启动。此外，用户还可以升级链码到新的版本。Fabric 2.5 LTS 和 3.x 中对应流程已经变为打包、安装、组织批准、提交链码定义，以及递增 `--sequence` 完成升级。

后面将以 Fabric 项目中自带的 Go 语言 example02 链码（路径在 examples/chaincode/go/chaincode\_example02）为例进行相关命令讲解。

## 命令选项

链码操作支持如下全局命令选项，对应的功能如下表所示。

| 全局选项                         | 类型     | 含义                           |
| ---------------------------- | ------ | ---------------------------- |
| --connTimeout                | int    | 客户端连接超时，默认为 3 秒              |
| --keyfile                    | string | 与排序服务双向 TLS 认证时使用的私钥文件       |
| -o, --orderer                | string | Orderer 服务地址                 |
| --transient                  | string | JSON 格式的临时数据，仅供 Peer 在背书环节使用 |
| --tls                        | bool   | 连接到 Orderer 服务时是否启用 TLS      |
| --cafile                     | string | 信任的排序服务的 TLSCA 证书，PEM 编码格式   |
| --certfile                   | string | 与排序服务双向 TLS 认证时使用的证书文件       |
| --clientauth                 | bool   | 与排序服务通信时是否启用双向 TLS 认证        |
| --ordererTLSHostnameOverride | string | 验证 Orderer TLS 时候覆盖所校验的主机名   |

此外，不同子命令还可能支持一些子选项，如下表所示。

| 子命令选项               | 类型          | 含义                                                          |
| ------------------- | ----------- | ----------------------------------------------------------- |
| --connectionProfile | string      | 指定连接配置（Connection Profile）文件                                |
| -C, --chainID       | string      | 所面向的通道，默认为 `testchainid`                                    |
| -c, --ctor          | string      | 指定链码命令的参数信息，JSON 格式，默认为 `{}`                                |
| -E, --escc          | string      | 指定所用背书系统链码的名称，默认为 `escc`                                    |
| -l, --lang          | string      | 链码实现语言，默认为 `golang`                                         |
| -n, --name          | string      | 链码名称                                                        |
| --peerAddresses     | string list | Peer 节点地址，可以指定多个                                            |
| -o, --orderer       | string      | 排序服务地址                                                      |
| --tlsRootCertFiles  | string list | 采用 TLS 时，信任的 Peer 的根证书列表，需要跟 Peer 节点地址列表给出的顺序一致             |
| -p, --path          | string      | 所操作链码的本地路径，如果是 Go 语言为包路径（相对于 $GOPAH/src），如果是其它语言则为绝对路径      |
| -P, --policy        | string      | 链码所关联的背书策略，例如 `-P "OR ('Org1MSP.member','Org2MSP.member')"` |
| -t, --tid           | string      | ChaincodeInvocationSpec 中的 ID 生成算法和编码，目前支持默认的 sha256+base64 |
| -v, --version       | string      | install/instantiate/upgrade 等命令中指定的版本信息                     |
| -V, --vscc          | string      | 指定所使用验证系统链码的名称，默认为 `vscc`                                   |

注意，不同子命令具体支持不同的参数，总结如下表所示。

| 命令          | -C 通道 | -c cc 参数 | -E escc | -l 语言 | -n 名称 | -o Orderer | -p 路径 | -P policy | -v 版本 | -V vscc |
| ----------- | ----- | -------- | ------- | ----- | ----- | ---------- | ----- | --------- | ----- | ------- |
| install     | 不支持   | 支持       | 不支持     | 支持    | 必需    | 不支持        | 必需    | 不支持       | 必需    | 不支持     |
| instantiate | 必需    | 必需       | 支持      | 支持    | 必需    | 支持         | 不支持   | 支持        | 必需    | 支持      |
| invoke      | 支持    | 必需       | 不支持     | 支持    | 必需    | 支持         | 不支持   | 不支持       | 不支持   | 不支持     |
| query       | 支持    | 必需       | 不支持     | 支持    | 必需    | 不支持        | 不支持   | 不支持       | 不支持   | 不支持     |
| upgrade     | 必需    | 必需       | 支持      | 支持    | 必需    | 支持         | 不支持   | 支持        | 必需    | 支持      |
| list        | 支持    | 不支持      | 不支持     | 不支持   | 不支持   | 不支持        | 不支持   | 不支持       | 不支持   | 不支持     |
| package     | 不支持   | 支持       | 不支持     | 支持    | 必需    | 不支持        | 必需    | 不支持       | 必需    | 不支持     |
| signpackage | 不支持   | 支持       | 不支持     | 支持    | 必需    | 不支持        | 必需    | 不支持       | 必需    | 不支持     |

其中，必需、支持和不支持三种情况的含义为：

* 必需：该参数必需被指定，包括通过命令行、环境变量、配置等。
* 支持：该参数可以被使用。某些时候如果不指定，可能采取默认值或自动获取。
* 不支持：该参数不应该使用。

## 安装链码

旧生命周期的 install 命令将链码的源码和环境等内容封装为一个链码安装打包文件（Chaincode Install Package，CIP），并传输到指定的 Peer 节点。当前生命周期的安装命令为 `peer lifecycle chaincode install <package.tar.gz>`。

此过程只需要跟 Peer 节点打交道，无需修改账本状态，并不产生交易。只有安装过链码的节点才能进行链码实例化和进行背书处理。默认情况下，执行者需要为该 Peer 节点的管理员角色。

例如，采用如下命令可以部署 test\_cc.1.0 的链码打包文件到指定的 Peer 节点。

```bash
$ CORE_PEER_ADDRESS=peer.your_domain.com:7051 # 设置默认的 Peer 地址
$ peer chaincode install \
	-n test_cc \
	-v 1.0 \
	-p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
```

Peer 节点收到请求，检查和解析后将 CIP 文件保存在 `${CORE_PEER_FILESYSTEMPATH}/chaincodes/` 目录下（一般以 cc\_name.version 格式命名，如 /var/hyperledger/production/chaincodes/testcc.1.0），供后续操作（如重新创建和启动链码容器）使用。

CIP 文件中主要包括如下数据。

* `ChaincodeDeploymentSpec`：链码的源码和一些关联环境，如名称和版本。
* 链码实例化策略，默认是任意通道上的 MSP 管理员身份均可。
* 拥有这个链码的实体的证书和签名。
* 安装时，添加本地 MSP 管理员的签名。

其中，ChaincodeDeploymentSpec（CDS）结构最为核心，主要包括 ChaincodeSpec（CS）数据结构（链码部署信息）和 CodePackage（链码源代码相关数据）。CDS 和 CS 结构同时也大量在其他链码命令（如实例化命令和升级命令）中使用。

链码安装的整体实现流程如下图所示。

![链码安装过程](/files/xaV8EfUe5So2KtpsN6qt)

主要步骤包括：

* 首先是构造带签名的提案结构（SignedProposal）。
  * 调用 `InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error)` 方法，初始化 EndoserClient（跟 Peer 通信）、BroadcastClient（跟 Orderer 通信）、Signer（签名操作）等辅助结构体。所有链码子命令都会执行该过程，会根据需求具体初始化不同的结构。
  * 然后根据命令行参数进行解析，判断是根据传入的打包文件来直接读取 ChaincodeDeploymentSpec（CDS）结构，还是根据传入参数从本地链码源代码文件来构造生成。
  * 以本地重新构造情况为例，首先根据命令行中传入的路径、名称等信息，构造生成 ChaincodeSpec（CS）结构。
  * 利用 ChaincodeSpec 结构，结合链码包数据生成一个 ChaincodeDeploymentSpec 结构（chainID 为空），调用本地的 `install(msg proto.Message, cf *ChaincodeCmdFactory) error` 方法。
  * install 方法基于传入的 ChaincodeDeploymentSpec 结构，构造一个对生命周期管理系统链码（LSCC）调用的 ChaincodeSpec 结构，其中，Type 为 ChaincodeSpec\_GOLANG，ChaincodeId.Name 为“lscc”，Input 为 “install”+ChaincodeDeploymentSpec。进一步地，构造了一个 LSCC 的 ChaincodeInvocationSpec（CIS）结构，对 ChaincodeSpec 结构进行封装。
  * 基于 LSCC 的 ChaincodeInvocationSpec 结构，添加头部结构，生成一个提案（Proposal）结构。其中，通道头部中类型为 ENDORSER\_TRANSACTION，TxID 为对随机数+签名实体，进行 Hash。
  * 对 Proposal 进行签名，转化为一个签名后的提案消息结构 SignedProposal。
* 将带签名的提案结构通过 EndorserClient 经由 gRPC 通道发送给 Peer 的 `ProcessProposal(ctx context.Context, in *SignedProposal, opts ...grpc.CallOption) (*ProposalResponse, error)` 接口。
* Peer 模拟运行生命周期链码的调用交易进行处理，检查格式、签名和权限等，通过则保存到本地文件系统。

下图给出了链码安装过程中最为重要的 SignedProposal 数据结构，该结构对于大部分链码操作命令都是类似的，其中最重要的是 ChannelHeader 结构和 ChaincodeSpec 结构中参数的差异。

![链码安装过程中所涉及的数据结构](/files/h0tGu2CGUNqPW5xi2JGc)

## 实例化链码

旧生命周期的 instantiate 命令通过构造生命周期管理系统链码（Lifecycle System Chaincode，LSCC）的交易，将安装过的链码在指定通道上进行实例化部署调用，在节点上创建容器启动，并执行初始化操作。实例化链码需要同时跟 Peer 和 Orderer 打交道。Fabric 2.5 LTS 和 3.x 不再用 instantiate 启动链码，而是由通道成员批准并提交链码定义。

执行 instantiate 命令的用户身份必须满足实例化的策略（默认为通道内 MSP 管理员角色），并且在所指定的通道上拥有写（Write）权限。在 instantiate 命令中可以通过 `-P` 参数指定链码调用的背书策略（Endorsement Policy），在 Commit 阶段会进行策略检查。

旧版资料通常会展示 `peer chaincode instantiate` 命令；由于该路径在 Fabric 2.5 LTS 的新通道和 Fabric 3.x 中不再作为推荐生命周期，本节不再给出可复制的旧命令。当前网络应按 [管理链上代码（Lifecycle）](/blockchain_guide/10_fabric_op/chaincode_v2.md) 使用 `peer lifecycle chaincode approveformyorg` 和 `commit` 提交链码定义。

旧命令中的 `collection.json` 为私密账本（sideDB）特性中使用（Fabric v1.1.0 开始支持），可以在同一通道内实现私密数据局部共享。在当前生命周期中，同样通过 collections config 随链码定义审批和提交。如果不指定该参数则默认不启用该特性，意味着通道内所有成员都可以看到链码调用结果。

collection.json 的一个示例如下所示。

```jsonc
[
 {
	 "name": "public", // 集合名称
	 "policy": "OR('Org1MSP.member', 'Org2MSP.member')", // 集合成员
	 "requiredPeerCount": 1, // 至少扩散私密数据到几个节点
	 "maxPeerCount": 3, // 最大扩散节点个数
	 "blockToLive":99999, //私密数据存活时长
	 "memberOnlyRead": true // 是否只允许集合的成员访问私密数据
},
 {
	 "name": "private",
	 "policy": "OR('Org1MSP.member')",
	 "requiredPeerCount": 1,
	 "maxPeerCount": 3,
	 "blockToLive":3,
	 "memberOnlyRead": false
 }
]
```

其中定义了 public 和 private 两个集合，分别包括两个组织和单个组织。当在链码逻辑中指定某个键值属于特定集合时，只有集合内成员能看到明文的读写集合，非集合成员即便在同一通道内也无法获取隐私数据。对应 policy 只支持 OR 语法，指定哪些组织可以看到隐私数据集合。

链码实例化实现的整体流程如下图所示。

![链码实例化过程](/files/0PkbHij7bKF9iaMY4G0B)

主要步骤包括：

* 首先，类似链码安装命令，需要创建一个 SignedProposal 消息。注意 instantiate 和 upgrade 支持 policy、escc、vscc 等参数。LSCC 的 ChaincodeSpec 结构中，Input 中包括类型（“deploy”）、通道 ID、ChaincodeDeploymentSpec 结构、背书策略、escc 和 vscc 等。
* 调用 EndorserClient，发送 gRPC 消息，将签名后的 Proposal 发给指定的 Peer 节点（Endorser），调用 `ProcessProposal(ctx context.Context, in *SignedProposal, opts ...grpc.CallOption) (*ProposalResponse, error)` 方法，进行背书处理。节点会模拟运行 LSCC 的调用交易，启动链码容器。实例化成功后会返回 ProposalResponse 消息（其中包括背书签名）。
* 根据 Peer 返回的 ProposalResponse 消息，创建一个 SignedTX（Envelop 结构的交易，带有签名）。
* 使用 BroadcastClient 将交易消息通过 gRPC 通道发给 Orderer，Orderer 会进行全网排序，并广播给 Peer 进行确认提交。

其中，SignedProposal 结构如下图所示。

![链码实例化时的 SignedProposal 结构](/files/iMVWzVovBy5LPFSifux3)

交易 Envelope 结构如下图所示。

![交易 Envelope 结构](/files/s3fIGjtI10hIDXTcfcCG)

Peer 返回的 ProposalResponse 消息定义如下。

```go
type ProposalResponse struct {
	// 消息协议版本
	Version int32 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"`
	// 消息创建时的时间戳
	Timestamp *google_protobuf1.Timestamp `protobuf:"bytes,2,opt,name=timestamp" json:"timestamp,omitempty"`
	// 返回消息，包括状态、消息、元数据载荷等
	Response *Response `protobuf:"bytes,4,opt,name=response" json:"response,omitempty"`
	// 数据载荷，包括提案的 Hash 值，和扩展的行动等
	Payload []byte `protobuf:"bytes,5,opt,name=payload,proto3" json:"payload,omitempty"`
	// 背书信息列表，包括背书者的证书，以及其对“载荷+背书者证书”的签名
	Endorsement *Endorsement `protobuf:"bytes,6,opt,name=endorsement" json:"endorsement,omitempty"`
}
```

*注：目前命令行下的 instantiate 命令还不支持指定实例化策略，Peer 会采用默认的实例化策略（组织管理员身份）。*

## 调用链码

通过 invoke 命令可以调用运行中的链码的方法。`-c` 参数指定的函数名和参数会被传入到链码的 Invoke() 方法进行处理。调用链码操作需要同时跟 Peer 和 Orderer 打交道。

例如，对部署成功的链码执行调用操作，由 `a` 向 `b` 转账 10 元。

在 peer0 容器中执行如下操作，注意验证最终结果状态正常 `response:<status:200 message:"OK" >`。

```bash
$ peer chaincode invoke \
	-o orderer0:7050 \
	-n test_cc \
	-C ${APP_CHANNEL} \
	-c '{"Args":["invoke","a","b","10"]}' \
	--tls \
	--cafile ${ORDERER_TLS_CA}
```

这一命令会调用最新版本的 test\_cc 链码，将参数 `'{"Args":["invoke","a","b","10"]}'` 传入链码中的 `Invoke()` 方法执行。命令会生成一笔交易，需指定排序者地址。

需要注意，invoke 命令不支持指定链码版本，默认调用最新版本的链码。

实现上，基本过程如下图所示。

![链码调用过程](/files/rvLWBP3dmnMh5XH8hgaf)

实现上，基本过程如下：

* 首先，也是要创建一个 SignedProposal 消息。根据传入的各种参数，生成 ChaincodeSpec 结构（其中，Input 为传入的调用参数）。然后，根据 ChaincodeSpec、chainID、签名实体等，生成 ChaincodeInvocationSpec 结构。进而封装生成 Proposal 结构（通道头部中类型为 ENDORSER\_TRANSACTION），并进行签名。
* 调用 EndorserClient，发送 gRPC 消息，将签名后的 Proposal 发给指定的 Peer 节点（Endorser），调用 `ProcessProposal(ctx context.Context, in *SignedProposal, opts ...grpc.CallOption) (*ProposalResponse, error)` 方法，进行背书处理。节点会模拟运行链码调用交易，成功后会返回 ProposalResponse 消息（带有背书签名）。
* 根据 Peer 返回的 ProposalResponse 消息，创建一个 SignedTX（Envelop 结构的交易，带有签名）。
* 调用 BroadcastClient 将交易消息通过 gRPC 通道发给 Orderer 进行全网排序并广播给 Peer 进行确认提交。

在此过程中，发给 Peer 节点的交易提案数据结构如下图所示。

![链码调用过程发给 Peer 节点的交易提案](/files/8bJQwPciUSy8FXnF0UYW)

发给排序服务的交易数据结构如下图所示。

![链码调用过程发给排序服务的交易结构](/files/tZlisePAhgOt4yjcVRPO)

注意 invoke 是异步操作，invoke 成功只能保证交易已经进入 Orderer 进行排序，但无法保证最终写到账本中（例如交易未通过 Committer 验证而被拒绝）。需要通过 eventHub 或查询方式来进行确认交易是否最终写入到账本上。

## 查询链码

查询链码可以通过 query 子命令。

该子命令实际上是 invoke 操作与 Peer 打交道的部分，即将签名后的 Proposal 发给指定的 Peer 节点的 ProcessProposal() gRPC 接口。最终将 `-c` 指定的命令参数发送给了链码中的 `Invoke()` 方法执行。

与 invoke 操作的区别在于，query 操作用来查询 Peer 上账本状态（需要链码支持查询逻辑），不生成交易，也不需要与 Orderer 打交道。

例如，执行如下命令会向 Peer 查询状态 `a` 的值，并返回查询结果。

```bash
$ peer chaincode query \
	-n test_cc \
	-C ${APP_CHANNEL} \
	-c '{"Args":["query","a"]}'
```

在实例化链码容器后，可以在 peer0 容器中执行如下命令，注意输出无错误信息，最后的结果为初始值 `Query Result: 100`。

```bash
$ peer chaincode query \
	-n test_cc \
	-C ${APP_CHANNEL} \
	-c '{"Args":["query","a"]}'
Query Result: 100
[main] main -> INFO 001 Exiting.....
```

类似的，查询 `b` 的余额，注意最终返回结果为初始值 `Query Result: 200`。

```bash
$ peer chaincode query \
	-n test_cc \
	-C ${APP_CHANNEL} \
	-c '{"Args":["query","b"]}'
Query Result: 200
[main] main -> INFO 001 Exiting.....
```

在执行完 a 向 b 转账 10 的交易后，再次查询 `a` 和 `b` 的余额，发现发生变化。

`a` 的新余额为 90。

```bash
$ peer chaincode query \
	-n test_cc \
	-C ${APP_CHANNEL} \
	-c '{"Args":["query","a"]}'
Query Result: 90
[main] main -> INFO 001 Exiting.....
```

`b` 的新余额为 210。

```bash
$ peer chaincode query \
	-n test_cc \
	-C ${APP_CHANNEL} \
	-c '{"Args":["query","b"]}'
Query Result: 210
[main] main -> INFO 001 Exiting.....
```

query 的主要实现过程如下所示。

* 根据传入的各种参数，最终构造签名提案，通过 endorserClient 发送给指定的 Peer。
* 成功的话，获取到 ProposalResponse，打印出 proposalResp.Response.Payload 内容。

注意 invoke 和 query 的区别，query 子命令会返回从 Peer 的查询结果，但不创建 SignedTx 发送到 Orderer。

## 升级链码

当需要修复链码漏洞或进行功能拓展时，可以对链码进行升级，部署新版本的链码。旧生命周期通过 `peer chaincode upgrade` 完成；Fabric 2.5 LTS 和 3.x 通过重新打包/安装、各组织批准新链码定义并递增 `--sequence` 后 `commit` 完成升级。

旧生命周期资料会先把新版本链码安装到 Peer，再通过 `peer chaincode upgrade` 在通道上实例化新版本链码。Fabric 2.5 LTS 和 3.x 的当前流程不再使用可复制的旧 `upgrade` 命令，而是重新打包和安装链码包，由各组织批准新链码定义，并在 `commit` 时递增 `--sequence`。如果需要执行初始化逻辑，应在当前生命周期中显式使用 `--init-required` 和 `--isInit` 语义，并谨慎设计初始化函数，避免升级时意外改写已有状态。

链码实例化实现的整体流程如下图所示，十分类似实例化过程。

![链码升级过程](/files/WTwzYqqx5Wf3DzQpD47L)

* 首先，需要创建一个封装了 LSCC 调用交易的 SignedProposal 消息。注意 instantiate 和 upgrade 支持指定 policy、escc、vscc 等参数。LSCC 的 ChaincodeSpec 结构中，Input 中包括类型（“upgrade”）、通道 ID、ChaincodeDeploymentSpec 结构、背书策略、escc 和 vscc 等。
* 调用 EndorserClient，发送 gRPC 消息，将签名后的 Proposal 发给指定的 Peer 节点（Endorser），调用 `ProcessProposal(ctx context.Context, in *SignedProposal, opts ...grpc.CallOption) (*ProposalResponse, error)` 方法，进行背书处理。节点会模拟运行 LSCC 的调用交易，启动链码容器。实例化成功后会返回 ProposalResponse 消息（其中包括背书签名）。
* 根据 Peer 返回的 ProposalResponse 消息，创建一个 SignedTX（Envelop 结构的交易，带有签名）。
* 使用 BroadcastClient 将交易消息通过 gRPC 通道发给 Orderer，Orderer 会进行全网排序，并广播给 Peer 进行确认提交。

## 查询链码安装和实例化信息（旧生命周期）

旧生命周期的 list 子命令支持查询某个节点上已经安装的链码信息（--installed）或某个通道内已经实例化的链码信息（--instantiated）。请求发送到 Peer 节点，调用 LSCC 系统链码提供的接口进行查询。当前生命周期应使用 `peer lifecycle chaincode queryinstalled`、`querycommitted` 或 `checkcommitreadiness`。

例如，如下命令查询默认 Peer 上安装的链码情况，返回结果显示已安装链码 exp02 的 v1.0 版本。

```bash
$ peer chaincode list \
		--installed \

Get installed chaincodes on peer:
Name: exp02, Version: 1.0, Path: examples/chaincode/go/chaincode_example02, Id: 08ca675c39a8bae2631847a521fc92e12969fe122bd4a9df0a707cf1059e8730
```

例如，如下命令查询通道 ${APP\_CHANNEL} 内实例化的链码情况，返回结果显示已实例化链码 exp02 的 v1.0 版本。

```bash
$ peer chaincode list \
		--instantiated \
		-C ${APP_CHANNEL}

Get instantiated chaincodes on channel businesschannel:
Name: exp02, Version: 1.0, Path: examples/chaincode/go/chaincode_example02, Escc: escc, Vscc: vscc
```

链码列出安装和实例化过程的实现流程如下图所示。

![链码列出安装和实例化过程](/files/eqmvlyhw15gnCwVjgumW)

* 首先，根据输入参数创建一个 LSCC 的链码调用规范。LSCC 的 ChaincodeSpec 结构中，Input 参数根据查询类型设置为 getinstalledchaincodes 或 getchaincodes。
* 根据 LSCC 的链码调用规范封装为一个 Proposal 结构，头部类型为 ENDORSER\_TRANSACTION，通道 ID 设置为空（查询安装信息）或给定的通道（查询实例化信息）。
* 调用 EndorserClient，发送 gRPC 消息，将签名后的 Proposal 发给指定的 Peer 节点（Endorser），调用 `ProcessProposal(ctx context.Context, in *SignedProposal, opts ...grpc.CallOption) (*ProposalResponse, error)` 方法，进行背书处理。节点会运行 LSCC 的相应逻辑，成功后会返回 ProposalResponse 消息。
* 客户端解析 Peer 返回的 ProposalResponse 消息，如果成功则打印查询结果。

## 打包链码和签名

旧生命周期通过将链码相关的数据进行封装，可以实现对其进行打包和签名操作。当前生命周期应使用 `peer lifecycle chaincode package` 生成 `.tar.gz` 链码包；旧的 `signpackage` 流程仅用于历史网络。

打包命令支持三个特定参数：

* -s, --cc-package：表示创建完整打包格式，而不是仅打包 ChaincodeDeploymentSpec 结构。
* -S, --sign：对打包的文件使用本地的 MSP（core.yaml 中的 localMspid 指定）进行签名。
* -i --instantiate-policy string：指定实例化策略。可选参数。

例如，通过如下命令创建一个本地的打包文件 ccpack.out。

```bash
$ peer chaincode package \
	-n test_cc -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 \
	-v 1.0 \
	-s \
	-S \
	-i "AND('Org1.admin')" \
	ccpack.out
```

打包后的文件，也可以直接用于 install 操作，如

```bash
$ peer chaincode install ccpack.out
```

签名命令则对一个打包文件进行签名操作（添加当前 MSP 签名到签名列表中）。

```bash
$ peer chaincode signpackage ccpack.out signedccpack.out
```

其中，打包文件结构主要包括三部分的信息：

* ChaincodeDeploymentSpec 结构；
* 实例化策略信息；
* 拥有者的签名列表。

实现的整体流程如下：

* 首先会调用 `InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error)` 方法初始化 Signer 等结构。对于打包命令来说纯属本地操作，不需要 Endorser 和 Orderer 的连接。
* 调用 getChaincodeSpec() 方法，解析命令行参数，根据所指定的数据生成 ChaincodeSpec 结构。
* 根据 ChaincodeSpec 结构，结合链码相关数据构造 ChaincodeDeploymentSpec 结构，并传入 getChaincodeInstallPackage 方法。
* getChaincodeInstallPackage 方法基于传入的 ChaincodeDeploymentSpec 结构，添加实例化策略和签名信息等，生成一个 SignedChaincodeDeploymentSpec，并进一步作为 Data 生成一个 Envelope 结构，其中 ChannelHeader 指定为 CHAINCODE\_PACKAGE。
* 将 Envelope 结构序列化，写到指定的本地文件。

其中，Envelope 结构如下图所示。

![打包过程中的 Envelope 结构](/files/SjXC2jwyxjdRvQ2CuTur)
