fabric基础知识

Fabric中典型的业务流程,简单地分析相关的代码,包括创建通道(channel)、加入通道(channel)、安装智能合约(chaincode)、认可智能合约(chaincode)定义、提交智能合约(chaincode)定义、调用智能合约(chaincode)。

交易流程如下

img

​ 一个交易,基本上从客户端(SDK或APP)发起,然后根据背书策略发送交易提案到相关的背书节点,背书节点拿到提案后,验证签名,模拟执行,创建读写集,签名然后返回结果给客户端,客户端依次收集背书策略中要求发送至节点的返回结果,如果符合背书策略后,利用SDK打包交易(验证策略,打包提案、提案反馈和背书签名),然后发送到排序节点。排序服务器根据不同的通道按时间顺序排序,生成区块(RAFT共识就在此处),注意,此处不进行区块交易合法性验证。广播区块到各个组织的Leader节点,其它验证区块后广播给相关组织的通道成员,并且每个成员都要进行相同的校验处理。所有合法的区块交易都会将写入状态写入数据库(状态数据库和帐本数据库)并更新世界状态。

bccsp

​ 区块链加密服务提供者(Blockchain Crypto Service Provider),提供一些密码学相关操作的实现,包括 Hash、签名、校验、加解密等。主要支持 MSP 的相关调用。

bddtests

​ 行为驱动开发测试(Behaviour Driven Development)相关代码。主要是关于各种测试,线下peer节点部署等相关的操作。

common
一些通用的功能模块。包括常用的配置config、加密签名的crypto、ledger设置,工具包含协议设置等等。
core

​ 大部分核心实现代码都在本包下。其它包的代码封装上层接口,最终调用本包内代码。包含区块链操作Chaincode代码实现、peer节点消息处理及行为的实现、容器container的实现如docker交互实现、策略实现policy及预处理endorser等等。

devenv

​ 主要是方便本地搭建开发平台的一些脚本。主要包含了CouchDB设置、golang编译脚本、64位ubantu配置脚本等等。

docs

​ 项目相关的所有文档。包含客户定制主题以及一些工具的源代码。

events

​ EventHub 服务处理相关的模块。主要是包含了消费者,生产者的实现代码。另外,Even服务其包含了四种类型定义如下:REGISTER = 0;BLOCK = 1;CHAINCODE = 2;REJECTION = 3。

examples

​ 示例文件夹,包括一些 chaincode 示例和监听事件的示例。

gossip

​ 流言算法–gossip算法。一个基于pull的gossip算法的实现。最终确保状态一致。 该协议大致如下:
​ 1)A发起者发送Hello(B唯一标识,nonce)消息到B远程节点(多个)。
​ 2)收Hello信息后发送SendDigest到A节点,其中包含nonce
​ 3)A收到SendDigest,校验数据和nonce,把B作为待发送节点,并封装想要pull的数据SendReq到B节点
4)B收到SendReq发送SendRes到A节点,数据为SendReq不包含的数据

gotools

​ go 相关的开发工具的安装脚本:golint、govendor、goimports、protoc-gen-go、ginkgo、gocov、gocov-xml 等。

images

​ 一些跟 Docker 镜像生成相关的配置和脚本。主要包括各个镜像的 Dockerfile.in 文件。这些文件是生成 Dockerfile 的模板。

msp

​ 成员服务提供者(Member Service Provider),提供一组认证相关的密码学机制和协议,用来负责对网络提供证书分发、校验,身份认证管理等。一些成员管理的实现代码等。

orderer

​ 在 fabric 1.0 架构中,共识功能被抽取出来,作为单独的 fabric-orderer 模块来实现,完成核心的排序功能。最核心的功能是实现从客户端过来的 broadcast 请求,和从 orderer 发送到 peer 节点的 deliver 接口。同时,orderer 需要支持多 channel 的维护。主要包含Solo、kafka及bft三个方法。

peer

​ peer节点的相关主命令模块。作为服务端时候,支持 node 子命令;作为命令行时候,支持 chaincode、channel 等子命令。其中包含一些命令操作的实现等等。

proposals

​ 一些建议,包含现在对区块的结构优化建议及时序图的呈现。还有其他方面的一些建议文件。

protos

​ Protobuf 格式的数据结构和消息协议都在同一个 protos 包内。
这里面是所有基本的数据结构(message)定义和 GRPC 的服务(service)接口声明。

