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 ( | ||||
create_coinbase_stakes, | |||||
create_stakes, | |||||
) | |||||
from test_framework.key import ECKey, bytes_to_wif | from test_framework.key import ECKey, bytes_to_wif | ||||
from test_framework.messages import AvalancheDelegation | 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, | ||||
wait_until, | wait_until, | ||||
Show All 36 Lines | def run_test(self): | ||||
def get_hex_pubkey(privkey): | def get_hex_pubkey(privkey): | ||||
return privkey.get_pubkey().get_bytes().hex() | return privkey.get_pubkey().get_bytes().hex() | ||||
proof_master = get_hex_pubkey(privkey) | 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)) | create_coinbase_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 | ||||
self.restart_node(0, self.extra_args[0] + [ | self.restart_node(0, self.extra_args[0] + [ | ||||
"-avaproof={}".format(proof), | "-avaproof={}".format(proof), | ||||
"-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | ||||
"-minimumchainwork=0x{:x}".format(minchainwork), | "-minimumchainwork=0x{:x}".format(minchainwork), | ||||
]) | ]) | ||||
self.log.info( | self.log.info( | ||||
"The proof verification should be delayed until IBD is complete") | "The proof verification should be delayed until IBD is complete") | ||||
assert node.getblockchaininfo()["initialblockdownload"] is True | assert node.getblockchaininfo()["initialblockdownload"] is True | ||||
# 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 | ||||
wait_until(lambda: len(node.getavalanchepeerinfo()) == 1, timeout=5) | wait_until(lambda: len(node.getavalanchepeerinfo()) == 1, timeout=5) | ||||
if self.is_wallet_compiled(): | |||||
self.log.info( | self.log.info( | ||||
"A proof using the maximum number of stakes is accepted...") | "A proof using the maximum number of stakes is accepted...") | ||||
blockhashes = node.generatetoaddress(AVALANCHE_MAX_PROOF_STAKES + 1, | |||||
addrkey0.address) | |||||
too_many_stakes = get_stakes(node, blockhashes, addrkey0.key) | new_blocks = node.generate(AVALANCHE_MAX_PROOF_STAKES // 10 + 1) | ||||
maximum_stakes = get_stakes(node, blockhashes[:-1], addrkey0.key) | # confirm the coinbase UTXOs | ||||
node.generate(101) | |||||
too_many_stakes = create_stakes( | |||||
node, new_blocks, AVALANCHE_MAX_PROOF_STAKES + 1) | |||||
maximum_stakes = too_many_stakes[:-1] | |||||
good_proof = node.buildavalancheproof( | good_proof = node.buildavalancheproof( | ||||
proof_sequence, proof_expiration, | proof_sequence, proof_expiration, | ||||
proof_master, maximum_stakes) | proof_master, maximum_stakes) | ||||
peerid1 = add_interface_node(node) | peerid1 = add_interface_node(node) | ||||
assert node.addavalanchenode(peerid1, proof_master, good_proof) | assert node.addavalanchenode(peerid1, proof_master, good_proof) | ||||
self.log.info("A proof using too many stakes should be rejected...") | self.log.info( | ||||
"A proof using too many stakes should be rejected...") | |||||
too_many_utxos = node.buildavalancheproof( | too_many_utxos = node.buildavalancheproof( | ||||
proof_sequence, proof_expiration, | proof_sequence, proof_expiration, | ||||
proof_master, too_many_stakes) | proof_master, too_many_stakes) | ||||
peerid2 = add_interface_node(node) | peerid2 = add_interface_node(node) | ||||
assert not node.addavalanchenode(peerid2, proof_master, too_many_utxos) | assert not node.addavalanchenode( | ||||
peerid2, proof_master, too_many_utxos) | |||||
self.log.info("Generate delegations for the proof") | self.log.info("Generate delegations for the proof") | ||||
# Stack up a few delegation levels | # Stack up a few delegation levels | ||||
def gen_privkey(): | def gen_privkey(): | ||||
pk = ECKey() | pk = ECKey() | ||||
pk.generate() | pk.generate() | ||||
return pk | return pk | ||||
Show All 9 Lines | def run_test(self): | ||||
delegation, | delegation, | ||||
) | ) | ||||
delegator_privkey = delegated_privkey | delegator_privkey = delegated_privkey | ||||
random_privkey = gen_privkey() | random_privkey = gen_privkey() | ||||
random_pubkey = get_hex_pubkey(random_privkey) | random_pubkey = get_hex_pubkey(random_privkey) | ||||
# Invalid proof | # Invalid proof | ||||
no_stake = node.buildavalancheproof( | |||||
proof_sequence, proof_expiration, proof_master, []) | |||||
assert_raises_rpc_error(-8, "The proof is invalid", | assert_raises_rpc_error(-8, "The proof is invalid", | ||||
node.delegateavalancheproof, | node.delegateavalancheproof, | ||||
too_many_utxos, | no_stake, | ||||
bytes_to_wif(privkey.get_bytes()), | bytes_to_wif(privkey.get_bytes()), | ||||
random_pubkey, | random_pubkey, | ||||
) | ) | ||||
# Invalid privkey | # Invalid privkey | ||||
assert_raises_rpc_error(-5, "The private key is invalid", | assert_raises_rpc_error(-5, "The private key is invalid", | ||||
node.delegateavalancheproof, | node.delegateavalancheproof, | ||||
proof, | proof, | ||||
Show All 25 Lines | def run_test(self): | ||||
proof, | proof, | ||||
bytes_to_wif(privkey.get_bytes()), | bytes_to_wif(privkey.get_bytes()), | ||||
random_pubkey, | random_pubkey, | ||||
delegation, | 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( | |||||
proof_sequence, proof_expiration, proof_master, []) | |||||
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")) | create_coinbase_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)) | create_coinbase_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") | ||||
Show All 34 Lines | def run_test(self): | ||||
check_proof_init_error(no_stake, | check_proof_init_error(no_stake, | ||||
"the avalanche proof has no stake") | "the avalanche proof has no stake") | ||||
check_proof_init_error(dust, | check_proof_init_error(dust, | ||||
"the avalanche proof stake is too low") | "the avalanche proof stake is too low") | ||||
check_proof_init_error(duplicate_stake, | check_proof_init_error(duplicate_stake, | ||||
"the avalanche proof has duplicated stake") | "the avalanche proof has duplicated stake") | ||||
check_proof_init_error(bad_sig, | check_proof_init_error(bad_sig, | ||||
"the avalanche proof has invalid stake signatures") | "the avalanche proof has invalid stake signatures") | ||||
if self.is_wallet_compiled(): | |||||
# The too many utxos case creates a proof which is that large that it | # The too many utxos case creates a proof which is that large that it | ||||
# cannot fit on the command line | # cannot fit on the command line | ||||
append_config(node.datadir, ["avaproof={}".format(too_many_utxos)]) | append_config(node.datadir, ["avaproof={}".format(too_many_utxos)]) | ||||
node.assert_start_raises_init_error( | node.assert_start_raises_init_error( | ||||
self.extra_args[0] + [ | self.extra_args[0] + [ | ||||
"-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | "-avamasterkey=cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN", | ||||
], | ], | ||||
expected_msg="Error: the avalanche proof has too many utxos", | expected_msg="Error: the avalanche proof has too many utxos", | ||||
match=ErrorMatch.PARTIAL_REGEX, | match=ErrorMatch.PARTIAL_REGEX, | ||||
) | ) | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
AvalancheProofTest().main() | AvalancheProofTest().main() |