区块链开发:共识机制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