diff --git a/test/functional/feature_asset_locks.py b/test/functional/feature_asset_locks.py index 990975a84c8c..4ddaec78efe3 100755 --- a/test/functional/feature_asset_locks.py +++ b/test/functional/feature_asset_locks.py @@ -45,6 +45,7 @@ from test_framework.wallet_util import bytes_to_wif llmq_type_test = 106 # LLMQType::LLMQ_TEST_PLATFORM +MNEHF_SIGNAL_TX_TYPE = 7 # TRANSACTION_MNHF_SIGNAL tiny_amount = int(Decimal("0.0007") * COIN) blocks_in_one_day = 100 HEIGHT_DIFF_EXPIRING = 48 @@ -175,9 +176,13 @@ def validate_credit_pool_balance(self, expected = None, block_hash = None): return expected def check_mempool_size(self): + # Masternodes submit the MnEHF signal transaction on their own as soon as a quorum + # able to sign it exists, so it is not part of what this test puts in the mempool. self.sync_mempools() for node in self.nodes: - assert_equal(node.getmempoolinfo()['size'], self.mempool_size) + own = [txid for txid in node.getrawmempool() + if node.getrawtransaction(txid, 1)['type'] != MNEHF_SIGNAL_TX_TYPE] + assert_equal(len(own), self.mempool_size) def check_mempool_result(self, result_expected, tx): """Wrapper to check result of testmempoolaccept on node_0's mempool""" diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 324d1498e579..eb00105ca1b8 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -2151,20 +2151,13 @@ def check_dkg_session(): self.wait_until(check_dkg_session, timeout=timeout, sleep=sleep) - def node_has_quorum_commitment(self, node, quorum_hash, llmq_type): + def node_quorum_commitment_types(self, node, quorum_hash): s = node.quorum("dkgstatus") - if "minableCommitments" not in s: - return False - commits = s["minableCommitments"] - for c in commits: - if c["llmqType"] != llmq_type: - continue - if c["quorumHash"] != quorum_hash: - continue - if c["quorumPublicKey"] == '0' * 96: - continue - return True - return False + return {c["llmqType"] for c in s.get("minableCommitments", []) + if c["quorumHash"] == quorum_hash and c["quorumPublicKey"] != '0' * 96} + + def node_has_quorum_commitment(self, node, quorum_hash, llmq_type): + return llmq_type in self.node_quorum_commitment_types(node, quorum_hash) def wait_for_quorum_commitment(self, quorum_hash, mninfos, llmq_type=100, timeout=15): def check_dkg_comitments(): @@ -2175,6 +2168,20 @@ def check_dkg_comitments(): self.wait_until(check_dkg_comitments, timeout=timeout) + def wait_for_quorum_commitments_on_miner(self, quorum_hash, mninfos, timeout=15): + # The final-commitment block carries a commitment for every llmq type whose mining + # window is open, so a commitment that has not reached the mining node in time is + # mined as a null one and its quorum is silently skipped for the whole cycle. Only + # commitments the masternodes actually produced are awaited, so a type whose DKG + # legitimately produced nothing does not hold this up. + def check_miner_commitments(): + expected = set() + for mn in mninfos: + expected |= self.node_quorum_commitment_types(mn.get_node(self), quorum_hash) + return expected <= self.node_quorum_commitment_types(self.nodes[0], quorum_hash) + + self.wait_until(check_miner_commitments, timeout=timeout) + def wait_for_quorum_list(self, quorum_hash, nodes, timeout=15, llmq_type_name="llmq_test"): def wait_func(): return quorum_hash in self.nodes[0].quorum('list')[llmq_type_name] @@ -2285,8 +2292,8 @@ def mine_quorum(self, llmq_type_name="llmq_test", llmq_type=100, expected_connec self.log.info("Waiting final commitment") self.wait_for_quorum_commitment(q, mninfos_online, llmq_type=llmq_type) - self.log.info("Waiting final commitment on mining node") - self.wait_until(lambda: self.node_has_quorum_commitment(self.nodes[0], q, llmq_type), timeout=15) + self.log.info("Waiting final commitments on mining node") + self.wait_for_quorum_commitments_on_miner(q, mninfos_online) self.log.info("Mining final commitment") self.bump_mocktime(1)