release

​ 关于如何从dockerhub中拉取docker镜像的相关操作及脚本代码。

release_notes

​ 关于最新2017年6月8日beta版本更新的相关资讯。主要包括release笔记内容及版本变根日志。

sampleconfig

​ 提供了一些样例证书文件和配置文件。pem格式,通过openssl来查看内容。内容基于BASE64来进行编码。

scripts

​ 一些辅助脚本,多数为外部 Makefile 调用。比如一些依赖环境的安装如python-pip、然后pip的安装包中的一些依赖环境等。还有一些配置,如让容器永不退出等。

test

​ 用于测试的一些脚本。 主要包含chaincode、回归测试脚本、容器关联order节点及peer节点测试脚本、环境构筑测试相关脚本如channel、以及一部分的工具LTE、OTE、PTE。

unit-test

​ 单点docker配置测试脚本

vendor

​ 关于部分提供商的内容及管理依赖,包含 github.com、golang.org、google系列及gopkg.in相关内容。


官方给出fabric-samples结构:

  1. asset-transfer-basic 资产转移的例子
    asset-transfer-ledger-queries 资产转移的例子
    asset-transfer-private-data 资产转移的例子
    asset-transfer-sbe 资产转移的例子
    asset-transfer-secured-agreement 资产转移的例子
  2. chaincode 一些简单的chaincode例子
    ci
    commercial-paper 商业票据例子
  3. fabcar 汽车例子(弃用)
    high-throughput 高吞吐量例子
    interest-rate-swaps 利率例子(弃用)
    off-chain-data 离链例子
  4. scripts changelog.sh
    test-application 两个工具包
    test-network fabric网络的示例

chaincode:里面有Java和go以及其他语言合约链码示例,其中fabcar一个关于汽车的综合示例,提供了Fabric功能的广泛演示。 演示与证书颁发机构进行交互并生成注册证书的过程,用这些身份来查询和更新帐本。

test-network :里面是测试网络的fabric,演示了生成证书,启动网络节点(两个组织,每个组织有2个节点),以及部署、实例化链码,并调用链码进行转账和查询,最后停止整个网络。本示例采用的是solo共识服务机制。


fabric开发需要我们自己配置多节点:crypto-config.yaml 和 configtx.yaml配置文件

configtx.yaml 配置文件一般包括四个部分: Profiles 、Organizations 、Orderer 和Application;crypto-config.yaml 配置如下:

OrderOrgs: 						#定义orderer节点
	- Name:Orderer 				#orderer节点的名称
	  Domain:example.com 		#orderer节点的根域名
	  Specs:
		- Hostname:orderer   	#orderer节点的主机名
PeerOrgs:
	- Name:Org1					#组织1的名称
	  Domain:org1.example.com	#组织1的根域名
	  Template:
		Count:2					#组织1中的节点数量
	  Users:
		Count:1					#组织1中的用户数量
	- Name:Org2					#组织2的名称
	  Domain:org2.example.com	#组织2的根域名
	  Template:
		Count:2					#组织2中的节点数量
	  User2:
		Count:1					#组织2的用户数量
configtx.yaml配置如下:
Profiles:
	TwoOrgsOrdererGenerGenesis:
		Orderer:
			<<:*OrdererDefaults
			Organizations:
				- *OrdererOrg
		Consortiums:
			SampleConsortium:
				Organizations:
					- *Org1
					- *Org2
	TwoOrgsChannel:
		Consortium:SampleConsortium
			Organizations:
				- *Org1
				- *Org2
Organizations:  			#指定 OrdererOrg 和 PeerOrg 的组织信息
	- &OrdererOrg			# OrdererOrg 组织定义
		Name:OrderOrgs		#组织名称
		ID:OrdererMSP 		#组织 MSP标识(ID是引用组织的关键)
		MSPDir:crypto-config/ordererOrganizations/example.com/msp
	-&Org1            #Org1组织定义
		Name:Org1MSP  #组织名称
		ID:Org1MSP    #组织MSP标识
		MSPDir:cryto-config/peerOrganizations/org1.example.com/msp
		AnchorPeers:
			- Host:peer0.org1.example.com
			  Port:7051
	-&Org2
		Name:Org2MSP
		ID:Org2MSP
		MSPDir:cryto-config/peerOrganizations/org2.example.com/msp
		AnchorPeers:
			- Host:peer0.org2.example.com
			  Port:7051
