Pinvon's Blog

所见, 所闻, 所思, 所想

九 SDK

概述

目前的SDK有以下几种:

Golang: https://github.com/hyperledger/fabric-sdk-go

Node.js: https://github.com/hyperledger/fabric-sdk-node (最全)

Python: https://github.com/hyperledger/fabric-sdk-py

Java: https://github.com/hyperledger/fabric-sdk-java

主要模块: FabricClient, Config, Channel, Peer, Orderer, User, KeyValueStore, EventHub, Logger等.

先介绍 Cli 里面的操作命令.

Cli

以下的命令, 几乎都需要用到下面几个选项.

--cafile 用于指定 orderer 节点的 root 证书, 使得我们可以验证握手包. 路径类似如下: /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

-c 指定需要查询的 Channel 名字.

peer channel fetch

获取指定的区块, 写到指定的文件中去.

Usage:
  peer channel fetch <newest|oldest|config|(number)> [outputfile] [flags]

Flags:
  -c, --channelID string   In case of a newChain command, the channel ID to create. It must be all lower case, less than 250 characters long and match the regular expression: [a-z][a-z0-9.-]*
  -h, --help               help for fetch

Global Flags:
      --cafile string                       Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint
      --certfile string                     Path to file containing PEM-encoded X509 public key to use for mutual TLS communication with the orderer endpoint
      --clientauth                          Use mutual TLS when communicating with the orderer endpoint
      --connTimeout duration                Timeout for client to connect (default 3s)
      --keyfile string                      Path to file containing PEM-encoded private key to use for mutual TLS communication with the orderer endpoint
      --logging-level string                Default logging level and overrides, see core.yaml for full syntax
  -o, --orderer string                      Ordering service endpoint
      --ordererTLSHostnameOverride string   The hostname override to use when validating the TLS connection to the orderer.
      --tls                                 Use TLS when communicating with the orderer endpoint

例子:

peer channel fetch 0 mychannel.block -o orderer.example.com:7050 -c mychannel --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

peer channel getinfo

获取区块链中指定 Channel 的信息.

Usage:
  peer channel getinfo [flags]

Flags:
  -c, --channelID string   In case of a newChain command, the channel ID to create. It must be all lower case, less than 250 characters long and match the regular expression: [a-z][a-z0-9.-]*
  -h, --help               help for getinfo

Global Flags:
      --cafile string                       Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint
      --certfile string                     Path to file containing PEM-encoded X509 public key to use for mutual TLS communication with the orderer endpoint
      --clientauth                          Use mutual TLS when communicating with the orderer endpoint
      --connTimeout duration                Timeout for client to connect (default 3s)
      --keyfile string                      Path to file containing PEM-encoded private key to use for mutual TLS communication with the orderer endpoint
      --logging-level string                Default logging level and overrides, see core.yaml for full syntax
  -o, --orderer string                      Ordering service endpoint
      --ordererTLSHostnameOverride string   The hostname override to use when validating the TLS connection to the orderer.
      --tls                                 Use TLS when communicating with the orderer endpoint

返回信息里包括: 区块高度, 当前区块哈希, 上一个区块哈希.

peer channel update

将用于更新的 configtx 文件发送到 channel.

-f 用于指定由 configtxgen 文件生成的配置交易文件.

Usage:
  peer channel update [flags]

Flags:
  -c, --channelID string   In case of a newChain command, the channel ID to create. It must be all lower case, less than 250 characters long and match the regular expression: [a-z][a-z0-9.-]*
  -f, --file string        Configuration transaction file generated by a tool such as configtxgen for submitting to orderer
  -h, --help               help for update

Global Flags:
      --cafile string                       Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint
      --certfile string                     Path to file containing PEM-encoded X509 public key to use for mutual TLS communication with the orderer endpoint
      --clientauth                          Use mutual TLS when communicating with the orderer endpoint
      --connTimeout duration                Timeout for client to connect (default 3s)
      --keyfile string                      Path to file containing PEM-encoded private key to use for mutual TLS communication with the orderer endpoint
      --logging-level string                Default logging level and overrides, see core.yaml for full syntax
  -o, --orderer string                      Ordering service endpoint
      --ordererTLSHostnameOverride string   The hostname override to use when validating the TLS connection to the orderer.
      --tls                                 Use TLS when communicating with the orderer endpoint

