filecoin技术架构分析之十四:filecoin源码分析之服务层链同步、共识协议及挖矿

本文作者:杨尉;原创作品,转载请注明出处

[上一篇链接] filecoin技术架构分析之十三:filecoin源码分析之服务层actor及vm

[下一篇链接] filecoin技术架构分析之十五:filecoin源码分析之节点运行逻辑

目录

  • 14.filecoin源码分析之服务层链同步、共识协议及挖矿
    • 14.1 chain
      • 14.1.1 基础结构
      • 14.1.2 链同步
      • 14.1.3 链存储
    • 14.2 consensus
    • 14.3 mining
      • 14.3.1 挖矿的主要逻辑
      • 14.3.2 其他细节源码简析
    • 14.4 服务层之其他服务
      • 14.4.1 消息池
      • 14.4.2 钱包
    • 14.5 插播线下分享

分析基于的源码版本:go-filecoin master a0598a54(2019年3月9日)

14.1 chain同步

14.1.1 基础结构

  • TipIndex 定义定义了tipset的基础结构及方法
▼ package
    chain

▼+TipIndex : struct
    [fields]
   -mu : sync.Mutex
    // 根据id来获取tipset及其状态根
   -tsasByID : tsasByTipSetID
    // 根据父块来获取tipset及其状态根
   -tsasByParentsAndHeight : map[string]tsasByTipSetID

    [methods]
    // 根据id来获取tipset及其状态根
   +Get(tsKey string) : *TipSetAndState, error
    // 根据父块来获取tipset及其状态根
   +GetByParentsAndHeight(pKey string, h uint64) : []*TipSetAndState, error
    // 根据Id判断是否有此tipset
   +Has(tsKey string) : bool
    // 根据父块判断是否有此tipset
   +HasByParentsAndHeight(pKey string, h uint64) : bool
    // 设置tipset和状态根
   +Put(tsas *TipSetAndState) : error
    [functions]
   +NewTipIndex() : *TipIndex

▼+TipSetAndState : struct
    [fields]
    // tipset
   +TipSet : types.TipSet
    // 相当于区块的root cid
   +TipSetStateRoot : cid.Cid

14.1.2 链同步

  • chain同步的接口定义
location: chain/syncer.go

▼ package
    chain

▼+Syncer : interface
    [methods]
    // 处理新区块的接口定义
   +HandleNewBlocks(ctx context.Context, blkCids []cid.Cid) : error

   具体接口实现在location: chain/defalut_syncer.go中
  • 特殊情况的错误
location: chain/reorg.go

    // 如果当前区块头不包含在最新的区块头之上时候,会报此错误
▼ functions
   +IsReorg(curHead types.TipSet, newChain []types.TipSet) : bool

14.1.3 链存储

  • 其中
    • Readstore是一个通用接口
    • Store的设计基本是给ChainSync使用的
location: chain/store.go

▼ package
    chain

▼ constants
    // 用于发布新的区块头的主题"new-head"
   +NewHeadTopic

▼ variables
    // 创世块的key
   +GenesisKey

▼+ReadStore : interface
    [methods]
    // 获取历史区块,通过channel实现
   +BlockHistory(ctx context.Context, tips types.TipSet) : chan interface{}
    // 获取创世区块cid
   +GenesisCid() : cid.Cid
    // 通过cid获取具体的block
   +GetBlock(ctx context.Context, id cid.Cid) : *types.Block, error
    // 通过cid获取具体的block
   +GetTipSetAndState(ctx context.Context, tsKey string) : *TipSetAndState, error
    // 获取最新区块
   +Head() : types.TipSet
    // 最新区块变更事件
   +HeadEvents() : *pubsub.PubSub
    // 最新合约状态
   +LatestState(ctx context.Context) : state.Tree, error
    // 加载chain
   +Load(ctx context.Context) : error
    // 停止
   +Stop()

    // 这个接口只是chain同步使用