Orderer:&OrdererDefaults
	OrdererType:solo
	Addresses:
		- orderer.example.com:7050
	BatchTimeout:2s
	BatchSize:
		MaxMessageCount:10
		AbsoluteMaxBytes:98 MB
		PreferredMaxBytes:512 KB
	Kafka:
		Brokers:
			- 127.0.0.1:9092
	Organizations:
Application: &ApplicationDefaults
	Organizations:

​ configtx.yaml是Hyperledger Fabric区块链网络运维工具configtxgen用于生成通道创世块或通道交易的配置文件,configtx.yaml的内容直接决定了所生成的创世区块的内容。

​ 主要功能有如下三个:

  1. 生成启动Orderer 需要的初始区块,并支持检查区块内容
  2. 生成创建应用通道需要的配置交易,并支持检查交易内容
  3. 生成锚点Peer 的更新配置交易
  4. Profiles部分。它是默认的,这部分包含一些用于开发或测试场景的示例配置,这些配置涉及fabric目录中的加密部分。configtxgen工具允许通过-profile标签来指定配置文件。Profiles部分可以显式声明所有配置,但是通常都是从默认配置中继承。
  5. Organizations部分。它是默认的,这部分包含示例配置MSP定义的单一引用。对于生产部署,应该删除这部分配置,并以新网络成员的MSP定义来替代它。组织中每一个元素都必须带有锚标签,如&orgName,这些标签可以在Profiles中部分引用。
  6. 默认部分。此部分是Orderer和Application的配置,包括一些属性配置,如BatchTimeout和一般用作继承的基础值。

fabric-go-sdk源码解析

加载配置文件:

​ 通过configFile解析配置文件,然后通过fabsdk.New创建fabric go sdk的入口实例。

func Setup(configFile string, info *SdkEnvInfo) (*fabsdk.FabricSDK, error) {
	// Create SDK setup for the integration tests
	var err error
	sdk, err := fabsdk.New(config.FromFile(configFile))
	if err != nil {
		return nil, err
	}

	// 为组织获得Client句柄和Context信息
	for _, org := range info.Orgs {
		org.orgMspClient, err = mspclient.New(sdk.Context(), mspclient.WithOrg(org.OrgName))
		if err != nil {
			return nil, err
		}
		orgContext := sdk.Context(fabsdk.WithUser(org.OrgAdminUser), fabsdk.WithOrg(org.OrgName))
		org.OrgAdminClientContext = &orgContext

		// New returns a resource management client instance.
		resMgmtClient, err := resmgmt.New(orgContext)
		if err != nil {
			return nil, fmt.Errorf("根据指定的资源管理客户端Context创建通道管理客户端失败: %v", err)
		}
		org.OrgResMgmt = resMgmtClient
	}

	// 为Orderer获得Context信息
	ordererClientContext := sdk.Context(fabsdk.WithUser(info.OrdererAdminUser), fabsdk.WithOrg(info.OrdererOrgName))
	info.OrdererClientContext = &ordererClientContext
	return sdk, nil
}
创建通道:

​ 使用用户账号创建fabric go sdk的通道客户端,以便进行fabric链码的调用和查询。使用sdk.ChannelContext创建channelProvider,需要指定channelID和用户User1,然后通过channel.New创建通道客户端,这个通道客户端就是调用channelID对应channel上链码的channel client。

func CreateAndJoinChannel(info *SdkEnvInfo) error {
	fmt.Println(">> 开始创建通道......")
	if len(info.Orgs) == 0 {
		return fmt.Errorf("通道组织不能为空,请提供组织信息")
	}

	// 获得所有组织的签名信息
	signIds := []msp.SigningIdentity{}
	for _, org := range info.Orgs {
		// Get signing identity that is used to sign create channel request
		orgSignId, err := org.orgMspClient.GetSigningIdentity(org.OrgAdminUser)
		if err != nil {
			return fmt.Errorf("GetSigningIdentity error: %v", err)
		}
		signIds = append(signIds, orgSignId)
	}

	fmt.Println(">> 创建通道")
	// 创建通道
	if err := createChannel(signIds, info); err != nil {
		return fmt.Errorf("Create channel error: %v", err)
	}

	fmt.Println(">> 创建通道成功")

	fmt.Println(">> 加入通道......")
	for _, org := range info.Orgs {
		// 加入通道
		// Org peers join channel
		if err := org.OrgResMgmt.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer0.example.com")); err != nil {
			return fmt.Errorf("%s peers failed to JoinChannel: %v", org.OrgName, err)
		}
	}
	fmt.Println(">> 加入通道成功")
	return nil
}

