1. fabric服务架构
api: 服务的grpc接口和http接口
sdk:不同开发语言的软件工具开发包 fabric-go-sd
事件: 链码中定义某些事件来进行区块链操作
身份:联盟链的身份控制(用户身份注册证书,交易签名证书,加密传输的tls证书)
账本:区块高度 区块哈希查询账本 交易id查询区块以及交易
交易: 交易背书==》交易排序(区块打包)==》交易分发
智能合约:交易调用智能合约服务
2.fabric网络拓扑结构
包括:
客户端
peer节点(锚节点/背书节点/提交节点)
Orderer
CA(可选)
3.交易流程
客户端构造交易提案,发送给一个或多个Peer节点,交易提案中包含本次交易要调用的合约标识、合约方法和参数信息以及客户端签名等。(根据背书策略)
peer背书节点收到交易提案后会模拟交易执行,然后将原始交易提案和执行结果打包到一起,进行签名打包发回客户端,其中模拟执行交易期间产生的数据修改不会写到账本上
客户端收到各个peer节点应答后打包到一起组成一个交易签名,提交交易给Orderer
Orderer对接收到的交易进行共识排序,然后按照区块生成策略,将一批交易打包到一起,生成新的区块,广播区块给peer节点。
peer节点收到区块后,对区块交易进行校验,检查交易的输入输出是否服务当前区块链状态,完成后将区块,写入账本(世界状态)
-
同步区块信息到其他节点并更新世界状态
注意:peer记账节点更新世界状态如果存在错误的交易信息就不会更新世界状态但是会存入区块信息中
4.peer
-
读集,写集和版本号
读集:读取的已经提交的状态值,这里的已提交是指“确认高度"区块之前的数据属于已提交的数据,对fabric来说就是上一个被提交的区块的状态值,这样做是为了防止区块链分叉。
写集:将要更新的状态键值对或状态键值对的删除标记,这里如果涉及同一个数据的多次更新则只会保留最后一次的值,即交易是最小的原子单位不能再细分(与数据库事务的原子性相似)
版本号:由区块高度和交易编号组成,用来标记一笔交易中读的交易所对应的版本号,用于验证读操作是否有效。
-
交易验证:包括签名验证、权限验证等 最重要的是交易读写集的验证
交易读写集的验证就是判断交易中读集的版本号是否等于世界状态的版本号,这里的世界状态受已提交的交易影响,也受到未提交交易即当前区块中该笔交易之前的交易的影响。例如:一个区块中的两个交易1、2,1对某个键值对进行了修改,2对该修改的键值对进行读那么交易2就是一笔无效交易。可以看出区块验证保证了写优先,读到的数据一定是最新的数据。 例如:
世界状态:(k1,1,v1)(k2,1,v2) (k3,1,v3) (k4,1,v4)
交易1: write(k1,v1') write(k2,v2')
交易2:Read(k1),write(k3,v3') 交易2无效(因为读取的k1的值(交易1中存在修改)和世界状态的不一样)
交易3:write(k2,v2'')
交易4: write(k2,v2''') read(k3) 由于交易2无效所以交易4有效
交易5:write(k5,v5) read(k4)
1.4源码的事务控制 /fabric/core/ledger/kvledger/txmgmt/txmgr/txmgr.go
1.4源码的读写集 /fabric/core/ledger/kvledger/txmgmt/rwsetutil/rwset_builder.go
-
世界状态: leveldb存储的是区块索引,couchDB支持模糊查询
世界状态存储是的是交易执行后的所有键值的最新值,它是区块链中的一个快照,查询区块的时候可以提升链码的执行效率(不需要每次查询区块),peer节点每次启动的时候就会检查世界状态与区块存储的内容是否一致,支持levedb和couchdb,
1.4源码leveldb:/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb
1.4源码couchb: /fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb
-
历史状态存储可选
记录某个键在某个区块中的某条交易中改变,只会记录改变的动作不会记录具体的改变,读取的时候首先获取键值对的改变位置,然后再从区块中读取交易。使用levelDB记录
1.4源码位置:/fabric/core/ledger/kvledger/history/historydb/historydb.go
-
区块存储
区块以文件的形式存储在系统中(文件名为blockfile_xxxxxx),区块大小可以按照指定大小修改,账本的最大容量等于
块大小 * 1000000
1.4源码位置:/fabric/common/ledger/blockledger/ledger.go
存储位置参数 peer.fileSystemPath
默认存储位置:/var/hyperledger/production/ledgersData/chains/chains/通道名
-
区块读取
目前已实现的有区块文件流、区块流及区块迭代器这三个类,分别用于读取文件块、从文件块中读取区块、在整个链条上读取区块等。
-
区块索引:
用于快速定位区块,查询条件(索引值)可以是区块高度、区块哈希、交易哈希等,区块的位置由区块文件编号、文件内偏移量、区块数据长度标记的(从那个文件读,从文件的哪个位置开始读,读多少的长度)
-
区块提交
首先把区块保存到区块文件中,peer会对区块中的交易进行验证,保存后更新区块索引及世界状态(只有有效交易才会更新世界状态),更新历史状态(可选如果智能合约有需要的话)这三步的同步非常重要,在peer节点启动时都会检查这三者是否同步,如果不一致则以区块文件为标准进行同步。 注意:即使是无效交易也会被存储在区块中,与区块哈希有关,因为区块哈希是在orderere产生的如果在peer处删除那么区块哈希就会改变,但是不会改变世界状态。
账本存储:相当于区块文件和世界状态的存储
5.Orderer
排序服务:
Broadcast的主要功能是接收来自客户端的交易请求,对客户端发送过来的数据格式校验,同事对客户端的权限进行检查,然后尝试请求打包给共识组件进行排序。(处理交易,通道创建和更新,链码实例化和初始化)
1.4源码位置:/fabric/orderer/common/broadcast/broadcast.go Handle
Deliver负责处理peer或者客户端获取区块文件请求,客户端发来请求之后,检查对应通道的orderer节点中是否存在,当网络中生成新的区块,或者客户端视图获取通道内的创世区块或具体区块的时候都是通过Orderer的Deliver模块进行处理,客户端来获取区块文件的时候,或发送一个区块序列号区间给Deliver,而Deliver会根据区间来从自己本地读取区块文件,每次自增+1的形式给客户端返回指定区块
1.4源码位置 /fabric/common/deliver/deliver.go Handle
共识组件:
solo:只有一个orderer节点通过golang的mutext,将排序函数加锁和解锁,才用简单的数组模式。不涉及Order节点之间的同步数据。一般用于测试环境
1.4源码位置:fabric/orderer/consensus/solo/chain.go main
raft:存在2n+1个orderer节点(至少3个),fabric的raft机制中存在领导者(Leader),跟随者和候选者三种角色,领导者负责区块排序,跟随者将会实时从领导者处接收区块文件使自己的状态和领导者的状态保持一致。候选者是指通道内的所有Orderer节点(没有领导节点的时候)
如果领导节点宕机,内部会通过选举产生新的领导节点(选举通过内部投票的方式),领导节点主要负责同步区块文件,位置内部稳定。当存活的orderer小于50%则整个网络无法进行交易。
存在问题:当网络流量大的时候,跟随者需要实时同步数据,链路维护以及最新排序状态,会占用大部分网络流量。
1.4源码位置:fabric/orderer/consensus/etcdraft/chain.go serveRequest
kafka: order节点解析数据封装为kafka的喜爱,发送交易到kafka集群,与kafka建立长连接,定期交换数据保持连接稳定性和消息的及时性,kafka会为每个通道建立对应的topic 用来区分通道和区块链的关系,kafka集群部署,不同kafka之间通过kafka自己内部进行同步 。order节点通过监听方式,当区块文件高度超过时间或者区块高度得到满足之后,就会拉取交易到orderer节点本地,然后打包分发给peer节点(原理和raft类似)
1.4源码位置:fabric/orderer/consensus/kafka/chain.go startThread
多通道数据隔离:
1.4源码位置:fabric/orderer/common/multichannel/registrar.go Initialize
6.智能合约(链码)
链码
一个fabric中间件,自己拥有独立的docker执行环境,与背书节点grpc连接
生命周期:
打包,安装,实例化,升级,交互
链码交互流程:
1.4 源码go语言链码docker打包: /fabric/core/chaincode/platforms/golang/platform.go GenerateDockerBuild
系统链码
系统链码在 peer 服务启动时随 peer 节点注册,同 peer 节点一起运行。系统链码为固定的 5 个:lscc、qscc、cscc、vscc、escc,这 5 个链码功能固定,分别用于链码生命周期管理、区块/交易查询、通道配置管理、交易背书和交易验证。
1.LSCC:链码实例化,升级
LSCC 用于管理链码的生命周期——在peer上安装、在通道上部署和升级、用户从运行中的链码获取信息。它提供了八个方法: install, deploy, upgrade, getid, getdepspec,getccdata, getchaincodes, getinstalledchaincodes。
1.4源码位置:fabric/peer/chaincode/instantiate.go
1.4源码位置:fabric/peer/chaincode/upgrade.go
2. CSCC:某条链的配置
CSCC 管理peer上通道相关的信息以及执行通道配置交易。它提供五个方法:JoinChain,
GetConfigBlock, GetConfigTree,SimulateConfigTreeUpdate, GetChannels。
3. QSCC:查询账本存储
QSCC 将特定的方法暴露给用户,使得用户可以查询在block storage中存储的区块和交易。它提供五个方法:(i) GetChainInfo, (ii) GetBlockByNumber, (iii) GetBlockByHash, (iv) GetTransactionByID, (v) GetBlockByTxID。
1.4源码位置:fabric/peer/chaincode/query.go
-
ESCC:交易模拟后的结果进行打包签名
背书节点在执行交易之后,将它的前面放在transaction response message中。其中,transaction response message也包括交集执行的结果,如交易状态、合约事件和read/write set等。一个调用功能可以包含5-7个参数,即Header、ChaincodeProposalPayload、ChaincodeID、Response、simulation result、events、payload visibility。
1.4源码位置:fabric/core/endorser/endorser.go
5. VSCC:交易验证 根据合约的背书策略验证每个交易的签名集合
1.4源码位置:fabric/core/committer/txvalidator/validator.go
链码编程接口
Init() : 链码初始化接口
Invoke():链码交易接口
1.4 源码位置 /fabric/core/chaincode/shim/interfaces.go Chaincode