12
12
TIME_GENESIS_BLOCK ,
13
13
)
14
14
from test_framework .merkle import merkle_root_and_branch
15
+ from test_framework .messages import (
16
+ COutPoint ,
17
+ CTransaction ,
18
+ CTxIn ,
19
+ CTxOut ,
20
+ FromHex ,
21
+ ToHex ,
22
+ )
23
+ from test_framework .script import OP_RETURN , OP_TRUE , CScript
15
24
from test_framework .test_framework import BitcoinTestFramework
25
+ from test_framework .txtools import pad_tx
16
26
from test_framework .util import assert_equal , hex_to_be_bytes
17
27
from test_framework .wallet import MiniWallet
18
28
@@ -44,6 +54,7 @@ def run_test(self):
44
54
self .test_invalid_params ()
45
55
self .test_transaction_get ()
46
56
self .test_transaction_get_height ()
57
+ self .test_transaction_broadcast ()
47
58
self .test_transaction_get_merkle ()
48
59
self .test_block_header ()
49
60
@@ -158,7 +169,7 @@ def test_transaction_get(self):
158
169
},
159
170
)
160
171
161
- self .generate (self .nodes [ 0 ] , 2 )
172
+ self .generate (self .wallet , 2 )
162
173
assert_equal (
163
174
self .client .blockchain .transaction .get (
164
175
txid = GENESIS_CB_TXID , verbose = True
@@ -171,7 +182,11 @@ def test_transaction_get_height(self):
171
182
assert_equal (response .result , 0 )
172
183
173
184
self .wallet .rescan_utxos ()
174
- tx = self .wallet .send_self_transfer (from_node = self .node )
185
+ tx = self .wallet .create_self_transfer ()
186
+
187
+ response = self .client .blockchain .transaction .broadcast (tx ["hex" ])
188
+ assert_equal (response .result , tx ["txid" ])
189
+ self .node .syncwithvalidationinterfacequeue ()
175
190
176
191
response = self .client .blockchain .transaction .get (tx ["txid" ])
177
192
assert_equal (response .result , tx ["hex" ])
@@ -181,13 +196,200 @@ def test_transaction_get_height(self):
181
196
assert_equal (response .result , 0 )
182
197
183
198
# Mine the tx
184
- self .generate (self .node , 1 )
199
+ self .generate (self .wallet , 1 )
185
200
response = self .client .blockchain .transaction .get_height (tx ["txid" ])
186
201
assert_equal (response .result , 203 )
187
202
188
203
response = self .client .blockchain .transaction .get_height (32 * "ff" )
189
204
assert_equal (response .error , {"code" : - 32600 , "message" : "Unknown txid" })
190
205
206
+ def test_transaction_broadcast (self ):
207
+ tx = self .wallet .create_self_transfer ()
208
+
209
+ for _ in range (3 ):
210
+ response = self .client .blockchain .transaction .broadcast (tx ["hex" ])
211
+ assert_equal (response .result , tx ["txid" ])
212
+
213
+ self .generate (self .wallet , 1 )
214
+ response = self .client .blockchain .transaction .broadcast (tx ["hex" ])
215
+ assert_equal (
216
+ response .error , {"code" : 1 , "message" : "Transaction already in block chain" }
217
+ )
218
+
219
+ spent_utxo = tx ["tx" ].vin [0 ]
220
+
221
+ tx_obj = self .wallet .create_self_transfer ()["tx" ]
222
+ tx_obj .vin [0 ] = spent_utxo
223
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
224
+ assert_equal (
225
+ response .error ,
226
+ {"code" : 1 , "message" : "Missing inputs: bad-txns-inputs-missingorspent" },
227
+ )
228
+
229
+ raw_tx_reference = self .wallet .create_self_transfer ()["hex" ]
230
+
231
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
232
+ tx_obj .vin [0 ].scriptSig = b"aaaaaaaaaaaaaaa"
233
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
234
+ assert_equal (
235
+ response .error ,
236
+ {
237
+ "code" : 1 ,
238
+ "message" : "Transaction rejected by mempool: scriptsig-not-pushonly" ,
239
+ },
240
+ )
241
+
242
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
243
+ tx_obj .vout [0 ].scriptPubKey = CScript ([OP_RETURN , b"\xff " ])
244
+ tx_obj .vout = [tx_obj .vout [0 ]] * 2
245
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
246
+ assert_equal (
247
+ response .error ,
248
+ {"code" : 1 , "message" : "Transaction rejected by mempool: multi-op-return" },
249
+ )
250
+
251
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
252
+ tx_obj .vin [0 ].nSequence = 0xFFFFFFFE
253
+ tx_obj .nLockTime = self .node .getblockcount () + 1
254
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
255
+ assert_equal (
256
+ response .error ,
257
+ {
258
+ "code" : 1 ,
259
+ "message" : "Transaction rejected by mempool: bad-txns-nonfinal, non-final transaction" ,
260
+ },
261
+ )
262
+
263
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
264
+ tx_obj .vout = []
265
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
266
+ assert_equal (
267
+ response .error ,
268
+ {
269
+ "code" : 1 ,
270
+ "message" : "Transaction rejected by mempool: bad-txns-vout-empty" ,
271
+ },
272
+ )
273
+
274
+ # Non-standard script
275
+ tx_obj .vout .append (CTxOut (0 , CScript ([OP_TRUE ])))
276
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
277
+ assert_equal (
278
+ response .error ,
279
+ {"code" : 1 , "message" : "Transaction rejected by mempool: scriptpubkey" },
280
+ )
281
+
282
+ tx_obj .vout [0 ] = CTxOut (0 , CScript ([OP_RETURN , b"\xff " ]))
283
+ assert len (ToHex (tx_obj )) // 2 < 100
284
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
285
+ assert_equal (
286
+ response .error ,
287
+ {
288
+ "code" : 1 ,
289
+ "message" : "Transaction rejected by mempool: bad-txns-undersize" ,
290
+ },
291
+ )
292
+
293
+ tx_obj = self .wallet .create_self_transfer ()["tx" ]
294
+ pad_tx (tx_obj , 100_001 )
295
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
296
+ assert_equal (
297
+ response .error ,
298
+ {"code" : 1 , "message" : "Transaction rejected by mempool: tx-size" },
299
+ )
300
+
301
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
302
+ tx_obj .vin .append (tx_obj .vin [0 ])
303
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
304
+ assert_equal (
305
+ response .error ,
306
+ {
307
+ "code" : 1 ,
308
+ "message" : "Transaction rejected by mempool: bad-txns-inputs-duplicate" ,
309
+ },
310
+ )
311
+
312
+ tx_obj .vin = []
313
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
314
+ assert_equal (
315
+ response .error ,
316
+ {
317
+ "code" : 1 ,
318
+ "message" : "Transaction rejected by mempool: bad-txns-vin-empty" ,
319
+ },
320
+ )
321
+
322
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
323
+ tx_obj .nVersion = 1337
324
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
325
+ assert_equal (
326
+ response .error ,
327
+ {"code" : 1 , "message" : "Transaction rejected by mempool: version" },
328
+ )
329
+
330
+ # Coinbase input in first position
331
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
332
+ tx_obj .vin [0 ] = CTxIn (COutPoint (txid = 0 , n = 0xFFFFFFFF ))
333
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
334
+ assert_equal (
335
+ response .error ,
336
+ {"code" : 1 , "message" : "Transaction rejected by mempool: bad-tx-coinbase" },
337
+ )
338
+
339
+ # Coinbase input in second position
340
+ tx_obj = FromHex (CTransaction (), raw_tx_reference )
341
+ tx_obj .vin .append (CTxIn (COutPoint (txid = 0 , n = 0xFFFFFFFF )))
342
+ response = self .client .blockchain .transaction .broadcast (ToHex (tx_obj ))
343
+ assert_equal (
344
+ response .error ,
345
+ {
346
+ "code" : 1 ,
347
+ "message" : "Transaction rejected by mempool: bad-txns-prevout-null" ,
348
+ },
349
+ )
350
+
351
+ tx = self .wallet .create_self_transfer (fee_rate = 0 , fee = 0 )
352
+ response = self .client .blockchain .transaction .broadcast (tx ["hex" ])
353
+ assert_equal (
354
+ response .error ,
355
+ {
356
+ "code" : 1 ,
357
+ "message" : "Transaction rejected by mempool: min relay fee not met, 0 < 100" ,
358
+ },
359
+ )
360
+
361
+ tx = self .wallet .create_self_transfer (fee_rate = 10_000_000 , fee = 0 )
362
+ response = self .client .blockchain .transaction .broadcast (tx ["hex" ])
363
+ assert_equal (
364
+ response .error ,
365
+ {
366
+ "code" : 1 ,
367
+ "message" : "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)" ,
368
+ },
369
+ )
370
+
371
+ # Mine enough blocks to ensure that the following test does not try to spend
372
+ # a utxo already spent in a previous test.
373
+ # Invalidate two blocks, so that miniwallet has access to a coin that
374
+ # will mature in the next block.
375
+ self .generate (self .wallet , 100 )
376
+ chain_height = self .node .getblockcount () - 3
377
+ block_to_invalidate = self .node .getblockhash (chain_height + 1 )
378
+ self .node .invalidateblock (block_to_invalidate )
379
+ immature_txid = self .nodes [0 ].getblock (
380
+ self .nodes [0 ].getblockhash (chain_height - 100 + 2 )
381
+ )["tx" ][0 ]
382
+ immature_utxo = self .wallet .get_utxo (txid = immature_txid )
383
+ tx = self .wallet .create_self_transfer (utxo_to_spend = immature_utxo )
384
+ response = self .client .blockchain .transaction .broadcast (tx ["hex" ])
385
+ assert_equal (
386
+ response .error ,
387
+ {
388
+ "code" : 1 ,
389
+ "message" : "Transaction rejected by mempool: bad-txns-premature-spend-of-coinbase, tried to spend coinbase at depth 99" ,
390
+ },
391
+ )
392
+
191
393
def test_transaction_get_merkle (self ):
192
394
for _ in range (42 ):
193
395
self .wallet .send_self_transfer (from_node = self .node )
0 commit comments