创建链码的生命周期

安装Fabric链码使用资源管理客户端的InstallCC接口,需要指定resmgmt.InstallCCRequest以及在哪些peers上面安装。resmgmt.InstallCCRequest指明了链码ID、链码路径、链码版本以及打包后的链码。

打包链码需要使用到链码路径CCPath和GoPath,GoPath即本机的$GOPATH,CCPath是相对于GoPath的相对路径,如果路径设置不对,会造成sdk找不到链码。

func CreateCCLifecycle(info *SdkEnvInfo, sequence int64, upgrade bool, sdk *fabsdk.FabricSDK) error {
	if len(info.Orgs) == 0 {
		return fmt.Errorf("the number of organization should not be zero.")
	}
	// Package cc
	fmt.Println(">> 开始打包链码......")
	label, ccPkg, err := packageCC(info.ChaincodeID, info.ChaincodeVersion, info.ChaincodePath)
	if err != nil {
		return fmt.Errorf("pakcagecc error: %v", err)
	}
	packageID := lcpackager.ComputePackageID(label, ccPkg)
	fmt.Println(">> 打包链码成功")

	// Install cc
	fmt.Println(">> 开始安装链码......")
	if err := installCC(label, ccPkg, info.Orgs); err != nil {
		return fmt.Errorf("installCC error: %v", err)
	}

	// Get installed cc package
	if err := getInstalledCCPackage(packageID, info.Orgs[0]); err != nil {
		return fmt.Errorf("getInstalledCCPackage error: %v", err)
	}

	// Query installed cc
	if err := queryInstalled(packageID, info.Orgs[0]); err != nil {
		return fmt.Errorf("queryInstalled error: %v", err)
	}
	fmt.Println(">> 安装链码成功")

	// Approve cc
	fmt.Println(">> 组织认可智能合约定义......")
	if err := approveCC(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint); err != nil {
		return fmt.Errorf("approveCC error: %v", err)
	}

	// Query approve cc
	if err := queryApprovedCC(info.ChaincodeID, sequence, info.ChannelID, info.Orgs); err != nil {
		return fmt.Errorf("queryApprovedCC error: %v", err)
	}
	fmt.Println(">> 组织认可智能合约定义完成")

	// Check commit readiness
	fmt.Println(">> 检查智能合约是否就绪......")
	if err := checkCCCommitReadiness(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs); err != nil {
		return fmt.Errorf("checkCCCommitReadiness error: %v", err)
	}
	fmt.Println(">> 智能合约已经就绪")

	// Commit cc
	fmt.Println(">> 提交智能合约定义......")
	if err := commitCC(info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint); err != nil {
		return fmt.Errorf("commitCC error: %v", err)
	}
	// Query committed cc
	if err := queryCommittedCC(info.ChaincodeID, info.ChannelID, sequence, info.Orgs); err != nil {
		return fmt.Errorf("queryCommittedCC error: %v", err)
	}
	fmt.Println(">> 智能合约定义提交完成")

	// Init cc
	fmt.Println(">> 调用智能合约初始化方法......")
	if err := initCC(info.ChaincodeID, upgrade, info.ChannelID, info.Orgs[0], sdk); err != nil {
		return fmt.Errorf("initCC error: %v", err)
	}
	fmt.Println(">> 完成智能合约初始化")
	return nil
}

初始化链码

func (t *SdkEnvInfo) InitService(chaincodeID, channelID string, org *OrgInfo, sdk *fabsdk.FabricSDK) error {
	handler := &SdkEnvInfo{
		ChaincodeID: chaincodeID,
	}
	//prepare channel client context using client context
	clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(org.OrgUser), fabsdk.WithOrg(org.OrgName))
	// Channel client is used to query and execute transactions (Org1 is default org)
	var err error
	t.Client, err = channel.New(clientChannelContext)
	if err != nil {
		return nil
	}
	handler.Client = t.Client
	return nil
}
posted @ 2021-11-04 11:58  panda's  阅读(527)  评论(0编辑  收藏  举报