Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-p2p-avalanche.py
Show All 23 Lines | |||||
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.round = 0 | ||||
self.last_avaresponse = None | self.avaresponses = [] | ||||
self.last_avapoll = None | self.avapolls = [] | ||||
super().__init__() | super().__init__() | ||||
def on_avaresponse(self, message): | def on_avaresponse(self, message): | ||||
self.last_avaresponse = message.response | with mininode_lock: | ||||
self.avaresponses.append(message.response) | |||||
def on_avapoll(self, message): | def on_avapoll(self, message): | ||||
self.last_avapoll = message.poll | with mininode_lock: | ||||
self.avapolls.append(message.poll) | |||||
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 wait_for_avaresponse(self, timeout=5): | ||||
previous_response = self.last_avaresponse | |||||
wait_until( | wait_until( | ||||
lambda: self.last_avaresponse is not previous_response, | lambda: len(self.avaresponses) > 0, | ||||
timeout=timeout, | timeout=timeout, | ||||
lock=mininode_lock) | lock=mininode_lock) | ||||
with mininode_lock: | |||||
return self.avaresponses.pop(0) | |||||
def wait_for_avapoll(self, timeout=5): | def wait_for_avapoll(self, timeout=5): | ||||
previous_poll = self.last_avapoll | |||||
wait_until( | wait_until( | ||||
lambda: self.last_avapoll is not previous_poll, | lambda: len(self.avapolls) > 0, | ||||
timeout=timeout, | timeout=timeout, | ||||
lock=mininode_lock) | lock=mininode_lock) | ||||
with mininode_lock: | |||||
return self.avapolls.pop(0) | |||||
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 All 19 Lines | def run_test(self): | ||||
sync_blocks([node, fork_node]) | sync_blocks([node, fork_node]) | ||||
# 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() | |||||
def assert_response(response, expected): | def assert_response(expected): | ||||
response = poll_node.wait_for_avaresponse() | |||||
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 | ||||
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([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...") | ||||
various_block_hashes = [ | various_block_hashes = [ | ||||
int(node.getblockhash(0), 16), | int(node.getblockhash(0), 16), | ||||
int(node.getblockhash(1), 16), | int(node.getblockhash(1), 16), | ||||
int(node.getblockhash(10), 16), | int(node.getblockhash(10), 16), | ||||
int(node.getblockhash(25), 16), | int(node.getblockhash(25), 16), | ||||
int(node.getblockhash(42), 16), | int(node.getblockhash(42), 16), | ||||
int(node.getblockhash(96), 16), | int(node.getblockhash(96), 16), | ||||
int(node.getblockhash(99), 16), | int(node.getblockhash(99), 16), | ||||
int(node.getblockhash(100), 16), | int(node.getblockhash(100), 16), | ||||
] | ] | ||||
poll_node.send_poll(various_block_hashes) | poll_node.send_poll(various_block_hashes) | ||||
poll_node.wait_for_avaresponse() | assert_response([AvalancheVote(BLOCK_ACCEPTED, h) | ||||
assert_response(poll_node.last_avaresponse, | 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(76) | 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( | ||||
26, '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() | assert_response([AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes[:5]] + | ||||
assert_response(poll_node.last_avaresponse, | |||||
[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:]]) | ||||
self.log.info("Poll for unknown blocks...") | self.log.info("Poll for unknown blocks...") | ||||
various_block_hashes = [ | various_block_hashes = [ | ||||
int(node.getblockhash(0), 16), | int(node.getblockhash(0), 16), | ||||
int(node.getblockhash(25), 16), | int(node.getblockhash(25), 16), | ||||
int(node.getblockhash(42), 16), | int(node.getblockhash(42), 16), | ||||
various_block_hashes[5], | various_block_hashes[5], | ||||
various_block_hashes[6], | various_block_hashes[6], | ||||
various_block_hashes[7], | various_block_hashes[7], | ||||
random.randrange(1 << 255, (1 << 256) - 1), | random.randrange(1 << 255, (1 << 256) - 1), | ||||
random.randrange(1 << 255, (1 << 256) - 1), | random.randrange(1 << 255, (1 << 256) - 1), | ||||
random.randrange(1 << 255, (1 << 256) - 1), | random.randrange(1 << 255, (1 << 256) - 1), | ||||
] | ] | ||||
poll_node.send_poll(various_block_hashes) | poll_node.send_poll(various_block_hashes) | ||||
poll_node.wait_for_avaresponse() | assert_response([AvalancheVote(BLOCK_ACCEPTED, h) for h in various_block_hashes[:3]] + | ||||
assert_response(poll_node.last_avaresponse, | |||||
[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...") | self.log.info("Trigger polling from the node...") | ||||
# duplicate the deterministic sig test from src/test/key_tests.cpp | # duplicate the deterministic sig test from src/test/key_tests.cpp | ||||
privkey = bytes.fromhex( | privkey = bytes.fromhex( | ||||
"12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747") | "12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747") | ||||
pubkey = schnorr.getpubkey(privkey, compressed=True) | pubkey = schnorr.getpubkey(privkey, compressed=True) | ||||
node.addavalanchepeer(nodeid, pubkey.hex()) | node.addavalanchepeer(nodeid, pubkey.hex()) | ||||
# Make sure the fork node has synced the blocks | # Make sure the fork node has synced the blocks | ||||
sync_blocks([node, fork_node]) | sync_blocks([node, fork_node]) | ||||
# Create a fork 2 blocks deep. This should trigger polling. | # Create a fork 2 blocks deep. This should trigger polling. | ||||
fork_node.invalidateblock(fork_node.getblockhash(100)) | fork_node.invalidateblock(fork_node.getblockhash(100)) | ||||
fork_address = fork_node.get_deterministic_priv_key().address | fork_address = fork_node.get_deterministic_priv_key().address | ||||
fork_node.generatetoaddress(2, fork_address) | fork_node.generatetoaddress(2, fork_address) | ||||
def can_find_block_in_poll(hash): | def can_find_block_in_poll(hash): | ||||
poll_node.wait_for_avapoll() | poll = poll_node.wait_for_avapoll() | ||||
invs = poll_node.last_avapoll.invs | invs = poll.invs | ||||
votes = [] | votes = [] | ||||
found_hash = False | found_hash = False | ||||
for inv in invs: | for inv in invs: | ||||
# Look for what we expect | # Look for what we expect | ||||
if inv.hash == hash: | if inv.hash == hash: | ||||
found_hash = True | found_hash = True | ||||
# Vote yes to everything | # Vote yes to everything | ||||
Show All 12 Lines |