目录
一、Fabric介绍
fabric是一种联盟链,是通过认证后才可以进入,其中MSP(Membership Service Provider)就是会员服务系统,基于PKI规范建立的用户证书和私钥体系。
fabric共识算法也和其他公联不同,fabric支持solo和kafka两种模式:
- solo:单个节点中完成排序,安全性稳定性差
- kafka:排序节点从kafka集群获取对应topic分区的数据,避免单点故障
二、Fabric快速入门
fabric的5个模块
fabric分为这5个模块,意思是fabric有这5个个命令关键字,开发前要用下面的方法编译好这些模块,这些关键字才可以使用
- peer:主节点,存储区块链数据,运行链码
- orderer:对交易进行排序,将排序好的交易打包
- cryptogen:组织和证书生成模块
- configtxgen:区块和交易生成模块
- configtxlator:区块和交易解析模块
开发时模块生成方法(任选一个):
- 直接编译源代码:可以将这些模块先编译,再放到系统目录中,就可以在任何路径下运行这些模块;
- 使用本地源代码生成fabric模块的docker镜像
- 从docker仓库中直接下载fabric模块的docker镜像文件(e2e_cli就是这种方法)
- baseos:基础镜像,其他镜像都在该镜像基础之上生成
- baseimage:包含jdk、golang、nodejs等,用来生成chaincode
- peer:peer模块镜像,从1.0开始peer不再提交数据,由客户端完成,peer还是会验证和背书
- orderer:oderer节点库镜像
- ca:ca模块镜像
- tools:工具镜像,包含cryptogen、configtxgen、configtxlator等工具 本地客户端镜像
- couchdb:couchdb数据库镜像
- kafka:kafka库镜像
- zookeeper:zookeeper库镜像
- testenv:测试环境库镜像
- buildenv:编译环境库镜像
- javaenv:java链码运行镜像
- ccenv:go链码运行镜像
docker compose:是使用docker容器分布式部署的工具,只需要定义一个多容器应用的yml文件,定义哪个容器运行哪个应用,然后用一条命令即可部署所有容器
fabric启动步骤
生成相关证书,cryptogen模块完成,根据yaml配置文件生成证书
生成创始块
系统创始块,系统创始块是存配置信息的,configtxgen模块完成,根据configtx.yaml生成创始块文件orderer.genesis.block
账本创始块,channel的创始块,configtxgen模块完成,根据configtx.yaml生成roberttestchannel.tx,该文件生成channel,还要生成锚节点文件Org1MSPanchors.tx和Org2MSPanchors.tx
启动orderer节点,orderer模块完成,根据orderer的yaml文件(还定义了log格式等)启动orderer节点
启动peer节点,peer模块完成,根据peer的yaml文件(还定义了log格式等)启动peer节点
创建通道(都是peer节点完成)
- 创建通道
- 让已经运行的peer的节点加入通道
- 更新锚节点
部署chaincode-实例化chaincode代码-调用chaincode代码
三、模块介绍
若是使用docker运行,尽量使用环境变量的配置方式;
若是使用命令直接启动,使用配置文件参数配置方式
1. cryptogen模块
开发第一步就是要编写cryptogen的配置文件,通过配置文件来生成各种证书
OrdererOrgs:
- Name: Orderer // 定义oderer节点的名字
Domain: example.com // 定义oderer节点的根域名
Spacs:
- Hostname: orderder // 定义orderer节点的主机名
PeerOrgs:
- Name: Org1 // 定义组织1的名字
Domain: org1.example.com // 定义组织1的根域名
Template:
Count: 2 // 定义组织1的节点数目
Users:
Count: 1 // 定义组织1的用户数目
- Name: Org2
Domain: org2.example.com
Template:
Count: 2
Users:
Count: 1
orderer节点的域名一般也是整个系统,测试环境中域名可以随意定义,正式生产环境中,域名选择已备案或放到白名单里
用下面命令生成证书
cryptogen generate --config=yaml文件位置 --output 存放证书的文件夹位置
生成的证书结构如下图:
2. configtxgen模块
configtxgen模块用来生成orderer初始化文件和channel初始化文件,更新锚节点
configtxgen -profile TestTwoOrdererGenesis -outputBlock ./orderer.genesis.block
configtxgen -profile TestTwoOrgsChannel -outputCreateChannelTx ./roberttestchannel.tx -channelID roberttestchannel
configtxgen -profile TestTwoOrgsChannel -outputAnchorPeersUpdate ./Org1MSPanchors.tx -channelID roberttestchannel -asOrg Org1MSP
3. configtxlator模块
configtxlator模块可以把区块链的二进制文件转为json文件格式
configtxlator的rest服务提供了解码、编码、计算配置更新、交易打包四个功能
在这里插入代码片
4. orderer模块
orderer模块负责对交易进行排序,并将排好序的交易打包成区块
orderer模块的配置文件
- General节点配置
- FileLedger节点配置
- RAMLedger节点配置
- Kafka节点配置
- Debug节点配置
工作原理
- 客户端向orderer模块发送交易
- orderer节点对交易进行检查,符合条件就发送到排序队列
- orderer节点从队列中取出交易并打包,打包后将相关消息存储到本地
- orderer节点根据客户端请求将区块链发送给客户端
5. peer模块
主节点模块,负责存储区块链数据、运行维护链码、提供对外服务接口
peer模块的配置文件
- logging相关配置
- peer节点配置
- vm节点配置:定义peer与orderer的交互
- chaincode节点配置
每个组织的peer节点有四种角色:
- 提交节点(committer):维护区块链账本,客户端发起交易时要指定一个提交节点(不能配置),作为接入区块链的入口来发起交易,提交节点还会定期从orderer节点获取包含交易的区块,并对区块校验、加入区块链
- 背书节点(Endorse)负责对交易校验,校验后会将结果发送给客户端,也是由客户端发起交易时指定的(不能配置)
- leader节点:代表组织从Orderer节点获取区块信息,一个组织只有一个,在peer模块的配置文件中配置,是自主选举还是强制指定
- 锚节点(Anchor):负责代表组织和其他组织进行信息交换,通过configtxgen模块的配置文件配置host和port
这四种角色可以放在一个或多个peer节点上
6. TLS
TLS是为了保证数据传输的安全性,不是必选项,可以通过相关配置(orderer模块和peer模块的配置文件)激活或关闭
四、Fabric账号体系
cryptogen模块生成的账号结构:
msp
admincerts ## 管理员证书
cacerts ## 根CA服务器证书
keystore ## 节点或账号的私钥
signcerts ## 符合X.509的节点或者用户证书文件
tlscacerts ## TLS根CA的证书
tls
ca.crt
server,crt
server.key
Fabric中,每个动作创建通道、部署链码、调用链码都需要账号
启动Orderer节点时需要通过环境变量或配置文件设置账号,设置的是Orderer账号
启动Peer节点时需要通过环境变量或配置文件设置账号,设置的是Peer账号
创建channel时需要用到账号,用到的是用户账号
但是cryptogen模块生成账号具有局限性,只能刚开始通过配置文件生成相应数量的账号,但是无法动态的增加用户账号(peer账号可以通过配置start属性添加)。可以使用fabric-ca项目
五、Fabric智能合约
chaincode是客户端程序和Fabric之间的桥梁,客户端通过chaincode发起、查询交易,chaincode运行在docker容器中,chaincode支持多种语言开发。chaincode的管理在peer模块。
编写chaincode
- 编写链码的代码
package main
import(
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim" // fabric上下文
pb "github.com/hyperledger/fabric/protos/peer" // 主要负责和客户端通信
)
// 定义结构体作为chaincode的主对象
type simplechaincode struct{
}
// 结构体必须要实现Init方法
func(t *simplechaincode) Init(stub shim.ChaincodesStubInterface) pb.Response {
fmt.Println("<< ======= success init it is view in docker ======== >>")
return shim.Sucess([]byte("success init "))
}
// 结构体必须要实现Invoke方法
func(t *simplechaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("<< ======= 1. success it is view in docker ======== >>")
_, args := stub.GetFunctionAndParameters()
var a_parm = args[0] // 按照下面的调用 set
var a_parm = args[1] // 按照下面的调用 akeym
var a_parm = args[2] // 按照下面的调用 11234343
return shim.Success([]byte("success invoke "))
}
func main() {
err := shim.Start(new(simplechaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
- 部署、实例化、调用chaincode
## 部署
peer chaincode install -n qlkszzncc -v 1.1 -p chaincode所在目录
## 实例化 指明链码发起的交易需要Org1MSP或Org2MSP中任何一个用户背书即可 只有写操作需要背书 背书策略还有AND
peer chaincode instantiate -o orderer.qklszzn.com:7050 -C qklszzlchannel -n qlkszzncc -v 1.1 -c '{"Args":["init", "a", "100", "b", "200"]}' -P "OR {'Org1MSP.member', 'Org2MSP.member'}"
## 调用
peer chaincode invoke -o orderer.qklszzn.com:7050 -C qklszzlchannel -n qlkszzncc -c '{"Args": ["invoke", "set", "akeym", "11234343"]}'
chaincode实例化后会多出来一个运行chaincode的容器,容器名包含chaincode。如果这个容器启动后,peer容器重启是不影响chaincode容器的,chaincode容器不需要重启,如果chaincode执行出错导致容器关闭,客户端再请求的话,系统会自动启动chaincode容器。
一个通道内,如果在多个peer上安装了相同chaincode,只需要在一个peer节点上实例化就可以了
- ChaincodeStubInterface接口方法
方法 | 功能 |
---|---|
PutState(key string, value []byte) error | 存储数据到账本 |
GetState(key string) ([]byte, error) | 从账本中获取指定的数据 |
GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) | 查询指定key指定范围的数据 |
GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) | 获取指定key的历史记录 |
DelState(key string) error | 删除账本的数据 |
CreateCompositeKey(objectType string, attributes []string) | 创建复合键 |
GetStateByPartialCompositeKey(objectType string, attributes []string) | 通过复合键取值 |
SplitCompositeKey(compositeKey string) (string, []string, error) | 拆分复合键 |
GetTxID() string | 获取交易编号 |
GetTxTimestamp() (*timestamp.Timestamp, error) | 获取交易的时间 |
GetCreator | 获取交易的创建者 |
InvokeChainCode(chaincodeName string, args [][]byte, channel string) pb.Response | 调用其他Chaincode |
- chaincode命令
peer chaincode insatll...
peer chaincode instantiate...
peer chaincode invoke...
peer chaincode list...
peer chaincode package...
peer chaincode query...
peer chaincode signpackage...
peer chaincode upgrade...
五、Fabric Java SDK
上面讲述的都是区块链内部节点的开发,下面讲如何通过客户端访问peer节点和orderer节点,下面是客户端开发的demo
<dependencies>
<dependency>
<groupId>org.hyperledger.fabric-sdk-java</groupId>
<artifactId>fabric-sdk-java</artifactId>
<version>1.1.0-SNAPSHOT</version>
</dependency>
<dependencies>
待补充
六、CouchDB
fabric网络实际上有三种类型的数据存储,一种是账本本身,也就是区块链数据,是以文件形式存储;第二种是区块数据和历史数据的索引数据库;第三种是状态数据库,即存储我们在chaincode中执行的业务数据。其中第一和第二种是不能更换,第三个状态数据库默认是采用的levelDB,也支持couchdb