diff --git a/test/functional/abc_p2p_avalanche_quorum.py b/test/functional/abc_p2p_avalanche_quorum.py --- a/test/functional/abc_p2p_avalanche_quorum.py +++ b/test/functional/abc_p2p_avalanche_quorum.py @@ -24,49 +24,56 @@ class AvalancheQuorumTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 1 + self.num_nodes = 2 self.min_avaproofs_node_count = 8 - self.extra_args = [ - [ - '-enableavalanche=1', - '-avacooldown=0', - '-avatimeout=0', - '-avaminquorumstake=100000000', - '-avaminquorumconnectedstakeratio=0.8', - f'-avaminavaproofsnodecount={self.min_avaproofs_node_count}', - ] - ] + self.extra_args = [[ + '-enableavalanche=1', + '-avacooldown=0', + '-avatimeout=0', + '-avaminquorumstake=100000000', + '-avaminquorumconnectedstakeratio=0.8', + ]] * self.num_nodes + self.extra_args[0] = self.extra_args[0] + \ + ['-avaminavaproofsnodecount=0'] + self.extra_args[1] = self.extra_args[1] + \ + [f'-avaminavaproofsnodecount={self.min_avaproofs_node_count}'] def run_test(self): - # Create a local node to poll from and a helper to send polls from it - # and assert on the response - node = self.nodes[0] - poll_node = get_ava_p2p_interface(node) - poll_node_pubkey = ECPubKey() - poll_node_pubkey.set(bytes.fromhex(node.getavalanchekey())) - - def poll_and_assert_response(expected): + # Prepare peers proofs + peers = [] + for i in range(0, self.min_avaproofs_node_count): + key, proof = gen_proof(self.nodes[0]) + peers.append({'key': key, 'proof': proof}) + + # Let the nodes known about all the blocks then disconnect them so we're + # sure they won't exchange proofs when we start connecting peers. + self.sync_all() + self.disconnect_nodes(0, 1) + + # Build polling nodes + pollers = [get_ava_p2p_interface(node) for node in self.nodes] + + def poll_and_assert_response(node, expected): + pubkey = ECPubKey() + pubkey.set(bytes.fromhex(node.getavalanchekey())) + + poller = pollers[node.index] + # Send poll for best block block = int(node.getbestblockhash(), 16) - poll_node.send_poll([block]) + poller.send_poll([block]) # Get response and check that the vote is what we expect - response = poll_node.wait_for_avaresponse() + response = poller.wait_for_avaresponse() r = response.response - assert poll_node_pubkey.verify_schnorr(response.sig, r.get_hash()) + assert pubkey.verify_schnorr(response.sig, r.get_hash()) assert_equal(len(r.votes), 1) actual = repr(r.votes[0]) expected = repr(AvalancheVote(expected, block)) assert_equal(actual, expected) - # Prepare peers proofs - peers = [] - for i in range(0, self.min_avaproofs_node_count): - key, proof = gen_proof(node) - peers.append({'key': key, 'proof': proof}) - - def addavalanchenode(peer): + def addavalanchenode(node, peer): pubkey = peer['key'].get_pubkey().get_bytes().hex() assert node.addavalanchenode( peer['node'].nodeid, @@ -76,11 +83,11 @@ p2p_idx = 0 - def get_ava_outbound(n, peer): + def get_ava_outbound(node, peer): nonlocal p2p_idx avapeer = AvaP2PInterface() - n.add_outbound_p2p_connection( + node.add_outbound_p2p_connection( avapeer, p2p_idx=p2p_idx, connection_type="avalanche", @@ -90,7 +97,7 @@ avapeer.nodeid = node.getpeerinfo()[-1]['id'] peer['node'] = avapeer - addavalanchenode(peer) + addavalanchenode(node, peer) avapeer.wait_until( lambda: avapeer.last_message.get("getavaproofs")) @@ -100,36 +107,44 @@ return avapeer + def add_avapeer_and_check_status(peer, expected_status): + for i, node in enumerate(self.nodes): + get_ava_outbound(node, peer) + poll_and_assert_response(node, expected_status[i]) + # Start polling. The response should be UNKNOWN because there's no # score - poll_and_assert_response(AvalancheVoteError.UNKNOWN) + [poll_and_assert_response(node, AvalancheVoteError.UNKNOWN) + for node in self.nodes] # Create one peer with half the score and add one node - get_ava_outbound(node, peers[0]) - poll_and_assert_response(AvalancheVoteError.UNKNOWN) + add_avapeer_and_check_status( + peers[0], [ + AvalancheVoteError.UNKNOWN, + AvalancheVoteError.UNKNOWN, + ]) # Create a second peer with the other half and add one node. - # This is not enough because we are lacking avaproofs messages - get_ava_outbound(node, peers[1]) - poll_and_assert_response(AvalancheVoteError.UNKNOWN) + # This is enough for node0 but not node1 + add_avapeer_and_check_status( + peers[1], [ + AvalancheVoteError.ACCEPTED, + AvalancheVoteError.UNKNOWN, + ]) # Add more peers for triggering the avaproofs messaging for i in range(2, self.min_avaproofs_node_count - 1): - get_ava_outbound(node, peers[i]) - poll_and_assert_response(AvalancheVoteError.UNKNOWN) - - get_ava_outbound(node, peers[self.min_avaproofs_node_count - 1]) - poll_and_assert_response(AvalancheVoteError.ACCEPTED) - - # Disconnect peer 1's node which drops us below the threshold, but we've - # latched that the quorum is established - peers[1]['node'].peer_disconnect() - peers[1]['node'].wait_for_disconnect() - poll_and_assert_response(AvalancheVoteError.ACCEPTED) - - # Reconnect node and re-establish quorum - get_ava_outbound(node, peers[1]) - poll_and_assert_response(AvalancheVoteError.ACCEPTED) + add_avapeer_and_check_status( + peers[i], [ + AvalancheVoteError.ACCEPTED, + AvalancheVoteError.UNKNOWN, + ]) + + add_avapeer_and_check_status( + peers[self.min_avaproofs_node_count - 1], [ + AvalancheVoteError.ACCEPTED, + AvalancheVoteError.ACCEPTED, + ]) if __name__ == '__main__':