例子:

peer channel update -f org3_update_in_envelope.pb -c mychannel -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

FabricClient

应用程序的入口模块, 提供通道管理, 链码管理, 数据存储, 密码学相关的功能. 每个FabricClient实例对应一个区块链网络, 包括记账节点, 排序节点等. 如果应用程序需要访问多个网络, 可以建立多个FabricClient实例, 不同的实例对应不同的网络.

接口名称 输入参数 输出参数 描述
NewChannel (name string) (Channel, error) 创建通道
Channel (name string) (Channel) 查询指定名称的通道
ExtractChannelConfig (configEnvelope []byte) ([]byte, error) 从ConfigEnvelope里解析出ConfigUpdate
SignChannelConfig (config []byte) (*common.ConfigSignature, error) 用FabricClient关联的用户身份对ExtractChannelConfig解析出来的config进行签名
CreateChannel (request fab.Create ChannelRequest) (apitxn.TransactionID, error) 创建通道, 创建通道的参数包括通道名称, 排序服务实例, 通道配置等信息, 返回包含随机数的交易号
QueryChannelInfo (name string, peers []fab.Peer) (fab.Channel, error) 从指定节点查询通道
StateStore () (fab.KeyValueStore) 返回状态存储的实例
SigningManager () (fab.SigningManager) 返回签名Manager实例
CryptoSuite () bccsp.BCCSP 返回BCCSP实例
SaveUserToStateStore (user fab.User, skip Persistence bool) (error) 保存用户实例到状态存储里
LoadUserFromStateStore (name string) (fab.User, error) 从状态存储里获取指定名称的用户实例
InstallChaincode (chaincodeName string, chaincodePath string, chaincodeVersion string, chaincodePackage []byte, targets []fab.Peer) ([]*apitxn.TransactionProposal Response, string, error) 安装指定链码名称, 路径, 版本的链码到指定的节点中
QueryChannels (peer fab.Peer) (*pb.ChannelQueryResponse, error) 查询指定节点加入的所有通道
QueryInstalledChaincodes (peer fab.Peer) (*pb.ChannelQueryResponse, error) 查询指定节点安装的所有链码
UserContext () (user fab.User) 返回当前FabricClient的用户实例
SetUserContext (user fab.User) () 设置当前FabricClient的用户实例
Config () (config.Config) 设置当前FabricClient的配置实例
NewTxnID () apitxn.TransactionID, error 本地生成包含随机数的交易号

Config

初始化FabricClient时需要离线获取配置信息, 包括可信的根证书, 排序服务节点证书和IP地址, 记账节点证书和IP地址等. Config读取后传递给FabricClient. 配置信息是动态传递的, SDK不会持久化存储, 应用程序负责维护这些配置信息.

51.png

52.png

Channel

通道是排序服务创建的隔离不同链上交易的实例, 加入到不同通道的节点接收到的是不同的交易. 通道在配置了排序服务节点和Peer节点后需要初始化, 初始化的时候给排序服务节点发送获取配置区块的请求.

53.png

54.png

Peer

Peer是客户端发送背书请求, 交易查询的节点. Peer实例包含节点名称, 地址, 角色, 注册证书等信息.

55.png

Orderer

Orderer是客户端发送交易进行排序的节点, Orderer实例包含了排序服务节点地址信息, 定义了发送原子广播请求和获取区块的接口.

56.png

User

User表示已经生成了注册证书和签名密钥的实体, 注册证书必须是CA颁发的证书, 只有生成了注册证书的实体, 都能进行部署链码, 提交交易, 查询交易等.

57.png

KeyValueStore

KeyValueStore提供给应用程序保存敏感信息的功能, 如用户私钥, 证书信息等.

58.png

EventHub

EventHub封装了与Peer节点交互的事件流, 接收Peer的各种异步通知事件.

59.png

FabricCAClient

60.png

UML图

61.png

应用场景

