Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-p2p-avalanche.py
Show All 14 Lines | |||||
BLOCK_ACCEPTED = 0 | BLOCK_ACCEPTED = 0 | ||||
BLOCK_REJECTED = 1 | BLOCK_REJECTED = 1 | ||||
BLOCK_UNKNOWN = -1 | BLOCK_UNKNOWN = -1 | ||||
class TestNode(P2PInterface): | class TestNode(P2PInterface): | ||||
def __init__(self): | def __init__(self): | ||||
self.round = 0 | |||||
self.last_avaresponse = None | self.last_avaresponse = None | ||||
self.last_avapoll = None | |||||
super().__init__() | super().__init__() | ||||
def on_avaresponse(self, message): | def on_avaresponse(self, message): | ||||
self.last_avaresponse = message.response | self.last_avaresponse = message.response | ||||
def on_avapoll(self, message): | |||||
self.last_avapoll = message.poll | |||||
def send_poll(self, hashes): | def send_poll(self, hashes): | ||||
msg = msg_avapoll() | msg = msg_avapoll() | ||||
msg.poll.round = self.round | |||||
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=10): | def wait_for_avaresponse(self, timeout=5): | ||||
self.sync_with_ping() | self.sync_with_ping() | ||||
def test_function(): | def test_function(): | ||||
m = self.last_message.get("avaresponse") | m = self.last_message.get("avaresponse") | ||||
return m is not None and m != self.last_avaresponse | return m is not None and m != self.last_avaresponse | ||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | wait_until(test_function, timeout=timeout, lock=mininode_lock) | ||||
def wait_for_avapoll(self, timeout=5): | |||||
self.sync_with_ping() | |||||
def test_function(): | |||||
m = self.last_message.get("avapoll") | |||||
return m is not None and m != self.last_avapoll | |||||
wait_until(test_function, timeout=timeout, lock=mininode_lock) | |||||
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 = 1 | self.num_nodes = 1 | ||||
self.extra_args = [['-enableavalanche=1', '-avacooldown=0']] | self.extra_args = [['-enableavalanche=1', '-avacooldown=0']] | ||||
''' | |||||
self.num_nodes = 2 | |||||
self.extra_args = [ | |||||
['-enableavalanche=1', '-avacooldown=0'], | |||||
['-enableavalanche=1', '-avacooldown=0', '-noparkdeepreorg', '-maxreorgdepth=-1']] | |||||
# ''' | |||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
# Create a fake node and connect it to our real node. | # Create a fake node and connect it to our real node. | ||||
poll_node = TestNode() | poll_node = TestNode() | ||||
node.add_p2p_connection(poll_node) | node.add_p2p_connection(poll_node) | ||||
poll_node.wait_for_verack() | poll_node.wait_for_verack() | ||||
poll_node.sync_with_ping() | poll_node.sync_with_ping() | ||||
# Get our own node id so we can use it later. | |||||
nodeid = node.getpeerinfo()[-1]['id'] | |||||
# 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 | ||||
node.generatetoaddress(100, address) | node.generatetoaddress(100, address) | ||||
# Get the key so we can verify signatures. | # Get the key so we can verify signatures. | ||||
avakey = bytes.fromhex(node.getavalanchekey()) | avakey = bytes.fromhex(node.getavalanchekey()) | ||||
self.log.info("Poll for the chain tip...") | self.log.info("Poll for the chain tip...") | ||||
best_block_hash = int(node.getbestblockhash(), 16) | best_block_hash = int(node.getbestblockhash(), 16) | ||||
poll_node.send_poll([best_block_hash]) | poll_node.send_poll([best_block_hash]) | ||||
poll_node.wait_for_avaresponse() | poll_node.wait_for_avaresponse() | ||||
def assert_response(response, expected): | def assert_response(response, expected): | ||||
r = response.response | r = response.response | ||||
assert_equal(r.cooldown, 0) | assert_equal(r.cooldown, 0) | ||||
# Verify signature. | # Verify signature. | ||||
assert schnorr.verify(response.sig, avakey, r.get_hash()) | assert schnorr.verify(response.sig, avakey, r.get_hash()) | ||||
votes = r.votes | votes = r.votes | ||||
self.log.info("response: {}".format(repr(response))) | |||||
assert_equal(len(votes), len(expected)) | assert_equal(len(votes), len(expected)) | ||||
for i in range(0, len(votes)): | for i in range(0, len(votes)): | ||||
assert_equal(repr(votes[i]), repr(expected[i])) | assert_equal(repr(votes[i]), repr(expected[i])) | ||||
assert_response(poll_node.last_avaresponse, [ | assert_response(poll_node.last_avaresponse, [ | ||||
AvalancheVote(BLOCK_ACCEPTED, best_block_hash)]) | AvalancheVote(BLOCK_ACCEPTED, best_block_hash)]) | ||||
self.log.info("Poll for a selection of blocks...") | self.log.info("Poll for a selection of blocks...") | ||||
Show All 10 Lines | def run_test(self): | ||||
poll_node.send_poll(various_block_hashes) | poll_node.send_poll(various_block_hashes) | ||||
poll_node.wait_for_avaresponse() | poll_node.wait_for_avaresponse() | ||||
assert_response(poll_node.last_avaresponse, | assert_response(poll_node.last_avaresponse, | ||||
[AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes]) | [AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes]) | ||||
self.log.info( | self.log.info( | ||||
"Poll for a selection of blocks, but some are now invalid...") | "Poll for a selection of blocks, but some are now invalid...") | ||||
invalidated_block = node.getblockhash(75) | invalidated_block = node.getblockhash(76) | ||||
node.invalidateblock(invalidated_block) | node.invalidateblock(invalidated_block) | ||||
# We need to send the coin to a new address in order to make sure we do | # We need to send the coin to a new address in order to make sure we do | ||||
# not regenerate the same block. | # not regenerate the same block. | ||||
node.generatetoaddress( | node.generatetoaddress( | ||||
30, 'bchreg:pqv2r67sgz3qumufap3h2uuj0zfmnzuv8v7ej0fffv') | 26, 'bchreg:pqv2r67sgz3qumufap3h2uuj0zfmnzuv8v7ej0fffv') | ||||
node.reconsiderblock(invalidated_block) | node.reconsiderblock(invalidated_block) | ||||
poll_node.send_poll(various_block_hashes) | poll_node.send_poll(various_block_hashes) | ||||
poll_node.wait_for_avaresponse() | poll_node.wait_for_avaresponse() | ||||
assert_response(poll_node.last_avaresponse, | assert_response(poll_node.last_avaresponse, | ||||
[AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes[:5]] + | [AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes[:5]] + | ||||
[AvalancheVote(BLOCK_REJECTED, h) for h in various_block_hashes[-3:]]) | [AvalancheVote(BLOCK_REJECTED, h) for h in various_block_hashes[-3:]]) | ||||
Show All 11 Lines | def run_test(self): | ||||
] | ] | ||||
poll_node.send_poll(various_block_hashes) | poll_node.send_poll(various_block_hashes) | ||||
poll_node.wait_for_avaresponse() | poll_node.wait_for_avaresponse() | ||||
assert_response(poll_node.last_avaresponse, | assert_response(poll_node.last_avaresponse, | ||||
[AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes[:3]] + | [AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes[:3]] + | ||||
[AvalancheVote(BLOCK_REJECTED, h) for h in various_block_hashes[3:6]] + | [AvalancheVote(BLOCK_REJECTED, h) for h in various_block_hashes[3:6]] + | ||||
[AvalancheVote(BLOCK_UNKNOWN, h) for h in various_block_hashes[-3:]]) | [AvalancheVote(BLOCK_UNKNOWN, h) for h in various_block_hashes[-3:]]) | ||||
self.log.info("Trigger polling from the node...") | |||||
node.addavalanchepeer(nodeid) | |||||
# Create a fork 2 block deep. This should trigger polling. | |||||
fork_node = self.nodes[1] | |||||
fork_node.invalidateblock(fork_node.getblockhash(100)) | |||||
fork_node.generate(1) | |||||
poll_node.wait_for_avapoll() | |||||
def can_find_block_in_poll(hash): | |||||
poll_node.wait_for_avapoll() | |||||
invs = poll_node.last_avapoll.invs | |||||
votes = [] | |||||
found_hash = False | |||||
for inv in invs: | |||||
# Look for what we expect | |||||
if inv.hash == hash: | |||||
found_hash = True | |||||
# Vote yes to everything | |||||
votes.append(AvalancheVote(BLOCK_ACCEPTED, inv.hash)) | |||||
return found_hash | |||||
# Because the new tip is a deep reorg, the node should start to poll | |||||
# for it. | |||||
hash_to_find = int(fork_node.getbestblockhash(), 16) | |||||
wait_until(lambda: can_find_block_in_poll(hash_to_find), timeout=5) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
AvalancheTest().main() | AvalancheTest().main() |