前文我们实现了网络模块,并将其加到区块链中,至此我们有了一条可以运行的DPoS链,但是该链是否可靠呢?
答案肯定是存在漏洞的。根据sqfasd同学的分析,由于在每个记账周期内,排序算法的种子是不变的,所以在当前周期内,记账人的顺序是确定的。
如果此时黑客控制一部分节点,然后再对忠诚节点发起DDOS攻击,使其无法响应,那么黑客控制的不连续节点有可能连续地产生区块。当分叉持续到6个区块之后,就可能造成双花。
模拟攻击
这里我们就来模拟一下上面所说的黑客攻击。首先在BlockChain中添加一个可以设置的标志bad,设置为bad的节点就是被黑客控制的节点,轮到怀节点记账时,它会产生分叉。
// blockchain.js
loop(cb) {
let self = this;
if (this.consensus_.prepared()) {
if (!self.is_bad_) {
this.generate_block(this.get_account_keypair(), () => {
// broadcast block
let block = self.get_last_block();
console.log(`node: ${self.get_account_id()} generate block! block height: ${block.height} hash: ${block.hash}`);
});
} else {
self.fork();
}
}
cb();
}
async fork() {
console.log('----------fork----------');
// load transactions
var tx1 = [{
amount: 1000,
recipient: 'bob',
sender: 'alice'
}];
// create block
let block1 = new Block({
"keypair": this.get_account_keypair(),
"previous_block": this.last_block_,
"transactions": tx1
}, this.consensus_);
// make proof of the block/mine
let self = this;
let block_data1 = await new Promise((resolve, reject) => {
block1.on('block completed', (data) => {
if (data.height == self.last_block_.height + 1) {
resolve(data);
} else {
reject('block1 failed');
}
});
});
// load transactions
var tx2 = [{
amount: 1000,
recipient: 'cracker',
sender: 'alice'
}];
// create block
let block2 = new Block({
"keypair": this.get_account_keypair(),
"previous_block": this.last_block_,
"transactions": tx2
}, this.consensus_);
let block_data2 = await new Promise((resolve, reject) => {
block2.on('block completed', (data) => {
if (data.height == self.last_block_.height + 1) {
resolve(data);
} else {
reject('block2 failed');
}
});
});
var i = 0;
for (var id in this.node_.peers_) {
let socket = this.node_.peers_[id];
if (i % 2 == 0) {
var msg1 = Msg.block(block_data1);
this.node_.send(socket, msg1);
} else {
var msg2 = Msg.block(block_data2);
this.node_.send(socket, msg2);
}
i++;
}
console.log("fork");
this.commit_block(block_data1);
}
可以看到在fork函数中,节点产生了两个区块,第一个区块中Alice向Bob转了1000币,第二个区块中Alice向Cracker转了1000币,这是一个很明显的双花现象。然后该节点广播区块时,轮流广播两个区块,从而产生分叉。
我们设置节点10为坏节点,然后进行测试:
node: 0 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 1 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 2 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 3 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 4 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 5 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 6 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 7 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 8 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 9 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 10 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 11 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 12 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 13 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 14 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 15 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 16 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 17 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 18 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:a66396:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
node: 19 (1:d611ed:null) -> (2:3681a8:8) -> (3:71a0c9:9) -> (4:006ebb:10) -> (5:6559da:11) -> (6:2998da:12) -> (7:2965e6:13) ->
(2:3681a8:8)中,2表示区块高度,3681a8表示区块hash值的前6个字符,8表示该区块时哪个节点产生的。从测试结果可以看出,在区块高度为4时产生了分叉,12个节点收到的区块hash值为a66396,剩下8个节点将另一个区块存入链中。在现实情况中,区块链可能产生1到2个分叉,超过3个以上分叉基本是网络分区造成的;而一个节点被控制就可以产生这么多的分叉,可以想象情况有多严重,如果黑客控制6个节点,就可以产生不可逆转的更改。
Pbft+DPoS
为了解决上述问题,我们引入Pbft算法,DPoS算法的前半部分中的出块过程不变,但是后半部分中对区块的验证改为用pbft算法进行全网投票,忠诚的节点不会立即将区块写入链中,而是等待投票数超过一定数量后才会采纳该区块。
// blockchain.js
commit_block(block_data) {
if (pbft && !this.is_bad_) {
var block = new Block();
block.set_data(block_data);
let self = this;
block.on('consensus completed', (data) => {
self.last_block_ = data;
self.save_last_block();
});
this.pbft_.make_consensus(block);
} else {
this.last_block_ = block_data;
this.save_last_block();
}
}
引入Pbft后,再次进行测试,可以看到会出现两种情况。由于节点10广播了两个不同的区块,自己记录区块A,如果其他节点经过投票也选择区块A,那么结果就是情况1,所有链都不产生分叉。如果其他节点经过投票选择了区块B,那么就如情况2所示,只有节点10一个节点产生了分叉,剩余的忠诚节点都保持了一致。
node: 0 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 1 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 2 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 3 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 4 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 5 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 6 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 7 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 8 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 9 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 10 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 11 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 12 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 13 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 14 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 15 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 16 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 17 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 18 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 19 (1:d611ed:null) -> (2:aab8cb:7) -> (3:05f490:8) -> (4:4d65ee:9) -> (5:26532b:10) -> (6:4d1851:11) ->
node: 0 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 1 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 2 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 3 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 4 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 5 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 6 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 7 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 8 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 9 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 10 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:3021f0:10) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 11 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 12 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 13 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 14 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 15 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 16 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 17 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 18 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
node: 19 (1:d611ed:null) -> (2:b86fc2:8) -> (3:26b8fe:9) -> (4:e4bee5:11) -> (5:9dce8c:12) -> (6:21ed7b:13) ->
引入Pbft后的DPoS机制就可以低于拜占庭攻击,而不是将这一任务留给社区去完成。
代码地址:https://github.com/yjjnls/awesome-blockchain/tree/v0.1.1/src/js










网友评论