wallet_create_tx.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2018-2022 The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 from decimal import Decimal
7
8 from test_framework.messages import (
9 tx_from_hex,
10 )
11 from test_framework.test_framework import LimenkaTestFramework
12 from test_framework.util import (
13 assert_equal,
14 assert_raises_rpc_error,
15 )
16 from test_framework.blocktools import (
17 TIME_GENESIS_BLOCK,
18 )
19
20
21 class CreateTxWalletTest(LimenkaTestFramework):
22 def add_options(self, parser):
23 self.add_wallet_options(parser)
24
25 def set_test_params(self):
26 self.setup_clean_chain = True
27 self.num_nodes = 1
28
29 def skip_test_if_missing_module(self):
30 self.skip_if_no_wallet()
31
32 def run_test(self):
33 self.log.info('Create some old blocks')
34 self.nodes[0].setmocktime(TIME_GENESIS_BLOCK)
35 self.generate(self.nodes[0], 200)
36 self.nodes[0].setmocktime(0)
37
38 self.test_anti_fee_sniping()
39 self.test_tx_size_too_large()
40 self.test_create_too_long_mempool_chain()
41 self.test_version3()
42 self.test_setfeerate()
43
44 def test_anti_fee_sniping(self):
45 self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
46
47 # sendtoaddress RPC
48 assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 200)
49 txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
50 tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
51 assert_equal(tx['locktime'], 0)
52
53 # send RPC
54 outputs = [{self.nodes[0].getnewaddress(): 1}]
55 res = self.nodes[0].send(outputs=outputs)
56 assert(res["complete"])
57 tx = self.nodes[0].gettransaction(txid=res['txid'], verbose=True)['decoded']
58 assert_equal(tx['locktime'], 0)
59
60 # sendall RPC (don't actually empty the wallet)
61 res = self.nodes[0].sendall(recipients=[self.nodes[0].getnewaddress()], add_to_wallet=False)
62 assert(res["complete"])
63 tx = self.nodes[0].decoderawtransaction(res['hex'])
64 assert_equal(tx['locktime'], 0)
65
66 self.log.info('Check that anti-fee-sniping is enabled when we mine a recent block')
67 self.generate(self.nodes[0], 1)
68 txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
69 tx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
70 assert 0 < tx['locktime'] <= 201
71
72 # send RPC
73 outputs = [{self.nodes[0].getnewaddress(): 1}]
74 res = self.nodes[0].send(outputs=outputs)
75 assert(res["complete"])
76 tx = self.nodes[0].gettransaction(txid=res['txid'], verbose=True)['decoded']
77 assert 0 < tx['locktime'] <= 201
78
79 # sendall RPC
80 res = self.nodes[0].sendall(recipients=[self.nodes[0].getnewaddress()], add_to_wallet=False)
81 assert(res["complete"])
82 tx = self.nodes[0].decoderawtransaction(res['hex'])
83 assert 0 < tx['locktime'] <= 201
84
85 def test_tx_size_too_large(self):
86 # More than 10kB of outputs, so that we hit -maxtxfee with a high feerate
87 outputs = {self.nodes[0].getnewaddress(address_type='bech32'): 0.000025 for _ in range(400)}
88 raw_tx = self.nodes[0].createrawtransaction(inputs=[], outputs=outputs)
89 msg = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"
90
91 for fee_setting in ['-minrelaytxfee=0.01', '-mintxfee=0.01', '-paytxfee=0.01']:
92 self.log.info('Check maxtxfee in combination with {}'.format(fee_setting))
93 self.restart_node(0, extra_args=[fee_setting])
94 assert_raises_rpc_error(
95 -6,
96 "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
97 lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
98 )
99 assert_raises_rpc_error(
100 -4,
101 "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
102 lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
103 )
104
105 self.log.info('Check maxtxfee in combination with settxfee')
106 self.restart_node(0)
107 self.nodes[0].settxfee(0.01)
108 assert_raises_rpc_error(
109 -6,
110 "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
111 lambda: self.nodes[0].sendmany(dummy="", amounts=outputs),
112 )
113 assert_raises_rpc_error(
114 -4,
115 "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
116 lambda: self.nodes[0].fundrawtransaction(hexstring=raw_tx),
117 )
118 self.nodes[0].settxfee(0)
119
120 self.log.info('Check maxtxfee in combination with setfeerate (sat/vB)')
121 self.nodes[0].setfeerate(1000)
122 assert_raises_rpc_error(-6, msg, self.nodes[0].sendmany, dummy="", amounts=outputs)
123 assert_raises_rpc_error(-4, msg, self.nodes[0].fundrawtransaction, hexstring=raw_tx)
124 self.nodes[0].setfeerate(0)
125
126 def test_create_too_long_mempool_chain(self):
127 self.log.info('Check too-long mempool chain error')
128 df_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
129
130 self.nodes[0].createwallet("too_long")
131 test_wallet = self.nodes[0].get_wallet_rpc("too_long")
132
133 tx_data = df_wallet.send(outputs=[{test_wallet.getnewaddress(): 25}], options={"change_position": 0})
134 txid = tx_data['txid']
135 vout = 1
136
137 self.nodes[0].syncwithvalidationinterfacequeue()
138 options = {"change_position": 0, "add_inputs": False}
139 for i in range(1, 25):
140 options['inputs'] = [{'txid': txid, 'vout': vout}]
141 tx_data = test_wallet.send(outputs=[{test_wallet.getnewaddress(): 25 - i}], options=options)
142 txid = tx_data['txid']
143
144 # Sending one more chained transaction will fail
145 options = {"minconf": 0, "include_unsafe": True, 'add_inputs': True}
146 assert_raises_rpc_error(-4, "Unconfirmed UTXOs are available, but spending them creates a chain of transactions that will be rejected by the mempool",
147 test_wallet.send, outputs=[{test_wallet.getnewaddress(): 0.3}], options=options)
148
149 test_wallet.unloadwallet()
150
151 def test_version3(self):
152 self.log.info('Check wallet does not create transactions with version=3 yet')
153 wallet_rpc = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
154
155 self.nodes[0].createwallet("version3")
156 wallet_v3 = self.nodes[0].get_wallet_rpc("version3")
157
158 tx_data = wallet_rpc.send(outputs=[{wallet_v3.getnewaddress(): 25}], options={"change_position": 0})
159 wallet_tx_data = wallet_rpc.gettransaction(tx_data["txid"])
160 tx_current_version = tx_from_hex(wallet_tx_data["hex"])
161
162 # While version=3 transactions are standard, the CURRENT_VERSION is 2.
163 # This test can be removed if CURRENT_VERSION is changed, and replaced with tests that the
164 # wallet handles TRUC rules properly.
165 assert_equal(tx_current_version.version, 2)
166 wallet_v3.unloadwallet()
167
168 def test_setfeerate(self):
169 self.log.info("Test setfeerate")
170 self.restart_node(0, extra_args=[
171 "-incrementalrelayfee=0.00001",
172 "-mintxfee=0.00003141", # 3.141 sat/vB
173 ])
174 node = self.nodes[0]
175
176 def test_response(*, requested=0, expected=0, error=None, msg):
177 assert_equal(node.setfeerate(requested), {"wallet_name": self.default_wallet_name, "fee_rate": expected, ("error" if error else "result"): msg})
178
179 # Test setfeerate with 10.0001 (CFeeRate rounding), "10.001" and "4" sat/vB
180 test_response(requested=10.0001, expected=10, msg="Fee rate for transactions with this wallet successfully set to 10.000 sat/vB")
181 assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00010000"))
182 test_response(requested="10.001", expected=Decimal("10.001"), msg="Fee rate for transactions with this wallet successfully set to 10.001 sat/vB")
183 assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00010001"))
184 test_response(requested="4", expected=4, msg="Fee rate for transactions with this wallet successfully set to 4.000 sat/vB")
185 assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00004000"))
186
187 # Test setfeerate with too-high/low values returns expected errors
188 test_response(requested=Decimal("10000.001"), expected=4, error=True, msg="The requested fee rate of 10000.001 sat/vB cannot be greater than the wallet max fee rate of 10000.000 sat/vB. The current setting of 4.000 sat/vB for this wallet remains unchanged.")
189 test_response(requested=Decimal("0.999"), expected=4, error=True, msg="The requested fee rate of 0.999 sat/vB cannot be less than the minimum relay fee rate of 1.000 sat/vB. The current setting of 4.000 sat/vB for this wallet remains unchanged.")
190 test_response(requested=Decimal("3.140"), expected=4, error=True, msg="The requested fee rate of 3.140 sat/vB cannot be less than the wallet min fee rate of 3.141 sat/vB. The current setting of 4.000 sat/vB for this wallet remains unchanged.")
191 assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00004000"))
192
193 # Test setfeerate to 3.141 sat/vB
194 test_response(requested=3.141, expected=Decimal("3.141"), msg="Fee rate for transactions with this wallet successfully set to 3.141 sat/vB")
195 assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00003141"))
196
197 # Test setfeerate with values non-representable by CFeeRate
198 for invalid_value in [0.00000001, 0.0009, 0.00099999]:
199 assert_raises_rpc_error(-3, "Invalid amount", node.setfeerate, amount=invalid_value)
200
201 # Test setfeerate with values rejected by ParseFixedPoint() called in AmountFromValue()
202 for invalid_value in ["", 0.000000001, "1.111111111", 11111111111]:
203 assert_raises_rpc_error(-3, "Invalid amount", node.setfeerate, amount=invalid_value)
204
205 # Test deactivating setfeerate
206 test_response(msg="Fee rate for transactions with this wallet successfully unset. By default, automatic fee selection will be used.")
207 assert_equal(node.getwalletinfo()["paytxfee"], 0)
208
209 # Test currently-unset setfeerate with too-high/low values returns expected errors
210 test_response(requested=Decimal("10000.001"), error=True, msg="The requested fee rate of 10000.001 sat/vB cannot be greater than the wallet max fee rate of 10000.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
211 assert_equal(node.getwalletinfo()["paytxfee"], 0)
212 test_response(requested=Decimal("0.999"), error=True, msg="The requested fee rate of 0.999 sat/vB cannot be less than the minimum relay fee rate of 1.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
213 test_response(requested=Decimal("3.140"), error=True, msg="The requested fee rate of 3.140 sat/vB cannot be less than the wallet min fee rate of 3.141 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
214 assert_equal(node.getwalletinfo()["paytxfee"], 0)
215
216
217 if __name__ == '__main__':
218 CreateTxWalletTest(__file__).main()
219