Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc_rpc_avalancheproof.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2021 The Bitcoin developers | # Copyright (c) 2021 The Bitcoin developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test building avalanche proofs and using them to add avalanche peers.""" | """Test building avalanche proofs and using them to add avalanche peers.""" | ||||
from test_framework.avatools import get_stakes | from test_framework.avatools import get_stakes | ||||
from test_framework.key import ECKey | from test_framework.key import ECKey, bytes_to_wif | ||||
from test_framework.messages import AvalancheDelegation | |||||
from test_framework.mininode import P2PInterface | from test_framework.mininode import P2PInterface | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.test_node import ErrorMatch | from test_framework.test_node import ErrorMatch | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
append_config, | append_config, | ||||
assert_equal, | assert_equal, | ||||
assert_raises_rpc_error, | |||||
) | ) | ||||
AVALANCHE_MAX_PROOF_STAKES = 1000 | AVALANCHE_MAX_PROOF_STAKES = 1000 | ||||
def add_interface_node(test_node) -> str: | def add_interface_node(test_node) -> str: | ||||
"""Create a mininode, connect it to test_node, return the nodeid | """Create a mininode, connect it to test_node, return the nodeid | ||||
of the mininode as registered by test_node. | of the mininode as registered by test_node. | ||||
Show All 17 Lines | def run_test(self): | ||||
blockhashes = node.generatetoaddress(100, addrkey0.address) | blockhashes = node.generatetoaddress(100, addrkey0.address) | ||||
self.log.info( | self.log.info( | ||||
"Make build a valid proof and restart the node to use it") | "Make build a valid proof and restart the node to use it") | ||||
privkey = ECKey() | privkey = ECKey() | ||||
privkey.set(bytes.fromhex( | privkey.set(bytes.fromhex( | ||||
"12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747"), True) | "12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747"), True) | ||||
proof_master = privkey.get_pubkey().get_bytes().hex() | def get_hex_pubkey(privkey): | ||||
return privkey.get_pubkey().get_bytes().hex() | |||||
proof_master = get_hex_pubkey(privkey) | |||||
proof_sequence = 11 | proof_sequence = 11 | ||||
proof_expiration = 12 | proof_expiration = 12 | ||||
proof = node.buildavalancheproof( | proof = node.buildavalancheproof( | ||||
proof_sequence, proof_expiration, proof_master, | proof_sequence, proof_expiration, proof_master, | ||||
get_stakes(node, [blockhashes[0]], addrkey0.key)) | get_stakes(node, [blockhashes[0]], addrkey0.key)) | ||||
# Restart the node, making sure it is initially in IBD mode | # Restart the node, making sure it is initially in IBD mode | ||||
minchainwork = int(node.getblockchaininfo()["chainwork"], 16) + 1 | minchainwork = int(node.getblockchaininfo()["chainwork"], 16) + 1 | ||||
Show All 9 Lines | def run_test(self): | ||||
# Our proof cannot be verified during IBD, so we should have no peer | # Our proof cannot be verified during IBD, so we should have no peer | ||||
assert not node.getavalanchepeerinfo() | assert not node.getavalanchepeerinfo() | ||||
# Mining a few more blocks should cause us to leave IBD | # Mining a few more blocks should cause us to leave IBD | ||||
node.generate(2) | node.generate(2) | ||||
# Our proof is now verified and our node is added as a peer | # Our proof is now verified and our node is added as a peer | ||||
assert node.getblockchaininfo()["initialblockdownload"] is False | assert node.getblockchaininfo()["initialblockdownload"] is False | ||||
assert_equal(len(node.getavalanchepeerinfo()), 1) | assert_equal(len(node.getavalanchepeerinfo()), 1) | ||||
self.log.info("Generate delegations for the proof") | |||||
# Stack up a few delegation levels | |||||
def gen_privkey(): | |||||
pk = ECKey() | |||||
pk.generate() | |||||
return pk | |||||
delegator_privkey = privkey | |||||
delegation = None | |||||
for _ in range(10): | |||||
delegated_privkey = gen_privkey() | |||||
delegation = node.delegateavalancheproof( | |||||
proof, | |||||
bytes_to_wif(delegator_privkey.get_bytes()), | |||||
get_hex_pubkey(delegated_privkey), | |||||
delegation, | |||||
) | |||||
delegator_privkey = delegated_privkey | |||||
# Invalid proof | |||||
blockhashes = node.generatetoaddress(AVALANCHE_MAX_PROOF_STAKES + 1, | |||||
addrkey0.address) | |||||
too_many_utxos = node.buildavalancheproof( | |||||
proof_sequence, proof_expiration, | |||||
proof_master, get_stakes(node, blockhashes, addrkey0.key)) | |||||
random_privkey = gen_privkey() | |||||
random_pubkey = get_hex_pubkey(random_privkey) | |||||
assert_raises_rpc_error(-8, "The proof is invalid", | |||||
node.delegateavalancheproof, | |||||
too_many_utxos, | |||||
bytes_to_wif(privkey.get_bytes()), | |||||
random_pubkey, | |||||
) | |||||
# Invalid privkey | |||||
assert_raises_rpc_error(-5, "The private key is invalid", | |||||
node.delegateavalancheproof, | |||||
proof, | |||||
bytes_to_wif(bytes(32)), | |||||
random_pubkey, | |||||
) | |||||
# Invalid delegation | |||||
bad_dg = AvalancheDelegation() | |||||
assert_raises_rpc_error(-8, "The supplied delegation is not valid", | |||||
node.delegateavalancheproof, | |||||
proof, | |||||
bytes_to_wif(privkey.get_bytes()), | |||||
random_pubkey, | |||||
bad_dg.serialize().hex(), | |||||
) | |||||
# Wrong privkey, does not match the proof | |||||
assert_raises_rpc_error(-8, "The private key does not match the proof or the delegation", | |||||
node.delegateavalancheproof, | |||||
proof, | |||||
bytes_to_wif(random_privkey.get_bytes()), | |||||
random_pubkey, | |||||
) | |||||
# Wrong privkey, match the proof but does not match the delegation | |||||
assert_raises_rpc_error(-8, "The private key does not match the proof or the delegation", | |||||
node.delegateavalancheproof, | |||||
proof, | |||||
bytes_to_wif(privkey.get_bytes()), | |||||
random_pubkey, | |||||
delegation, | |||||
) | |||||
# Test invalid proofs | # Test invalid proofs | ||||
self.log.info("Bad proof should be rejected at startup") | self.log.info("Bad proof should be rejected at startup") | ||||
no_stake = node.buildavalancheproof( | no_stake = node.buildavalancheproof( | ||||
proof_sequence, proof_expiration, proof_master, []) | proof_sequence, proof_expiration, proof_master, []) | ||||
blockhashes = node.generatetoaddress(AVALANCHE_MAX_PROOF_STAKES + 1, | |||||
addrkey0.address) | |||||
dust = node.buildavalancheproof( | dust = node.buildavalancheproof( | ||||
proof_sequence, proof_expiration, proof_master, | proof_sequence, proof_expiration, proof_master, | ||||
get_stakes(node, [blockhashes[0]], addrkey0.key, amount="0")) | get_stakes(node, [blockhashes[0]], addrkey0.key, amount="0")) | ||||
duplicate_stake = node.buildavalancheproof( | duplicate_stake = node.buildavalancheproof( | ||||
proof_sequence, proof_expiration, proof_master, | proof_sequence, proof_expiration, proof_master, | ||||
get_stakes(node, [blockhashes[0]] * 2, addrkey0.key)) | get_stakes(node, [blockhashes[0]] * 2, addrkey0.key)) | ||||
bad_sig = ("0b000000000000000c0000000000000021030b4c866585dd868a9d62348" | bad_sig = ("0b000000000000000c0000000000000021030b4c866585dd868a9d62348" | ||||
"a9cd008d6a312937048fff31670e7e920cfc7a7440105c5f72f5d6da3085" | "a9cd008d6a312937048fff31670e7e920cfc7a7440105c5f72f5d6da3085" | ||||
"583e75ee79340eb4eff208c89988e7ed0efb30b87298fa30000000000f20" | "583e75ee79340eb4eff208c89988e7ed0efb30b87298fa30000000000f20" | ||||
"52a0100000003000000210227d85ba011276cf25b51df6a188b75e604b3" | "52a0100000003000000210227d85ba011276cf25b51df6a188b75e604b3" | ||||
"8770a462b2d0e9fb2fc839ef5d3faf07f001dd38e9b4a43d07d5d449cc0" | "8770a462b2d0e9fb2fc839ef5d3faf07f001dd38e9b4a43d07d5d449cc0" | ||||
"f7d2888d96b82962b3ce516d1083c0e031773487fc3c4f2e38acd1db974" | "f7d2888d96b82962b3ce516d1083c0e031773487fc3c4f2e38acd1db974" | ||||
"1321b91a79b82d1c2cfd47793261e4ba003cf5") | "1321b91a79b82d1c2cfd47793261e4ba003cf5") | ||||
too_many_utxos = node.buildavalancheproof( | |||||
proof_sequence, proof_expiration, | |||||
proof_master, get_stakes(node, blockhashes, addrkey0.key)) | |||||
self.stop_node(0) | self.stop_node(0) | ||||
def check_proof_init_error(proof, message): | def check_proof_init_error(proof, message): | ||||
node.assert_start_raises_init_error( | node.assert_start_raises_init_error( | ||||
self.extra_args[0] + [ | self.extra_args[0] + [ | ||||
"-avaproof={}".format(proof), | "-avaproof={}".format(proof), | ||||
"-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | ||||
], | ], | ||||
Show All 25 Lines |