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, | ||||
create_coinbase_stakes, | create_coinbase_stakes, | ||||
gen_proof, | gen_proof, | ||||
get_ava_p2p_interface, | get_ava_p2p_interface, | ||||
get_proof_ids, | get_proof_ids, | ||||
wait_for_proof, | |||||
) | ) | ||||
from test_framework.key import ECPubKey | from test_framework.key import ECPubKey | ||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
MSG_AVA_PROOF, | MSG_AVA_PROOF, | ||||
AvalancheProofVoteResponse, | AvalancheProofVoteResponse, | ||||
AvalancheVote, | AvalancheVote, | ||||
) | ) | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.immature_stakes = create_coinbase_stakes( | self.immature_stakes = create_coinbase_stakes( | ||||
node, blockhash[9:], addrkey0.key) | node, blockhash[9:], addrkey0.key) | ||||
self.poll_tests(node) | self.poll_tests(node) | ||||
self.update_tests(node) | self.update_tests(node) | ||||
self.vote_tests(node) | self.vote_tests(node) | ||||
self.stale_proof_tests(node) | self.stale_proof_tests(node) | ||||
self.unorphan_poll_tests(node) | self.unorphan_poll_tests(node) | ||||
self.repeated_conflicting_proofs_tests(node, increase_sequence=True) | |||||
self.repeated_conflicting_proofs_tests(node, increase_sequence=False) | |||||
def poll_tests(self, node): | def poll_tests(self, node): | ||||
proof_seq10 = self.build_conflicting_proof(node, 10) | proof_seq10 = self.build_conflicting_proof(node, 10) | ||||
proof_seq20 = self.build_conflicting_proof(node, 20) | proof_seq20 = self.build_conflicting_proof(node, 20) | ||||
proof_seq30 = self.build_conflicting_proof(node, 30) | proof_seq30 = self.build_conflicting_proof(node, 30) | ||||
proof_seq40 = self.build_conflicting_proof(node, 40) | proof_seq40 = self.build_conflicting_proof(node, 40) | ||||
orphan = node.buildavalancheproof( | orphan = node.buildavalancheproof( | ||||
Show All 14 Lines | def poll_tests(self, node): | ||||
mock_time = int(time.time()) | mock_time = int(time.time()) | ||||
node.setmocktime(mock_time) | node.setmocktime(mock_time) | ||||
self.log.info("Check we poll for valid proof") | self.log.info("Check we poll for valid proof") | ||||
self.send_and_check_for_polling(peer, proof_seq30) | self.send_and_check_for_polling(peer, proof_seq30) | ||||
self.log.info( | self.log.info( | ||||
"Check we don't poll for subsequent proofs if the cooldown is not elapsed, proof not the favorite") | "Check we don't poll for subsequent proofs if the cooldown is not elapsed, proof not the favorite") | ||||
with node.assert_debug_log(["Misbehaving", "cooldown-not-elapsed"]): | with node.assert_debug_log(["Not polling the avalanche proof (cooldown-not-elapsed)"]): | ||||
peer.send_avaproof(avalanche_proof_from_hex(proof_seq20)) | peer.send_avaproof(avalanche_proof_from_hex(proof_seq20)) | ||||
self.log.info( | self.log.info( | ||||
"Check we don't poll for subsequent proofs if the cooldown is not elapsed, proof is the favorite") | "Check we don't poll for subsequent proofs if the cooldown is not elapsed, proof is the favorite") | ||||
with node.assert_debug_log(["Misbehaving", "cooldown-not-elapsed"]): | with node.assert_debug_log(["Not polling the avalanche proof (cooldown-not-elapsed)"]): | ||||
peer.send_avaproof(avalanche_proof_from_hex(proof_seq40)) | peer.send_avaproof(avalanche_proof_from_hex(proof_seq40)) | ||||
self.log.info( | self.log.info( | ||||
"Check we poll for conflicting proof if the proof is not the favorite") | "Check we poll for conflicting proof if the proof is not the favorite") | ||||
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_seq20, response=AvalancheProofVoteResponse.REJECTED) | peer, proof_seq20, response=AvalancheProofVoteResponse.REJECTED) | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | def update_tests(self, node): | ||||
# Not enough | # Not enough | ||||
assert self.conflicting_proof_cooldown < self.peer_replacement_cooldown | assert self.conflicting_proof_cooldown < self.peer_replacement_cooldown | ||||
mock_time += self.conflicting_proof_cooldown | mock_time += self.conflicting_proof_cooldown | ||||
node.setmocktime(mock_time) | node.setmocktime(mock_time) | ||||
peer = get_ava_p2p_interface(node) | peer = get_ava_p2p_interface(node) | ||||
with node.assert_debug_log(["Misbehaving", "cooldown-not-elapsed"]): | with node.assert_debug_log(["Not polling the avalanche proof (cooldown-not-elapsed)"]): | ||||
self.send_proof(peer, proof_seq50) | self.send_proof(peer, proof_seq50) | ||||
mock_time += self.peer_replacement_cooldown | mock_time += self.peer_replacement_cooldown | ||||
node.setmocktime(mock_time) | node.setmocktime(mock_time) | ||||
self.log.info("Test proof rejection") | self.log.info("Test proof rejection") | ||||
self.send_proof(peer, proof_seq50) | self.send_proof(peer, proof_seq50) | ||||
▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | def unorphan_poll_tests(self, node): | ||||
with node.assert_debug_log(["Not polling the avalanche proof (orphan-proof)"]): | with node.assert_debug_log(["Not polling the avalanche proof (orphan-proof)"]): | ||||
peer.send_avaproof(immature_proof) | peer.send_avaproof(immature_proof) | ||||
self.log.info("Unorphaned proofs are polled") | self.log.info("Unorphaned proofs are polled") | ||||
node.generate(1) | node.generate(1) | ||||
self.send_and_check_for_polling(peer, immature_proof.serialize().hex()) | self.send_and_check_for_polling(peer, immature_proof.serialize().hex()) | ||||
def repeated_conflicting_proofs_tests( | |||||
self, node, increase_sequence, banscore=5): | |||||
self.log.info( | |||||
"Send a lot of conflicting proofs ({} sequence) and check the peer " | |||||
"gets banned".format( | |||||
"increasing" if increase_sequence else "decreasing" | |||||
) | |||||
) | |||||
self.restart_node(0) | |||||
sequence = 100 | |||||
# Build and send a first proof | |||||
proof_base = avalanche_proof_from_hex( | |||||
self.build_conflicting_proof(node, sequence)) | |||||
peer = get_ava_p2p_interface(node) | |||||
peer.send_avaproof(proof_base) | |||||
wait_for_proof(node, f"{proof_base.proofid:0{64}x}") | |||||
def send_conflicting_proof(): | |||||
nonlocal sequence | |||||
sequence += 1 if increase_sequence else -1 | |||||
proof = self.build_conflicting_proof(node, sequence) | |||||
with node.assert_debug_log(["Misbehaving", "cooldown-not-elapsed"]): | |||||
peer.send_avaproof(avalanche_proof_from_hex(proof)) | |||||
# Ramp up the banscore by sending conflicting proofs repeatidly | |||||
for _ in range(100 // banscore - 1): | |||||
send_conflicting_proof() | |||||
# This one will trigger the ban | |||||
send_conflicting_proof() | |||||
peer.wait_for_disconnect() | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
AvalancheProofVotingTest().main() | AvalancheProofVotingTest().main() |