▼+Store : interface
    [embedded]
   +ReadStore
    [methods]
   +GetBlocks(ctx context.Context, ids types.SortedCidSet) : []*types.Block, error
   +GetTipSetAndStatesByParentsAndHeight(ctx context.Context, pTsKey string, h uint64) : []*TipSetAndState, error
   +HasAllBlocks(ctx context.Context, cs []cid.Cid) : bool
   +HasBlock(ctx context.Context, c cid.Cid) : bool
   +HasTipSetAndState(ctx context.Context, tsKey string) : bool
   +HasTipSetAndStatesWithParentsAndHeight(ctx context.Context, pTsKey string, h uint64) : bool
    // 存储并更新最新区块信息
   +PutTipSetAndState(ctx context.Context, tsas *TipSetAndState) : error
   +SetHead(ctx context.Context, s types.TipSet) : error

14.2 consensus

  • 主要功能
    • 提供创建选票方法,验证中奖选票方法,确定最终的tipset
    • 将合法的tipset消息取出,生效actor状态
▼ package
    consensus

▶ imports

▼ constants
   +ECPrM : uint64
   +ECV : uint64
   +LookBackParameter

▼ variables
   +AncestorRoundsNeeded
   +ErrInvalidBase
   +ErrStateRootMismatch
   +ErrUnorderedTipSets
   -log
   -ticketDomain : *big.Int

    // Expected实现EC共识
▼+Expected : struct
    [fields]
    // 全局功率表
   +PwrTableView : PowerTableView
   -bstore : blockstore.Blockstore
   -cstore : *hamt.CborIpldStore
   -genesisCid : cid.Cid
   -processor : Processor
   -verifier : proofs.Verifier
    [methods]
    // 比较两个tipset的权重
   +IsHeavier(ctx context.Context, a, b types.TipSet, aSt, bSt state.Tree) : bool, error
    // 建立新的tipset
   +NewValidTipSet(ctx context.Context, blks []*types.Block) : types.TipSet, error
    // 运行状态转换
    // 1 新区块到来的时候出发状态转换(chain sync逻辑)
    // 2 进入后判断tipset的有效性,包括验证选票是否中奖
    // 3 逐一执行消息,切换状态
   +RunStateTransition(ctx context.Context, ts types.TipSet, ancestors []types.TipSet, pSt state.Tree) : state.Tree, error
    // 计算tipset权重
   +Weight(ctx context.Context, ts types.TipSet, pSt state.Tree) : uint64, error
   -runMessages(ctx context.Context, st state.Tree, vms vm.StorageMap, ts types.TipSet, ancestors []types.TipSet) : state.Tree, error
   -validateBlockStructure(ctx context.Context, b *types.Block) : error
   -validateMining(ctx context.Context, st state.Tree, ts types.TipSet, parentTs types.TipSet) : error

▼+Processor : interface
    // 会被RunStateTransition间接掉用,进行状态切换(生效挖矿成功的tipset消息)
    [methods]
    // 从tipset中逐一取出block处理
   +ProcessBlock(ctx context.Context, st state.Tree, vms vm.StorageMap, blk *types.Block, ancestors []types.TipSet) : []*ApplicationResult, error
   +ProcessTipSet(ctx context.Context, st state.Tree, vms vm.StorageMap, ts types.TipSet, ancestors []types.TipSet) : *ProcessTipSetResponse, error

