Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc_p2p_avalanche.py
Show All 16 Lines | |||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
AvalancheDelegation, | AvalancheDelegation, | ||||
AvalancheProof, | AvalancheProof, | ||||
AvalancheResponse, | AvalancheResponse, | ||||
AvalancheVote, | AvalancheVote, | ||||
CInv, | CInv, | ||||
FromHex, | FromHex, | ||||
hash256, | hash256, | ||||
msg_avahello, | |||||
msg_avapoll, | msg_avapoll, | ||||
msg_avahello, | |||||
MSG_AVA_PROOF, | MSG_AVA_PROOF, | ||||
msg_avaproof, | |||||
msg_getdata, | msg_getdata, | ||||
msg_tcpavaresponse, | msg_tcpavaresponse, | ||||
NODE_AVALANCHE, | NODE_AVALANCHE, | ||||
NODE_NETWORK, | NODE_NETWORK, | ||||
TCPAvalancheResponse, | TCPAvalancheResponse, | ||||
) | ) | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
Show All 16 Lines | |||||
class TestNode(P2PInterface): | class TestNode(P2PInterface): | ||||
def __init__(self): | def __init__(self): | ||||
self.round = 0 | self.round = 0 | ||||
self.avahello = None | self.avahello = None | ||||
self.avaresponses = [] | self.avaresponses = [] | ||||
self.avapolls = [] | self.avapolls = [] | ||||
self.avaproof = None | |||||
super().__init__() | super().__init__() | ||||
def peer_connect(self, *args, **kwargs): | def peer_connect(self, *args, **kwargs): | ||||
create_conn = super().peer_connect(*args, **kwargs) | create_conn = super().peer_connect(*args, **kwargs) | ||||
# Save the nonce and extra entropy so they can be reused later. | # Save the nonce and extra entropy so they can be reused later. | ||||
self.local_nonce = self.on_connection_send_msg.nNonce | self.local_nonce = self.on_connection_send_msg.nNonce | ||||
self.local_extra_entropy = self.on_connection_send_msg.nExtraEntropy | self.local_extra_entropy = self.on_connection_send_msg.nExtraEntropy | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | def send_avahello(self, delegation_hex: str, delegated_privkey: ECKey): | ||||
delegation.getid() + | delegation.getid() + | ||||
struct.pack("<QQQQ", self.local_nonce, self.remote_nonce, | struct.pack("<QQQQ", self.local_nonce, self.remote_nonce, | ||||
self.local_extra_entropy, self.remote_extra_entropy)) | self.local_extra_entropy, self.remote_extra_entropy)) | ||||
msg = msg_avahello() | msg = msg_avahello() | ||||
msg.hello.delegation = delegation | msg.hello.delegation = delegation | ||||
msg.hello.sig = delegated_privkey.sign_schnorr(local_sighash) | msg.hello.sig = delegated_privkey.sign_schnorr(local_sighash) | ||||
self.send_message(msg) | self.send_message(msg) | ||||
return delegation.proofid | return delegation.proofid | ||||
def on_avaproof(self, message): | |||||
assert(self.avaproof is None) | |||||
self.avaproof = message | |||||
def wait_for_avaproof(self, timeout=10): | |||||
wait_until( | |||||
lambda: self.avaproof is not None, | |||||
timeout=timeout, | |||||
lock=p2p_lock) | |||||
with p2p_lock: | |||||
return self.avaproof | |||||
def send_avaproof(self, proof_hex): | |||||
msg_proof = msg_avaproof() | |||||
msg_proof.proof = FromHex(AvalancheProof(), proof_hex) | |||||
self.send_message(msg_proof) | |||||
class AvalancheTest(BitcoinTestFramework): | class AvalancheTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
self.extra_args = [ | self.extra_args = [ | ||||
['-enableavalanche=1', '-avacooldown=0'], | ['-enableavalanche=1', '-avacooldown=0'], | ||||
['-enableavalanche=1', '-avacooldown=0', '-noparkdeepreorg', '-maxreorgdepth=-1']] | ['-enableavalanche=1', '-avacooldown=0', '-noparkdeepreorg', '-maxreorgdepth=-1']] | ||||
▲ Show 20 Lines • Show All 222 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
assert_equal( | assert_equal( | ||||
int(node.getnetworkinfo()['localservices'], 16) & NODE_AVALANCHE, | int(node.getnetworkinfo()['localservices'], 16) & NODE_AVALANCHE, | ||||
0) | 0) | ||||
# Restart the node | # Restart the node | ||||
self.restart_node(0, self.extra_args[0] + [ | self.restart_node(0, self.extra_args[0] + [ | ||||
"-avaproof={}".format(proof), | "-avaproof={}".format(proof), | ||||
"-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | ||||
"-exchangeavaproofs=1", | |||||
]) | ]) | ||||
assert_equal( | assert_equal( | ||||
int(node.getnetworkinfo()['localservices'], 16) & NODE_AVALANCHE, | int(node.getnetworkinfo()['localservices'], 16) & NODE_AVALANCHE, | ||||
NODE_AVALANCHE) | NODE_AVALANCHE) | ||||
self.log.info("Test the avahello signature (node -> P2PInterface)") | self.log.info("Test the avahello signature (node -> P2PInterface)") | ||||
good_interface = get_node() | good_interface = get_node() | ||||
Show All 14 Lines | def run_test(self): | ||||
# delegate | # delegate | ||||
delegated_key = ECKey() | delegated_key = ECKey() | ||||
delegated_key.generate() | delegated_key.generate() | ||||
interface_delegation_hex = node.delegateavalancheproof( | interface_delegation_hex = node.delegateavalancheproof( | ||||
f"{limited_id:0{64}x}", | f"{limited_id:0{64}x}", | ||||
bytes_to_wif(privkey.get_bytes()), | bytes_to_wif(privkey.get_bytes()), | ||||
delegated_key.get_pubkey().get_bytes().hex(), | delegated_key.get_pubkey().get_bytes().hex(), | ||||
None) | None) | ||||
good_interface.send_avahello(interface_delegation_hex, delegated_key) | |||||
expected_proofid = FromHex( | |||||
AvalancheProof(), | |||||
interface_proof_hex).proofid | |||||
good_interface.wait_for_getdata([expected_proofid]) | |||||
self.log.info("Test that node adds an avalanche peer") | |||||
good_interface.send_avaproof(interface_proof_hex) | |||||
wait_until( | |||||
lambda: len(node.getavalanchepeerinfo()) > 0, | |||||
timeout=5, | |||||
lock=p2p_lock) | |||||
self.log.info("Test that wrong avahello signature causes a ban") | self.log.info("Test that wrong avahello signature causes a ban") | ||||
bad_interface = get_node() | bad_interface = get_node() | ||||
wrong_key = ECKey() | wrong_key = ECKey() | ||||
wrong_key.generate() | wrong_key.generate() | ||||
with self.nodes[0].assert_debug_log( | with self.nodes[0].assert_debug_log( | ||||
["Misbehaving", | ["Misbehaving", | ||||
"peer=1 (0 -> 100) BAN THRESHOLD EXCEEDED: invalid-avahello-signature"]): | "peer=1 (0 -> 100) BAN THRESHOLD EXCEEDED: invalid-avahello-signature"]): | ||||
Show All 23 Lines | def run_test(self): | ||||
wait_for_proof_validation() | wait_for_proof_validation() | ||||
getdata = msg_getdata([CInv(MSG_AVA_PROOF, node_proofid)]) | getdata = msg_getdata([CInv(MSG_AVA_PROOF, node_proofid)]) | ||||
self.log.info( | self.log.info( | ||||
"Proof has been inv'ed recently, check it can be requested") | "Proof has been inv'ed recently, check it can be requested") | ||||
good_interface.send_message(getdata) | good_interface.send_message(getdata) | ||||
def proof_received(peer): | avaproof = good_interface.wait_for_avaproof() | ||||
with p2p_lock: | assert_equal(avaproof.proof.serialize().hex(), proof) | ||||
return peer.last_message.get( | |||||
"avaproof") and peer.last_message["avaproof"].proof.proofid == node_proofid | |||||
wait_until(lambda: proof_received(good_interface)) | |||||
# Restart the node | # Restart the node | ||||
self.restart_node(0, self.extra_args[0] + [ | self.restart_node(0, self.extra_args[0] + [ | ||||
"-avaproof={}".format(proof), | "-avaproof={}".format(proof), | ||||
"-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | ||||
]) | ]) | ||||
wait_for_proof_validation() | wait_for_proof_validation() | ||||
self.log.info( | self.log.info( | ||||
"The proof has not been announced, it cannot be requested") | "The proof has not been announced, it cannot be requested") | ||||
peer = get_node(services=NODE_NETWORK) | peer = get_node(services=NODE_NETWORK) | ||||
peer.send_message(getdata) | peer.send_message(getdata) | ||||
# Give enough time for the node to answer. Since we cannot check for a | # Give enough time for the node to answer. Since we cannot check for a | ||||
# non-event this is the best we can do | # non-event this is the best we can do | ||||
time.sleep(2) | time.sleep(2) | ||||
def proof_received(peer): | |||||
with p2p_lock: | |||||
return peer.last_message.get( | |||||
"avaproof") and peer.last_message["avaproof"].proof.proofid == node_proofid | |||||
wait_until(lambda: proof_received(good_interface)) | |||||
assert not proof_received(peer) | assert not proof_received(peer) | ||||
self.log.info("The proof is known for long enough to be requested") | self.log.info("The proof is known for long enough to be requested") | ||||
current_time = int(time.time()) | current_time = int(time.time()) | ||||
node.setmocktime(current_time + UNCONDITIONAL_RELAY_DELAY) | node.setmocktime(current_time + UNCONDITIONAL_RELAY_DELAY) | ||||
peer.send_message(getdata) | peer.send_message(getdata) | ||||
wait_until(lambda: proof_received(peer)) | avaproof = peer.wait_for_avaproof() | ||||
assert_equal(avaproof.proof.serialize().hex(), proof) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
AvalancheTest().main() | AvalancheTest().main() |