Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc_p2p_avalanche_proof_voting.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2021 The Bitcoin developers | # Copyright (c) 2021 The Bitcoin developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test the resolution of conflicting proofs via avalanche.""" | """Test the resolution of conflicting proofs via avalanche.""" | ||||
import time | import time | ||||
from test_framework.avatools import ( | from test_framework.avatools import ( | ||||
avalanche_proof_from_hex, | avalanche_proof_from_hex, | ||||
can_find_inv_in_poll, | |||||
create_coinbase_stakes, | create_coinbase_stakes, | ||||
gen_proof, | gen_proof, | ||||
get_ava_p2p_interface, | get_ava_p2p_interface, | ||||
get_proof_ids, | get_proof_ids, | ||||
) | ) | ||||
from test_framework.key import ECKey, ECPubKey | from test_framework.key import ECKey, ECPubKey | ||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
MSG_AVA_PROOF, | MSG_AVA_PROOF, | ||||
Show All 35 Lines | def set_test_params(self): | ||||
] | ] | ||||
self.supports_cli = False | self.supports_cli = False | ||||
# Build a fake quorum of nodes. | # Build a fake quorum of nodes. | ||||
def get_quorum(self, node): | def get_quorum(self, node): | ||||
return [get_ava_p2p_interface(self, node, stake_utxo_confirmations=self.avaproof_stake_utxo_confirmations) | return [get_ava_p2p_interface(self, node, stake_utxo_confirmations=self.avaproof_stake_utxo_confirmations) | ||||
for _ in range(0, QUORUM_NODE_COUNT)] | for _ in range(0, QUORUM_NODE_COUNT)] | ||||
def can_find_proof_in_poll(self, hash, response): | |||||
found_hash = False | |||||
for n in self.quorum: | |||||
poll = n.get_avapoll_if_available() | |||||
# That node has not received a poll | |||||
if poll is None: | |||||
continue | |||||
# We got a poll, check for the hash and repond | |||||
votes = [] | |||||
for inv in poll.invs: | |||||
# Vote yes to everything | |||||
r = AvalancheProofVoteResponse.ACTIVE | |||||
# Look for what we expect | |||||
if inv.hash == hash: | |||||
r = response | |||||
found_hash = True | |||||
votes.append(AvalancheVote(r, inv.hash)) | |||||
n.send_avaresponse(poll.round, votes, n.delegated_privkey) | |||||
return found_hash | |||||
@staticmethod | @staticmethod | ||||
def send_proof(from_peer, proof_hex): | def send_proof(from_peer, proof_hex): | ||||
proof = avalanche_proof_from_hex(proof_hex) | proof = avalanche_proof_from_hex(proof_hex) | ||||
from_peer.send_avaproof(proof) | from_peer.send_avaproof(proof) | ||||
return proof.proofid | return proof.proofid | ||||
def send_and_check_for_polling(self, peer, | def send_and_check_for_polling(self, peer, | ||||
proof_hex, response=AvalancheProofVoteResponse.ACTIVE): | proof_hex, response=AvalancheProofVoteResponse.ACTIVE): | ||||
proofid = self.send_proof(peer, proof_hex) | proofid = self.send_proof(peer, proof_hex) | ||||
self.wait_until(lambda: self.can_find_proof_in_poll(proofid, response)) | self.wait_until(lambda: can_find_inv_in_poll(self.quorum, proofid, response)) | ||||
def build_conflicting_proof(self, node, sequence): | def build_conflicting_proof(self, node, sequence): | ||||
return node.buildavalancheproof( | return node.buildavalancheproof( | ||||
sequence, 0, self.privkey_wif, self.conflicting_stakes) | sequence, 0, self.privkey_wif, self.conflicting_stakes) | ||||
def wait_for_invalidated_proof(self, node, proofid): | def wait_for_invalidated_proof(self, node, proofid): | ||||
def invalidate_proof(proofid): | def invalidate_proof(proofid): | ||||
self.wait_until( | self.wait_until( | ||||
lambda: self.can_find_proof_in_poll( | lambda: can_find_inv_in_poll(self.quorum, | ||||
proofid, response=AvalancheProofVoteResponse.REJECTED)) | proofid, response=AvalancheProofVoteResponse.REJECTED)) | ||||
return try_rpc(-8, "Proof not found", | return try_rpc(-8, "Proof not found", | ||||
node.getrawavalancheproof, uint256_hex(proofid)) | node.getrawavalancheproof, uint256_hex(proofid)) | ||||
with node.assert_debug_log( | with node.assert_debug_log( | ||||
[f"Avalanche invalidated proof {uint256_hex(proofid)}"], | [f"Avalanche invalidated proof {uint256_hex(proofid)}"], | ||||
["Failed to reject proof"] | ["Failed to reject proof"] | ||||
): | ): | ||||
self.wait_until(lambda: invalidate_proof(proofid)) | self.wait_until(lambda: invalidate_proof(proofid)) | ||||
def wait_for_finalized_proof(self, node, proofid): | def wait_for_finalized_proof(self, node, proofid): | ||||
def finalize_proof(proofid): | def finalize_proof(proofid): | ||||
self.can_find_proof_in_poll( | can_find_inv_in_poll(self.quorum, | ||||
proofid, response=AvalancheProofVoteResponse.ACTIVE) | proofid, response=AvalancheProofVoteResponse.ACTIVE) | ||||
return node.getrawavalancheproof( | return node.getrawavalancheproof( | ||||
uint256_hex(proofid)).get("finalized", False) | uint256_hex(proofid)).get("finalized", False) | ||||
with node.assert_debug_log([f"Avalanche finalized proof {uint256_hex(proofid)}"]): | with node.assert_debug_log([f"Avalanche finalized proof {uint256_hex(proofid)}"]): | ||||
self.wait_until(lambda: finalize_proof(proofid)) | self.wait_until(lambda: finalize_proof(proofid)) | ||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | def update_tests(self, node): | ||||
self.wait_until(lambda: proofid_seq40 in get_proof_ids(node)) | self.wait_until(lambda: proofid_seq40 in get_proof_ids(node)) | ||||
assert proofid_seq40 in get_proof_ids(node) | assert proofid_seq40 in get_proof_ids(node) | ||||
assert proofid_seq30 not in get_proof_ids(node) | assert proofid_seq30 not in get_proof_ids(node) | ||||
self.log.info("Test proof acceptance") | self.log.info("Test proof acceptance") | ||||
def accept_proof(proofid): | def accept_proof(proofid): | ||||
self.wait_until(lambda: self.can_find_proof_in_poll( | self.wait_until(lambda: can_find_inv_in_poll(self.quorum, | ||||
proofid, response=AvalancheProofVoteResponse.ACTIVE)) | proofid, response=AvalancheProofVoteResponse.ACTIVE)) | ||||
return proofid in get_proof_ids(node) | return proofid in get_proof_ids(node) | ||||
mock_time += self.conflicting_proof_cooldown | mock_time += self.conflicting_proof_cooldown | ||||
node.setmocktime(mock_time) | node.setmocktime(mock_time) | ||||
self.send_and_check_for_polling(peer, proof_seq30) | self.send_and_check_for_polling(peer, proof_seq30) | ||||
# Let the quorum vote for it | # Let the quorum vote for it | ||||
Show All 17 Lines | def update_tests(self, node): | ||||
self.log.info("Test proof rejection") | self.log.info("Test proof rejection") | ||||
self.send_proof(peer, proof_seq50) | self.send_proof(peer, proof_seq50) | ||||
self.wait_until(lambda: proofid_seq50 in get_proof_ids(node)) | self.wait_until(lambda: proofid_seq50 in get_proof_ids(node)) | ||||
assert proofid_seq40 not in get_proof_ids(node) | assert proofid_seq40 not in get_proof_ids(node) | ||||
def reject_proof(proofid): | def reject_proof(proofid): | ||||
self.wait_until( | self.wait_until( | ||||
lambda: self.can_find_proof_in_poll( | lambda: can_find_inv_in_poll(self.quorum, | ||||
proofid, response=AvalancheProofVoteResponse.REJECTED)) | proofid, response=AvalancheProofVoteResponse.REJECTED)) | ||||
return proofid not in get_proof_ids(node) | return proofid not in get_proof_ids(node) | ||||
with node.assert_debug_log( | with node.assert_debug_log( | ||||
[f"Avalanche rejected proof {uint256_hex(proofid_seq50)}"], | [f"Avalanche rejected proof {uint256_hex(proofid_seq50)}"], | ||||
["Failed to reject proof"] | ["Failed to reject proof"] | ||||
): | ): | ||||
self.wait_until(lambda: reject_proof(proofid_seq50)) | self.wait_until(lambda: reject_proof(proofid_seq50)) | ||||
▲ Show 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | def stale_proof_tests(self, node): | ||||
mock_time += self.conflicting_proof_cooldown | mock_time += self.conflicting_proof_cooldown | ||||
node.setmocktime(mock_time) | node.setmocktime(mock_time) | ||||
self.send_and_check_for_polling( | self.send_and_check_for_polling( | ||||
peer, proof_seq1, response=AvalancheProofVoteResponse.UNKNOWN) | peer, proof_seq1, response=AvalancheProofVoteResponse.UNKNOWN) | ||||
def vote_until_dropped(proofid): | def vote_until_dropped(proofid): | ||||
self.can_find_proof_in_poll( | can_find_inv_in_poll(self.quorum, | ||||
proofid, response=AvalancheProofVoteResponse.UNKNOWN) | proofid, response=AvalancheProofVoteResponse.UNKNOWN) | ||||
return try_rpc(-8, "Proof not found", | return try_rpc(-8, "Proof not found", | ||||
node.getrawavalancheproof, uint256_hex(proofid)) | node.getrawavalancheproof, uint256_hex(proofid)) | ||||
with node.assert_debug_log([f"Avalanche stalled proof {uint256_hex(proofid_seq1)}"]): | with node.assert_debug_log([f"Avalanche stalled proof {uint256_hex(proofid_seq1)}"]): | ||||
self.wait_until(lambda: vote_until_dropped(proofid_seq1)) | self.wait_until(lambda: vote_until_dropped(proofid_seq1)) | ||||
# Verify that proof_seq2 was not replaced | # Verify that proof_seq2 was not replaced | ||||
assert proofid_seq2 in get_proof_ids(node) | assert proofid_seq2 in get_proof_ids(node) | ||||
▲ Show 20 Lines • Show All 43 Lines • Show Last 20 Lines |