光明源于黑暗,黑暗涌现光明
《魔兽》
该相信谁?
共识是指一群人的一致性认可,众所周知,区块链是一个不属于任何个体的分布式数据库/账本,那么谁有权记账?记账的人造假怎么办?如果不解决这些问题,没人会认可这些数据的真实性,也就没人使用,无法产生价值。
对于第一个问题,比特股用的是 DPOS(代理权益证明)机制,通过投票的方式,选举多名见证人(接下来都用见证人代表记账人)来负责数据库的记账,每个区块记账权随机分给某一个见证人。投票的条件是拥有比特股,参加选举的条件是将账号升级为高级账号。这种机制能够驱使参选人为了得到社区人群的支持而做有益于社区的事。
第二个问题比较复杂,可以先拆分为两部分,为什么要造假,如何才能造假。
1. 为什么要造假?
那肯定是有利益驱动的,假设记账权在 A 手上,如果先把付款交易记录到区块里,拿到货物后再撤销这笔付款交易,钱不用给,货却到手了。
2. 如何才能造假?
造假又分交易造假和区块造假,交易需要用户的私钥签名不可能造假,区块只要是见证人就能生成,存在造假可能。针对打包区块这个行为,有两个可选操作,将交易包含到区块中,或者不将交易包含到区块中。假如打包时先包含一个交易,之后再选择不包含这个交易,就能达到欺骗的目的。这个问题在应用于金融的区块链叫做双花问题,在其他应用方面就不知道叫啥了,但是原理是一样的。区块链是不可篡改的,要实现撤销交易只能依靠最长链原则。最长链原则是指在区块链出现分叉时,所有节点都会从当前分叉链切换到最长的分叉链,并认为这条链的数据才是正确的。
正常情况下区块记账流程:
少数见证人修改数据产生分叉:
这时候数据正常的链因为有更多见证人,生成块的速度也会比数据造假的链更快,所以数据正常的链依旧被其他节点当作主链。
多数见证人修改数据产生分叉:
这时候数据造假的链更长,被其他节点接受并成为了主链。
造假流程:
完成交易后,造假的见证人返回到记录转账交易的区块,把转账记录删除并重新打包区块,然后继续打包后面的区块直到长度超过数据正常的链,取代它成为主链,这时造假就成功了。
思考时间:
造假的条件是至少控制了一半以上的见证人,那能不能只用一个见证人,加快它的打包时间达成造假?
不行,因为区块链网络中所有节点都知道当前网络下哪些账户是见证人,也知道哪个时间间隔内的区块是由谁来打包,这一点是能够验证的,如果你原本应该在下午2点和3点打包区块,想在2点30打包,打包出来的块不会被其他节点接受。
验证代码如下:
文件/libraries/chain/db_block.cpp
const witness_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const
{
// 验证参数是否有效
const witness_object& witness = next_block.witness(*this);
if( !(skip&skip_witness_schedule_check) )
{
// 获取第一个区块生成时间到当前时间的打包周期数
uint32_t slot_num = get_slot_at_time( next_block.timestamp );
FC_ASSERT( slot_num > 0 );
// 获取当前区块应该由哪个见证人打包
witness_id_type scheduled_witness = get_scheduled_witness( slot_num );
// 验证区块的见证人是否正确
FC_ASSERT( next_block.witness == scheduled_witness, "Witness produced block at wrong time",
("block witness",next_block.witness)("scheduled",scheduled_witness)("slot_num",slot_num) );
}
return witness;
}
那是不是遭到这种攻击就没办法了?
有避免的方法,等到区块被确认无法回溯时,才能认为交易成功了,再进行下一步操作。区块确认的条件是被 2/3 的见证人确认,耗时大概是1分钟左右。怎么才算确认?当一个见证人打包区块 B 时是基于区块 A 的,此时可以认为见证人认可/确认了区块A。2/3的比例是可以调的,只是个经验值,比例太高确认耗时会提高,太小又会降低容错性。如果节点收到了比已经确认的区块还要早的区块会拒绝处理,所以被确认的区块是不会因为分叉而修改的,分叉只会发生在未确认的区块之间。
具体代码如下:
文件/libraries/chain/fork_database.cpp
void fork_database::_push_block(const item_ptr& item)
{
if( _head ){
// 验证区块高度高于已确认区块
FC_ASSERT( item->num > std::max( 0, int64_t(_head->num) - (_max_size) ),
"attempting to push a block that is too old",
("item->num",item->num)("head",_head->num)("max_size",_max_size));
}
// 省略
}
即使不是故意造假产生分叉,在复杂的网络情况下还是会存在分叉的,但是 DPOS 都能保证正确运行,可见其强大的稳定性,更多的情况可以查看文末的DPOS Consensus Algorithm - The Missing White Paper 。
如果有什么感到疑惑的问题欢迎留言,大家一起学习~
参考文章:
DPOS Consensus Algorithm - The Missing White Paper
https://steemit.com/dpos/@dantheman/dpos-consensus-algorithm-this-missing-white-paper
领取专属 10元无门槛券
私享最新 技术干货