diff --git a/test/functional/feature_asset_locks.py b/test/functional/feature_asset_locks.py index 887461016a2b..d09ad35a97f2 100755 --- a/test/functional/feature_asset_locks.py +++ b/test/functional/feature_asset_locks.py @@ -95,15 +95,19 @@ def create_assetlock(self, coin, amount, pubkey): return tx_from_hex(lock_tx["hex"]) + def create_assetunlock_request_id(self, index): + # request ID = sha256("plwdtx", index) + request_id_buf = ser_string(b"plwdtx") + struct.pack(" 0: self.log.info(f"Generating batch of blocks {count} left") batch = min(50, count) count -= batch self.bump_mocktime(10 * 60 + 1) - self.generate(self.nodes[1], batch) + self.generate(self.nodes[1], batch, sync_fun=sync_fun) # This functional test intentionally setup only 2 MN and only 2 Evo nodes # to ensure that corner case of quorum with minimum amount of nodes as possible @@ -371,11 +393,12 @@ def test_asset_unlocks(self, node_wallet, node, pubkey): self.check_mempool_result(tx=asset_unlock_tx_duplicate_index, result_expected={'allowed': False, 'reject-reason' : 'bad-assetunlock-not-verified'}) - self.log.info("Validating payload hash calculation by using hard-coded message hash") + self.log.info("Validating payload quorum selection") asset_unlock_tx_payload = CAssetUnlockTx() asset_unlock_tx_payload.deserialize(BytesIO(asset_unlock_tx.vExtraPayload)) - assert_equal(asset_unlock_tx_payload.quorumHash, int(self.mninfo[0].get_node(self).quorum("selectquorum", llmq_type_test, 'e6c7a809d79f78ea85b72d5df7e9bd592aecf151e679d6e976b74f053a7f9056')["quorumHash"], 16)) + request_id = self.create_assetunlock_request_id(101) + assert_equal(asset_unlock_tx_payload.quorumHash, int(self.mninfo[0].get_node(self).quorum("selectquorum", llmq_type_test, request_id)["quorumHash"], 16)) txid = self.send_tx(asset_unlock_tx) @@ -451,7 +474,7 @@ def test_asset_unlocks(self, node_wallet, node, pubkey): for inode in self.nodes: inode.invalidateblock(block_asset_unlock) self.validate_credit_pool_balance(locked) - self.generate_batch(25) + self.generate_batch(25, sync_fun=lambda: self.sync_blocks()) self.validate_credit_pool_balance(locked) for inode in self.nodes: inode.reconsiderblock(block_to_reconsider) @@ -658,24 +681,9 @@ def test_withdrawals_fork(self, node_wallet, node, pubkey): assert softfork_active(node_wallet, 'withdrawals') self.log.info(f'post-withdrawals height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}') - index = 501 - while index < 511: - self.log.info(f"Generating new Asset Unlock tx, index={index}...") - asset_unlock_tx = self.create_assetunlock(index, COIN, pubkey) - asset_unlock_tx_payload = CAssetUnlockTx() - asset_unlock_tx_payload.deserialize(BytesIO(asset_unlock_tx.vExtraPayload)) - - self.log.info("Check that Asset Unlock tx is valid for current quorum") - self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) - - quorumHash_str = format(asset_unlock_tx_payload.quorumHash, '064x') - assert quorumHash_str in node_wallet.quorum('list')['llmq_test_platform'] - - if quorumHash_str != node_wallet.quorum('list')['llmq_test_platform'][-1]: - self.log.info("The quorum for this msg-hash is not the last one in the list of active quorums. Try again!") - index += 1 - else: - break + asset_unlock_tx, asset_unlock_tx_payload, quorumHash_str = self.create_assetunlock_for_oldest_quorum(501, COIN, pubkey) + self.log.info("Check that Asset Unlock tx is valid for current quorum") + self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) assert quorumHash_str in node_wallet.quorum('list')['llmq_test_platform'] self.log.info("Generate one more quorum to make signing quorum inactive but still valid") @@ -710,24 +718,9 @@ def test_v24_fork(self, node_wallet, node, pubkey): self.activate_by_name('v24', 750) self.log.info(f'post-v24 height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}') - index = 601 - while index < 611: - self.log.info(f"Generating new Asset Unlock tx, index={index}...") - asset_unlock_tx = self.create_assetunlock(index, COIN, pubkey) - asset_unlock_tx_payload = CAssetUnlockTx() - asset_unlock_tx_payload.deserialize(BytesIO(asset_unlock_tx.vExtraPayload)) - - self.log.info("Check that Asset Unlock tx is valid for current quorum") - self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) - - quorumHash_str = format(asset_unlock_tx_payload.quorumHash, '064x') - assert quorumHash_str in node_wallet.quorum('list')['llmq_test_platform'] - - if quorumHash_str != node_wallet.quorum('list')['llmq_test_platform'][-1]: - self.log.info("The quorum for this msg-hash is not the last one in the list of active quorums. Try again!") - index += 1 - else: - break + asset_unlock_tx, asset_unlock_tx_payload, quorumHash_str = self.create_assetunlock_for_oldest_quorum(601, COIN, pubkey) + self.log.info("Check that Asset Unlock tx is valid for current quorum") + self.check_mempool_result(tx=asset_unlock_tx, result_expected={'allowed': True, 'fees': {'base': Decimal(str(tiny_amount / COIN))}}) assert quorumHash_str in node_wallet.quorum('list')['llmq_test_platform'] self.log.info("Generate one more quorum to make signing quorum inactive but still valid") diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index a9fe5d2aeae8..fc5030a62bc4 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -2095,24 +2095,25 @@ 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): + 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 + def wait_for_quorum_commitment(self, quorum_hash, mninfos, llmq_type=100, timeout=15): def check_dkg_comitments(): for mn in mninfos: - s = mn.get_node(self).quorum("dkgstatus") - if "minableCommitments" not in s: - return False - commits = s["minableCommitments"] - c_ok = False - for c in commits: - if c["llmqType"] != llmq_type: - continue - if c["quorumHash"] != quorum_hash: - continue - if c["quorumPublicKey"] == '0' * 96: - continue - c_ok = True - break - if not c_ok: + if not self.node_has_quorum_commitment(mn.get_node(self), quorum_hash, llmq_type): return False return True @@ -2201,6 +2202,9 @@ 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("Mining final commitment") self.bump_mocktime(1) self.nodes[0].getblocktemplate() # this calls CreateNewBlock