用户登记和注册

  1. 应用程序根据配置文件获取CA和CSP的配置信息.
  2. 应用程序根据配置信息创建FabricClient的实例, 并设置CryptoSuite和KeyValueStore等信息, FabricClient实例是整个操作的入口.
  3. 应用程序获取负责提交用户资料的登记员信息Registrar, 如果不存在, 需要先初始化登记员用户, 获取登记员的注册证书和私钥信息.
  4. 应用程序根据配置信息和组织信息创建FabricCAClient实例.
  5. 应用程序根据需要登记的用户信息, 生成RegistrationRequest请求, 提交给FabricCAClient.
  6. 登记员Registrar会提交访问Fabric-CA的POST请求, 请求的URL是/api/v1/register.
  7. Fabric-CA验证请求, 生成用户注册的密码Secret, 最终返回给应用程序, 完成用户信息登记的步骤.
  8. 应用程序利用申请的用户信息和返回的注册密码, 调用FabricCAClient的enroll().
  9. FabricCAClient生成私钥和证书签名请求CSR, 调用Fabrica-CA的enroll()生成注册证书.
  10. Fabric-CA返回生成的注册证书和私钥给应用程序.
  11. 应用程序可选地保存用户信息到KeyValueStore里.

62.png

在排序服务上创建通道

创建通道需要先使用工具configtxgen生成通道的配置文件mychannel.tx.

  1. 应用程序读取通道配置文件mychannel.tx, 这个文件是用configtxgen生成的, 包含了通道名称, 组织配置等信息.
  2. 创建通道只需要和Orderer节点通信, 需要通过Orderer节点的配置, 生成Orderer实例.
  3. 应用程序指定通道名称, 并通过mychannel.tx和Orderer实例, 生成创建通道请求CreateChannelRequest.
  4. 应用程序创建FabricClient实例, 调用CreateChannel(CreateChannelRequest)创建通道.
  5. SDK将CreateChannelRequest转换, 生成HeaderType_CONFIG_UPDATE类型的交易common.Payload.
  6. SDK对common.Payload进行签名, 签名者需要有创建通道的管理员权限.
  7. SDK通过Orderer实例, 发送SendBroadcast请求, 提交请求给Orderer节点.
  8. Orderer节点检查提交的请求, 校验是否有权限创建新的通道, 创建通道以后, Orderer节点就可以接收新的通道请求了.

63.png

Peer节点加入通道

创建通道以后, Orderer节点上就有了新通道的基本信息, 可以对新通道的交易进行排序打包生成区块了. 现在, 将Peer节点加入到新通道中, 应用程序才能通过Peer节点发起交易请求. 将Peer节点加入通道, 需要从Orderer节点上获取genesis.block, 然后在Peer节点本地初始化链.

  1. 应用程序进行必要的初始化配置, 如创建FabricClient实例, 设置发起加入通道请求的用户, Channel实例, Orderer实例等.
  2. 应用程序调用GenesisBlock的请求, 获取创世区块, Channel实例会构造HeaderType_DELIVER_SEEK_INFO的请求, 通过Orderer实例发送sendDeliver请求给Orderer节点, 获取该通道的genesis.block.
  3. 应用程序利用获取到的genesis.block, 构造JoinChannelRequest请求, 通过Channel实例发起JoinChannel请求.
  4. SDK的JoinChannel会根据JoinChannelRequest请求, 重新构造类型为HeaderType_ENDORSER_TRANSACTION的Proposal, Proposal会利用FabricClient实例设置的用户进行签名, 生成SignedProposal.
  5. 需要为每个加入通道的Peer节点创建一个Peer实例, 通过Peer实例调用ProcessProposal向Peer节点发送加入通道的SignedProposal.
  6. SignedProposal调用CSCC的JoinChain请求, Peer节点接收到SignedProposal请求后, 会调用CSCC进行必要的消息有效性检查和权限检查, 然后在本地Peer节点初始化链. 初始化的过程会根据通道名称在本地目录创建账本数据, 写入通道的genesis.block.
  7. 创建好通道的本地账本以后, Peer节点会启动Gossip服务, 从排序服务节点同步最新的区块数据. 根据Peer节点的配置, 参与主节点的选举或者直接作为主节点进行Peer节点之间的P2P通信.
  8. Peer节点初始化链以后, 就可以接收新链的交易请求了.

64.png

安装链码

把包含链码源码的ChaincodeDeploymentSpec上传到Peer节点.

通过Peer节点实例化链码

