Fabric源码分析-账本机制01

超级账本的最终目的是将交易记录打包为区块保存到账本中,账本模块用来保存区块,检索区块,记录账本的最终状态。本节介绍了Peer账本的初始化过程。

1. 账本对象

Fabric的orderer会将交易信息打包为Block,Peer会对Block进行校验,然后保存起来,最后修改key的最终状态,在此过程中,还会记录历史信息。因此,对于一个Peer账本来说,需要完成以下功能:

  • 使用Ledger维护整个账本,包括区块的校验,写入,查询
  • 使用LedgerProvider维护账本的通用配置,最终为Channel生成Ledger
  • 使用BlockStore保存区块数据
  • 使用VersionedDBProvider维护最终状态
  • 使用HistoryDBProvider维护历史记录
账本相关对象
账本相关对象

1.1 Ledger

common/ledger/ledger_interface.go中定义了账本Ledger的接口及其基本功能,从中我们可以对Ledger的作用有个概要了解。在Fabric中,一个Channel对应着一个账本,一个账本是包含一条由交易记录组成的区块链,以及交易导致的最终状态数据库和历史数据库。下面是Ledger的主要方法:

  • GetBlockchainInfo() 获取当前账本的区块链信息,主要是区块链的高度,当前区块的Hash值及上一个区块的Hash值
  • GetBlockByNumber(blockNumber) 返回指定编号的区块
  • GetBlocksIterator(startBlockNumber) 获取一个从指定编号开始的迭代器,用于不断获取后续的区块
  • Close() 关闭账本
  • Commit(block) 提交区块

可以看出,Ledger主要用于提交区块,查询区块及区块链信息。其他对象中,ResultsIterator用于迭代器,可以不断查询下一个区块,QueryResult是查询结果,PrunePolicy是账本的修剪策略。

1.2 PeerLedger

PeerLedger在core/ledger/ledger_interface.go中定义,实现了1.1 Ledger中的方法,并且添加了额外的其他方法。在common/ledger中,主要是定义通用的Ledger,在core//ledger中是实现。
PeerLedger类额外实现的功能主要是通过Block中的交易txid/hash值获取区块,初次之外,提供了TxSimulator交易模拟器,QueryExecutor查询器,HistoryQueryExecutor历史查询器等功能。

1.3 PeerLedgerProvider

PeerLedgerProvider 保存了账本的通用信息,用来创建/打开账本。例如,Provider里有保存账本的BlockStoreProvider,状态数据库和历史数据库的Provider,对于新创建的通道Channel1,PeerLedgerProvider为其返回一个PeerLedger实例,用来操作Channel1的账本。从下面的主要方法中就可以看出其功能:

  • Create(genesisBlock) 使用创世块创建一个PeerLedger,创世块中包含了账本的相关配置信息
  • Open(ledgerID) 根据账本ID返回一个PeerLedger,ledgerID就是Channel的名称
  • Exists(ledgerID) 判断账本ID是否存在
  • List() 列出当前的所有账本ID

PeerLedgerProvider中包含几个属性:

  • 保存provider信息的数据库,使用leveldb保存provider的内容,在ledgerProvider文件夹中。
  • blockStoreProvider 会生产BlockStore,定义如何保存区块,目前只有一种实现FSBlockSotre,其将区块数据保存到文件系统中。
  • VersionedDBProvider 提供VersionDB的处理类,用于保存账本key值的最终状态。
  • HistoryDBProvider 提供HistoryDB的处理类,用于保存key的历史记录。

2.账本初始化

使用peer start启动peer节点时,首先会初始化与账本相关的对象,主要代码在ledger_mgmt.go中,主要就是创建一个PeerLedgerProvider。
kvLedger是PeerLedger的实现类,由其创建PeerLedgerProvider,主要逻辑为:

  1. 获取peer.fileSystemPath配置的路径,在内部的ledgersData/ledgerProvider文件夹初始化一个leveldb的数据库,保存到idStore中。idStore用于维护账本id相关信息。
  2. 在保存区块的过程中,为了快速检索区块,需要建立索引,因此,创建了一个indexConfig,指定了需要建立索引的字段数组
  3. 生成blockStoreProvider,指定存储区块的路径,每个区块文件的最大大小,索引配置信息
  4. 初始化VersionedDBProvider,根据配置返回leveldb和couchdb的provider
  5. 初始化HistoryDBProvider
  6. 构造PeerLedgerProvider,准备返回
  7. 最后一步,查看是否存在创建账本时发生崩溃的账本id,如果存在的话,需要恢复,在Create()创建账本时会设置这个flag,内容为账本id,成功创建后会删除这个flag。因此如果中途程序崩溃,会留下这个flag。恢复的逻辑是重新创建KVLedger,使用blockStore从文件中获取blockchaininfo,如果高度为0,说明还没有提交创世块,删除flag即可,如果高度为1,已经提交创世块,使用idStore维护账本id,其他情况说明账本已经创建了。

