From c8e4d67d69970221dc85678db9830663c49bcd99 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 22 Apr 2022 00:39:55 +0300 Subject: [PATCH 1/7] tests: move `move_to_next_cycle` to `DashTestFramework` --- test/functional/feature_llmq_is_migration.py | 17 +---------------- test/functional/feature_llmq_rotation.py | 16 ---------------- .../functional/test_framework/test_framework.py | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/test/functional/feature_llmq_is_migration.py b/test/functional/feature_llmq_is_migration.py index 213a16c4bc2e..2ad25c31125e 100755 --- a/test/functional/feature_llmq_is_migration.py +++ b/test/functional/feature_llmq_is_migration.py @@ -6,7 +6,7 @@ from test_framework.messages import CTransaction, FromHex, hash256, ser_compact_size, ser_string from test_framework.test_framework import DashTestFramework -from test_framework.util import wait_until, connect_nodes, sync_blocks +from test_framework.util import wait_until, connect_nodes ''' feature_llmq_is_migration.py @@ -118,20 +118,5 @@ def run_test(self): assert not n.quorum("hasrecsig", 104, request_id2, txid2) - def move_to_next_cycle(self): - cycle_length = 24 - mninfos_online = self.mninfo.copy() - nodes = [self.nodes[0]] + [mn.node for mn in mninfos_online] - cur_block = self.nodes[0].getblockcount() - - # move forward to next DKG - skip_count = cycle_length - (cur_block % cycle_length) - if skip_count != 0: - self.bump_mocktime(1, nodes=nodes) - self.nodes[0].generate(skip_count) - sync_blocks(nodes) - time.sleep(1) - self.log.info('Moved from block %d to %d' % (cur_block, self.nodes[0].getblockcount())) - if __name__ == '__main__': LLMQISMigrationTest().main() diff --git a/test/functional/feature_llmq_rotation.py b/test/functional/feature_llmq_rotation.py index 18f574a0e872..6e737ed49b3e 100755 --- a/test/functional/feature_llmq_rotation.py +++ b/test/functional/feature_llmq_rotation.py @@ -9,7 +9,6 @@ Checks LLMQs Quorum Rotation ''' -import time from test_framework.test_framework import DashTestFramework from test_framework.util import ( assert_equal, @@ -128,21 +127,6 @@ def run_test(self): wait_until(lambda: self.nodes[0].getbestblockhash() == new_quorum_blockhash, sleep=1) assert_equal(self.nodes[0].quorum("list", llmq_type), new_quorum_list) - def move_to_next_cycle(self): - cycle_length = 24 - mninfos_online = self.mninfo.copy() - nodes = [self.nodes[0]] + [mn.node for mn in mninfos_online] - cur_block = self.nodes[0].getblockcount() - - # move forward to next DKG - skip_count = cycle_length - (cur_block % cycle_length) - if skip_count != 0: - self.bump_mocktime(1, nodes=nodes) - self.nodes[0].generate(skip_count) - sync_blocks(nodes) - time.sleep(1) - self.log.info('Moved from block %d to %d' % (cur_block, self.nodes[0].getblockcount())) - if __name__ == '__main__': LLMQQuorumRotationTest().main() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index a0a589db9256..6a5516ae74bd 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1467,6 +1467,21 @@ def mine_cycle_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected return (quorum_info_0, quorum_info_1) + def move_to_next_cycle(self): + cycle_length = 24 + mninfos_online = self.mninfo.copy() + nodes = [self.nodes[0]] + [mn.node for mn in mninfos_online] + cur_block = self.nodes[0].getblockcount() + + # move forward to next DKG + skip_count = cycle_length - (cur_block % cycle_length) + if skip_count != 0: + self.bump_mocktime(1, nodes=nodes) + self.nodes[0].generate(skip_count) + sync_blocks(nodes) + time.sleep(1) + self.log.info('Moved from block %d to %d' % (cur_block, self.nodes[0].getblockcount())) + def get_recovered_sig(self, rec_sig_id, rec_sig_msg_hash, llmq_type=100, node=None): # Note: recsigs aren't relayed to regular nodes by default, # make sure to pick a mn as a node to query for recsigs. From 211d4849c371e3bd7686b8f4dde892cedf3b297a Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 22 Apr 2022 00:47:36 +0300 Subject: [PATCH 2/7] tests: set correct defaults for `mine_cycle_quorum` --- test/functional/feature_llmq_is_migration.py | 2 +- test/functional/test_framework/test_framework.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/feature_llmq_is_migration.py b/test/functional/feature_llmq_is_migration.py index 2ad25c31125e..68ff79291c6b 100755 --- a/test/functional/feature_llmq_is_migration.py +++ b/test/functional/feature_llmq_is_migration.py @@ -91,7 +91,7 @@ def run_test(self): self.move_to_next_cycle() self.log.info("Cycle H+2C height:" + str(self.nodes[0].getblockcount())) - (quorum_info_0_0, quorum_info_0_1) = self.mine_cycle_quorum("llmq_test_dip0024", 103) + (quorum_info_0_0, quorum_info_0_1) = self.mine_cycle_quorum() q_list = self.nodes[0].quorum("list") self.log.info(q_list) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 6a5516ae74bd..85ad30f3a866 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1330,7 +1330,7 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec return new_quorum - def mine_cycle_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connections=None, expected_members=None, expected_contributions=None, expected_complaints=0, expected_justifications=0, expected_commitments=None, mninfos_online=None, mninfos_valid=None): + def mine_cycle_quorum(self, llmq_type_name="llmq_test_dip0024", llmq_type=103, expected_connections=None, expected_members=None, expected_contributions=None, expected_complaints=0, expected_justifications=0, expected_commitments=None, mninfos_online=None, mninfos_valid=None): spork21_active = self.nodes[0].spork('show')['SPORK_21_QUORUM_ALL_CONNECTED'] <= 1 spork23_active = self.nodes[0].spork('show')['SPORK_23_QUORUM_POSE'] <= 1 From 378c8282f8ebc43ddba560b3f6366ccc1d126fd5 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 22 Apr 2022 00:48:39 +0300 Subject: [PATCH 3/7] tests: use correct quorum type in `create_islock` --- test/functional/test_framework/test_framework.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 85ad30f3a866..c7c53ae4875b 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1038,13 +1038,14 @@ def create_islock(self, hextx, deterministic=False): request_id = hash256(request_id_buf)[::-1].hex() message_hash = tx.hash + llmq_type = 103 if deterministic else 104 quorum_member = None for mn in self.mninfo: - res = mn.node.quorum('sign', 104, request_id, message_hash) + res = mn.node.quorum('sign', llmq_type, request_id, message_hash) if (res and quorum_member is None): quorum_member = mn - rec_sig = self.get_recovered_sig(request_id, message_hash, node=quorum_member.node, llmq_type=104) + rec_sig = self.get_recovered_sig(request_id, message_hash, node=quorum_member.node, llmq_type=llmq_type) if deterministic: block_count = quorum_member.node.getblockcount() From 70179ac776d70a7079128525f22b07da4582954f Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 22 Apr 2022 01:12:30 +0300 Subject: [PATCH 4/7] tests: fix `rpc_verifyislock.py` --- test/functional/rpc_verifyislock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/rpc_verifyislock.py b/test/functional/rpc_verifyislock.py index 7c503fee9725..e16a8f694abd 100755 --- a/test/functional/rpc_verifyislock.py +++ b/test/functional/rpc_verifyislock.py @@ -82,7 +82,7 @@ def run_test(self): break assert selected_hash == oldest_quorum_hash # Create the ISLOCK, then mine a quorum to move the signing quorum out of the active set - islock = self.create_islock(rawtx, True) + islock = self.create_islock(rawtx, False) # Mine one block to trigger the "signHeight + dkgInterval" verification for the ISLOCK self.mine_quorum() # Verify the ISLOCK for a transaction that is not yet known by the node From 86fa00ac31279c3c2fbf84b7c440252560784cad Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 22 Apr 2022 00:50:44 +0300 Subject: [PATCH 5/7] tests: fix `feature_llmq_is_cl_conflicts.py` --- test/functional/feature_llmq_is_cl_conflicts.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/functional/feature_llmq_is_cl_conflicts.py b/test/functional/feature_llmq_is_cl_conflicts.py index 70177051186f..c0821db3adca 100755 --- a/test/functional/feature_llmq_is_cl_conflicts.py +++ b/test/functional/feature_llmq_is_cl_conflicts.py @@ -51,8 +51,8 @@ def on_getdata(self, message): class LLMQ_IS_CL_Conflicts(DashTestFramework): def set_test_params(self): - self.set_dash_test_params(4, 3, fast_dip3_enforcement=True) - #disable_mocktime() + self.set_dash_test_params(5, 4, fast_dip3_enforcement=True) + self.set_dash_llmq_test_params(4, 4) def run_test(self): self.activate_dip8() @@ -72,6 +72,14 @@ def run_test(self): self.test_chainlock_overrides_islock(True, False) self.test_chainlock_overrides_islock(True, True) self.test_chainlock_overrides_islock_overrides_nonchainlock(False) + self.activate_dip0024() + self.log.info("Activated DIP0024 at height:" + str(self.nodes[0].getblockcount())) + self.test_chainlock_overrides_islock_overrides_nonchainlock(False) + # At this point, we need to move forward 3 cycles (3 x 24 blocks) so the first 3 quarters can be created (without DKG sessions) + self.move_to_next_cycle() + self.move_to_next_cycle() + self.move_to_next_cycle() + self.mine_cycle_quorum() self.test_chainlock_overrides_islock_overrides_nonchainlock(True) def test_chainlock_overrides_islock(self, test_block_conflict, mine_confllicting=False): @@ -320,7 +328,7 @@ def create_block(self, node, vtx=[]): coinbase.calc_sha256() - block = create_block(int(tip_hash, 16), coinbase, ntime=bt['curtime']) + block = create_block(int(tip_hash, 16), coinbase, ntime=bt['curtime'], version=bt['version']) block.vtx += vtx # Add quorum commitments from template From e9978168cec25d9d5a4b17231e45fa3500184efe Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 22 Apr 2022 00:56:10 +0300 Subject: [PATCH 6/7] tests: isolate zmq subscribers in `interface_zmq_dash.py` this lets us call `test_*_publishers()` in any order and any number of times --- test/functional/interface_zmq_dash.py | 73 ++++++++++++++++----------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/test/functional/interface_zmq_dash.py b/test/functional/interface_zmq_dash.py index 4410ae53b12a..0a5a9c714df2 100755 --- a/test/functional/interface_zmq_dash.py +++ b/test/functional/interface_zmq_dash.py @@ -52,6 +52,21 @@ class ZMQPublisher(Enum): raw_recovered_sig = "rawrecoveredsig" +class ZMQSubscriber: + def __init__(self, socket, topic): + self.socket = socket + self.topic = topic + + import zmq + self.socket.setsockopt(zmq.SUBSCRIBE, self.topic) + + def receive(self, flags=0): + topic, body, seq = self.socket.recv_multipart(flags) + # Topic should match the subscriber topic. + assert_equal(topic, self.topic) + return io.BytesIO(body) + + class TestP2PConn(P2PInterface): def __init__(self): super().__init__() @@ -97,16 +112,15 @@ def skip_test_if_missing_module(self): self.skip_if_no_wallet() def run_test(self): + self.subscribers = {} # Check that dashd has been built with ZMQ enabled. config = configparser.ConfigParser() config.read_file(open(self.options.configfile)) import zmq try: - # Setup the ZMQ subscriber socket + # Setup the ZMQ subscriber context self.zmq_context = zmq.Context() - self.socket = self.zmq_context.socket(zmq.SUB) - self.socket.connect(self.address) # Initialize the network self.activate_dip8() self.nodes[0].spork("SPORK_17_QUORUM_DKG_ENABLED", 0) @@ -131,21 +145,19 @@ def run_test(self): self.zmq_context.destroy(linger=None) def subscribe(self, publishers): + import zmq + # Setup the ZMQ subscriber socket + socket = self.zmq_context.socket(zmq.SUB) + socket.set(zmq.RCVTIMEO, 60000) + socket.connect(self.address) # Subscribe to a list of ZMQPublishers for pub in publishers: - self.socket.subscribe(pub.value) + self.subscribers[pub] = ZMQSubscriber(socket, pub.value.encode()) def unsubscribe(self, publishers): # Unsubscribe from a list of ZMQPublishers for pub in publishers: - self.socket.unsubscribe(pub.value) - - def receive(self, publisher, flags=0): - # Receive a ZMQ message and validate it's sent from the correct ZMQPublisher - topic, body, seq = self.socket.recv_multipart(flags) - # Topic should match the publisher value - assert_equal(topic.decode(), publisher.value) - return io.BytesIO(body) + del self.subscribers[pub] def test_recovered_signature_publishers(self): @@ -153,11 +165,11 @@ def validate_recovered_sig(request_id, msg_hash): # Make sure the recovered sig exists by RPC rpc_recovered_sig = self.get_recovered_sig(request_id, msg_hash) # Validate hashrecoveredsig - zmq_recovered_sig_hash = self.receive(ZMQPublisher.hash_recovered_sig).read(32).hex() + zmq_recovered_sig_hash = self.subscribers[ZMQPublisher.hash_recovered_sig].receive().read(32).hex() assert_equal(zmq_recovered_sig_hash, msg_hash) # Validate rawrecoveredsig zmq_recovered_sig_raw = CRecoveredSig() - zmq_recovered_sig_raw.deserialize(self.receive(ZMQPublisher.raw_recovered_sig)) + zmq_recovered_sig_raw.deserialize(self.subscribers[ZMQPublisher.raw_recovered_sig].receive()) assert_equal(zmq_recovered_sig_raw.llmqType, rpc_recovered_sig['llmqType']) assert_equal(uint256_to_string(zmq_recovered_sig_raw.quorumHash), rpc_recovered_sig['quorumHash']) assert_equal(uint256_to_string(zmq_recovered_sig_raw.id), rpc_recovered_sig['id']) @@ -207,15 +219,15 @@ def test_chainlock_publishers(self): rpc_chain_lock_hash = rpc_chain_locked_block["hash"] assert_equal(generated_hash, rpc_chain_lock_hash) # Validate hashchainlock - zmq_chain_lock_hash = self.receive(ZMQPublisher.hash_chain_lock).read(32).hex() + zmq_chain_lock_hash = self.subscribers[ZMQPublisher.hash_chain_lock].receive().read(32).hex() assert_equal(zmq_chain_lock_hash, rpc_best_chain_lock_hash) # Validate rawchainlock zmq_chain_locked_block = CBlock() - zmq_chain_locked_block.deserialize(self.receive(ZMQPublisher.raw_chain_lock)) + zmq_chain_locked_block.deserialize(self.subscribers[ZMQPublisher.raw_chain_lock].receive()) assert zmq_chain_locked_block.is_valid() assert_equal(zmq_chain_locked_block.hash, rpc_chain_lock_hash) # Validate rawchainlocksig - zmq_chain_lock_sig_stream = self.receive(ZMQPublisher.raw_chain_lock_sig) + zmq_chain_lock_sig_stream = self.subscribers[ZMQPublisher.raw_chain_lock_sig].receive() zmq_chain_locked_block = CBlock() zmq_chain_locked_block.deserialize(zmq_chain_lock_sig_stream) assert zmq_chain_locked_block.is_valid() @@ -251,15 +263,15 @@ def test_instantsend_publishers(self): rpc_raw_tx_1_hash = self.nodes[0].sendrawtransaction(rpc_raw_tx_1['hex']) self.wait_for_instantlock(rpc_raw_tx_1_hash, self.nodes[0]) # Validate hashtxlock - zmq_tx_lock_hash = self.receive(ZMQPublisher.hash_tx_lock).read(32).hex() + zmq_tx_lock_hash = self.subscribers[ZMQPublisher.hash_tx_lock].receive().read(32).hex() assert_equal(zmq_tx_lock_hash, rpc_raw_tx_1['txid']) # Validate rawtxlock zmq_tx_lock_raw = CTransaction() - zmq_tx_lock_raw.deserialize(self.receive(ZMQPublisher.raw_tx_lock)) + zmq_tx_lock_raw.deserialize(self.subscribers[ZMQPublisher.raw_tx_lock].receive()) assert zmq_tx_lock_raw.is_valid() assert_equal(zmq_tx_lock_raw.hash, rpc_raw_tx_1['txid']) # Validate rawtxlocksig - zmq_tx_lock_sig_stream = self.receive(ZMQPublisher.raw_tx_lock_sig) + zmq_tx_lock_sig_stream = self.subscribers[ZMQPublisher.raw_tx_lock_sig].receive() zmq_tx_lock_tx = CTransaction() zmq_tx_lock_tx.deserialize(zmq_tx_lock_sig_stream) assert zmq_tx_lock_tx.is_valid() @@ -271,17 +283,17 @@ def test_instantsend_publishers(self): # which already got the InstantSend lock. assert_raises_rpc_error(-26, "tx-txlock-conflict", self.nodes[0].sendrawtransaction, rpc_raw_tx_2['hex']) # Validate hashinstantsenddoublespend - zmq_double_spend_hash2 = self.receive(ZMQPublisher.hash_instantsend_doublespend).read(32).hex() - zmq_double_spend_hash1 = self.receive(ZMQPublisher.hash_instantsend_doublespend).read(32).hex() + zmq_double_spend_hash2 = self.subscribers[ZMQPublisher.hash_instantsend_doublespend].receive().read(32).hex() + zmq_double_spend_hash1 = self.subscribers[ZMQPublisher.hash_instantsend_doublespend].receive().read(32).hex() assert_equal(zmq_double_spend_hash2, rpc_raw_tx_2['txid']) assert_equal(zmq_double_spend_hash1, rpc_raw_tx_1['txid']) # Validate rawinstantsenddoublespend zmq_double_spend_tx_2 = CTransaction() - zmq_double_spend_tx_2.deserialize(self.receive(ZMQPublisher.raw_instantsend_doublespend)) + zmq_double_spend_tx_2.deserialize(self.subscribers[ZMQPublisher.raw_instantsend_doublespend].receive()) assert zmq_double_spend_tx_2.is_valid() assert_equal(zmq_double_spend_tx_2.hash, rpc_raw_tx_2['txid']) zmq_double_spend_tx_1 = CTransaction() - zmq_double_spend_tx_1.deserialize(self.receive(ZMQPublisher.raw_instantsend_doublespend)) + zmq_double_spend_tx_1.deserialize(self.subscribers[ZMQPublisher.raw_instantsend_doublespend].receive()) assert zmq_double_spend_tx_1.is_valid() assert_equal(zmq_double_spend_tx_1.hash, rpc_raw_tx_1['txid']) # No islock notifications when tx is not received yet @@ -292,7 +304,7 @@ def test_instantsend_publishers(self): # Validate NO hashtxlock time.sleep(1) try: - self.receive(ZMQPublisher.hash_tx_lock, zmq.NOBLOCK) + self.subscribers[ZMQPublisher.hash_tx_lock].receive(zmq.NOBLOCK) assert False except zmq.ZMQError: # this is expected @@ -301,7 +313,7 @@ def test_instantsend_publishers(self): self.test_node.send_tx(FromHex(msg_tx(), rpc_raw_tx_3['hex'])) self.wait_for_instantlock(rpc_raw_tx_3['txid'], self.nodes[0]) # Validate hashtxlock - zmq_tx_lock_hash = self.receive(ZMQPublisher.hash_tx_lock).read(32).hex() + zmq_tx_lock_hash = self.subscribers[ZMQPublisher.hash_tx_lock].receive().read(32).hex() assert_equal(zmq_tx_lock_hash, rpc_raw_tx_3['txid']) # Drop test node connection self.nodes[0].disconnect_p2ps() @@ -337,10 +349,11 @@ def test_governance_publishers(self): self.sync_blocks() rpc_proposal_hash = self.nodes[0].gobject("submit", "0", proposal_rev, proposal_time, proposal_hex, collateral) # Validate hashgovernanceobject - zmq_governance_object_hash = self.receive(ZMQPublisher.hash_governance_object).read(32).hex() + zmq_governance_object_hash = self.subscribers[ZMQPublisher.hash_governance_object].receive().read(32).hex() assert_equal(zmq_governance_object_hash, rpc_proposal_hash) + # Validate rawgovernanceobject zmq_governance_object_raw = CGovernanceObject() - zmq_governance_object_raw.deserialize(self.receive(ZMQPublisher.raw_governance_object)) + zmq_governance_object_raw.deserialize(self.subscribers[ZMQPublisher.raw_governance_object].receive()) assert_equal(zmq_governance_object_raw.nHashParent, 0) assert_equal(zmq_governance_object_raw.nRevision, proposal_rev) assert_equal(zmq_governance_object_raw.nTime, proposal_time) @@ -365,11 +378,11 @@ def test_governance_publishers(self): self.nodes[0].gobject("vote-many", rpc_proposal_hash, map_vote_signals[1], map_vote_outcomes[1]) rpc_proposal_votes = self.nodes[0].gobject('getcurrentvotes', rpc_proposal_hash) # Validate hashgovernancevote - zmq_governance_vote_hash = self.receive(ZMQPublisher.hash_governance_vote).read(32).hex() + zmq_governance_vote_hash = self.subscribers[ZMQPublisher.hash_governance_vote].receive().read(32).hex() assert zmq_governance_vote_hash in rpc_proposal_votes # Validate rawgovernancevote zmq_governance_vote_raw = CGovernanceVote() - zmq_governance_vote_raw.deserialize(self.receive(ZMQPublisher.raw_governance_vote)) + zmq_governance_vote_raw.deserialize(self.subscribers[ZMQPublisher.raw_governance_vote].receive()) assert_equal(uint256_to_string(zmq_governance_vote_raw.nParentHash), rpc_proposal_hash) rpc_vote_parts = rpc_proposal_votes[zmq_governance_vote_hash].split(':') rpc_outpoint_parts = rpc_vote_parts[0].split('-') From aa38b74fbc7533ab7250585da801ad3511d5463c Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 22 Apr 2022 00:58:58 +0300 Subject: [PATCH 7/7] tests: check zmq for both deterministic and non-deterministic islocks --- test/functional/interface_zmq_dash.py | 39 +++++++++++++++++++-------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/test/functional/interface_zmq_dash.py b/test/functional/interface_zmq_dash.py index 0a5a9c714df2..dc5caaf4b974 100755 --- a/test/functional/interface_zmq_dash.py +++ b/test/functional/interface_zmq_dash.py @@ -28,6 +28,7 @@ msg_clsig, msg_inv, msg_isdlock, + msg_islock, msg_tx, ser_string, uint256_from_str, @@ -73,18 +74,18 @@ def __init__(self): self.islocks = {} self.txes = {} - def send_islock(self, islock): + def send_islock(self, islock, deterministic): hash = uint256_from_str(hash256(islock.serialize())) self.islocks[hash] = islock - inv = msg_inv([CInv(30, hash)]) + inv = msg_inv([CInv(31 if deterministic else 30, hash)]) self.send_message(inv) - def send_tx(self, tx): + def send_tx(self, tx, deterministic): hash = uint256_from_str(hash256(tx.serialize())) self.txes[hash] = tx - inv = msg_inv([CInv(30, hash)]) + inv = msg_inv([CInv(31 if deterministic else 30, hash)]) self.send_message(inv) def on_getdata(self, message): @@ -104,7 +105,10 @@ def set_test_params(self): node0_extra_args.append("-whitelist=127.0.0.1") node0_extra_args.append("-watchquorums") # have to watch quorums to receive recsigs and trigger zmq - self.set_dash_test_params(4, 3, fast_dip3_enforcement=True, extra_args=[node0_extra_args, [], [], []]) + extra_args = [[]] * 5 + extra_args[0] = node0_extra_args + self.set_dash_test_params(5, 4, fast_dip3_enforcement=True, extra_args=extra_args) + self.set_dash_llmq_test_params(4, 4) def skip_test_if_missing_module(self): self.skip_if_no_py3_zmq() @@ -136,9 +140,22 @@ def run_test(self): # Test all dash related ZMQ publisher self.test_recovered_signature_publishers() self.test_chainlock_publishers() - self.test_instantsend_publishers() self.test_governance_publishers() self.test_getzmqnotifications() + self.test_instantsend_publishers(False) + self.activate_dip0024() + self.wait_for_chainlocked_block_all_nodes(self.nodes[0].getbestblockhash()) + self.log.info("Activated DIP0024 at height:" + str(self.nodes[0].getblockcount())) + self.test_instantsend_publishers(False) + # At this point, we need to move forward 3 cycles (3 x 24 blocks) so the first 3 quarters can be created (without DKG sessions) + self.move_to_next_cycle() + self.test_instantsend_publishers(False) + self.move_to_next_cycle() + self.test_instantsend_publishers(False) + self.move_to_next_cycle() + self.test_instantsend_publishers(False) + self.mine_cycle_quorum() + self.test_instantsend_publishers(True) finally: # Destroy the ZMQ context. self.log.debug("Destroying ZMQ context") @@ -240,7 +257,7 @@ def test_chainlock_publishers(self): # Unsubscribe from ChainLock messages self.unsubscribe(chain_lock_publishers) - def test_instantsend_publishers(self): + def test_instantsend_publishers(self, deterministic): import zmq instantsend_publishers = [ ZMQPublisher.hash_tx_lock, @@ -276,7 +293,7 @@ def test_instantsend_publishers(self): zmq_tx_lock_tx.deserialize(zmq_tx_lock_sig_stream) assert zmq_tx_lock_tx.is_valid() assert_equal(zmq_tx_lock_tx.hash, rpc_raw_tx_1['txid']) - zmq_tx_lock = msg_isdlock() + zmq_tx_lock = msg_isdlock() if deterministic else msg_islock() zmq_tx_lock.deserialize(zmq_tx_lock_sig_stream) assert_equal(uint256_to_string(zmq_tx_lock.txid), rpc_raw_tx_1['txid']) # Try to send the second transaction. This must throw an RPC error because it conflicts with rpc_raw_tx_1 @@ -299,8 +316,8 @@ def test_instantsend_publishers(self): # No islock notifications when tx is not received yet self.nodes[0].generate(1) rpc_raw_tx_3 = self.create_raw_tx(self.nodes[0], self.nodes[0], 1, 1, 100) - islock = self.create_islock(rpc_raw_tx_3['hex']) - self.test_node.send_islock(islock) + islock = self.create_islock(rpc_raw_tx_3['hex'], deterministic) + self.test_node.send_islock(islock, deterministic) # Validate NO hashtxlock time.sleep(1) try: @@ -310,7 +327,7 @@ def test_instantsend_publishers(self): # this is expected pass # Now send the tx itself - self.test_node.send_tx(FromHex(msg_tx(), rpc_raw_tx_3['hex'])) + self.test_node.send_tx(FromHex(msg_tx(), rpc_raw_tx_3['hex']), deterministic) self.wait_for_instantlock(rpc_raw_tx_3['txid'], self.nodes[0]) # Validate hashtxlock zmq_tx_lock_hash = self.subscribers[ZMQPublisher.hash_tx_lock].receive().read(32).hex()