一.区块链从何而来?如何简单的理解?
区块链是比特币的核心技术,而比特币最初是以按照货币来设计的,所以先阐述下货币的三个主要功能
-
交易媒介->货币可以用来购买产品或者服务
-
记账单位->可以作为价值衡量的手段
-
价值储藏->跨时间段的购买力和储藏
区块链即满足了第二点,所以我们可以把它看做一个自带对账功能的账本。
区块链属于一种去中心化的记录技术。参与到系统上的节点,可能不属于同一组织、彼此无需信任;区块链数据由所有节点共同维护,每个参与维护节点都能复制获得一份完整记录的拷贝。
那么区块链又跟传统的记账技术有什么区别呢?
其特点应该包括:
- 维护一条不断增长的链,只可能添加记录,而发生过的记录都不可篡改;
- 去中心化,或者说多中心化,无需集中的控制而能达成共识,实现上尽量分布式;
- 通过密码学的机制来确保交易无法抵赖和破坏,并尽量保护用户信息和记录的隐私性。
- 可以将智能合约跟区块链结合到一起,让其提供除了交易(比特币区块链已 经支持简单的脚本计算) 功能外更灵活的合约功能,执行更为复杂的操作。
区块链的基本概念
- 交易(Transaction) :一次操作,导致账本状态的一次改变,如添加一条记录;
- 区块(Block) :记录一段时间内发生的交易和状态结果,是对当前账本状态的一次共识;
- 链(Chain) :由一个个区块按照发生顺序串联而成,是整个状态变化的日志记录。
一句话解释
可以把区块链看作是一个==状态机==,则每次交易就是试图改变一次状态,参与者对于区块中所有交易内容导致状态改变的结果达成共识后生成区块,而区块之间以密码学特征(如Hash值)方式按先后顺序链接起来,形成以区块为基本单元的"链",并且将该"链"在区块链网络参与节点间复制和共享,同时链上内容依据不同的共识机制由参与节点组成的网络集体维护。
分类
根据参与者的不同,可以分为公开链、联盟链和私有链。
公开链
顾名思义,任何人都可以参与使用和维护,典型的如比特币区块链,信息是完全公开的。
私有链
则是集中管理者进行限制,只能得到内部少数人可以使用,信息不公开。
联盟链
则介于两者之间,由若干组织一起合作维护一条区块链,该区块链的使用必须是有权限的管理,相关信息会得到保护,典型如银联组织。
区块链涉及技术
区块链涉及到的领域比较杂,包括分布式、存储、密码学、心理学、经济 学、博弈论、网络协议等。
密码学相关技术
包括 hash 算法与摘要、加密算法、数字签名和证书、PKI 体系、Merkle 树、同态加密等后面涉及到再介绍,忘记介绍了就自行百度。
分布式共识算法
如比特币基于博弈论提出了工作量证明”(Proof of Work) 策略等。让恶意破坏的参与者损失经济利益,从而保证大部分人的合作。PoS、DPoS 、Casper 、Paxos、拜占庭、 PBFT、二阶段提交、三阶段提交、Gossip等。等后面涉及到相关的再介绍,忘记介绍了就自行百度。
好,认识了以上基本概念,理解我们的重点Hyperledger-Fabric就容易多了
Hyperledger-Fabric
IBM主推的区块链开源项目
直奔主题,看下Fabric的架构图
备注: 核心组件:
1.节点(Peer)
在网络中具有一定功能的服务或者软件。它在Fabric网络中,主要负责接收交易请求。维护账本,一般都是运行在容器。节点之间通过gRPC进行通信。 在Fabric中,Peer主要有三种类型 Endorser(背书节点):负责对交易提案进行检查背书。 Committer(确认节点):负责检查交易请求,执行交易,并维护区块链和账本结构。
2.排序者(Orderer)
负责对收到的交易在网络中进行全局排序。
3.CA节点
负责对网络中的成员身份进行管理
目前采用的事数字证书机制,CA节点实现了PKI服务。
数字签名和数字证书
数字签名 类似在纸质合同上签名确认合同内容,数字签名用于证实某数字内容的完整性和来源。 A 发给 B一个文件。A先对文件进行摘要,然后用自己的私钥进行加密,将文件和加密串都 发给 B。B 收到后文件和加密串,用A的公钥来解密加密串,得到原始的数字摘要,跟对文 件进行摘要后的结果进行比对。如果一致,说明该文件确实是 A 发过来的,并且文件内容没 有被修改过。
数字证书用来证明某个公钥是谁的。 对于数字签名应用来说,很重要的一点就是公钥的分发。一旦公钥被人替换,则整个安全体 系将被破坏掉。 怎么确保一个公钥确实是某个人的原始公钥? 这就需要数字证书机制。
数字证书就是像一个证书一样,证明信息和合法性。由证书认证机构 (Certification Authority,CA) 来签发。
数字证书内容可能包括版本、序列号、签名算法类型、签发者信息、有效期、被签发人、签 发的公开密钥、CA 数字签名、其它信息等等。 其中,最重要的包括 签发的公开密钥 、 CA 数字签名 两个信息。因此,只要通过这个证书就能 证明某个公钥是合法的,因为带有 CA 的数字签名。 更进一步地,怎么证明 CA 的签名合法不合法呢? 类似的,CA 的数字签名合法不合法也是通过 CA 的证书来证明的。
PKI 体系 PKI (Public Key Infrastructure)
它不是某一种技术,而是综合多种密码学手段来实现安 全可靠传递消息和身份确认的一个框架和规范。 一般情况下,包括如下组件: CA(Certification Authority) :负责证书的颁发和作废,接收来自 RA 的请求; RA(Registration Authority):对用户身份进行验证,校验数据合法性,负责登记,审 核过了就发给 CA; 证书数据库:存放证书,一般采用 LDAP 目录服务,标准格式采用 X.500 系列。
CA是最核心的组件,主要完成对公钥的管理。密钥有两种 类型:用于签名和用于加解密,对应称为 签名密钥对 和 加密密钥对 。 用户基于 PKI 体系要申请一个证书,一般可以由 CA 来生成证书和私钥,也可以自己生成公 钥和私钥,然后由 CA 来对公钥进行签发。
4.组织
拥有共同信任的根证书(如根CA证书)的成员的集合。 Fabric组织中的成员由于拥有共同的信任根,所以可以交换比较敏感的内容。 组织一般包括名称,ID,MSP信息,管理策略,认证采用的密码类型等信息。 多个组织为了便于沟通,可以加入同一通道。如三家银行,一共三个组织,两两加入到同一个通道中彼此就可以进行相关的数据交互。
5.联盟
若干个组织的集合
6.交易
可以把Fabric中的每一次请求都看成一次交易,要构造一次交易,首先要创建交易提案,当一个交易提案获得足够的背书支持的时候,再构造出合法的交易请求,然后发送给排序节点进行排序,排序完成后,会广播到网络中的各个节点进行确认,如果节点对交易进行本地验证通过,则更新本地账本。
7.区块
一组排序后的交易集合。 区块结构包含区块头(Header),数据(Data),元数据(Metadata). 区块头用于构建区块链结构,包括Number,PerviousHash,DataHash. Number记录区块的序号,PerviousHash记录梭关联的前一个区块的头部域的Hash值,DataHash为本区块Data域的Hash值。 Data域中以Envelope结构记录区块的多个交易信息,这些交易采用Merkle树结构进行组织。
Merkle 树(又叫哈希树)
是一种二叉树,由一个根节点、一组中间节点和一组叶节点组成。 最下面的叶节点包含存储数据或其哈希值,每个中间节点是它的两个孩子节点内容的哈希值,根节点也是由它的两个子节点内容的哈希值组成。
Merkle树的特点是,底层数据的任何变动,都会传递到其父亲节点,一直到树根。 Merkle树的典型应用场景包括: 快速比较大量数据:当两个默克尔树根相同时,则意味着所代表的数据必然相同。 快速定位修改:可以快速定位到发生改变的节点;
8.链码
即智能合约
9.通道
通道与绑定到该通道上的配置和数据(包括交易,账本,链码实例,成员身份等)一起构成一条完整的区块链,这些数据智慧被加入到通道的组织成员所感知和访问到,通道外的成员无法访问到通道内的数据。
Fabric中的通道分为应用通道和系统通道,前者供用户使用,负责承载各种交易。后者则负责对应用通道的管理。
通道再创建的时候,会指定所关联的访问策略,初始化所包括的组织身份(证书范围等,通过MSP校验),锚节点,Orderer服务地址等。通道创建后会构成一条区块链结构,初始区块中包含初始配置相关的信息。
我们创建新的应用通道时,需要向系统通道发送配置交易,同时配置交易所构成的区块,会作为新建应用通道的初始区块。
10.账本(ledger)
可以看作是数据储存结构了,主要包括
状态数据库:记录区块链结构中的交易执行过程。记录最新的状态。
索引数据库:存放索引信息,例如从Hahs,编号索引到区块,从ID索引到交易等。
历史数据库:存放各个状态的历史变化记录。
备注:Fabric的区块链结构主要通过文件系统进行存储;状态数据库目前支持LevelDB,CounchDB.
1.a:校验Proposal签名
1.b:检查是否满足Channel ACL
1.C:模拟执行交易并对结果签名(ESCC)
2.a:校验签名
2.b:比对多个Endorser的回复结果
2.c:检查是否收集了足够的Endorsement
5.a:检查交易结构完整性,签名,是否重复
5.b:校验交易是否符合Endorsement策略(VSCC)
5.c:检查读集合中版本跟账本是否一致
5.d:执行区块中的合法交易,更新账本状态
应用程序通过SDK发送请求道Peer节点(一个或多个)peer节点分别执行交易(通过chaincode),但是并不将执行结果提交到本地的账本中(可以认为是模拟执行,交易处于挂起状态),参与背书的peer将执行结果返回给应用程序(其中包括自身对背书结果的签名)应用程序 收集背书结果并将结果提交给Ordering服务节点Ordering服务节点执行共识过程并生成block,通过消息通道发布给Peer节点,由peer节点各自验证交易并提交到本地的ledger中(包括state状态的变化)
关于通道:通道是有共识服务(ordering)提供的一种通讯机制,类似于消息系统中的发布-订阅(PUB/SUB)中的topic;基于这种发布-订阅关系,将peer和orderer连接在一起,形成一个个具有保密性的通讯链路(虚拟),实现了业务隔离的要求;通道也与账本(ledger)-状态(worldstate)紧密相关;
共识服务与(P1、PN)、(P1、P2、P3)、(P2、P3)组成了三个相互独立的通道,加入到不同通道的Peer节点能够维护各个通道对应的账本和状态;也其实也对应现实世界中,不同业务场景下的参与方,例如银行、保险公司;物流企业、生产企业等实体结构;
整個Fabric的安裝部署就不在这一一细讲了,感兴趣的可以私下找我。这里主要解释怎么进行开发,以及一些对应的解读。
具体步骤如下: 一:新建yjmtest.go
代码如下
package main import ( "encoding/json" "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) type SimpleChaincode struct { } type Person struct { Name string } func main() { err := shim.Start(new(SimpleChaincode)) if err != nil { fmt.Printf("Error starting Simple chaincode: %s", err) } } func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { return shim.Success([]byte("Init OK! params:::")) } func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() fmt.Println("invoke is running " + function) if function == "test" { return t.testYJM(stub, args) } else if function == "delete" { // 删除 return t.deletePerson(stub, args) } else if function == "query" { // 查询 return t.queryPerson(stub, args) } else if function == "create" { // cchuangjian return t.createPerson(stub, args) } return shim.Error("Received unknown function==" + string(function) + ":::args:::" + string(args[0])) } func (t *SimpleChaincode) testYJM(stub shim.ChaincodeStubInterface, args []string) pb.Response { return shim.Success([]byte("-------------Called testYJM hello world------------")) } //新建 func (t *SimpleChaincode) createPerson(stub shim.ChaincodeStubInterface, params []string) pb.Response { sname := params[0] person := &Person{ Name: sname, } personJsonBytes, err := json.Marshal(person) //Json序列号 if err != nil { return shim.Error("") } err = stub.PutState(sname, personJsonBytes) if err != nil { return shim.Error(err.Error()) } return shim.Success([]byte("create Person success!")) } //刪除 func (t *SimpleChaincode) deletePerson(stub shim.ChaincodeStubInterface, params []string) pb.Response { //params := stub.GetStringArgs() key := params[0] //name err := stub.DelState(key) if err != nil { return shim.Error("Failed to delete Person from DB, key is: " + key) } return shim.Success([]byte("delete Person success!")) } //查詢 func (t *SimpleChaincode) queryPerson(stub shim.ChaincodeStubInterface, params []string) pb.Response { //params := stub.GetStringArgs() key := params[0] //name dbPersonBytes, err := stub.GetState(key) var person Person err = json.Unmarshal(dbPersonBytes, &person) //反序列化 if err != nil { return shim.Error("{\"Error\":\"Failed to decode JSON of: " + string(dbPersonBytes) + "\" to Person}") } fmt.Println("query Person from DB, name:" + person.Name) return shim.Success([]byte("query Person success!")) }
对应解读:
目前是基于fabric-1.0.0版本进行开发(最新版本刚出1.0.1),开发方式可以基于SDK和链码。当然由于太新了,SDK还不太成熟,所以就给大家示范了下基于链码的方式,本来也想用java弄的,后面发下坑太大,目前还趟不过去,就选择了相对容易的方式。
链码就是智能合约,也可以理解成一个服务,里面可以有很多我们自定义的方法,我们的自定义方法都是通过invoke来进行调用,除了用if-else來判断对应的方法,当然也可以使用fabric提供的状态机,这里就不在演示。此外可以引用第三方包,放入到import里面就可以使用,所以我们可以把自己的代码打包然后到进来直接使用,go是可以直接从github上引用的,只要把我们自己放在github上包的路径引进来即可。此外,当前链码还可以引用其它链码,所以可以理解为一个服务可以调用另外的服务,所以可以实现相对复杂的业务。
package main import ( "encoding/json" "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) type SimpleChaincode struct { } func main() { err := shim.Start(new(SimpleChaincode)) if err != nil { fmt.Printf("Error starting Simple chaincode: %s", err) } } func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { return shim.Success([]byte("Init OK! params:::")) } func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() fmt.Println("invoke is running " + function) if function == "test" { return t.testYJM(stub, args) } return shim.Error("Received unknown function==" + string(function) + ":::args:::" + string(args[0])) } func (t *SimpleChaincode) testYJM(stub shim.ChaincodeStubInterface, args []string) pb.Response { return shim.Success([]byte("-------------Called testYJM hello world------------")) }
必须要引入的包
"github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer"
shim是fabric和我們的交互接口。ChaincodeStubInterface就就相当与我们平时理解的Dao层,可以进行相关的数据操作。它在/go/src/github.com/hyperledger/fabric/core/chaincode/shim/interfaces.go定义,在chaincode.go中实现。感兴趣的可以自己查看。
shim.ChaincodeStubInterface APIs如下
账本交互API
API| 方法格式 | 简单说明
- GetState | GetState(key String)([]byte,error) | 根據键查詢賬本
- PutState | GetState(key String,value []byte) | 添加
- DelState|...| ...
- GetStateByRange | GetStateByRange(startKey,endKey string)(StateQueryIteratorInterface,error) | 查詢指定范围的键值,返回结果是个迭代器。
- GetByHistoryForKey|GetByHistoryForKey(key String)(QueryIteratorInterface,error) | 返回某个键的所有历史
- GetQueryResult| GetQUeryResult(key String)(QueryIteratorInterface,error) | 支持富查询,不过目前只有CouchDB支持
交易相关API
API| 方法格式 | 简单说明
- GetTxID | GetState(key String)([]byte,error)
参数读取API API| 方法格式 | 简单说明
- GetFunctionAndParameters解析调用的时候传入的参数 | GetFunctionAndParameters() (string, []string) |解析调用的时候传入的参数。
- GetArgs | GetArgs() [][]byte|以byte数组的数组的形式获得传入的参数列表
- GetStringArgs|GetStringArgs() []string |以字符串数组的形式获得传入的参数列表
其它API API| 方法格式 | 简单说明
- InvokeChaincode | InvokeChaincode(chaincodeName string,args[][]byte,channel string) pb.Response |调用其他链码。
注:API详细说明: https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim
二:将代码放到/home/yjm/go/src/github.com/hyperledger/fabric/examples/chaincode/go/yjmtest/路径(为什么要放到这个路径后面会解释,稍安勿躁)
三:进入到/home/yjm/go/src/github.com/hyperledger/fabric/examples/e2e_cli路径下,启动终端。
四:输入network_setup.sh up启动整个区块链网络 启动成功结果如下:
五:/home/yjm/go/src/github.com/hyperledger/fabric/examples/e2e_cli路径下输入如下命令进入到容器:
docker exec -it cli bash
六:输入如下命令安装链码:
peer chaincode install -n yjmtest -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/yjmtest
七:输入如下命令颁发证书(否则后面没有权限实例化链码):
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
八:实例化链码:
peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n testyjm -v 1.0 -c '{"Args":[]}'
九:执行命令进行相关操作
peer chaincode query -C mychannel -n yjmtest -c '{"Function":"create",Args":["yejunmeng"]}'
对应解读:
Fabric的链码分为系统链码和用户链码。
系统链码与一般chaincode一样,有相同的编程模型,只不过系统链码是运行在peer程序中,即其是peer程序的一部分,而一般的chaincode是单独运行在一个容器中的。因此,系统链码是内建在peer程序中且不遵循一般chaincode那样的生命周期。特别的,install,instantiate和upgrade操作也不应用于系统链码。
系统链码区别与一般的chaincode的目的是缩短grpc在peer结点与chaincode之间通信的时间消耗(因为peer结点在一个容器,chaincode是单独的一个容器),并权衡管理上的灵活性。比如,一个系统链码可以仅通过升级peer程序的二进制包来得到升级。系统链码可以用预定义的元素注册并编译到peer程序中,而且不需要有类似于背书策略或背书功能等这样的冗杂的功能。
系统链码被用在fabric中,去操纵整个系统的配置表现,这样的话可以随时把系统改变到合适的状态。
几个系统链码如下:
LSCC Lifecycle system chaincode,处理生命周期请求。我理解的生命周期请求应该指的是一个chaincode的安装,实例化,升级,卸载等对其生命周期起关键作用的一系列操作请求。
CSCC Configuration system chaincode,处理在peer程序端的频道配置。
QSCC Query system chaincode,提供账本查询接口,如获取块和交易信息。
ESCC Endorsement system chaincode,通过对交易申请的应答信息进行签名,来提供背书功能。
VSCC Validation system chaincode,处理交易校验,包括检查背书策略和版本在并发时的控制。
链码操作支持的常见命令参数
参数 | 含义
- 这里是列表文本-C,-chainID| 所面向的通道 -c,ctor| 链码的具体执行参数信息,Json格式 -E,escc | 指定背书系统链码的名称 -l,lang | 链码的 编写语言 -n,name | 链码名称 -o,orderer | orderder服务地址 -P,policy | 链码所关联的背书策略 ..... | ......
关于整个系统链码,以下统称为SCC, 用户链码,以下统称为ACC。
SCC启动占用的是inproc容器,可以当作就是内存里概念上的容器,在core/container/inproccontroller下实现。 ACC启动占用的是docker容器,在core/container/dockercontroller下实现。
chaincode接口是定义在core/chaincode/shim/interfaces.go中的Chaincode,只有两个接口:Init(stub)和Invoke(stub),统一用同文件中的ChaincodeStubInterface接口实例作为唯一的参数。
ChaincodeStubInterface接口唯一的实现是在同目录下的chaincode.go中的ChaincodeStub。
每一个chaincode都会起两个Handler,每一个Handler都是一个以状态机(FSM)驱动的通信机器,在驱动的过程中执行具体的状态事件,完成一个chaincode所需做的事情。一个可以称为服务端Handler(相当于服务端)实现在core/chaincode/handler.go中,另一个可以称为shim端Handler(相当于客户端),实现在core/chaincode/shim/handler.go中。
core/chaincode下的为chaincode服务端的代码,主要用于处理chaincode的请求;core/chaincode/shim下的为chaincode客户端的代码,用于定义供开发者使用的接口和客户端提交申请。
一个chaincode实质还是一个结构体对象,该结构体实现了Chaincode接口。SCC如core/scc/lscc/lscc.go中的LifeCycleSysCC。
ACC如examples/chaincode/go/chaincode_example01/chaincode_example01.go中的SimpleChaincode。
ACC的安装过程
peer chaincode install命令执行安装命令,命令定义在peer/chaincode/install.go中,这也是安装的起点。注:执行install之时,peer结点的基本的模块(包括SCC)都已初始化完毕,channel也已经建立。
install最终所要做的事情,就是将yjmtest的源码包放入docker容器的安装目录中。
一:生成签名申请包 以peer/chaincode/install.go的chaincodeInstall(...)为起点。一路组装数据,至形成SignedProposal,然后将SignedProposal通过cf.EndorserClient.ProcessProposal(...)提交至Endorser服务端。
二:处理安装申请
ProcessProposal(...)所做的主要的两件事就是:
(1)e.simulateProposal(...),模拟执行申请。
(2)e.endorseProposal(...),背书申请执行的结果。但是由于chainID为空,所以install命令不会执行此步(同样,部署时会用到),而是直接以ProposalResponse的形式返回(1)中执行的结果。
三:执行申请
从(2)中继续进入,会看到最后一步调用了e.callChaincode(...)
callChaincode()函数做了三件事:
(1)cccid:=ccprovider.NewCCContext(...),根据传入的参数,创建一个CCContext对象供执行申请所用。
(2)chaincode.ExecuteChaincode(...),执行申请。
ExecuteChaincode(...)函数,在core/chaincode/chaincodeexec.go中定义。
继续chaincode.Execute(ctxt, cccid, cds)依次执行theChaincodeSupport.Launch(...), theChaincodeSupport.Execute(...) 以及引出Fabric中另外一个用的会比较多的ChaincodeSupport。 在core/chaincode/Chaincode_Support中定義。
进入theChaincodeSupport.Launch(ctxt, cccid, spec)
chrte, ok=chaincodeSupport.chaincodeHasBeenLaunched(canName)
chrte.handler.sendExecuteMessage(...)
在sendExecuteMessage(...)中,通过调用handler.triggerNextState(msg,true),ServerHandler将msg发给自身的handler.nextState通道以触发ServerHandler的状态机进入下一个状态
ServerHandler的processStream()收到来自handler.nextState通道的msg,先交给handler.HandleMessage(in)处理,ServerHandler状态机无任何变化,然后handler.serialSendAsync(in,errc)给lscc的ShimHandler发送msg。
lscc的ShimHandler的chatWithPeer()收到msg,交由handler.handleMessage(in)处理,触发beforeTransaction事件函数,该事件函数主要调用同文件中的handleTransaction(...)函数。
handleTransaction(...)函数主要做的就是根据ShimHandler收到的msg生成并初始化一个ChaincodeStub,然后handler.cc.Invoke(stub)调用lscc的Invoke()方法对yjmtest进行安装。
lscc.policyChecker.CheckPolicyNoChannel(...)专门使用了检测未指定Channel的chaincode的函数来检查要安装的yjmtest
最后,lscc.executeInstall(stub, depSpec),调用lscc的函数,依据stub和yjmtest的CDS,执行安装
在executeInstall(...)中,首先,ccpack,err:=ccprovider.GetCCPackage(ccbytes),根据yjmtest被Marshal过的CDS创建一个CDSPackage(core/common/ccprovider/cdspackage.go中定义)。其次,简单的验证了yjmtest的Name和Version。最后,调用ccpack.PutChaincodeToFS()将example02源码写入文件系统。
ioutil.WriteFile(path, ccpack.buf, 0644)将CDSPackage中成员buf写入path指定的地方。
至此,yjmtest的安装申请执行完毕。
安裝完成后,会执行ShimHandler的事件函数handleTransaction(...)
将nextStateMsg赋值为ChaincodeMessage_COMPLETED类型的消息,并执行defer中的handler.triggerNextState(nextStateMsg,send)将该消息发送给自己的状态机。ShimHandler只将ChaincodeMessage_COMPLETED消息发送给ServerHandler。
ServerHandler收到ChaincodeMessage_COMPLETED消息,通知仍处于等待之中的Execute(...)函数。
Execute(...)函数结束之后,会调用core/endorser/endorser.go中的callChaincode(...),chaincode.ExecuteChaincode(...)执行完毕。
ACC实例化
比较复杂,自己也还没搞透,就先不讲了。
当整个Fabric网络启动的时候回去读取docker-compose-cli.yaml
docker-compose-cli.yaml内容如下
# Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # version: '2' services: orderer.example.com: extends: file: base/docker-compose-base.yaml service: orderer.example.com container_name: orderer.example.com peer0.org1.example.com: container_name: peer0.org1.example.com extends: file: base/docker-compose-base.yaml service: peer0.org1.example.com peer1.org1.example.com: container_name: peer1.org1.example.com extends: file: base/docker-compose-base.yaml service: peer1.org1.example.com peer0.org2.example.com: container_name: peer0.org2.example.com extends: file: base/docker-compose-base.yaml service: peer0.org2.example.com peer1.org2.example.com: container_name: peer1.org2.example.com extends: file: base/docker-compose-base.yaml service: peer1.org2.example.com cli: container_name: cli image: hyperledger/fabric-tools tty: true environment: - GOPATH=/opt/gopath - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_LOGGING_LEVEL=DEBUG - CORE_PEER_ID=cli - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 - CORE_PEER_LOCALMSPID=Org1MSP - CORE_PEER_TLS_ENABLED=true - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp working_dir:/opt/gopath/src/github.com/hyperledger/fabric/peer command: /bin/bash -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT' volumes: - /var/run/:/host/var/run/ - ../chaincode/go/:/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts depends_on: - orderer.example.com - peer0.org1.example.com - peer1.org1.example.com - peer0.org2.example.com - peer1.org2.example.com
下面就来解读一下network_setup.sh脚本做了什么 脚本内容如下:
#!/bin/bash # # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # UP_DOWN="$1" CH_NAME="$2" CLI_TIMEOUT="$3" IF_COUCHDB="$4" : ${CLI_TIMEOUT:="10000"} COMPOSE_FILE=docker-compose-cli.yaml COMPOSE_FILE_COUCH=docker-compose-couch.yaml #COMPOSE_FILE=docker-compose-e2e.yaml function printHelp () { echo "Usage: ./network_setup <up|down> <\$channel-name> <\$cli_timeout> <couchdb>.\nThe arguments must be in order." } function validateArgs () { if [ -z "${UP_DOWN}" ]; then echo "Option up / down / restart not mentioned" printHelp exit 1 fi if [ -z "${CH_NAME}" ]; then echo "setting to default channel 'mychannel'" CH_NAME=mychannel fi } function clearContainers () { CONTAINER_IDS=$(docker ps -aq) if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" = " " ]; then echo "---- No containers available for deletion ----" else docker rm -f $CONTAINER_IDS fi } function removeUnwantedImages() { DOCKER_IMAGE_IDS=$(docker images | grep "dev\|none\|test-vp\|peer[0-9]-" | awk '{print $3}') if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" = " " ]; then echo "---- No images available for deletion ----" else docker rmi -f $DOCKER_IMAGE_IDS fi } function networkUp () { if [ -f "./crypto-config" ]; then echo "crypto-config directory already exists." else #Generate all the artifacts that includes org certs, orderer genesis block, # channel configuration transaction source generateArtifacts.sh $CH_NAME fi if [ "${IF_COUCHDB}" == "couchdb" ]; then CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1 else CHANNEL_NAME=$CH_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE up -d 2>&1 fi if [ $? -ne 0 ]; then echo "ERROR !!!! Unable to pull the images " exit 1 fi docker logs -f cli } function networkDown () { docker-compose -f $COMPOSE_FILE down #Cleanup the chaincode containers clearContainers #Cleanup images removeUnwantedImages # remove orderer block and other channel configuration transactions and certs rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config } validateArgs #Create the network using docker compose if [ "${UP_DOWN}" == "up" ]; then networkUp elif [ "${UP_DOWN}" == "down" ]; then ## Clear the network networkDown elif [ "${UP_DOWN}" == "restart" ]; then ## Restart the network networkDown networkUp else printHelp exit 1 fi
目前存在的一些问题以及解决办法
传统的比特币的交易网络最为人诟病的一点便是交易性能:全网每秒 7 笔的交易速度,远低于传统的金融交易系统;同时,等待 6 个块的可信确认导致约 1 个小时的最终确认时间。
闪电网络提出的思路十分简单
将大量交易放到比特币区块链之外进行。该设计最早是 2015 年 2 月在论文《The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments》 中提出。 比特币的区块链机制自身提供了很好的可信保障,但是很慢;另一方面考虑,对于大量的小额交易来说,是否真实需要这么高的可信性?闪电网络通过智能合约来完善链下的交易渠道。
核心的概念主要有两个:RSMC(Recoverable Sequence Maturity Contract) 和 HTLC(Hashed TimelockContract)。前者解决了链下交易的确认问题,后者解决了支付通道的问题。
RSMC
Recoverable Sequence Maturity Contract,中文可以翻译为“可撤销的顺序成熟度合同”。主要原理类似准备金机制。 我们先假定交易双方之间存在一个“微支付通道”(资金池)。双方都预存一部分资金到“微支付通道”里,之后每次交易,就对交易后的资金分配方案共同进行确认,同时签字作废旧的版本。当需要提现时,将最终交易结果写到区块链网络中,被最终确认。可以看到,只有在提现时候才需要通过区块链。
任何一个版本的方案都需要经过双方的签名认证才合法。任何一方在任何时候都可以提出提 现,提现需要提供一个双方都签名过的资金分配方案(意味着肯定是某次交易后的结果) 。 在一定时间内,如果另外一方提出证明表明这个方案其实之前被作废了(非最新的交易结 果) ,则资金罚没给质疑成功方。这就确保了没人会拿一个旧的交易结果来提现。 另外,即使双方都确认了某次提现,首先提出提现一方的资金到账时间要晚于对方,这就鼓 励大家尽量都在链外完成交易。
HTLC
微支付通道是通过 Hashed Timelock Contract 来实现的,中文意思是“哈希的带时钟的合约”。这个其实就是限时转账。理解起来其实也很简单,通过智能合约,双方约定转账方先冻结一笔钱,并提供一个哈希值,如果在一定时间内有人能提出一个字符串,使得它哈希后的值跟已知值匹配(实际上意味着转账方授权了接收方来提现) ,则这笔钱转给接收方。
进一步推广,甲想转账给丙,丙先发给甲一个哈希值。甲可以跟先乙签订一个合同,如果你在 一定时间内能告诉我一个暗语,我就给你多少钱。乙于是跑去跟丙签订一个合同,如果你告 诉我那个暗语,我就给你多少钱。丙于是告诉乙暗语,拿到乙的钱,乙又从甲拿到钱。最终 达到结果是甲转账给丙。这样甲和丙之间似乎构成了一条完整的虚拟的“支付通道”。
HTLC 的机制可以扩展到多个人。
RSMC 保障了两个人之间的直接交易可以在链下完。
HTLC 保障了任意两个人之间的转账都可以通过一条“支付”通道来完成。
整合这两种机制,就可以实现任意两个人之间的交易都可以在链下完成了。 在整个交易中,智能合约起到了中介的重要角色,而区块链则确保最终的交易结果被确认。
Fabric主要还是运行在容器上,但目前容器在测试环境有用,但据说生产环境用的比较少,容器的生命周期不太便于管理,也没有很好的稳定性。但最近阿里好像开源了一个自研的容器技术Pouch,还没看过。暂时不知道会带来什么样的影响。
此外由于Fabric相对比较新,学习资料,参考文档这些还比较少,又是基于分布式来运行,开发调试起来确实会比较困难。本身又是用Go写的,Go的项目管理工具又没有那么成熟,有时候你想导个包都要折腾半天。开发工具也参差不齐,目前感觉还没遇到完美的开发工至于为什么没有用java来讲,感觉没必要,具,都是几个混起来用,心累。
本身Fabric就是用Go写的,也是基于容器,而容器技术本身也是用Go来写的,所以有一个原生的go-dockerclient,可以在fabric-release\core\container\dockercontroller\中的dockercontroller.go中的import包里可以找到。此外Fabric中用到的corba和viper都可以研究一下,在Fabric中用到的确实很多。
最后
站在岸上的人学不会游泳。去做,无论对错,那都会是你的收获。不做,很多事只会与你无关。共勉