3. idStore

idStore定义在kv_ledger_provider.go中,内部包含一个用于操作leveldb的成员db,初始化账本的时候,会为provider创建一个leveldb数据库,主要功能:

  1. underConstructionFlag 操作underConstructionLedgerKey,这个key用来记录账本创建过程中是否发生了异常,发生异常时会有补偿机制重建账本。
  2. 在PeerLedgerProvider使用Create(genesisBlock *common.Block)创建PeerLedger时,会将账本的id和创世块内容组成键值对保存在数据库中。
  3. 获取所有的账本id,遍历数据库的key,如果是账本id的前缀,则说明有这个账本,存放在返回的list中。

4.BlockStore

BlockStore定义在common/ledger/blkstorage/blockstorage.go中,只有一个实现fsblkstorage,在文件系统中保存区块。这是账本机制的重要组成部分,PeerLedger的实现kvLedger中的大部分操作都是使用的BlockStore,VersionedDB和HistroyDB。

下面是BlockStore接口的方法,可以看到,主要是添加取款,获取区块链信息,检索区块的功能。

type BlockStore interface {
    AddBlock(block *common.Block) error
    GetBlockchainInfo() (*common.BlockchainInfo, error)
    RetrieveBlocks(startNum uint64) (ledger.ResultsIterator, error)
    ......
    Shutdown()
}

fs_blockstore.go中的fsBlockStore实现了BlockStore,在文件系统中保存区块。需要注意的是fsBlockStore中有一个blockfileMgr,里面负责具体的文件操作。

4.1 blockfileMgr

blockfileMgr定义在blockfile_mgr.go中,用来管理将Block写入文件,建立索引,获取Block。blockfileMgr中有如下几个重要的组件,用来帮助完成存储Block的操作。
blockfileWriter
blockfileWriter用于将数据写入文件,由于fsBlockStore将区块保存到了多个文件中,blockfileWriter记录了写入文件的文件夹和操作当前文件的os.File

checkpointinfo
checkpointInfo定义在blockfile_mgr.go中,用来记录当前最后一个文件的序号,已写入文件的字节数,最后一个Block的编号信息。checkpointinfo会被保存在数据库中,每次初始化时,可以从数据库中读出,更新Block时,需要更新checkpointinfo并更新数据库。

BlockchainInfo
BlockchainInfo记录了区块链的状态,如高度,最后一个Block的Hash等

blockIndex

blockIndex用来建立索引,indexBlock

4.2 提交Block

blockfileMgr的addBlock负责添加Block,主要逻辑为:

  1. 校验区块编号,必须与BlockChain的高度相同,例如:区块链现有3个区块,编号分别为0-1-2,高度为3,那么新添加的Block编号需要是3。
  2. 使用protobuf将Block序列化,Block的结构为
common.Block
  - BlockHeader
      - Number  > 区块编号
      - PrevHash > 上一个区块的Hash
      - DataHash  > BlockData的Hash
  - BlockData 
      - txEnvelope  > 一个交易
        - Payload 
          - Header  > 头
            - ChannelHeader  > 类型 版本和ChannelID
            - SignatureHeader > 签名头
        - Signature  
          - Creator  > 创建者
          - Nonce   >  随机数
  - BlockMetadata

在序列化的过程中,可以提取出Block的交易id列表,元数据等信息,保存在serializedBlockInfo中;还可以计算出区块占用的字节数。

  1. 计算Block的Hash值,逻辑为将Block的编号,上一个Block的Hash和当前区块的DataHash使用ASN.1 编码为byte[],然后再使用SHA-256生成当前区块的Hash值。
  2. 将Block的byte数组(记为A)写入文件,写入内容为B:A的长度+A的内容,那么B的大小就是要写入文件的总大小,由于每个文件有大小限制,如果剩余的文件容量不足以支持写入,就重新创建一个文件并写入。blockfileMgr中维护了checkpointinfo,内部记录了账本当前所处于的文件及offset,每次写入之后,都会修改checkpointinfo信息。
  3. 在写入数据期间,如果发生error,会将文件恢复到写入之前的状态,并抛出异常。
  4. 更新checkpointInfo的信息,并保存到数据库中。
  5. 写入成功后,使用blockIndex,创建索引,这样可以根据block编号,hash值,交易id快速定位到Block所在文件的位置
  6. 添加Block后,BlockChainInfo最后一个Block发生变化,更新这些信息,。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容