区块链开发:共识机制DPoS #C08

作者: 纳兰少 | 来源:发表于2019-03-19 09:02 被阅读0次

    原理介绍

    在PoS中,任何持有币的节点都可以参与挖矿,这样网络中有多少记账节点是不可知的,记账节点之间的网络环境也是不可知的。节点越多,网络越复杂,越容易造成网络分区,从而延长达成共识的时间。

    而DPoS将记账权限定在一定数量内,获得记账权的节点才会进行挖矿记账,这样就能极大地提高系统的吞吐量。记账节点通过选举产生,全网投票哪些节点能获得记账权,这个过程我们称为“投票选举”,所以DPoS (Delegated proof of stake) 称为委托权益证明。获得记账权的节点也可以被称为“超级节点”或者见证人,以区分普通用户节点。

    由于DPoS记账节点数量不多,那么我们就可以在共识算法中设置出块时间为固定值,通过轮流出块来进行记账。

    以上就是DPoS的设计思路,下面以伪代码的形式来描述DPoS的过程。

    for round i //分成很多个round,round无限持续
       dlist_i = get N delegates sort by votes //根据投票结果选出得票率最高的N个受托人
       dlist_i = shuffle(dlist_i) //随机改变顺序
       loop //round完了,退出循环
           slot = global_time_offset / block_interval
           pos = slot % N
           if dlist_i[pos] exists in this node //delegate在这个节点
               generateBlock(keypair of dlist_i[pos]) //产生block
           else
               skip
    

    如果某个超级节点在非指定时间产生了区块,则会被认为是无效的。每经过一段时间,超级节点的出块顺序会重新排列,而超级节点也可能重新选举。

    下图就是一个理想的轮流记账状态。

    如果节点B恶意分叉,那会形成下面的局面:

    诚实节点A和C在相同时间内产生的区块数量是坏节点B的两倍,A和C形成的链更长,而诚实节点始终会选择最长的链,从而抵御攻击。

    DPoS 白皮书中介绍了少数记账节点恶意或故障造成的分叉、网络分区情况下重复出块、少数记账节点重复出块、记账节点数量不足、多数记账节点的联合腐败等各种情况。

    代码实现

    基本结构

    class DPos {
        constructor(blockchain) {
            this.last_slot_ = -1;
            this.block_chain_ = blockchain;
        }
    
        prepared() {
            let current_slot = slot.get_slot_number();
            let current_id = current_slot % slot.delegates;
            // console.log(current_slot + ' ' + current_id + ' ' + this.block_chain_.get_account_id());
    
            if (current_id != this.block_chain_.get_account_id())
                return false;
    
            if (current_slot == this.last_slot_)
                return false;
    
            this.last_slot_ = current_slot;
            return true;
        }
        make_consensus(block_data) {
            let self = this;
            setImmediate((block) => {
                let time_stamp = block.get_timestamp();
                let block_slot = slot.get_slot_number(time_stamp);
                let target_id = block_slot % slot.delegates;
    
                let current_slot = slot.get_slot_number();
                let current_id = current_slot % slot.delegates;
    
                if (target_id != current_id) {
                    block.emit('consensus failed');
                    return;
                }
    
                if (target_id != self.block_chain_.get_account_id()) {
                    block.emit('consensus failed');
                    return;
                }
                block.set_consensus_data({
                    "generator_id": self.block_chain_.get_account_id()
                });
                block.emit('consensus completed');
            }, block_data);
    
        }
    }
    

    DPoS的结构十分简单,但是其共识过程与其他方法不同。依据时间戳来确定当前出块的委托人ID,如果轮到当前节点出块,那么就发射对应信号。同时还要将当前节点的ID写入区块中,用于后续验证委托人节点是否在当前时间段出块。

    轮流记账

    我们假设有20个超级节点,每10s出一个块。由以上内容可知,超级节点需要依据投票选出,并且其出票顺序需要定期更换。而在这里我们将节点的选举与排位留给社区去完成,假设已有20个代理人节点,我们需要依据当前时间来决定谁出块。

    'use strict';
    const delegates = 20;
    const interval = 10;// second
    
    function get_time(time) {
        if (time === undefined) {
            time = (new Date()).getTime();
        }
        var base_time = new Date(1548988864492).getTime();
        return Math.floor((time - base_time) / 1000);
    }
    
    function get_slot_number(time_stamp) {
        time_stamp = get_time(time_stamp);
        return Math.floor(time_stamp / interval);
    }
    
    
    • base_time 表示系统启动的时间

    • getSlotNumber 用于计算时间戳偏移量,每经过10s,其结果加1,对其结果取20的余数就可以获得出票节点编号。

      getSlotNumber不仅可以计算出当前该由谁出票,也可以推导出某个特定时间点该由谁来出票。

    劣势

    DPoS系统中,少数的超级节点提高了系统的吞吐率,但是也使得DPoS系统更像传统的分布式系统,这也是V神一致批评DPoS的地方。

    DPoS系统并不考虑拜占庭问题,它的设计目的就是为了快速记账,而把发生的问题留给社区治理,也就是最终归为投票,但是投票并不能解决所有问题。

    代码地址:https://github.com/yjjnls/awesome-blockchain/tree/v0.1.1/src/js

    相关文章

      网友评论

        本文标题:区块链开发:共识机制DPoS #C08

        本文链接:https://www.haomeiwen.com/subject/kokhmqtx.html