forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement tests for retroactive signing of IS and CLs
- Loading branch information
Showing
2 changed files
with
193 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
#!/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 DashTestFramework | ||
from test_framework.util import sync_blocks, set_node_times, \ | ||
isolate_node, reconnect_isolated_node | ||
|
||
''' | ||
llmq-is-retroactive.py | ||
Tests retroactive signing | ||
We have 6 nodes where node 0 is the control node, nodes 1-5 are masternodes. | ||
Mempool inconsistencies are simulated via disconnecting/reconnecting node 3 | ||
and by having a higher relay fee on nodes 4 and 5. | ||
''' | ||
|
||
class LLMQ_IS_RetroactiveSigning(DashTestFramework): | ||
def set_test_params(self): | ||
self.set_dash_test_params(6, 5, [[], [], [], [], ["-minrelaytxfee=0.001"], ["-minrelaytxfee=0.001"]], fast_dip3_enforcement=True) | ||
|
||
def run_test(self): | ||
while self.nodes[0].getblockchaininfo()["bip9_softforks"]["dip0008"]["status"] != "active": | ||
self.nodes[0].generate(10) | ||
sync_blocks(self.nodes, timeout=60*5) | ||
|
||
self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0) | ||
self.nodes[0].spork("SPORK_19_CHAINLOCKS_ENABLED", 0) | ||
self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 0) | ||
self.nodes[0].spork("SPORK_3_INSTANTSEND_BLOCK_FILTERING", 0) | ||
self.wait_for_sporks_same() | ||
|
||
self.mine_quorum() | ||
self.mine_quorum() | ||
|
||
# Make sure that all nodes are chainlocked at the same height before starting actual tests | ||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash()) | ||
|
||
self.log.info("trying normal IS lock") | ||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) | ||
# Make sure the TX wasn't accepted by nodes 4 and 5 | ||
self.wait_for_tx(txid, self.nodes[4], False, 5) | ||
self.wait_for_tx(txid, self.nodes[5], False, 1) | ||
# 3 nodes should be enough to create an IS lock even if nodes 4 and 5 (which have no tx itself) | ||
# are the only "neighbours" in intra-quorum connections for one of them. | ||
self.wait_for_instantlock(txid, self.nodes[0]) | ||
self.bump_mocktime(1) | ||
set_node_times(self.nodes, self.mocktime) | ||
block = self.nodes[0].generate(1)[0] | ||
self.wait_for_chainlocked_block_all_nodes(block) | ||
|
||
self.log.info("testing normal signing with partially known TX") | ||
isolate_node(self.nodes[3]) | ||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) | ||
# Make sure nodes 1 and 2 received the TX before we continue, | ||
# otherwise it might announce the TX to node 3 when reconnecting | ||
self.wait_for_tx(txid, self.nodes[1]) | ||
self.wait_for_tx(txid, self.nodes[2]) | ||
reconnect_isolated_node(self.nodes[3], 0) | ||
self.wait_for_mnauth(self.nodes[3], 2) | ||
# node 3 fully reconnected but the TX wasn't relayed to it, so there should be no IS lock | ||
self.wait_for_instantlock(txid, self.nodes[0], False, 5) | ||
# push the tx directly via rpc | ||
self.nodes[3].sendrawtransaction(self.nodes[0].getrawtransaction(txid)) | ||
# Make sure the TX wasn't accepted by nodes 4 and 5 | ||
self.wait_for_tx(txid, self.nodes[4], False, 5) | ||
self.wait_for_tx(txid, self.nodes[5], False, 1) | ||
# node 3 should vote on a tx now since it became aware of it via sendrawtransaction | ||
# and this should be enough to complete an IS lock | ||
self.wait_for_instantlock(txid, self.nodes[0]) | ||
|
||
self.log.info("testing retroactive signing with unknown TX") | ||
isolate_node(self.nodes[0]) | ||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) | ||
# Make sure the TX wasn't accepted by nodes 4 and 5 | ||
self.wait_for_tx(txid, self.nodes[4], False, 5) | ||
self.wait_for_tx(txid, self.nodes[5], False, 1) | ||
# Make node 0 consider the TX as safe | ||
self.bump_mocktime(10 * 60 + 1) | ||
set_node_times(self.nodes, self.mocktime) | ||
block = self.nodes[0].generate(1)[0] | ||
reconnect_isolated_node(self.nodes[0], 1) | ||
self.wait_for_chainlocked_block_all_nodes(block) | ||
self.nodes[0].setmocktime(self.mocktime) | ||
|
||
self.log.info("testing retroactive signing with partially known TX") | ||
isolate_node(self.nodes[3]) | ||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) | ||
# Make sure nodes 1 and 2 received the TX before we continue, | ||
# otherwise it might announce the TX to node 3 when reconnecting | ||
self.wait_for_tx(txid, self.nodes[1]) | ||
self.wait_for_tx(txid, self.nodes[2]) | ||
# Make sure the TX wasn't accepted by nodes 4 and 5 | ||
self.wait_for_tx(txid, self.nodes[4], False, 5) | ||
self.wait_for_tx(txid, self.nodes[5], False, 1) | ||
reconnect_isolated_node(self.nodes[3], 0) | ||
self.wait_for_mnauth(self.nodes[3], 2) | ||
# node 3 fully reconnected but the TX wasn't relayed to it, so there should be no IS lock | ||
self.wait_for_instantlock(txid, self.nodes[0], False, 5) | ||
# Make node0 consider the TX as safe | ||
self.bump_mocktime(10 * 60 + 1) | ||
set_node_times(self.nodes, self.mocktime) | ||
block = self.nodes[0].generate(1)[0] | ||
self.wait_for_chainlocked_block_all_nodes(block) | ||
|
||
self.log.info("testing retroactive signing with partially known TX and all nodes session timeout") | ||
self.test_all_nodes_session_timeout(False) | ||
self.log.info("repeating test, but with cycled LLMQs") | ||
self.test_all_nodes_session_timeout(True) | ||
|
||
self.log.info("testing retroactive signing with partially known TX and single node session timeout") | ||
self.test_single_node_session_timeout(False) | ||
self.log.info("repeating test, but with cycled LLMQs") | ||
self.test_single_node_session_timeout(True) | ||
|
||
def cycle_llmqs(self): | ||
self.mine_quorum() | ||
self.mine_quorum() | ||
self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash()) | ||
|
||
def test_all_nodes_session_timeout(self, do_cycle_llmqs): | ||
set_node_times(self.nodes, self.mocktime) | ||
isolate_node(self.nodes[3]) | ||
rawtx = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1}) | ||
rawtx = self.nodes[0].fundrawtransaction(rawtx)['hex'] | ||
rawtx = self.nodes[0].signrawtransaction(rawtx)['hex'] | ||
txid = self.nodes[0].sendrawtransaction(rawtx) | ||
txid = self.nodes[3].sendrawtransaction(rawtx) | ||
# Make sure nodes 1 and 2 received the TX before we continue | ||
self.wait_for_tx(txid, self.nodes[1]) | ||
self.wait_for_tx(txid, self.nodes[2]) | ||
# Make sure signing is done on nodes 1 and 2 (it's async) | ||
time.sleep(5) | ||
# Make sure the TX wasn't accepted by nodes 4 and 5 | ||
self.wait_for_tx(txid, self.nodes[4], False, 1) | ||
self.wait_for_tx(txid, self.nodes[5], False, 1) | ||
# Make the signing session for the IS lock timeout on nodes 1-3 | ||
self.bump_mocktime(61) | ||
set_node_times(self.nodes, self.mocktime) | ||
time.sleep(2) # make sure Cleanup() is called | ||
reconnect_isolated_node(self.nodes[3], 0) | ||
self.wait_for_mnauth(self.nodes[3], 2) | ||
# node 3 fully reconnected but the signing session is already timed out on all nodes, so no IS lock | ||
self.wait_for_instantlock(txid, self.nodes[0], False, 5) | ||
if do_cycle_llmqs: | ||
self.cycle_llmqs() | ||
self.wait_for_instantlock(txid, self.nodes[0], False, 5) | ||
# Make node 0 consider the TX as safe | ||
self.bump_mocktime(10 * 60 + 1) | ||
self.nodes[0].setmocktime(self.mocktime) | ||
block = self.nodes[0].generate(1)[0] | ||
self.wait_for_chainlocked_block_all_nodes(block) | ||
|
||
def test_single_node_session_timeout(self, do_cycle_llmqs): | ||
set_node_times(self.nodes, self.mocktime) | ||
isolate_node(self.nodes[3]) | ||
rawtx = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1}) | ||
rawtx = self.nodes[0].fundrawtransaction(rawtx)['hex'] | ||
rawtx = self.nodes[0].signrawtransaction(rawtx)['hex'] | ||
txid = self.nodes[3].sendrawtransaction(rawtx) | ||
time.sleep(2) # make sure signing is done on node 2 (it's async) | ||
# Make the signing session for the IS lock timeout on node 3 | ||
self.bump_mocktime(61) | ||
set_node_times(self.nodes, self.mocktime) | ||
time.sleep(2) # make sure Cleanup() is called | ||
reconnect_isolated_node(self.nodes[3], 0) | ||
self.wait_for_mnauth(self.nodes[3], 2) | ||
self.nodes[0].sendrawtransaction(rawtx) | ||
# Make sure nodes 1 and 2 received the TX | ||
self.wait_for_tx(txid, self.nodes[1]) | ||
self.wait_for_tx(txid, self.nodes[2]) | ||
# Make sure signing is done on nodes 1 and 2 (it's async) | ||
time.sleep(5) | ||
# Make sure the TX wasn't accepted by nodes 4 and 5 | ||
self.wait_for_tx(txid, self.nodes[4], False, 1) | ||
self.wait_for_tx(txid, self.nodes[5], False, 1) | ||
# node 3 fully reconnected but the signing session is already timed out on it, so no IS lock | ||
self.wait_for_instantlock(txid, self.nodes[0], False, 1) | ||
if do_cycle_llmqs: | ||
self.cycle_llmqs() | ||
self.wait_for_instantlock(txid, self.nodes[0], False, 5) | ||
# Make node 0 consider the TX as safe | ||
self.bump_mocktime(10 * 60 + 1) | ||
self.nodes[0].setmocktime(self.mocktime) | ||
block = self.nodes[0].generate(1)[0] | ||
self.wait_for_chainlocked_block_all_nodes(block) | ||
|
||
if __name__ == '__main__': | ||
LLMQ_IS_RetroactiveSigning().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