▼ functions
    // 与白皮书描述一致,按照存储功率出块,用以判断是否中奖
   +CompareTicketPower(ticket types.Signature, minerPower uint64, totalPower uint64) : bool
    // 产生随机挑战种子,针对时空证明
   +CreateChallengeSeed(parents types.TipSet, nullBlkCount uint64) : proofs.PoStChallengeSeed, error
    // 生成选票
    // 用上一个区块的时空证明+矿工地址(目前直接用的矿工地址,issue1054讨论中) 生成256bit哈希
   +CreateTicket(proof proofs.PoStProof, minerAddr address.Address) : []byte
    // 判断是否中奖,调用CompareTicketPower
   +IsWinningTicket(ctx context.Context, bs blockstore.Blockstore, ptv PowerTableView, st state.Tree, ticket types.Signature, miner address.Address) : bool, error
    // 实例化Expected
   +NewExpected(cs *hamt.CborIpldStore, bs blockstore.Blockstore, processor Processor, pt PowerTableView, gCid cid.Cid, verifier proofs.Verifier) : Protocol
   -init()

14.3 mining

14.3.1 挖矿的主要逻辑

  • 1 不能将空块最为基准块
  • 2 基于上一个Tipset信息(如果上一个为空块,必须找到空块之前高度最高的Tipset,并记录中间空块数据)和空块数目生成合法的时空证明挑战参数
  • 3 生成时空证明
  • 4 时空证明成功,调用共识协议创建奖票
  • 5 如果奖票中奖,将未打包的消息打包区块
location: mining/working

//这里是挖矿逻辑的真正入口

// Mine implements the DefaultWorkers main mining function..
// The returned bool indicates if this miner created a new block or not.
func (w *DefaultWorker) Mine(ctx context.Context, base types.TipSet, nullBlkCount int, outCh chan<- Output) bool {
    log.Info("Worker.Mine")
    ctx = log.Start(ctx, "Worker.Mine")
    defer log.Finish(ctx)
    // 不能将空块作为基准块挖矿
    if len(base) == 0 {
        log.Warning("Worker.Mine returning because it can't mine on an empty tipset")
        outCh <- Output{Err: errors.New("bad input tipset with no blocks sent to Mine()")}
        return false
    }

    st, err := w.getStateTree(ctx, base)
    if err != nil {
        log.Errorf("Worker.Mine couldn't get state tree for tipset: %s", err.Error())
        outCh <- Output{Err: err}
        return false
    }

    log.Debugf("Mining on tipset: %s, with %d null blocks.", base.String(), nullBlkCount)
    if ctx.Err() != nil {
        log.Warningf("Worker.Mine returning with ctx error %s", ctx.Err().Error())
        return false
    }

    // 基于上一个基准Tipset以及空块数目生成Post随机挑战参数
    challenge, err := consensus.CreateChallengeSeed(base, uint64(nullBlkCount))
    if err != nil {
        outCh <- Output{Err: err}
        return false
    }

    // 生成时空证明
    prCh := createProof(challenge, w.createPoSTFunc)

    var proof proofs.PoStProof
    var ticket []byte
    select {
    case <-ctx.Done():
        log.Infof("Mining run on base %s with %d null blocks canceled.", base.String(), nullBlkCount)
        return false
    case prChRead, more := <-prCh:
        if !more {
            log.Errorf("Worker.Mine got zero value from channel prChRead")
            return false
        }
        copy(proof[:], prChRead[:])
        // 时空证明成功,调用共识协议创建奖票
        ticket = consensus.CreateTicket(proof, w.minerAddr)
    }

    // TODO: Test the interplay of isWinningTicket() and createPoSTFunc()
    // https://github.com/filecoin-project/go-filecoin/issues/1791
    // 调用共识协议确认是否中奖
    weHaveAWinner, err := consensus.IsWinningTicket(ctx, w.blockstore, w.powerTable, st, ticket, w.minerAddr)

    if err != nil {
        log.Errorf("Worker.Mine couldn't compute ticket: %s", err.Error())
        outCh <- Output{Err: err}
        return false
    }

    if weHaveAWinner {
        // 如果中奖将打包消息,生成区块
        next, err := w.Generate(ctx, base, ticket, proof, uint64(nullBlkCount))
        if err == nil {
            log.SetTag(ctx, "block", next)
            log.Debugf("Worker.Mine generates new winning block! %s", next.Cid().String())
        }
        outCh <- NewOutput(next, err)
        return true
    }

    return false
}

