rpc_generate.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2020-present The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test generate* RPCs."""
6
7 from concurrent.futures import ThreadPoolExecutor
8
9 from test_framework.test_framework import BitcoinTestFramework
10 from test_framework.wallet import MiniWallet
11 from test_framework.util import (
12 assert_equal,
13 assert_raises_rpc_error,
14 )
15
16
17 class RPCGenerateTest(BitcoinTestFramework):
18 def set_test_params(self):
19 self.num_nodes = 1
20
21 def run_test(self):
22 self.test_generatetoaddress()
23 self.test_generate()
24 self.test_generateblock()
25
26 def test_generatetoaddress(self):
27 self.generatetoaddress(self.nodes[0], 1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
28 assert_raises_rpc_error(-5, "Invalid address", self.generatetoaddress, self.nodes[0], 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
29
30 def test_generateblock(self):
31 node = self.nodes[0]
32 miniwallet = MiniWallet(node)
33
34 self.log.info('Mine an empty block to address and return the hex')
35 address = miniwallet.get_address()
36 generated_block = self.generateblock(node, output=address, transactions=[], submit=False)
37 node.submitblock(hexdata=generated_block['hex'])
38 assert_equal(generated_block['hash'], node.getbestblockhash())
39
40 self.log.info('Generate an empty block to address')
41 hash = self.generateblock(node, output=address, transactions=[])['hash']
42 block = node.getblock(blockhash=hash, verbose=2)
43 assert_equal(len(block['tx']), 1)
44 assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], address)
45
46 self.log.info('Generate an empty block to a descriptor')
47 hash = self.generateblock(node, 'addr(' + address + ')', [])['hash']
48 block = node.getblock(blockhash=hash, verbosity=2)
49 assert_equal(len(block['tx']), 1)
50 assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], address)
51
52 self.log.info('Generate an empty block to a combo descriptor with compressed pubkey')
53 combo_key = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
54 combo_address = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080'
55 hash = self.generateblock(node, 'combo(' + combo_key + ')', [])['hash']
56 block = node.getblock(hash, 2)
57 assert_equal(len(block['tx']), 1)
58 assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], combo_address)
59
60 self.log.info('Generate an empty block to a combo descriptor with uncompressed pubkey')
61 combo_key = '0408ef68c46d20596cc3f6ddf7c8794f71913add807f1dc55949fa805d764d191c0b7ce6894c126fce0babc6663042f3dde9b0cf76467ea315514e5a6731149c67'
62 combo_address = 'mkc9STceoCcjoXEXe6cm66iJbmjM6zR9B2'
63 hash = self.generateblock(node, 'combo(' + combo_key + ')', [])['hash']
64 block = node.getblock(hash, 2)
65 assert_equal(len(block['tx']), 1)
66 assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], combo_address)
67
68 # Generate some extra mempool transactions to verify they don't get mined
69 for _ in range(10):
70 miniwallet.send_self_transfer(from_node=node)
71
72 self.log.info('Generate block with txid')
73 txid = miniwallet.send_self_transfer(from_node=node)['txid']
74 hash = self.generateblock(node, address, [txid])['hash']
75 block = node.getblock(hash, 1)
76 assert_equal(len(block['tx']), 2)
77 assert_equal(block['tx'][1], txid)
78
79 self.log.info('Generate block with raw tx')
80 rawtx = miniwallet.create_self_transfer()['hex']
81 hash = self.generateblock(node, address, [rawtx])['hash']
82
83 block = node.getblock(hash, 1)
84 assert_equal(len(block['tx']), 2)
85 txid = block['tx'][1]
86 assert_equal(node.getrawtransaction(txid=txid, verbose=False, blockhash=hash), rawtx)
87
88 # Ensure that generateblock can be called concurrently by many threads.
89 self.log.info('Generate blocks in parallel')
90 generate_50_blocks = lambda n: [n.generateblock(output=address, transactions=[]) for _ in range(50)]
91 rpcs = [node.cli for _ in range(6)]
92 with ThreadPoolExecutor(max_workers=len(rpcs)) as threads:
93 list(threads.map(generate_50_blocks, rpcs))
94
95 self.log.info('Fail to generate block with out of order txs')
96 txid1 = miniwallet.send_self_transfer(from_node=node)['txid']
97 utxo1 = miniwallet.get_utxo(txid=txid1)
98 rawtx2 = miniwallet.create_self_transfer(utxo_to_spend=utxo1)['hex']
99 assert_raises_rpc_error(-25, 'TestBlockValidity failed: bad-txns-inputs-missingorspent', self.generateblock, node, address, [rawtx2, txid1])
100
101 self.log.info('Fail to generate block with txid not in mempool')
102 missing_txid = '0000000000000000000000000000000000000000000000000000000000000000'
103 assert_raises_rpc_error(-5, 'Transaction ' + missing_txid + ' not in mempool.', self.generateblock, node, address, [missing_txid])
104
105 self.log.info('Fail to generate block with invalid raw tx')
106 invalid_raw_tx = '0000'
107 assert_raises_rpc_error(-22, 'Transaction decode failed for ' + invalid_raw_tx, self.generateblock, node, address, [invalid_raw_tx])
108
109 self.log.info('Fail to generate block with invalid address/descriptor')
110 assert_raises_rpc_error(-5, 'Invalid address or descriptor', self.generateblock, node, '1234', [])
111
112 self.log.info('Fail to generate block with a ranged descriptor')
113 ranged_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0/*)'
114 assert_raises_rpc_error(-8, 'Ranged descriptor not accepted. Maybe pass through deriveaddresses first?', self.generateblock, node, ranged_descriptor, [])
115
116 self.log.info('Fail to generate block with a descriptor missing a private key')
117 child_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0\'/0)'
118 assert_raises_rpc_error(-5, 'Cannot derive script without private keys', self.generateblock, node, child_descriptor, [])
119
120 def test_generate(self):
121 message = (
122 "generate\n\n"
123 "has been replaced by the -generate "
124 "cli option. Refer to -help for more information.\n"
125 )
126
127 if not self.options.usecli:
128 self.log.info("Test rpc generate raises with message to use cli option")
129 assert_raises_rpc_error(-32601, message, self.nodes[0]._rpc.generate)
130
131 self.log.info("Test rpc generate help prints message to use cli option")
132 assert_equal(message, self.nodes[0].help("generate"))
133
134 self.log.info("Test rpc generate is a hidden command not discoverable in general help")
135 assert message not in self.nodes[0].help()
136
137
138 if __name__ == "__main__":
139 RPCGenerateTest(__file__).main()
140