Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc_rpc_isfinal.py
Show All 16 Lines | |||||
class AvalancheIsFinalTest(BitcoinTestFramework): | class AvalancheIsFinalTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
self.num_nodes = 1 | self.num_nodes = 1 | ||||
self.extra_args = [ | self.extra_args = [ | ||||
[ | [ | ||||
'-avaproofstakeutxodustthreshold=1000000', | "-avaproofstakeutxodustthreshold=1000000", | ||||
'-avaproofstakeutxoconfirmations=1', | "-avaproofstakeutxoconfirmations=1", | ||||
'-avacooldown=0', | "-avacooldown=0", | ||||
'-avaminquorumstake=0', | "-avaminquorumstake=0", | ||||
'-avaminavaproofsnodecount=0', | "-avaminavaproofsnodecount=0", | ||||
] | ] | ||||
] | ] | ||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
assert_raises_rpc_error( | assert_raises_rpc_error( | ||||
-1, | -1, | ||||
"Avalanche is not ready to poll yet.", | "Avalanche is not ready to poll yet.", | ||||
self.nodes[0].isfinalblock, | self.nodes[0].isfinalblock, | ||||
tip, | tip, | ||||
) | ) | ||||
assert_raises_rpc_error( | assert_raises_rpc_error( | ||||
-1, | -1, | ||||
"Avalanche is not ready to poll yet.", | "Avalanche is not ready to poll yet.", | ||||
self.nodes[0].isfinaltransaction, | self.nodes[0].isfinaltransaction, | ||||
node.getblock(tip)['tx'][0], | node.getblock(tip)["tx"][0], | ||||
tip, | tip, | ||||
) | ) | ||||
# Build a fake quorum of nodes. | # Build a fake quorum of nodes. | ||||
def get_quorum(): | def get_quorum(): | ||||
return [node.add_p2p_connection(AvaP2PInterface(self, node)) | return [ | ||||
for _ in range(0, QUORUM_NODE_COUNT)] | node.add_p2p_connection(AvaP2PInterface(self, node)) | ||||
for _ in range(0, QUORUM_NODE_COUNT) | |||||
] | |||||
# Pick one node from the quorum for polling. | # Pick one node from the quorum for polling. | ||||
quorum = get_quorum() | quorum = get_quorum() | ||||
def is_quorum_established(): | def is_quorum_established(): | ||||
return node.getavalancheinfo()['ready_to_poll'] is True | return node.getavalancheinfo()["ready_to_poll"] is True | ||||
self.wait_until(is_quorum_established) | self.wait_until(is_quorum_established) | ||||
blockhash = self.generate(node, 1, sync_fun=self.no_op)[0] | blockhash = self.generate(node, 1, sync_fun=self.no_op)[0] | ||||
cb_txid = node.getblock(blockhash)['tx'][0] | cb_txid = node.getblock(blockhash)["tx"][0] | ||||
assert not node.isfinalblock(blockhash) | assert not node.isfinalblock(blockhash) | ||||
assert not node.isfinaltransaction(cb_txid, blockhash) | assert not node.isfinaltransaction(cb_txid, blockhash) | ||||
def is_finalblock(blockhash): | def is_finalblock(blockhash): | ||||
can_find_inv_in_poll(quorum, int(blockhash, 16)) | can_find_inv_in_poll(quorum, int(blockhash, 16)) | ||||
return node.isfinalblock(blockhash) | return node.isfinalblock(blockhash) | ||||
with node.assert_debug_log([f"Avalanche finalized block {blockhash}"]): | with node.assert_debug_log([f"Avalanche finalized block {blockhash}"]): | ||||
self.wait_until(lambda: is_finalblock(blockhash)) | self.wait_until(lambda: is_finalblock(blockhash)) | ||||
assert node.isfinaltransaction(cb_txid, blockhash) | assert node.isfinaltransaction(cb_txid, blockhash) | ||||
self.log.info("Check block ancestors are finalized as well") | self.log.info("Check block ancestors are finalized as well") | ||||
tip_height = node.getblockheader(blockhash)['height'] | tip_height = node.getblockheader(blockhash)["height"] | ||||
for height in range(0, tip_height): | for height in range(0, tip_height): | ||||
blockhash = node.getblockhash(height) | blockhash = node.getblockhash(height) | ||||
assert node.isfinalblock(blockhash) | assert node.isfinalblock(blockhash) | ||||
txid = node.getblock(blockhash)['tx'][0] | txid = node.getblock(blockhash)["tx"][0] | ||||
assert node.isfinaltransaction(txid, blockhash) | assert node.isfinaltransaction(txid, blockhash) | ||||
if self.is_wallet_compiled(): | if self.is_wallet_compiled(): | ||||
self.log.info("Check mempool transactions are not finalized") | self.log.info("Check mempool transactions are not finalized") | ||||
# Mature some utxos | # Mature some utxos | ||||
tip = self.generate(node, 100, sync_fun=self.no_op)[-1] | tip = self.generate(node, 100, sync_fun=self.no_op)[-1] | ||||
wallet_txid = node.sendtoaddress( | wallet_txid = node.sendtoaddress(ADDRESS_ECREG_UNSPENDABLE, 1_000_000) | ||||
ADDRESS_ECREG_UNSPENDABLE, 1_000_000) | |||||
assert wallet_txid in node.getrawmempool() | assert wallet_txid in node.getrawmempool() | ||||
assert_raises_rpc_error( | assert_raises_rpc_error( | ||||
-5, | -5, | ||||
"No such transaction found in the provided block.", | "No such transaction found in the provided block.", | ||||
node.isfinaltransaction, | node.isfinaltransaction, | ||||
wallet_txid, | wallet_txid, | ||||
tip, | tip, | ||||
) | ) | ||||
self.log.info( | self.log.info( | ||||
"A transaction is only finalized if the containing block is finalized") | "A transaction is only finalized if the containing block is finalized" | ||||
) | |||||
tip = self.generate(node, 1, sync_fun=self.no_op)[0] | tip = self.generate(node, 1, sync_fun=self.no_op)[0] | ||||
assert wallet_txid not in node.getrawmempool() | assert wallet_txid not in node.getrawmempool() | ||||
assert not node.isfinaltransaction(wallet_txid, tip) | assert not node.isfinaltransaction(wallet_txid, tip) | ||||
self.wait_until(lambda: is_finalblock(tip)) | self.wait_until(lambda: is_finalblock(tip)) | ||||
assert node.isfinaltransaction(wallet_txid, tip) | assert node.isfinaltransaction(wallet_txid, tip) | ||||
# Needs -txindex | # Needs -txindex | ||||
assert_raises_rpc_error( | assert_raises_rpc_error( | ||||
-5, | -5, | ||||
"No such transaction. Use -txindex or provide a block hash to enable blockchain transaction queries.", | ( | ||||
"No such transaction. Use -txindex or provide a block hash to" | |||||
" enable blockchain transaction queries." | |||||
), | |||||
node.isfinaltransaction, | node.isfinaltransaction, | ||||
wallet_txid, | wallet_txid, | ||||
) | ) | ||||
self.log.info( | self.log.info("Repeat with -txindex so we don't need the blockhash") | ||||
"Repeat with -txindex so we don't need the blockhash") | self.restart_node(0, self.extra_args[0] + ["-txindex"]) | ||||
self.restart_node(0, self.extra_args[0] + ['-txindex']) | |||||
quorum = get_quorum() | quorum = get_quorum() | ||||
self.wait_until(is_quorum_established) | self.wait_until(is_quorum_established) | ||||
# Try to raise a -txindex not synced yet error. This is not | # Try to raise a -txindex not synced yet error. This is not | ||||
# guaranteed because syncing is fast! | # guaranteed because syncing is fast! | ||||
try: | try: | ||||
node.isfinaltransaction( | node.isfinaltransaction( | ||||
uint256_hex(random.randint(0, 2**256 - 1)), | uint256_hex(random.randint(0, 2**256 - 1)), | ||||
) | ) | ||||
except JSONRPCException as e: | except JSONRPCException as e: | ||||
assert_equal(e.error['code'], -5) | assert_equal(e.error["code"], -5) | ||||
if e.error['message'] == "No such mempool or blockchain transaction.": | if e.error["message"] == "No such mempool or blockchain transaction.": | ||||
# If we got a regular "not found" error, the txindex should | # If we got a regular "not found" error, the txindex should | ||||
# have synced. | # have synced. | ||||
assert node.getindexinfo()['txindex']['synced'] is True | assert node.getindexinfo()["txindex"]["synced"] is True | ||||
else: | else: | ||||
# Otherwise we might have successfully raised before the | # Otherwise we might have successfully raised before the | ||||
# indexer completed. Checking the status now is useless as | # indexer completed. Checking the status now is useless as | ||||
# the indexer might have completed the synchronization in | # the indexer might have completed the synchronization in | ||||
# the meantime and the status is no longer relevant. | # the meantime and the status is no longer relevant. | ||||
assert e.error['message'] == "No such transaction. Blockchain transactions are still in the process of being indexed." | assert ( | ||||
e.error["message"] | |||||
== "No such transaction. Blockchain transactions are still in" | |||||
" the process of being indexed." | |||||
) | |||||
else: | else: | ||||
assert False, "The isfinaltransaction RPC call did not throw as expected." | assert ( | ||||
False | |||||
), "The isfinaltransaction RPC call did not throw as expected." | |||||
self.wait_until(lambda: node.getindexinfo()[ | self.wait_until(lambda: node.getindexinfo()["txindex"]["synced"] is True) | ||||
'txindex']['synced'] is True) | |||||
self.wait_until(lambda: is_finalblock(tip)) | self.wait_until(lambda: is_finalblock(tip)) | ||||
assert node.isfinaltransaction(wallet_txid) | assert node.isfinaltransaction(wallet_txid) | ||||
wallet_txid = node.sendtoaddress( | wallet_txid = node.sendtoaddress(ADDRESS_ECREG_UNSPENDABLE, 1_000_000) | ||||
ADDRESS_ECREG_UNSPENDABLE, 1_000_000) | |||||
assert wallet_txid in node.getrawmempool() | assert wallet_txid in node.getrawmempool() | ||||
assert not node.isfinaltransaction(wallet_txid) | assert not node.isfinaltransaction(wallet_txid) | ||||
assert_raises_rpc_error( | assert_raises_rpc_error( | ||||
-5, | -5, | ||||
"No such mempool or blockchain transaction.", | "No such mempool or blockchain transaction.", | ||||
node.isfinaltransaction, | node.isfinaltransaction, | ||||
uint256_hex(random.randint(0, 2**256 - 1)), | uint256_hex(random.randint(0, 2**256 - 1)), | ||||
Show All 12 Lines | def run_test(self): | ||||
"Block not found", | "Block not found", | ||||
node.isfinaltransaction, | node.isfinaltransaction, | ||||
uint256_hex(random.randint(0, 2**256 - 1)), | uint256_hex(random.randint(0, 2**256 - 1)), | ||||
uint256_hex(random.randint(0, 2**256 - 1)), | uint256_hex(random.randint(0, 2**256 - 1)), | ||||
) | ) | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
height = node.getblockcount() + 1 | height = node.getblockcount() + 1 | ||||
time = node.getblock(tip)['time'] + 1 | time = node.getblock(tip)["time"] + 1 | ||||
block = create_block(int(tip, 16), create_coinbase(height), time) | block = create_block(int(tip, 16), create_coinbase(height), time) | ||||
block.solve() | block.solve() | ||||
peer = node.add_p2p_connection(AvaP2PInterface()) | peer = node.add_p2p_connection(AvaP2PInterface()) | ||||
msg = msg_headers() | msg = msg_headers() | ||||
msg.headers = [CBlockHeader(block)] | msg.headers = [CBlockHeader(block)] | ||||
peer.send_message(msg) | peer.send_message(msg) | ||||
self.wait_until(lambda: node.getchaintips()[0]['height'] == height) | self.wait_until(lambda: node.getchaintips()[0]["height"] == height) | ||||
assert_raises_rpc_error( | assert_raises_rpc_error( | ||||
-1, | -1, | ||||
"Block data not downloaded yet.", | "Block data not downloaded yet.", | ||||
node.isfinaltransaction, | node.isfinaltransaction, | ||||
uint256_hex(random.randint(0, 2**256 - 1)), | uint256_hex(random.randint(0, 2**256 - 1)), | ||||
uint256_hex(block.sha256), | uint256_hex(block.sha256), | ||||
) | ) | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
AvalancheIsFinalTest().main() | AvalancheIsFinalTest().main() |