Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc_p2p_compactproofs.py
Show First 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | class ProofStoreP2PInterface(AvaP2PInterface): | ||||
def get_proofs(self): | def get_proofs(self): | ||||
with p2p_lock: | with p2p_lock: | ||||
return self.proofs | return self.proofs | ||||
class CompactProofsTest(BitcoinTestFramework): | class CompactProofsTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
self.extra_args = [[ | self.extra_args = [ | ||||
'-avaproofstakeutxodustthreshold=1000000', | [ | ||||
'-avaproofstakeutxoconfirmations=1', | "-avaproofstakeutxodustthreshold=1000000", | ||||
'-avacooldown=0', | "-avaproofstakeutxoconfirmations=1", | ||||
]] * self.num_nodes | "-avacooldown=0", | ||||
] | |||||
] * self.num_nodes | |||||
def setup_network(self): | def setup_network(self): | ||||
# Don't connect the nodes | # Don't connect the nodes | ||||
self.setup_nodes() | self.setup_nodes() | ||||
@staticmethod | @staticmethod | ||||
def received_avaproofs(peer): | def received_avaproofs(peer): | ||||
with p2p_lock: | with p2p_lock: | ||||
return peer.last_message.get("avaproofs") | return peer.last_message.get("avaproofs") | ||||
def test_send_outbound_getavaproofs(self): | def test_send_outbound_getavaproofs(self): | ||||
self.log.info( | self.log.info( | ||||
"Check we send a getavaproofs message to our avalanche outbound peers") | "Check we send a getavaproofs message to our avalanche outbound peers" | ||||
) | |||||
node = self.nodes[0] | node = self.nodes[0] | ||||
p2p_idx = 0 | p2p_idx = 0 | ||||
non_avapeers = [] | non_avapeers = [] | ||||
for _ in range(4): | for _ in range(4): | ||||
peer = P2PInterface() | peer = P2PInterface() | ||||
node.add_outbound_p2p_connection( | node.add_outbound_p2p_connection( | ||||
peer, | peer, | ||||
p2p_idx=p2p_idx, | p2p_idx=p2p_idx, | ||||
connection_type="outbound-full-relay", | connection_type="outbound-full-relay", | ||||
services=NODE_NETWORK, | services=NODE_NETWORK, | ||||
) | ) | ||||
non_avapeers.append(peer) | non_avapeers.append(peer) | ||||
p2p_idx += 1 | p2p_idx += 1 | ||||
inbound_avapeers = [ | inbound_avapeers = [ | ||||
node.add_p2p_connection( | node.add_p2p_connection(NoHandshakeAvaP2PInterface()) for _ in range(4) | ||||
NoHandshakeAvaP2PInterface()) for _ in range(4)] | ] | ||||
outbound_avapeers = [] | outbound_avapeers = [] | ||||
# With a proof and the service bit set | # With a proof and the service bit set | ||||
for _ in range(4): | for _ in range(4): | ||||
peer = AvaP2PInterface(self, node) | peer = AvaP2PInterface(self, node) | ||||
node.add_outbound_p2p_connection( | node.add_outbound_p2p_connection( | ||||
peer, | peer, | ||||
p2p_idx=p2p_idx, | p2p_idx=p2p_idx, | ||||
Show All 12 Lines | def test_send_outbound_getavaproofs(self): | ||||
connection_type="outbound-full-relay", | connection_type="outbound-full-relay", | ||||
services=NODE_NETWORK, | services=NODE_NETWORK, | ||||
) | ) | ||||
outbound_avapeers.append(peer) | outbound_avapeers.append(peer) | ||||
p2p_idx += 1 | p2p_idx += 1 | ||||
def all_peers_received_getavaproofs(): | def all_peers_received_getavaproofs(): | ||||
with p2p_lock: | with p2p_lock: | ||||
return all(p.last_message.get("getavaproofs") | return all( | ||||
for p in outbound_avapeers) | p.last_message.get("getavaproofs") for p in outbound_avapeers | ||||
) | |||||
self.wait_until(all_peers_received_getavaproofs) | self.wait_until(all_peers_received_getavaproofs) | ||||
with p2p_lock: | with p2p_lock: | ||||
assert all(p.message_count.get( | assert all( | ||||
"getavaproofs", 0) >= 1 for p in outbound_avapeers) | p.message_count.get("getavaproofs", 0) >= 1 for p in outbound_avapeers | ||||
assert all(p.message_count.get( | ) | ||||
"getavaproofs", 0) == 0 for p in non_avapeers) | assert all( | ||||
assert all(p.message_count.get( | p.message_count.get("getavaproofs", 0) == 0 for p in non_avapeers | ||||
"getavaproofs", 0) == 0 for p in inbound_avapeers) | ) | ||||
assert all( | |||||
p.message_count.get("getavaproofs", 0) == 0 for p in inbound_avapeers | |||||
) | |||||
self.log.info( | self.log.info( | ||||
"Check we send periodic getavaproofs message to some of our peers") | "Check we send periodic getavaproofs message to some of our peers" | ||||
) | |||||
def count_outbounds_getavaproofs(): | def count_outbounds_getavaproofs(): | ||||
with p2p_lock: | with p2p_lock: | ||||
return sum([p.message_count.get("getavaproofs", 0) | return sum( | ||||
for p in outbound_avapeers]) | [p.message_count.get("getavaproofs", 0) for p in outbound_avapeers] | ||||
) | |||||
outbounds_getavaproofs = count_outbounds_getavaproofs() | outbounds_getavaproofs = count_outbounds_getavaproofs() | ||||
node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | ||||
self.wait_until(lambda: count_outbounds_getavaproofs() | self.wait_until( | ||||
== outbounds_getavaproofs + 3) | lambda: count_outbounds_getavaproofs() == outbounds_getavaproofs + 3 | ||||
) | |||||
outbounds_getavaproofs += 3 | outbounds_getavaproofs += 3 | ||||
with p2p_lock: | with p2p_lock: | ||||
assert all(p.message_count.get( | assert all( | ||||
"getavaproofs", 0) == 0 for p in non_avapeers) | p.message_count.get("getavaproofs", 0) == 0 for p in non_avapeers | ||||
assert all(p.message_count.get( | ) | ||||
"getavaproofs", 0) == 0 for p in inbound_avapeers) | assert all( | ||||
p.message_count.get("getavaproofs", 0) == 0 for p in inbound_avapeers | |||||
) | |||||
self.log.info( | self.log.info( | ||||
"After the first avaproofs has been received, all the peers are requested periodically") | "After the first avaproofs has been received, all the peers are requested" | ||||
" periodically" | |||||
) | |||||
responding_outbound_avapeer = AvaP2PInterface(self, node) | responding_outbound_avapeer = AvaP2PInterface(self, node) | ||||
node.add_outbound_p2p_connection( | node.add_outbound_p2p_connection( | ||||
responding_outbound_avapeer, | responding_outbound_avapeer, | ||||
p2p_idx=p2p_idx, | p2p_idx=p2p_idx, | ||||
connection_type="avalanche", | connection_type="avalanche", | ||||
services=NODE_NETWORK | NODE_AVALANCHE, | services=NODE_NETWORK | NODE_AVALANCHE, | ||||
) | ) | ||||
p2p_idx += 1 | p2p_idx += 1 | ||||
outbound_avapeers.append(responding_outbound_avapeer) | outbound_avapeers.append(responding_outbound_avapeer) | ||||
self.wait_until(all_peers_received_getavaproofs) | self.wait_until(all_peers_received_getavaproofs) | ||||
_, proof = gen_proof(self, node) | _, proof = gen_proof(self, node) | ||||
# Send the avaproofs message | # Send the avaproofs message | ||||
avaproofs = build_msg_avaproofs([proof]) | avaproofs = build_msg_avaproofs([proof]) | ||||
responding_outbound_avapeer.send_and_ping(avaproofs) | responding_outbound_avapeer.send_and_ping(avaproofs) | ||||
# Now the node will request from all its peers at each time period | # Now the node will request from all its peers at each time period | ||||
outbounds_getavaproofs = count_outbounds_getavaproofs() | outbounds_getavaproofs = count_outbounds_getavaproofs() | ||||
num_outbound_avapeers = len(outbound_avapeers) | num_outbound_avapeers = len(outbound_avapeers) | ||||
node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | ||||
self.wait_until(lambda: count_outbounds_getavaproofs() | self.wait_until( | ||||
== outbounds_getavaproofs + num_outbound_avapeers) | lambda: count_outbounds_getavaproofs() | ||||
== outbounds_getavaproofs + num_outbound_avapeers | |||||
) | |||||
outbounds_getavaproofs += num_outbound_avapeers | outbounds_getavaproofs += num_outbound_avapeers | ||||
self.log.info("Empty avaproofs will not trigger any request") | self.log.info("Empty avaproofs will not trigger any request") | ||||
for p in outbound_avapeers: | for p in outbound_avapeers: | ||||
p.send_message(build_msg_avaproofs([])) | p.send_message(build_msg_avaproofs([])) | ||||
with p2p_lock: | with p2p_lock: | ||||
# Only this peer actually sent a proof | # Only this peer actually sent a proof | ||||
assert_equal( | assert_equal( | ||||
responding_outbound_avapeer.message_count.get( | responding_outbound_avapeer.message_count.get("avaproofsreq", 0), 1 | ||||
"avaproofsreq", 0), 1) | ) | ||||
assert_equal(sum([p.message_count.get("avaproofsreq", 0) | assert_equal( | ||||
for p in outbound_avapeers]), 1) | sum( | ||||
[p.message_count.get("avaproofsreq", 0) for p in outbound_avapeers] | |||||
), | |||||
1, | |||||
) | |||||
# Sanity checks | # Sanity checks | ||||
assert all(p.message_count.get( | assert all( | ||||
"getavaproofs", 0) == 0 for p in non_avapeers) | p.message_count.get("getavaproofs", 0) == 0 for p in non_avapeers | ||||
assert all(p.message_count.get( | ) | ||||
"getavaproofs", 0) == 0 for p in inbound_avapeers) | assert all( | ||||
p.message_count.get("getavaproofs", 0) == 0 for p in inbound_avapeers | |||||
) | |||||
def test_send_manual_getavaproofs(self): | def test_send_manual_getavaproofs(self): | ||||
self.log.info( | self.log.info( | ||||
"Check we send a getavaproofs message to our manually connected peers that support avalanche") | "Check we send a getavaproofs message to our manually connected peers that" | ||||
" support avalanche" | |||||
) | |||||
node = self.nodes[0] | node = self.nodes[0] | ||||
# Get rid of previously connected nodes | # Get rid of previously connected nodes | ||||
node.disconnect_p2ps() | node.disconnect_p2ps() | ||||
def added_node_connected(ip_port): | def added_node_connected(ip_port): | ||||
added_node_info = node.getaddednodeinfo(ip_port) | added_node_info = node.getaddednodeinfo(ip_port) | ||||
return len( | return len(added_node_info) == 1 and added_node_info[0]["connected"] | ||||
added_node_info) == 1 and added_node_info[0]['connected'] | |||||
def connect_callback(address, port): | def connect_callback(address, port): | ||||
self.log.debug(f"Connecting to {address}:{port}") | self.log.debug(f"Connecting to {address}:{port}") | ||||
p = AvaP2PInterface(self, node) | p = AvaP2PInterface(self, node) | ||||
p2p_idx = 1 | p2p_idx = 1 | ||||
p.peer_accept_connection( | p.peer_accept_connection( | ||||
connect_cb=connect_callback, | connect_cb=connect_callback, | ||||
connect_id=p2p_idx, | connect_id=p2p_idx, | ||||
net=node.chain, | net=node.chain, | ||||
timeout_factor=node.timeout_factor, | timeout_factor=node.timeout_factor, | ||||
services=NODE_NETWORK | NODE_AVALANCHE, | services=NODE_NETWORK | NODE_AVALANCHE, | ||||
)() | )() | ||||
ip_port = f"127.0.0.1:{p2p_port(MAX_NODES - p2p_idx)}" | ip_port = f"127.0.0.1:{p2p_port(MAX_NODES - p2p_idx)}" | ||||
node.addnode(node=ip_port, command="add") | node.addnode(node=ip_port, command="add") | ||||
self.wait_until(lambda: added_node_connected(ip_port)) | self.wait_until(lambda: added_node_connected(ip_port)) | ||||
assert_equal(node.getpeerinfo()[-1]['addr'], ip_port) | assert_equal(node.getpeerinfo()[-1]["addr"], ip_port) | ||||
assert_equal(node.getpeerinfo()[-1]['connection_type'], 'manual') | assert_equal(node.getpeerinfo()[-1]["connection_type"], "manual") | ||||
# Make sure p.is_connected is set, otherwise the last_message check | # Make sure p.is_connected is set, otherwise the last_message check | ||||
# below will assert. | # below will assert. | ||||
p.wait_for_connect() | p.wait_for_connect() | ||||
p.wait_until(lambda: p.last_message.get("getavaproofs")) | p.wait_until(lambda: p.last_message.get("getavaproofs")) | ||||
def test_respond_getavaproofs(self): | def test_respond_getavaproofs(self): | ||||
self.log.info("Check the node responds to getavaproofs messages") | self.log.info("Check the node responds to getavaproofs messages") | ||||
Show All 24 Lines | def test_respond_getavaproofs(self): | ||||
proofids = get_proof_ids(node) | proofids = get_proof_ids(node) | ||||
assert_equal(len(proofids), 50) | assert_equal(len(proofids), 50) | ||||
receiving_peer = node.add_p2p_connection(NoHandshakeAvaP2PInterface()) | receiving_peer = node.add_p2p_connection(NoHandshakeAvaP2PInterface()) | ||||
send_getavaproof_check_shortid_len(receiving_peer, len(proofids)) | send_getavaproof_check_shortid_len(receiving_peer, len(proofids)) | ||||
avaproofs = self.received_avaproofs(receiving_peer) | avaproofs = self.received_avaproofs(receiving_peer) | ||||
expected_shortids = [ | expected_shortids = [ | ||||
calculate_shortid( | calculate_shortid(avaproofs.key0, avaproofs.key1, proofid) | ||||
avaproofs.key0, | for proofid in sorted(proofids) | ||||
avaproofs.key1, | ] | ||||
proofid) for proofid in sorted(proofids)] | |||||
assert_equal(expected_shortids, avaproofs.shortids) | assert_equal(expected_shortids, avaproofs.shortids) | ||||
# Don't expect any prefilled proof for now | # Don't expect any prefilled proof for now | ||||
assert_equal(len(avaproofs.prefilled_proofs), 0) | assert_equal(len(avaproofs.prefilled_proofs), 0) | ||||
def test_request_missing_proofs(self): | def test_request_missing_proofs(self): | ||||
self.log.info( | self.log.info( | ||||
"Check the node requests the missing proofs after receiving an avaproofs message") | "Check the node requests the missing proofs after receiving an avaproofs" | ||||
" message" | |||||
) | |||||
node = self.nodes[0] | node = self.nodes[0] | ||||
self.restart_node(0) | self.restart_node(0) | ||||
key0 = random.randint(0, 2**64 - 1) | key0 = random.randint(0, 2**64 - 1) | ||||
key1 = random.randint(0, 2**64 - 1) | key1 = random.randint(0, 2**64 - 1) | ||||
proofs = [gen_proof(self, node)[1] for _ in range(10)] | proofs = [gen_proof(self, node)[1] for _ in range(10)] | ||||
# Build a map from proofid to shortid. Use sorted proofids so we don't | # Build a map from proofid to shortid. Use sorted proofids so we don't | ||||
# have the same indices than the `proofs` list. | # have the same indices than the `proofs` list. | ||||
proofids = [p.proofid for p in proofs] | proofids = [p.proofid for p in proofs] | ||||
shortid_map = {} | shortid_map = {} | ||||
for proofid in sorted(proofids): | for proofid in sorted(proofids): | ||||
shortid_map[proofid] = calculate_shortid(key0, key1, proofid) | shortid_map[proofid] = calculate_shortid(key0, key1, proofid) | ||||
self.log.info("The node ignores unsollicited avaproofs") | self.log.info("The node ignores unsollicited avaproofs") | ||||
spam_peer = get_ava_p2p_interface(self, node) | spam_peer = get_ava_p2p_interface(self, node) | ||||
msg = build_msg_avaproofs( | msg = build_msg_avaproofs(proofs, prefilled_proofs=[], key_pair=[key0, key1]) | ||||
proofs, prefilled_proofs=[], key_pair=[ | |||||
key0, key1]) | |||||
with node.assert_debug_log(["Ignoring unsollicited avaproofs"]): | with node.assert_debug_log(["Ignoring unsollicited avaproofs"]): | ||||
spam_peer.send_message(msg) | spam_peer.send_message(msg) | ||||
def received_avaproofsreq(peer): | def received_avaproofsreq(peer): | ||||
with p2p_lock: | with p2p_lock: | ||||
return peer.last_message.get("avaproofsreq") | return peer.last_message.get("avaproofsreq") | ||||
Show All 14 Lines | def test_request_missing_proofs(self): | ||||
peer.wait_until(lambda: peer.last_message.get("getavaproofs")) | peer.wait_until(lambda: peer.last_message.get("getavaproofs")) | ||||
return peer | return peer | ||||
def expect_indices(shortids, expected_indices, prefilled_proofs=None): | def expect_indices(shortids, expected_indices, prefilled_proofs=None): | ||||
nonlocal p2p_idx | nonlocal p2p_idx | ||||
msg = build_msg_avaproofs( | msg = build_msg_avaproofs( | ||||
[], prefilled_proofs=prefilled_proofs, key_pair=[ | [], prefilled_proofs=prefilled_proofs, key_pair=[key0, key1] | ||||
key0, key1]) | ) | ||||
msg.shortids = shortids | msg.shortids = shortids | ||||
peer = add_avalanche_p2p_outbound() | peer = add_avalanche_p2p_outbound() | ||||
peer.send_message(msg) | peer.send_message(msg) | ||||
self.wait_until(lambda: received_avaproofsreq(peer)) | self.wait_until(lambda: received_avaproofsreq(peer)) | ||||
avaproofsreq = received_avaproofsreq(peer) | avaproofsreq = received_avaproofsreq(peer) | ||||
assert_equal(avaproofsreq.indices, expected_indices) | assert_equal(avaproofsreq.indices, expected_indices) | ||||
self.log.info("Check no proof is requested if there is no shortid") | self.log.info("Check no proof is requested if there is no shortid") | ||||
msg = build_msg_avaproofs([]) | msg = build_msg_avaproofs([]) | ||||
sender = add_avalanche_p2p_outbound() | sender = add_avalanche_p2p_outbound() | ||||
with node.assert_debug_log(["Got an avaproofs message with no shortid"]): | with node.assert_debug_log(["Got an avaproofs message with no shortid"]): | ||||
sender.send_message(msg) | sender.send_message(msg) | ||||
# Make sure we don't get an avaproofsreq message | # Make sure we don't get an avaproofsreq message | ||||
sender.sync_send_with_ping() | sender.sync_send_with_ping() | ||||
with p2p_lock: | with p2p_lock: | ||||
assert_equal(sender.message_count.get("avaproofsreq", 0), 0) | assert_equal(sender.message_count.get("avaproofsreq", 0), 0) | ||||
self.log.info( | self.log.info("Check the node requests all the proofs if it known none") | ||||
"Check the node requests all the proofs if it known none") | |||||
expect_indices( | expect_indices(list(shortid_map.values()), list(range(len(shortid_map)))) | ||||
list(shortid_map.values()), | |||||
list(range(len(shortid_map))) | |||||
) | |||||
self.log.info( | self.log.info("Check the node requests only the missing proofs") | ||||
"Check the node requests only the missing proofs") | |||||
known_proofids = [] | known_proofids = [] | ||||
for proof in proofs[:5]: | for proof in proofs[:5]: | ||||
node.sendavalancheproof(proof.serialize().hex()) | node.sendavalancheproof(proof.serialize().hex()) | ||||
known_proofids.append(proof.proofid) | known_proofids.append(proof.proofid) | ||||
expected_indices = [i for i, proofid in enumerate( | expected_indices = [ | ||||
shortid_map) if proofid not in known_proofids] | i for i, proofid in enumerate(shortid_map) if proofid not in known_proofids | ||||
] | |||||
expect_indices(list(shortid_map.values()), expected_indices) | expect_indices(list(shortid_map.values()), expected_indices) | ||||
self.log.info( | self.log.info("Check the node don't request prefilled proofs") | ||||
"Check the node don't request prefilled proofs") | |||||
# Get the indices for a couple of proofs | # Get the indices for a couple of proofs | ||||
indice_proof5 = list(shortid_map.keys()).index(proofids[5]) | indice_proof5 = list(shortid_map.keys()).index(proofids[5]) | ||||
indice_proof6 = list(shortid_map.keys()).index(proofids[6]) | indice_proof6 = list(shortid_map.keys()).index(proofids[6]) | ||||
prefilled_proofs = [ | prefilled_proofs = [ | ||||
AvalanchePrefilledProof(indice_proof5, proofs[5]), | AvalanchePrefilledProof(indice_proof5, proofs[5]), | ||||
AvalanchePrefilledProof(indice_proof6, proofs[6]), | AvalanchePrefilledProof(indice_proof6, proofs[6]), | ||||
] | ] | ||||
prefilled_proofs = sorted( | prefilled_proofs = sorted( | ||||
prefilled_proofs, | prefilled_proofs, key=lambda prefilled_proof: prefilled_proof.index | ||||
key=lambda prefilled_proof: prefilled_proof.index) | ) | ||||
remaining_shortids = [shortid for proofid, shortid in shortid_map.items( | remaining_shortids = [ | ||||
) if proofid not in proofids[5:7]] | shortid | ||||
for proofid, shortid in shortid_map.items() | |||||
if proofid not in proofids[5:7] | |||||
] | |||||
known_proofids.extend(proofids[5:7]) | known_proofids.extend(proofids[5:7]) | ||||
expected_indices = [i for i, proofid in enumerate( | expected_indices = [ | ||||
shortid_map) if proofid not in known_proofids] | i for i, proofid in enumerate(shortid_map) if proofid not in known_proofids | ||||
] | |||||
expect_indices( | expect_indices( | ||||
remaining_shortids, | remaining_shortids, expected_indices, prefilled_proofs=prefilled_proofs | ||||
expected_indices, | ) | ||||
prefilled_proofs=prefilled_proofs) | |||||
self.log.info( | self.log.info("Check the node requests no proof if it knows all of them") | ||||
"Check the node requests no proof if it knows all of them") | |||||
for proof in proofs[5:]: | for proof in proofs[5:]: | ||||
node.sendavalancheproof(proof.serialize().hex()) | node.sendavalancheproof(proof.serialize().hex()) | ||||
known_proofids.append(proof.proofid) | known_proofids.append(proof.proofid) | ||||
expect_indices(list(shortid_map.values()), []) | expect_indices(list(shortid_map.values()), []) | ||||
self.log.info("Check out of bounds index") | self.log.info("Check out of bounds index") | ||||
bad_peer = add_avalanche_p2p_outbound() | bad_peer = add_avalanche_p2p_outbound() | ||||
msg = build_msg_avaproofs([], prefilled_proofs=[ | msg = build_msg_avaproofs( | ||||
AvalanchePrefilledProof( | [], | ||||
len(shortid_map) + 1, | prefilled_proofs=[ | ||||
gen_proof(self, node)[1])], key_pair=[key0, key1]) | AvalanchePrefilledProof(len(shortid_map) + 1, gen_proof(self, node)[1]) | ||||
], | |||||
key_pair=[key0, key1], | |||||
) | |||||
msg.shortids = list(shortid_map.values()) | msg.shortids = list(shortid_map.values()) | ||||
with node.assert_debug_log(["Misbehaving", "avaproofs-bad-indexes"]): | with node.assert_debug_log(["Misbehaving", "avaproofs-bad-indexes"]): | ||||
bad_peer.send_message(msg) | bad_peer.send_message(msg) | ||||
bad_peer.wait_for_disconnect() | bad_peer.wait_for_disconnect() | ||||
self.log.info("An invalid prefilled proof will trigger a ban") | self.log.info("An invalid prefilled proof will trigger a ban") | ||||
_, no_stake = gen_proof(self, node) | _, no_stake = gen_proof(self, node) | ||||
no_stake.stakes = [] | no_stake.stakes = [] | ||||
bad_peer = add_avalanche_p2p_outbound() | bad_peer = add_avalanche_p2p_outbound() | ||||
msg = build_msg_avaproofs([], prefilled_proofs=[ | msg = build_msg_avaproofs( | ||||
[], | |||||
prefilled_proofs=[ | |||||
AvalanchePrefilledProof(len(shortid_map), no_stake), | AvalanchePrefilledProof(len(shortid_map), no_stake), | ||||
], key_pair=[key0, key1]) | ], | ||||
key_pair=[key0, key1], | |||||
) | |||||
msg.shortids = list(shortid_map.values()) | msg.shortids = list(shortid_map.values()) | ||||
with node.assert_debug_log(["Misbehaving", "invalid-proof"]): | with node.assert_debug_log(["Misbehaving", "invalid-proof"]): | ||||
bad_peer.send_message(msg) | bad_peer.send_message(msg) | ||||
bad_peer.wait_for_disconnect() | bad_peer.wait_for_disconnect() | ||||
def test_send_missing_proofs(self): | def test_send_missing_proofs(self): | ||||
self.log.info("Check the node respond to missing proofs requests") | self.log.info("Check the node respond to missing proofs requests") | ||||
Show All 38 Lines | def test_send_missing_proofs(self): | ||||
requester = node.add_p2p_connection(ProofStoreP2PInterface()) | requester = node.add_p2p_connection(ProofStoreP2PInterface()) | ||||
avaproofs = request_proofs(requester) | avaproofs = request_proofs(requester) | ||||
req = msg_avaproofsreq() | req = msg_avaproofsreq() | ||||
req.indices = indices | req.indices = indices | ||||
requester.send_message(req) | requester.send_message(req) | ||||
# Check we got the expected number of proofs | # Check we got the expected number of proofs | ||||
self.wait_until( | self.wait_until(lambda: len(requester.get_proofs()) == len(indices)) | ||||
lambda: len( | |||||
requester.get_proofs()) == len(indices)) | |||||
# Check we got the expected proofs | # Check we got the expected proofs | ||||
received_shortids = [ | received_shortids = [ | ||||
calculate_shortid( | calculate_shortid(avaproofs.key0, avaproofs.key1, proof.proofid) | ||||
avaproofs.key0, | for proof in requester.get_proofs() | ||||
avaproofs.key1, | ] | ||||
proof.proofid) for proof in requester.get_proofs()] | assert_equal( | ||||
assert_equal(set(received_shortids), | set(received_shortids), {avaproofs.shortids[i] for i in indices} | ||||
{avaproofs.shortids[i] for i in indices}) | ) | ||||
# Only the first proof | # Only the first proof | ||||
check_received_proofs([0]) | check_received_proofs([0]) | ||||
# Only the last proof | # Only the last proof | ||||
check_received_proofs([numof_proof - 1]) | check_received_proofs([numof_proof - 1]) | ||||
# Half first | # Half first | ||||
check_received_proofs(range(0, numof_proof // 2)) | check_received_proofs(range(0, numof_proof // 2)) | ||||
# Half last | # Half last | ||||
check_received_proofs(range(numof_proof // 2, numof_proof)) | check_received_proofs(range(numof_proof // 2, numof_proof)) | ||||
# Even | # Even | ||||
check_received_proofs([i for i in range(numof_proof) if i % 2 == 0]) | check_received_proofs([i for i in range(numof_proof) if i % 2 == 0]) | ||||
# Odds | # Odds | ||||
check_received_proofs([i for i in range(numof_proof) if i % 2 == 1]) | check_received_proofs([i for i in range(numof_proof) if i % 2 == 1]) | ||||
# All | # All | ||||
check_received_proofs(range(numof_proof)) | check_received_proofs(range(numof_proof)) | ||||
self.log.info( | self.log.info( | ||||
"Check the node will not send the proofs if not requested before the timeout elapsed") | "Check the node will not send the proofs if not requested before the" | ||||
" timeout elapsed" | |||||
) | |||||
# Disconnect the peers | # Disconnect the peers | ||||
for peer in node.p2ps: | for peer in node.p2ps: | ||||
peer.peer_disconnect() | peer.peer_disconnect() | ||||
peer.wait_for_disconnect() | peer.wait_for_disconnect() | ||||
mocktime = int(time.time()) | mocktime = int(time.time()) | ||||
node.setmocktime(mocktime) | node.setmocktime(mocktime) | ||||
slow_peer = ProofStoreP2PInterface() | slow_peer = ProofStoreP2PInterface() | ||||
node.add_outbound_p2p_connection( | node.add_outbound_p2p_connection( | ||||
slow_peer, | slow_peer, | ||||
p2p_idx=0, | p2p_idx=0, | ||||
connection_type="avalanche", | connection_type="avalanche", | ||||
services=NODE_NETWORK | NODE_AVALANCHE, | services=NODE_NETWORK | NODE_AVALANCHE, | ||||
) | ) | ||||
slow_peer.wait_until( | slow_peer.wait_until(lambda: slow_peer.last_message.get("getavaproofs")) | ||||
lambda: slow_peer.last_message.get("getavaproofs")) | |||||
slow_peer.nodeid = node.getpeerinfo()[-1]['id'] | slow_peer.nodeid = node.getpeerinfo()[-1]["id"] | ||||
_ = request_proofs(slow_peer) | _ = request_proofs(slow_peer) | ||||
# Elapse the timeout | # Elapse the timeout | ||||
mocktime += AVALANCHE_AVAPROOFS_TIMEOUT + 1 | mocktime += AVALANCHE_AVAPROOFS_TIMEOUT + 1 | ||||
node.setmocktime(mocktime) | node.setmocktime(mocktime) | ||||
node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | ||||
# Periodic compact proofs requests are sent in the same loop than the | # Periodic compact proofs requests are sent in the same loop than the | ||||
# cleanup, so when such a request is made we are sure the cleanup did | # cleanup, so when such a request is made we are sure the cleanup did | ||||
# happen. | # happen. | ||||
slow_peer.wait_until( | slow_peer.wait_until(lambda: slow_peer.message_count.get("getavaproofs") > 1) | ||||
lambda: slow_peer.message_count.get("getavaproofs") > 1) | |||||
req = msg_avaproofsreq() | req = msg_avaproofsreq() | ||||
req.indices = range(numof_proof) | req.indices = range(numof_proof) | ||||
slow_peer.send_and_ping(req) | slow_peer.send_and_ping(req) | ||||
# Check we get no proof | # Check we get no proof | ||||
assert_equal(len(slow_peer.get_proofs()), 0) | assert_equal(len(slow_peer.get_proofs()), 0) | ||||
def test_compact_proofs_download_on_connect(self): | def test_compact_proofs_download_on_connect(self): | ||||
self.log.info( | self.log.info( | ||||
"Check the node get compact proofs upon avalanche outbound discovery") | "Check the node get compact proofs upon avalanche outbound discovery" | ||||
) | |||||
requestee = self.nodes[0] | requestee = self.nodes[0] | ||||
requester = self.nodes[1] | requester = self.nodes[1] | ||||
self.restart_node(0) | self.restart_node(0) | ||||
numof_proof = 10 | numof_proof = 10 | ||||
proofs = [gen_proof(self, requestee)[1] for _ in range(numof_proof)] | proofs = [gen_proof(self, requestee)[1] for _ in range(numof_proof)] | ||||
for proof in proofs: | for proof in proofs: | ||||
requestee.sendavalancheproof(proof.serialize().hex()) | requestee.sendavalancheproof(proof.serialize().hex()) | ||||
proofids = get_proof_ids(requestee) | proofids = get_proof_ids(requestee) | ||||
assert all(proof.proofid in proofids for proof in proofs) | assert all(proof.proofid in proofids for proof in proofs) | ||||
# Start the requester and check it gets all the proofs | # Start the requester and check it gets all the proofs | ||||
self.start_node(1) | self.start_node(1) | ||||
self.connect_nodes(0, 1) | self.connect_nodes(0, 1) | ||||
self.wait_until( | self.wait_until( | ||||
lambda: all( | lambda: all(proof.proofid in proofids for proof in get_proof_ids(requester)) | ||||
proof.proofid in proofids for proof in get_proof_ids(requester))) | ) | ||||
def test_no_compactproofs_during_ibs(self): | def test_no_compactproofs_during_ibs(self): | ||||
self.log.info( | self.log.info("Check the node don't request compact proofs during IBD") | ||||
"Check the node don't request compact proofs during IBD") | |||||
node = self.nodes[0] | node = self.nodes[0] | ||||
chainwork = int(node.getblockchaininfo()['chainwork'], 16) | chainwork = int(node.getblockchaininfo()["chainwork"], 16) | ||||
self.restart_node( | self.restart_node( | ||||
0, | 0, extra_args=self.extra_args[0] + [f"-minimumchainwork={chainwork + 2:#x}"] | ||||
extra_args=self.extra_args[0] + | ) | ||||
[f'-minimumchainwork={chainwork + 2:#x}']) | |||||
assert node.getblockchaininfo()['initialblockdownload'] | assert node.getblockchaininfo()["initialblockdownload"] | ||||
peer = P2PInterface() | peer = P2PInterface() | ||||
node.add_outbound_p2p_connection( | node.add_outbound_p2p_connection( | ||||
peer, | peer, | ||||
p2p_idx=0, | p2p_idx=0, | ||||
connection_type="avalanche", | connection_type="avalanche", | ||||
services=NODE_NETWORK | NODE_AVALANCHE, | services=NODE_NETWORK | NODE_AVALANCHE, | ||||
) | ) | ||||
# Force the node to process the sending loop | # Force the node to process the sending loop | ||||
peer.sync_send_with_ping() | peer.sync_send_with_ping() | ||||
with p2p_lock: | with p2p_lock: | ||||
assert_equal(peer.message_count.get("getavaproofs", 0), 0) | assert_equal(peer.message_count.get("getavaproofs", 0), 0) | ||||
# Make sure there is no message sent as part as the periodic network | # Make sure there is no message sent as part as the periodic network | ||||
# messaging either | # messaging either | ||||
node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | ||||
peer.sync_send_with_ping() | peer.sync_send_with_ping() | ||||
with p2p_lock: | with p2p_lock: | ||||
assert_equal(peer.message_count.get("getavaproofs", 0), 0) | assert_equal(peer.message_count.get("getavaproofs", 0), 0) | ||||
def test_send_inbound_getavaproofs_until_quorum_is_established(self): | def test_send_inbound_getavaproofs_until_quorum_is_established(self): | ||||
self.log.info( | self.log.info( | ||||
"Check we also request the inbounds until the quorum is established") | "Check we also request the inbounds until the quorum is established" | ||||
) | |||||
node = self.nodes[0] | node = self.nodes[0] | ||||
self.restart_node( | self.restart_node( | ||||
0, | 0, | ||||
extra_args=self.extra_args[0] + | extra_args=self.extra_args[0] | ||||
['-avaminquorumstake=1000000', '-avaminavaproofsnodecount=0']) | + ["-avaminquorumstake=1000000", "-avaminavaproofsnodecount=0"], | ||||
) | |||||
assert_equal(node.getavalancheinfo()['ready_to_poll'], False) | assert_equal(node.getavalancheinfo()["ready_to_poll"], False) | ||||
outbound = AvaP2PInterface() | outbound = AvaP2PInterface() | ||||
node.add_outbound_p2p_connection(outbound, p2p_idx=0) | node.add_outbound_p2p_connection(outbound, p2p_idx=0) | ||||
inbound = AvaP2PInterface() | inbound = AvaP2PInterface() | ||||
node.add_p2p_connection(inbound) | node.add_p2p_connection(inbound) | ||||
inbound.nodeid = node.getpeerinfo()[-1]['id'] | inbound.nodeid = node.getpeerinfo()[-1]["id"] | ||||
def count_getavaproofs(peers): | def count_getavaproofs(peers): | ||||
with p2p_lock: | with p2p_lock: | ||||
return sum([peer.message_count.get("getavaproofs", 0) | return sum( | ||||
for peer in peers]) | [peer.message_count.get("getavaproofs", 0) for peer in peers] | ||||
) | |||||
# Upon connection only the outbound gets a compact proofs message | # Upon connection only the outbound gets a compact proofs message | ||||
assert_equal(count_getavaproofs([inbound]), 0) | assert_equal(count_getavaproofs([inbound]), 0) | ||||
self.wait_until(lambda: count_getavaproofs([outbound]) == 1) | self.wait_until(lambda: count_getavaproofs([outbound]) == 1) | ||||
# Periodic send will include the inbound as well | # Periodic send will include the inbound as well | ||||
current_total = count_getavaproofs([inbound, outbound]) | current_total = count_getavaproofs([inbound, outbound]) | ||||
while count_getavaproofs([inbound]) == 0: | while count_getavaproofs([inbound]) == 0: | ||||
node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | ||||
self.wait_until(lambda: count_getavaproofs( | self.wait_until( | ||||
[inbound, outbound]) > current_total) | lambda: count_getavaproofs([inbound, outbound]) > current_total | ||||
) | |||||
current_total = count_getavaproofs([inbound, outbound]) | current_total = count_getavaproofs([inbound, outbound]) | ||||
# Connect the minimum amount of stake and nodes | # Connect the minimum amount of stake and nodes | ||||
for _ in range(8): | for _ in range(8): | ||||
node.add_p2p_connection(AvaP2PInterface(self, node)) | node.add_p2p_connection(AvaP2PInterface(self, node)) | ||||
self.wait_until(lambda: node.getavalancheinfo() | self.wait_until(lambda: node.getavalancheinfo()["ready_to_poll"] is True) | ||||
['ready_to_poll'] is True) | |||||
# From now only the outbound is requested | # From now only the outbound is requested | ||||
count_inbound = count_getavaproofs([inbound]) | count_inbound = count_getavaproofs([inbound]) | ||||
while count_getavaproofs([inbound, outbound]) < current_total + 20: | while count_getavaproofs([inbound, outbound]) < current_total + 20: | ||||
node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | node.mockscheduler(AVALANCHE_MAX_PERIODIC_NETWORKING_INTERVAL) | ||||
inbound.sync_send_with_ping() | inbound.sync_send_with_ping() | ||||
outbound.sync_send_with_ping() | outbound.sync_send_with_ping() | ||||
Show All 9 Lines | def run_test(self): | ||||
self.test_respond_getavaproofs() | self.test_respond_getavaproofs() | ||||
self.test_request_missing_proofs() | self.test_request_missing_proofs() | ||||
self.test_send_missing_proofs() | self.test_send_missing_proofs() | ||||
self.test_compact_proofs_download_on_connect() | self.test_compact_proofs_download_on_connect() | ||||
self.test_no_compactproofs_during_ibs() | self.test_no_compactproofs_during_ibs() | ||||
self.test_send_inbound_getavaproofs_until_quorum_is_established() | self.test_send_inbound_getavaproofs_until_quorum_is_established() | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
CompactProofsTest().main() | CompactProofsTest().main() |