14.3.2 其他细节源码简析

  • 消息队列(交易消息集)的处理
location: mining/mqueue.go

▼ package
    mining

▶ imports

▼+MessageQueue : struct
    [fields]
   -senderQueues : queueHeap
    [methods]
    // 取出消息切片,即多条消息
   +Drain() : []*types.SignedMessage
   +Empty() : bool
    // 从队列取出一条消息
   +Pop() : *types.SignedMessage, bool
    [functions]
    // 实例化消息队列
   +NewMessageQueue(msgs []*types.SignedMessage) : MessageQueue

 -nonceQueue : []*types.SignedMessage

    // 一些队列的基本操作
    // 1 长度、push、pop功能
    // 2 Less主要是比较两条交易中的Gas价格,大家可以回头看看type中的消息定义,这里不赘述了
    // 3 为什么要提供Less接口,留给大家思索一下,熟悉以太坊的可能一眼就看出了
▼-queueHeap : []nonceQueue
    [methods]
   +Len() : int
   +Less(i, j int) : bool
   +Pop() : interface{}
   +Push(x interface{})
   +Swap(i, j int)
  • 调度器
    • 入口
    • node实例会调用NewScheduler创建相关实例并启动挖矿
▼ package
    mining

▶ imports

▼ constants
   +MineDelayConversionFactor

▼-timingScheduler : struct
    [fields]
   -isStarted : bool
   -mineDelay : time.Duration
    // 查找权重最高的Tipset
   -pollHeadFunc : func() types.TipSet
    // 底层的挖矿逻辑,在下面会分析Worker
   -worker : Worker
    [methods]
    // 判断是否启动挖矿
   +IsStarted() : bool
    // 启动挖矿
   +Start(miningCtx context.Context) : chan Output, *sync.WaitGroup

▼+Scheduler : interface
    [methods]
   +IsStarted() : bool
   +Start(miningCtx context.Context) : chan Output, *sync.WaitGroup

▼ functions
   +MineOnce(ctx context.Context, w Worker, md time.Duration, ts types.TipSet) : Output, error
    // 实例化timingScheduler 
   +NewScheduler(w Worker, md time.Duration, f func() types.TipSet) : Scheduler
   -nextNullBlkCount(prevNullBlkCount int, prevBase, currBase types.TipSet) : int
  • 打包区块
    • 具体见如下注释,可对应此查阅源码。
location: mining/block_generate.go

▼ package
    mining

▶ imports

▼ DefaultWorker* : ctype
    [methods]
    // 1 如果节点没有产生过有效存储,无法参与挖矿
    // 2 计算区块高度= 基准Tipset高度+空块数目
    // 3 取出未打包消息,调用vm执行,生成收据,并更新状态
    // 4 打包区块信息,返回
   +Generate(ctx context.Context, baseTipSet types.TipSet, ticket types.Signature, proof proofs.PoStProof, nullBlockCount uint64) : *types.Block, error

14.4 服务层之其他服务

14.4.1 消息池

  • location: core/message_pool.go
  • 消息池相关方法

14.4.2 钱包

  • location:./wallet
  • 钱包相关操作方法

14.5 插播线下分享

filecoin作为存储区块链领域的明星项目,已于今年2月14日情人节开放源码以及开发网络。那么filecoin 到底是什么?它与IPFS又有什么千丝万缕的联系?它对传统存储市场带来什么变化和机会?我们应该怎样更快地认识和走进filecoin 的开发或应用?3月16号周六下午深圳•南山•深圳湾科技园欢迎您的到来,笔者将和大家一起分享filecoin,纯技术分享讨论,欢迎大家 [勾引]

[上一篇链接] filecoin技术架构分析之十三:filecoin源码分析之服务层actor及vm

[下一篇链接] filecoin技术架构分析之十五:filecoin源码分析之节点运行逻辑

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容