Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc_p2p_avalanche.py
Show All 10 Lines | |||||
) | ) | ||||
from test_framework.mininode import P2PInterface, mininode_lock | from test_framework.mininode import P2PInterface, mininode_lock | ||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
AvalancheResponse, | AvalancheResponse, | ||||
AvalancheVote, | AvalancheVote, | ||||
CInv, | CInv, | ||||
msg_avapoll, | msg_avapoll, | ||||
msg_tcpavaresponse, | msg_tcpavaresponse, | ||||
NODE_AVALANCHE, | |||||
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 ( | ||||
assert_equal, | assert_equal, | ||||
wait_until, | wait_until, | ||||
) | ) | ||||
BLOCK_ACCEPTED = 0 | BLOCK_ACCEPTED = 0 | ||||
BLOCK_INVALID = 1 | BLOCK_INVALID = 1 | ||||
BLOCK_PARKED = 2 | BLOCK_PARKED = 2 | ||||
BLOCK_FORK = 3 | BLOCK_FORK = 3 | ||||
BLOCK_UNKNOWN = -1 | BLOCK_UNKNOWN = -1 | ||||
BLOCK_MISSING = -2 | BLOCK_MISSING = -2 | ||||
BLOCK_PENDING = -3 | BLOCK_PENDING = -3 | ||||
class TestNode(P2PInterface): | class TestNode(P2PInterface): | ||||
def __init__(self): | def __init__(self): | ||||
self.round = 0 | self.round = 0 | ||||
self.avahello = None | |||||
self.avaresponses = [] | self.avaresponses = [] | ||||
self.avapolls = [] | self.avapolls = [] | ||||
super().__init__() | super().__init__() | ||||
def peer_connect(self, *args, **kwargs): | |||||
create_conn = super().peer_connect(*args, **kwargs) | |||||
# Save the nonce and extra entropy so they can be reused later. | |||||
self.local_nonce = self.on_connection_send_msg.nNonce | |||||
self.local_extra_entropy = self.on_connection_send_msg.nExtraEntropy | |||||
return create_conn | |||||
def on_version(self, message): | |||||
super().on_version(message) | |||||
# Save the nonce and extra entropy so they can be reused later. | |||||
self.remote_nonce = message.nNonce | |||||
self.remote_extra_entropy = message.nExtraEntropy | |||||
def on_avaresponse(self, message): | def on_avaresponse(self, message): | ||||
with mininode_lock: | with mininode_lock: | ||||
self.avaresponses.append(message.response) | self.avaresponses.append(message.response) | ||||
def on_avapoll(self, message): | def on_avapoll(self, message): | ||||
with mininode_lock: | with mininode_lock: | ||||
self.avapolls.append(message.poll) | self.avapolls.append(message.poll) | ||||
def on_avahello(self, message): | |||||
with mininode_lock: | |||||
assert(self.avahello is None) | |||||
self.avahello = message | |||||
def send_avaresponse(self, round, votes, privkey): | def send_avaresponse(self, round, votes, privkey): | ||||
response = AvalancheResponse(round, 0, votes) | response = AvalancheResponse(round, 0, votes) | ||||
sig = privkey.sign_schnorr(response.get_hash()) | sig = privkey.sign_schnorr(response.get_hash()) | ||||
msg = msg_tcpavaresponse() | msg = msg_tcpavaresponse() | ||||
msg.response = TCPAvalancheResponse(response, sig) | msg.response = TCPAvalancheResponse(response, sig) | ||||
self.send_message(msg) | self.send_message(msg) | ||||
def wait_for_avaresponse(self, timeout=5): | |||||
wait_until( | |||||
lambda: len(self.avaresponses) > 0, | |||||
timeout=timeout, | |||||
lock=mininode_lock) | |||||
with mininode_lock: | |||||
return self.avaresponses.pop(0) | |||||
def send_poll(self, hashes): | def send_poll(self, hashes): | ||||
msg = msg_avapoll() | msg = msg_avapoll() | ||||
msg.poll.round = self.round | msg.poll.round = self.round | ||||
self.round += 1 | self.round += 1 | ||||
for h in hashes: | for h in hashes: | ||||
msg.poll.invs.append(CInv(2, h)) | msg.poll.invs.append(CInv(2, h)) | ||||
self.send_message(msg) | self.send_message(msg) | ||||
def wait_for_avaresponse(self, timeout=5): | def get_avapoll_if_available(self): | ||||
with mininode_lock: | |||||
return self.avapolls.pop(0) if len(self.avapolls) > 0 else None | |||||
def wait_for_avahello(self, timeout=5): | |||||
wait_until( | wait_until( | ||||
lambda: len(self.avaresponses) > 0, | lambda: self.avahello is not None, | ||||
timeout=timeout, | timeout=timeout, | ||||
lock=mininode_lock) | lock=mininode_lock) | ||||
with mininode_lock: | with mininode_lock: | ||||
return self.avaresponses.pop(0) | return self.avahello | ||||
def get_avapoll_if_available(self): | |||||
with mininode_lock: | |||||
return self.avapolls.pop(0) if len(self.avapolls) > 0 else None | |||||
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']] | ||||
self.supports_cli = False | self.supports_cli = False | ||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
# Build a fake quorum of nodes. | # Build a fake quorum of nodes. | ||||
def get_quorum(): | def get_quorum(): | ||||
def get_node(): | def get_node(): | ||||
n = TestNode() | n = TestNode() | ||||
node.add_p2p_connection(n) | node.add_p2p_connection( | ||||
n, services=NODE_NETWORK | NODE_AVALANCHE) | |||||
n.wait_for_verack() | n.wait_for_verack() | ||||
# Get our own node id so we can use it later. | # Get our own node id so we can use it later. | ||||
n.nodeid = node.getpeerinfo()[-1]['id'] | n.nodeid = node.getpeerinfo()[-1]['id'] | ||||
return n | return n | ||||
return [get_node() for _ in range(0, 16)] | return [get_node() for _ in range(0, 16)] | ||||
quorum = get_quorum() | |||||
# Pick on node from the quorum for polling. | # Pick on node from the quorum for polling. | ||||
quorum = get_quorum() | |||||
poll_node = quorum[0] | poll_node = quorum[0] | ||||
# Generate many block and poll for them. | # Generate many block and poll for them. | ||||
address = node.get_deterministic_priv_key().address | address = node.get_deterministic_priv_key().address | ||||
blocks = node.generatetoaddress(100, address) | blocks = node.generatetoaddress(100, address) | ||||
def get_coinbase(h): | def get_coinbase(h): | ||||
b = node.getblock(h, 2) | b = node.getblock(h, 2) | ||||
▲ Show 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# Because everybody answers yes, the node will accept that block. | # Because everybody answers yes, the node will accept that block. | ||||
wait_until(has_accepted_new_tip, timeout=15) | wait_until(has_accepted_new_tip, timeout=15) | ||||
assert_equal(node.getbestblockhash(), fork_tip) | assert_equal(node.getbestblockhash(), fork_tip) | ||||
self.log.info("Answer all polls to park...") | self.log.info("Answer all polls to park...") | ||||
node.generate(1) | node.generate(1) | ||||
tip_to_park = node.getbestblockhash() | tip_to_park = node.getbestblockhash() | ||||
self.log.info(tip_to_park) | |||||
hash_to_find = int(tip_to_park, 16) | hash_to_find = int(tip_to_park, 16) | ||||
assert(tip_to_park != fork_tip) | assert(tip_to_park != fork_tip) | ||||
def has_parked_new_tip(): | def has_parked_new_tip(): | ||||
can_find_block_in_poll(hash_to_find, BLOCK_PARKED) | can_find_block_in_poll(hash_to_find, BLOCK_PARKED) | ||||
return node.getbestblockhash() == fork_tip | return node.getbestblockhash() == fork_tip | ||||
# Because everybody answers no, the node will park that block. | # Because everybody answers no, the node will park that block. | ||||
wait_until(has_parked_new_tip, timeout=15) | wait_until(has_parked_new_tip, timeout=15) | ||||
assert_equal(node.getbestblockhash(), fork_tip) | assert_equal(node.getbestblockhash(), fork_tip) | ||||
# Restart the node and rebuild the quorum | |||||
self.restart_node(0, self.extra_args[0] + [ | |||||
"-avaproof={}".format(proof), | |||||
"-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | |||||
]) | |||||
quorum = get_quorum() | |||||
poll_node = quorum[0] | |||||
# Check the avahello is consistent | |||||
avahello = poll_node.wait_for_avahello().hello | |||||
avakey.set(bytes.fromhex(node.getavalanchekey())) | |||||
assert avakey.verify_schnorr( | |||||
avahello.sig, avahello.get_sighash(poll_node)) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
AvalancheTest().main() | AvalancheTest().main() |