-
-
Notifications
You must be signed in to change notification settings - Fork 355
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Removed znode-payments * Znodesync-interface cleaned * darksend has been dealt with * Removed RPC broadcast * Znode GUI removed * Removed znodeman * Removed znode-sync * Removed activeznode * Removed ping and broadcast * Removed znode * Removed from GUI and net exchange * Removed -znodeprivkey * Removed znode-payments * Znodesync-interface cleaned * darksend has been dealt with * Removed RPC broadcast * Znode GUI removed * Removed znodeman * Removed znode-sync * Removed activeznode * Removed ping and broadcast * Removed znode * Removed from GUI and net exchange * Removed -znodeprivkey * Tests are adjusted * Removed sporks * Cleanup in comments, other minor clean-ups * Stopped using *.dat cache files * Minor improvements for LLMQs * Minor improvements for LLMQs * All LLMQ tests are done * Llmqs for chainlocks were enabled * Chainlocks start working * Wrapping up with chainlocks * Removed znode-payments * Znodesync-interface cleaned * darksend has been dealt with * Removed znodeman * Removed znode * Cleanup in comments, other minor clean-ups * Minor improvements for LLMQs * All LLMQ tests are done * Llmqs for chainlocks were enabled * Chainlocks start working * Wrapping up with chainlocks * Tests are fixed * Removed the original test * Evospork controls chainlocks * More tests for CL * Blockchain params change for testnet * Formatting fix * Minor improvments * Dealing with the code review comments * Version update * HF date is set * Minor cleanup * Dealing with code review Co-authored-by: Andrey <[email protected]> Co-authored-by: Peter Shugalev <[email protected]>
- Loading branch information
1 parent
e743e9c
commit 5525561
Showing
31 changed files
with
1,096 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright (c) 2015-2018 The Dash Core developers | ||
# Distributed under the MIT software license, see the accompanying | ||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
from test_framework.mininode import * | ||
from test_framework.test_framework import EvoZnodeTestFramework | ||
from test_framework.util import * | ||
from time import * | ||
|
||
''' | ||
llmq-chainlocks.py | ||
Checks LLMQs based ChainLocks | ||
''' | ||
|
||
class LLMQChainLocksTest(EvoZnodeTestFramework): | ||
def __init__(self): | ||
super().__init__(6, 5, extra_args=[['-debug=chainlocks']] * 6) | ||
|
||
def run_test(self): | ||
|
||
for i in range(4): | ||
self.mine_quorum() | ||
|
||
# mine single block, wait for chainlock | ||
self.nodes[0].generate(1) | ||
|
||
self.wait_for_chainlock_tip_all_nodes() | ||
|
||
# mine many blocks, wait for chainlock | ||
self.nodes[0].generate(20) | ||
self.wait_for_chainlock_tip_all_nodes() | ||
|
||
# assert that all blocks up until the tip are chainlocked | ||
for h in range(1, self.nodes[0].getblockcount()): | ||
block = self.nodes[0].getblock(self.nodes[0].getblockhash(h)) | ||
assert(block['chainlock']) | ||
|
||
# Isolate node, mine on another, and reconnect | ||
isolate_node(self.nodes[0]) | ||
node0_tip = self.nodes[0].getbestblockhash() | ||
self.nodes[1].generate(5) | ||
self.wait_for_chainlock_tip(self.nodes[1]) | ||
assert(self.nodes[0].getbestblockhash() == node0_tip) | ||
reconnect_isolated_node(self.nodes[0], 1) | ||
self.nodes[1].generate(1) | ||
self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) | ||
|
||
# Isolate node, mine on both parts of the network, and reconnect | ||
isolate_node(self.nodes[0]) | ||
self.nodes[0].generate(5) | ||
self.nodes[1].generate(1) | ||
good_tip = self.nodes[1].getbestblockhash() | ||
self.wait_for_chainlock_tip(self.nodes[1]) | ||
assert(not self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]) | ||
reconnect_isolated_node(self.nodes[0], 1) | ||
self.nodes[1].generate(1) | ||
self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) | ||
assert(self.nodes[0].getblock(self.nodes[0].getbestblockhash())["previousblockhash"] == good_tip) | ||
assert(self.nodes[1].getblock(self.nodes[1].getbestblockhash())["previousblockhash"] == good_tip) | ||
|
||
# Keep node connected and let it try to reorg the chain | ||
good_tip = self.nodes[0].getbestblockhash() | ||
# Restart it so that it forgets all the chainlocks from the past | ||
stop_node(self.nodes[0], 0) | ||
self.nodes[0] = start_node(0, self.options.tmpdir, self.extra_args[0]) | ||
connect_nodes(self.nodes[0], 1) | ||
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) | ||
# Now try to reorg the chain | ||
self.nodes[0].generate(2) | ||
assert(self.nodes[1].getbestblockhash() == good_tip) | ||
self.nodes[0].generate(2) | ||
sleep(6) | ||
assert(self.nodes[1].getbestblockhash() == good_tip) | ||
|
||
# Now let the node which is on the wrong chain reorg back to the locked chain | ||
self.nodes[0].reconsiderblock(good_tip) | ||
assert(self.nodes[0].getbestblockhash() != good_tip) | ||
self.nodes[1].generate(1) | ||
self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) | ||
assert(self.nodes[0].getbestblockhash() == self.nodes[1].getbestblockhash()) | ||
|
||
isolate_node(self.nodes[0]) | ||
self.nodes[0].generate(1) | ||
reconnect_isolated_node(self.nodes[0], 1) | ||
self.wait_for_chainlock(self.nodes[0], self.nodes[1].getbestblockhash()) | ||
|
||
def wait_for_chainlock_tip_all_nodes(self): | ||
for node in self.nodes: | ||
tip = node.getbestblockhash() | ||
self.wait_for_chainlock(node, tip) | ||
|
||
def wait_for_chainlock_tip(self, node): | ||
tip = node.getbestblockhash() | ||
self.wait_for_chainlock(node, tip) | ||
|
||
def wait_for_chainlock(self, node, block_hash): | ||
t = time() | ||
while time() - t < 15: | ||
try: | ||
block = node.getblock(block_hash) | ||
if block["confirmations"] > 0 and block["chainlock"]: | ||
return | ||
except: | ||
# block might not be on the node yet | ||
pass | ||
sleep(0.1) | ||
raise AssertionError("wait_for_chainlock timed out") | ||
|
||
def create_chained_txs(self, node, amount): | ||
txid = node.sendtoaddress(node.getnewaddress(), amount) | ||
tx = node.getrawtransaction(txid, 1) | ||
inputs = [] | ||
valueIn = 0 | ||
for txout in tx["vout"]: | ||
inputs.append({"txid": txid, "vout": txout["n"]}) | ||
valueIn += txout["value"] | ||
outputs = { | ||
node.getnewaddress(): round(float(valueIn) - 0.0001, 6) | ||
} | ||
|
||
rawtx = node.createrawtransaction(inputs, outputs) | ||
rawtx = node.signrawtransaction(rawtx) | ||
rawtxid = node.sendrawtransaction(rawtx["hex"]) | ||
|
||
return [txid, rawtxid] | ||
|
||
|
||
if __name__ == '__main__': | ||
LLMQChainLocksTest().main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright (c) 2015-2018 The Dash Core developers | ||
# Distributed under the MIT software license, see the accompanying | ||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
from test_framework.mininode import * | ||
from test_framework.test_framework import EvoZnodeTestFramework | ||
from test_framework.util import * | ||
from time import * | ||
|
||
''' | ||
llmq-chainlocks.py | ||
Checks LLMQs based ChainLocks | ||
00 Mine quorum and produce chainlocks | ||
01 Make sure the chainlocked tip does not change after invalidateblock | ||
02 Make sure a rogue miner cannot inject a longer chain | ||
10 Disable chainlocks | ||
11 Make sure 01-09 work as usual | ||
- | ||
''' | ||
|
||
class LLMQChainLocksTest(EvoZnodeTestFramework): | ||
def __init__(self): | ||
super().__init__(6, 5, extra_args=[['-debug=chainlocks']] * 6) | ||
self.sporkprivkey = "cW2YM2xaeCaebfpKguBahUAgEzLXgSserWRuD29kSyKHq1TTgwRQ" | ||
|
||
def run_test(self): | ||
|
||
for i in range(4): | ||
self.mine_quorum() | ||
|
||
# mine single block, wait for chainlock | ||
self.nodes[0].generate(1) | ||
|
||
self.wait_for_chainlock_tip_all_nodes() | ||
self.payment_address = self.nodes[0].getaccountaddress("") | ||
self.nodes[0].sendtoaddress(self.payment_address, 1) | ||
|
||
# mine many blocks, wait for chainlock | ||
while self.nodes[0].getblockcount() < 1000: | ||
self.nodes[0].generate(20) | ||
self.wait_for_chainlock_tip_all_nodes() | ||
|
||
# assert that all blocks up until the tip are chainlocked | ||
for h in range(1, self.nodes[0].getblockcount()): | ||
block = self.nodes[0].getblock(self.nodes[0].getblockhash(h)) | ||
assert(block['chainlock']) | ||
|
||
# cannot invalidate tip | ||
current_tip = self.nodes[0].getbestblockhash() | ||
self.nodes[0].invalidateblock(current_tip) | ||
assert(current_tip == self.nodes[0].getbestblockhash()) | ||
|
||
##### Disable chainlocks for 10 blocks | ||
|
||
self.nodes[0].importprivkey(self.sporkprivkey) | ||
self.disable_chainlocks(self.nodes[0].getblockcount() + 10) | ||
self.nodes[0].generate(1) | ||
assert(False == self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]) | ||
|
||
# can invalidate block now | ||
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) | ||
assert(current_tip == self.nodes[0].getbestblockhash()) | ||
|
||
isolate_node(self.nodes[5]) | ||
|
||
##### Enable chainlocks | ||
|
||
self.nodes[0].generate(10) | ||
self.nodes[0].spork('list') | ||
self.wait_for_chainlock_tip_all_nodes() | ||
sporks = self.nodes[0].spork("list") | ||
assert(not sporks["blockchain"]) | ||
assert(not sporks["mempool"]) | ||
assert(True == self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]) | ||
|
||
# generate a longer chain on the isolated node then reconnect it back and make sure it picks the chainlocked chain | ||
self.nodes[5].generate(20) | ||
reconnect_isolated_node(self.nodes[5], 1) | ||
self.nodes[0].generate(1) | ||
current_tip = self.nodes[0].getbestblockhash() | ||
timeout = 10 | ||
while current_tip != self.nodes[5].getbestblockhash(): | ||
assert timeout > 0, "Timed out when waiting for a chainlocked chain" | ||
sleep(1) | ||
timeout = timeout - 1 | ||
|
||
|
||
def wait_for_chainlock_tip_all_nodes(self): | ||
for node in self.nodes: | ||
tip = node.getbestblockhash() | ||
self.wait_for_chainlock(node, tip) | ||
|
||
def wait_for_chainlock_tip(self, node): | ||
tip = node.getbestblockhash() | ||
self.wait_for_chainlock(node, tip) | ||
|
||
def wait_for_chainlock(self, node, block_hash): | ||
t = time() | ||
while time() - t < 15: | ||
try: | ||
block = node.getblock(block_hash) | ||
if block["confirmations"] > 0 and block["chainlock"]: | ||
return | ||
except: | ||
# block might not be on the node yet | ||
pass | ||
sleep(0.1) | ||
raise AssertionError("wait_for_chainlock timed out") | ||
|
||
def disable_chainlocks(self, till_height): | ||
self.nodes[0].spork(self.sporkprivkey, self.payment_address, {"disable":{"chainlocks": till_height}}) | ||
|
||
|
||
if __name__ == '__main__': | ||
LLMQChainLocksTest().main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright (c) 2015-2018 The Dash Core developers | ||
# Distributed under the MIT software license, see the accompanying | ||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
from test_framework.test_framework import EvoZnodeTestFramework | ||
from test_framework.util import * | ||
|
||
''' | ||
llmq-dkgerrors.py | ||
Simulate and check DKG errors | ||
''' | ||
|
||
class LLMQDKGErrors(EvoZnodeTestFramework): | ||
def __init__(self): | ||
super().__init__(6, 5) | ||
|
||
def run_test(self): | ||
|
||
# Mine one quorum without simulating any errors | ||
qh = self.mine_quorum() | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) | ||
|
||
# Lets omit the contribution | ||
self.mninfo[0].node.quorum('dkgsimerror', 'contribution-omit', '1') | ||
qh = self.mine_quorum(expected_contributions=4) | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) | ||
|
||
# Lets lie in the contribution but provide a correct justification | ||
self.mninfo[0].node.quorum('dkgsimerror', 'contribution-omit', '0') | ||
self.mninfo[0].node.quorum('dkgsimerror', 'contribution-lie', '1') | ||
qh = self.mine_quorum(expected_contributions=5, expected_complaints=4, expected_justifications=1) | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) | ||
|
||
# Lets lie in the contribution and then omit the justification | ||
self.mninfo[0].node.quorum('dkgsimerror', 'justify-omit', '1') | ||
qh = self.mine_quorum(expected_contributions=4, expected_complaints=4) | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) | ||
|
||
# Heal some damage (don't get PoSe banned) | ||
self.heal_masternodes(33) | ||
|
||
# Lets lie in the contribution and then also lie in the justification | ||
self.mninfo[0].node.quorum('dkgsimerror', 'justify-omit', '0') | ||
self.mninfo[0].node.quorum('dkgsimerror', 'justify-lie', '1') | ||
qh = self.mine_quorum(expected_contributions=4, expected_complaints=4, expected_justifications=1) | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, False) | ||
|
||
# Lets lie about another MN | ||
self.mninfo[0].node.quorum('dkgsimerror', 'contribution-lie', '0') | ||
self.mninfo[0].node.quorum('dkgsimerror', 'justify-lie', '0') | ||
self.mninfo[0].node.quorum('dkgsimerror', 'complain-lie', '1') | ||
qh = self.mine_quorum(expected_contributions=5, expected_complaints=1, expected_justifications=4) | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) | ||
|
||
# Lets omit 2 premature commitments | ||
self.mninfo[0].node.quorum('dkgsimerror', 'complain-lie', '0') | ||
self.mninfo[0].node.quorum('dkgsimerror', 'commit-omit', '1') | ||
self.mninfo[1].node.quorum('dkgsimerror', 'commit-omit', '1') | ||
qh = self.mine_quorum(expected_contributions=5, expected_complaints=0, expected_justifications=0, expected_commitments=3) | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) | ||
|
||
# Lets lie in 2 premature commitments | ||
self.mninfo[0].node.quorum('dkgsimerror', 'commit-omit', '0') | ||
self.mninfo[1].node.quorum('dkgsimerror', 'commit-omit', '0') | ||
self.mninfo[0].node.quorum('dkgsimerror', 'commit-lie', '1') | ||
self.mninfo[1].node.quorum('dkgsimerror', 'commit-lie', '1') | ||
qh = self.mine_quorum(expected_contributions=5, expected_complaints=0, expected_justifications=0, expected_commitments=3) | ||
self.assert_member_valid(qh, self.mninfo[0].proTxHash, True) | ||
|
||
def assert_member_valid(self, quorumHash, proTxHash, expectedValid): | ||
q = self.nodes[0].quorum('info', 100, quorumHash, True) | ||
for m in q['members']: | ||
if m['proTxHash'] == proTxHash: | ||
if expectedValid: | ||
assert(m['valid']) | ||
else: | ||
assert(not m['valid']) | ||
else: | ||
assert(m['valid']) | ||
|
||
def heal_masternodes(self, blockCount): | ||
# We're not testing PoSe here, so lets heal the MNs :) | ||
for i in range(blockCount): | ||
set_mocktime(get_mocktime() + 1) | ||
set_node_times(self.nodes, get_mocktime()) | ||
self.nodes[0].generate(1) | ||
self.sync_all() | ||
|
||
|
||
if __name__ == '__main__': | ||
LLMQDKGErrors().main() |
Oops, something went wrong.