实例化链码时, 会创建链码镜像, 启动链码容器, 并调用链码的Init接口初始化, 生成的交易会发送到Orderer节点, 生成区块并记录到账本中.

  1. 应用程序创建多个实例, 包含FabricClient实例, User实例, Channel实例, Peer实例等.
  2. 通过调用Channel实例的SendInstantiateProposal进行链码实例化.
  3. SDK会构造包含ChaincodeDeploymentSpec的ChaincodeInvocationSpec, 调用的是LSCC的deploy请求.
  4. 应用程序发送Peer节点的请求同样会用Channel关联的用户进行签名, 通过Peer实例的ProcessTransactionProposal提交生成的SignedProposal.
  5. 每次给Peer节点发送SignedProposal时, 都会新建一个gRPC的连接, 通过ProcessProposal接口提交请求.
  6. Peer节点通过SignedProposal进行验证以后, 会调用LSCC执行链码部署的操作.
  7. Peer节点返回的只是背书节点模拟执行和背书签名的结果, 还要提交给Orderer节点生成最终的区块才能生效, 调用的过程与Peer节点接入通道的过程一样.
  8. 生成的新区块会通过主节点分发给组织内的其他Peer节点.

65.png

发起交易请求并生成区块

其实, 实例化链码的过程, 也是一种交易, 所以发起交易请求其实和实例化链码很相似.

不同之处在于:

  1. 普通的交易请求调用链码的Invoke接口, 实例化链码调用的是Init接口.
  2. 普通的交易请求是不嵌套的ChaincodeInvocationSpec请求, 包含通道的名称和调用链码的函数和参数等.
  3. 实例化链码的时候才开始构建链码镜像并启动链码容器, 所以速度较慢; 调用链码的背书节点已经启动了链码容器, 所以调用链码的速度较快.

66.png

链码的开发和调试

链码的SDK是shim, 链码是通过SDK和背书节点通信的, 链码的SDK只要实现接口的定义就能和背书节点交互.

shim提供给链码的接口主要有:

  1. 链码调用参数解析
  2. 交易信息解析
  3. 状态数据库操作
  4. 链码调用
  5. 事件处理
  6. 辅助操作

链码调用参数解析

接口名称 描述
GetArgs() byte 返回调用函数名称和参数的列表, 类型是字节数组
GetStringArgs() []string 返回调用函数名称和参数的列表, 类型是字符串
GetFunctionAndParameters() (string, []string) 返回调用的函数名称和参数, 类型是字符串

交易信息解析

接口名称 描述
GetTxID() string 获取交易号
GetCreator() ([]byte, error) 获取提交交易的身份信息(MSP, 证书)
GetTransient() (map[string][]byte, error) 获取私密信息, 这部分信息不会写入账本数据
GetBinding() ([]byte, error) 获取交易的绑定信息, 包含随机数和提交交易的身份信息等
GetSignedProposal() (*pb.SignedProposal, error) 获取签名的Proposal, 签名者是和提交交易的身份一样的
GetTxTimestamp() (*timestamp.Timestamp, error) 获取提交交易的时间戳

状态数据操作

接口名称 描述
GetState(key string)([]byte, error) 根据指定键查询状态数据库里存储的值
PutState(key string, value []byte)error 向状态数据库写入链值对(模拟写入)
DelState(key string) error 删除状态数据库里键对应的值(模拟删除, 记账时才删)
GetHistoryForKey(key string) (HistoryQueryIterator Interface, error) 查询一个键的历史数据
GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) 查询状态数据库里链在[startKey, endKey)之间的值
CreateCompositeKey(objectType string, attributes []string)(string, error) 构造组合键
SplitCompositeKey(compositeKey string)(string, []string, error) 分割组合键
GetStateByPartialCompositeKey(objectType string, keys []string)(StateQueryIteratorInterface, error) 部分组合键查询
GetQueryResult(query string)(StateQueryIteratorInterface, error) 根据指定条件查询状态数据库里存储的值

链码调用

接口名称 描述
InvokeChaincode(chaincodeName string, args byte, channel string) pb.Response 根据指定条件查询状态数据库里存储的值

事件处理

接口名称 描述
SetEvent(name string, payload []byte) error 设置事件的名称和内容

Comments

使用 Disqus 评论
comments powered by Disqus