2016-05-06 11:23:48 +02:00
#!/usr/bin/env python3
# Copyright (c) 2014-2016 The Bitcoin Core developers
2014-12-29 20:17:21 +01:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-08-16 00:35:19 +02:00
""" Test the rawtransaction RPCs.
2014-12-29 20:17:21 +01:00
2019-01-07 10:55:35 +01:00
Test the following RPCs :
- createrawtransaction
2018-02-20 03:29:22 +01:00
- signrawtransactionwithwallet
2019-01-07 10:55:35 +01:00
- sendrawtransaction
- decoderawtransaction
- getrawtransaction
2016-11-23 07:13:35 +01:00
"""
2014-12-29 20:17:21 +01:00
2018-03-13 17:58:52 +01:00
from collections import OrderedDict
Merge #13054: tests: Enable automatic detection of undefined names in Python tests scripts. Remove wildcard imports.
68400d8b96 tests: Use explicit imports (practicalswift)
Pull request description:
Enable automatic detection of undefined names in Python tests scripts. Remove wildcard imports.
Wildcard imports make it unclear which names are present in the namespace, confusing both readers and many automated tools.
An additional benefit of not using wildcard imports in tests scripts is that readers of a test script then can infer the rough testing scope just by looking at the imports.
Before this commit:
```
$ contrib/devtools/lint-python.sh | head -10
./test/functional/feature_rbf.py:8:1: F403 'from test_framework.util import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:9:1: F403 'from test_framework.script import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:10:1: F403 'from test_framework.mininode import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:15:12: F405 bytes_to_hex_str may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:17:58: F405 CScript may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:25:13: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:26:31: F405 satoshi_round may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:26:60: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:30:41: F405 satoshi_round may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:30:68: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
$
```
After this commit:
```
$ contrib/devtools/lint-python.sh | head -10
$
```
Tree-SHA512: 3f826d39cffb6438388e5efcb20a9622ff8238247e882d68f7b38609877421b2a8e10e9229575f8eb6a8fa42dec4256986692e92922c86171f750a0e887438d9
2018-08-13 14:24:43 +02:00
from decimal import Decimal
2021-05-31 11:26:16 +02:00
from test_framework . blocktools import COINBASE_MATURITY
2021-06-24 12:47:04 +02:00
from test_framework . messages import (
CTransaction ,
tx_from_hex ,
)
2015-05-02 13:58:10 +02:00
from test_framework . test_framework import BitcoinTestFramework
2019-09-18 20:41:14 +02:00
from test_framework . util import (
assert_equal ,
assert_raises_rpc_error ,
2020-11-02 16:54:06 +01:00
find_vout_for_address ,
2019-09-18 20:41:14 +02:00
)
2014-12-29 20:17:21 +01:00
2017-12-13 17:12:12 +01:00
class multidict ( dict ) :
""" Dictionary that allows duplicate keys.
Constructed with a list of ( key , value ) tuples . When dumped by the json module ,
will output invalid json with repeated keys , eg :
>> > json . dumps ( multidict ( [ ( 1 , 2 ) , ( 1 , 2 ) ] )
' { " 1 " : 2, " 1 " : 2} '
Used to test calls to rpc methods with repeated keys in the json object . """
def __init__ ( self , x ) :
dict . __init__ ( self , x )
self . x = x
def items ( self ) :
return self . x
2014-12-29 20:17:21 +01:00
# Create one-input, one-output, no-fee transaction:
class RawTransactionsTest ( BitcoinTestFramework ) :
2017-09-01 18:47:13 +02:00
def set_test_params ( self ) :
2016-05-20 15:16:51 +02:00
self . setup_clean_chain = True
self . num_nodes = 3
2019-05-24 12:46:28 +02:00
self . extra_args = [
[ " -txindex " ] ,
[ " -txindex " ] ,
[ " -txindex " ] ,
]
2019-12-09 19:52:38 +01:00
self . supports_cli = False
2014-12-29 20:17:21 +01:00
2018-09-13 12:33:15 +02:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2018-12-29 20:18:43 +01:00
def setup_network ( self ) :
2017-05-02 20:02:55 +02:00
super ( ) . setup_network ( )
2022-09-24 14:36:35 +02:00
self . connect_nodes ( 0 , 2 )
2015-04-01 15:08:00 +02:00
2014-12-29 20:17:21 +01:00
def run_test ( self ) :
2018-03-13 17:58:52 +01:00
self . log . info ( ' prepare some coins for multiple *rawtransaction commands ' )
2014-12-29 20:17:21 +01:00
self . nodes [ 2 ] . generate ( 1 )
2015-07-10 20:11:44 +02:00
self . sync_all ( )
2021-05-31 11:26:16 +02:00
self . nodes [ 0 ] . generate ( COINBASE_MATURITY + 1 )
2014-12-29 20:17:21 +01:00
self . sync_all ( )
2015-12-02 18:12:23 +01:00
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 1.5 )
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 1.0 )
self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 5.0 )
2014-12-29 20:17:21 +01:00
self . sync_all ( )
self . nodes [ 0 ] . generate ( 5 )
self . sync_all ( )
2018-03-13 17:58:52 +01:00
self . log . info ( ' Test getrawtransaction on genesis block coinbase returns an error ' )
2018-01-30 09:59:22 +01:00
block = self . nodes [ 0 ] . getblock ( self . nodes [ 0 ] . getblockhash ( 0 ) )
assert_raises_rpc_error ( - 5 , " The genesis block coinbase is not considered an ordinary transaction " , self . nodes [ 0 ] . getrawtransaction , block [ ' merkleroot ' ] )
2018-03-13 17:58:52 +01:00
self . log . info ( ' Check parameter types and required parameters of createrawtransaction ' )
2017-12-13 17:12:12 +01:00
# Test `createrawtransaction` required parameters
assert_raises_rpc_error ( - 1 , " createrawtransaction " , self . nodes [ 0 ] . createrawtransaction )
assert_raises_rpc_error ( - 1 , " createrawtransaction " , self . nodes [ 0 ] . createrawtransaction , [ ] )
# Test `createrawtransaction` invalid extra parameters
assert_raises_rpc_error ( - 1 , " createrawtransaction " , self . nodes [ 0 ] . createrawtransaction , [ ] , { } , 0 , False , ' foo ' )
# Test `createrawtransaction` invalid `inputs`
txid = ' 1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000 '
assert_raises_rpc_error ( - 3 , " Expected type array " , self . nodes [ 0 ] . createrawtransaction , ' foo ' , { } )
assert_raises_rpc_error ( - 1 , " JSON value is not an object as expected " , self . nodes [ 0 ] . createrawtransaction , [ ' foo ' ] , { } )
2018-09-24 20:54:10 +02:00
assert_raises_rpc_error ( - 1 , " JSON value is not a string as expected " , self . nodes [ 0 ] . createrawtransaction , [ { } ] , { } )
assert_raises_rpc_error ( - 8 , " txid must be of length 64 (not 3, for ' foo ' ) " , self . nodes [ 0 ] . createrawtransaction , [ { ' txid ' : ' foo ' } ] , { } )
assert_raises_rpc_error ( - 8 , " txid must be hexadecimal string (not ' ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844 ' ) " , self . nodes [ 0 ] . createrawtransaction , [ { ' txid ' : ' ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844 ' } ] , { } )
2017-12-13 17:12:12 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, missing vout key " , self . nodes [ 0 ] . createrawtransaction , [ { ' txid ' : txid } ] , { } )
assert_raises_rpc_error ( - 8 , " Invalid parameter, missing vout key " , self . nodes [ 0 ] . createrawtransaction , [ { ' txid ' : txid , ' vout ' : ' foo ' } ] , { } )
2020-10-03 03:56:59 +02:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, vout cannot be negative " , self . nodes [ 0 ] . createrawtransaction , [ { ' txid ' : txid , ' vout ' : - 1 } ] , { } )
2017-12-13 17:12:12 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, sequence number is out of range " , self . nodes [ 0 ] . createrawtransaction , [ { ' txid ' : txid , ' vout ' : 0 , ' sequence ' : - 1 } ] , { } )
# Test `createrawtransaction` invalid `outputs`
address = self . nodes [ 0 ] . getnewaddress ( )
2018-03-13 17:58:52 +01:00
address2 = self . nodes [ 0 ] . getnewaddress ( )
assert_raises_rpc_error ( - 1 , " JSON value is not an array as expected " , self . nodes [ 0 ] . createrawtransaction , [ ] , ' foo ' )
self . nodes [ 0 ] . createrawtransaction ( inputs = [ ] , outputs = { } ) # Should not throw for backwards compatibility
self . nodes [ 0 ] . createrawtransaction ( inputs = [ ] , outputs = [ ] )
2017-12-13 17:12:12 +01:00
assert_raises_rpc_error ( - 8 , " Data must be hexadecimal string " , self . nodes [ 0 ] . createrawtransaction , [ ] , { ' data ' : ' foo ' } )
2020-01-31 16:38:47 +01:00
assert_raises_rpc_error ( - 5 , " Invalid Dash address " , self . nodes [ 0 ] . createrawtransaction , [ ] , { ' foo ' : 0 } )
2017-12-13 17:12:12 +01:00
assert_raises_rpc_error ( - 3 , " Invalid amount " , self . nodes [ 0 ] . createrawtransaction , [ ] , { address : ' foo ' } )
assert_raises_rpc_error ( - 3 , " Amount out of range " , self . nodes [ 0 ] . createrawtransaction , [ ] , { address : - 1 } )
assert_raises_rpc_error ( - 8 , " Invalid parameter, duplicated address: %s " % address , self . nodes [ 0 ] . createrawtransaction , [ ] , multidict ( [ ( address , 1 ) , ( address , 1 ) ] ) )
2018-03-13 17:58:52 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, duplicated address: %s " % address , self . nodes [ 0 ] . createrawtransaction , [ ] , [ { address : 1 } , { address : 1 } ] )
2018-12-07 17:19:28 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, duplicate key: data " , self . nodes [ 0 ] . createrawtransaction , [ ] , [ { " data " : ' aa ' } , { " data " : " bb " } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, duplicate key: data " , self . nodes [ 0 ] . createrawtransaction , [ ] , multidict ( [ ( " data " , ' aa ' ) , ( " data " , " bb " ) ] ) )
2018-03-13 17:58:52 +01:00
assert_raises_rpc_error ( - 8 , " Invalid parameter, key-value pair must contain exactly one key " , self . nodes [ 0 ] . createrawtransaction , [ ] , [ { ' a ' : 1 , ' b ' : 2 } ] )
assert_raises_rpc_error ( - 8 , " Invalid parameter, key-value pair not an object as expected " , self . nodes [ 0 ] . createrawtransaction , [ ] , [ [ ' key-value pair1 ' ] , [ ' 2 ' ] ] )
2017-12-13 17:12:12 +01:00
# Test `createrawtransaction` invalid `locktime`
assert_raises_rpc_error ( - 3 , " Expected type number " , self . nodes [ 0 ] . createrawtransaction , [ ] , { } , ' foo ' )
assert_raises_rpc_error ( - 8 , " Invalid parameter, locktime out of range " , self . nodes [ 0 ] . createrawtransaction , [ ] , { } , - 1 )
assert_raises_rpc_error ( - 8 , " Invalid parameter, locktime out of range " , self . nodes [ 0 ] . createrawtransaction , [ ] , { } , 4294967296 )
2018-03-13 17:58:52 +01:00
self . log . info ( ' Check that createrawtransaction accepts an array and object as outputs ' )
# One output
2021-06-24 12:47:04 +02:00
tx = tx_from_hex ( self . nodes [ 2 ] . createrawtransaction ( inputs = [ { ' txid ' : txid , ' vout ' : 9 } ] , outputs = { address : 99 } ) )
2018-03-13 17:58:52 +01:00
assert_equal ( len ( tx . vout ) , 1 )
assert_equal (
2021-08-27 21:03:02 +02:00
tx . serialize ( ) . hex ( ) ,
2018-03-13 17:58:52 +01:00
self . nodes [ 2 ] . createrawtransaction ( inputs = [ { ' txid ' : txid , ' vout ' : 9 } ] , outputs = [ { address : 99 } ] ) ,
)
# Two outputs
2021-06-24 12:47:04 +02:00
tx = tx_from_hex ( self . nodes [ 2 ] . createrawtransaction ( inputs = [ { ' txid ' : txid , ' vout ' : 9 } ] , outputs = OrderedDict ( [ ( address , 99 ) , ( address2 , 99 ) ] ) ) )
2018-03-13 17:58:52 +01:00
assert_equal ( len ( tx . vout ) , 2 )
assert_equal (
2021-08-27 21:03:02 +02:00
tx . serialize ( ) . hex ( ) ,
2018-03-13 17:58:52 +01:00
self . nodes [ 2 ] . createrawtransaction ( inputs = [ { ' txid ' : txid , ' vout ' : 9 } ] , outputs = [ { address : 99 } , { address2 : 99 } ] ) ,
)
# Multiple mixed outputs
2021-06-24 12:47:04 +02:00
tx = tx_from_hex ( self . nodes [ 2 ] . createrawtransaction ( inputs = [ { ' txid ' : txid , ' vout ' : 9 } ] , outputs = multidict ( [ ( address , 99 ) , ( address2 , 99 ) , ( ' data ' , ' 99 ' ) ] ) ) )
2018-03-13 17:58:52 +01:00
assert_equal ( len ( tx . vout ) , 3 )
assert_equal (
2021-08-27 21:03:02 +02:00
tx . serialize ( ) . hex ( ) ,
2018-12-07 17:19:28 +01:00
self . nodes [ 2 ] . createrawtransaction ( inputs = [ { ' txid ' : txid , ' vout ' : 9 } ] , outputs = [ { address : 99 } , { address2 : 99 } , { ' data ' : ' 99 ' } ] ) ,
2018-03-13 17:58:52 +01:00
)
self . log . info ( ' sendrawtransaction with missing input ' )
2014-12-29 20:17:21 +01:00
inputs = [ { ' txid ' : " 1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000 " , ' vout ' : 1 } ] #won't exists
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 4.998 }
rawtx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
2018-02-20 03:29:22 +01:00
rawtx = self . nodes [ 2 ] . signrawtransactionwithwallet ( rawtx )
2015-04-01 15:08:00 +02:00
2017-03-09 10:02:13 +01:00
# This will raise an exception since there are missing inputs
2019-10-30 15:27:22 +01:00
assert_raises_rpc_error ( - 25 , " bad-txns-inputs-missingorspent " , self . nodes [ 2 ] . sendrawtransaction , rawtx [ ' hex ' ] )
2015-04-01 15:08:00 +02:00
Merge #10275: [rpc] Allow fetching tx directly from specified block in getrawtransaction
434526a [test] Add tests for getrawtransaction with block hash. (Karl-Johan Alm)
b167951 [rpc] Allow getrawtransaction to take optional blockhash to fetch transaction from a block directly. (Karl-Johan Alm)
a5f5a2c [rpc] Fix fVerbose parsing (remove excess if cases). (Karl-Johan Alm)
Pull request description:
[Reviewer hint: use [?w=1](https://github.com/bitcoin/bitcoin/pull/10275/files?w=1) to avoid seeing a bunch of indentation changes.]
Presuming a user knows the block hash of the block containing a given transaction, this PR allows them to fetch the raw transaction, even without `-txindex`. It also enables support for getting transactions that are in orphaned blocks.
Note that supplying a block hash will override mempool and txindex support in `GetTransaction`. The rationale behind this is that a transaction may be in multiple places (orphaned blocks) and if the user supplies an explicit block hash it should be adhered to.
```Bash
$ # a41.. is a tx inside an orphan block ..3c6f.. -- first try getting it normally
$ ./bitcoin-cli getrawtransaction a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79 1
error code: -5
error message:
No such mempool transaction. Use -txindex to enable blockchain transaction queries. Use gettransaction for wallet transactions.
$ # now try with block hash
$ ./bitcoin-cli getrawtransaction a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79 1 0000000000000000003c6fe479122bfa4a9187493937af1734e1e5cd9f198ec7
{
"hex": "01000000014e7e81144e42f6d65550e59b715d470c9301fd7ac189[...]90488ac00000000",
"inMainChain": false,
"txid": "a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79",
"hash": "a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79",
"size": 225,
[...]
}
$ # another tx 6c66... in block 462000
$ ./bitcoin-cli getrawtransaction 6c66b98191e9d6cc671f6817142152ebf6c5cab2ef008397b5a71ac13255a735 1 00000000000000000217f2c12922e321f6d4aa933ce88005a9a493c503054a40
{
"hex": "0200000004d157[...]88acaf0c0700",
"inMainChain": true,
"txid": "6c66b98191e9d6cc671f6817142152ebf6c5cab2ef008397b5a71ac13255a735",
"hash": "6c66b98191e9d6cc671f6817142152ebf6c5cab2ef008397b5a71ac13255a735",
"size": 666,
[...]
}
$
```
Tree-SHA512: 279be3818141edd3cc194a9ee65929331920afb30297ab2d6da07293a2d7311afee5c8b00c6457477d9f1f86e86786a9b56878ea3ee19fa2629b829d042d0cda
2017-12-06 12:10:16 +01:00
#####################################
# getrawtransaction with block hash #
#####################################
# make a tx by sending then generate 2 blocks; block1 has the tx in it
tx = self . nodes [ 2 ] . sendtoaddress ( self . nodes [ 1 ] . getnewaddress ( ) , 1 )
block1 , block2 = self . nodes [ 2 ] . generate ( 2 )
self . sync_all ( )
# We should be able to get the raw transaction by providing the correct block
gottx = self . nodes [ 0 ] . getrawtransaction ( tx , True , block1 )
assert_equal ( gottx [ ' txid ' ] , tx )
assert_equal ( gottx [ ' in_active_chain ' ] , True )
# We should not have the 'in_active_chain' flag when we don't provide a block
gottx = self . nodes [ 0 ] . getrawtransaction ( tx , True )
assert_equal ( gottx [ ' txid ' ] , tx )
assert ' in_active_chain ' not in gottx
# We should not get the tx if we provide an unrelated block
assert_raises_rpc_error ( - 5 , " No such transaction found " , self . nodes [ 0 ] . getrawtransaction , tx , True , block2 )
# An invalid block hash should raise the correct errors
2018-09-24 20:54:10 +02:00
assert_raises_rpc_error ( - 1 , " JSON value is not a string as expected " , self . nodes [ 0 ] . getrawtransaction , tx , True , True )
assert_raises_rpc_error ( - 8 , " parameter 3 must be of length 64 (not 6, for ' foobar ' ) " , self . nodes [ 0 ] . getrawtransaction , tx , True , " foobar " )
assert_raises_rpc_error ( - 8 , " parameter 3 must be of length 64 (not 8, for ' abcd1234 ' ) " , self . nodes [ 0 ] . getrawtransaction , tx , True , " abcd1234 " )
assert_raises_rpc_error ( - 8 , " parameter 3 must be hexadecimal string (not ' ZZZ0000000000000000000000000000000000000000000000000000000000000 ' ) " , self . nodes [ 0 ] . getrawtransaction , tx , True , " ZZZ0000000000000000000000000000000000000000000000000000000000000 " )
2017-12-07 17:36:46 +01:00
assert_raises_rpc_error ( - 5 , " Block hash not found " , self . nodes [ 0 ] . getrawtransaction , tx , True , " 0000000000000000000000000000000000000000000000000000000000000000 " )
# Undo the blocks and check in_active_chain
self . nodes [ 0 ] . invalidateblock ( block1 )
gottx = self . nodes [ 0 ] . getrawtransaction ( txid = tx , verbose = True , blockhash = block1 )
assert_equal ( gottx [ ' in_active_chain ' ] , False )
self . nodes [ 0 ] . reconsiderblock ( block1 )
assert_equal ( self . nodes [ 0 ] . getbestblockhash ( ) , block2 )
Merge #10275: [rpc] Allow fetching tx directly from specified block in getrawtransaction
434526a [test] Add tests for getrawtransaction with block hash. (Karl-Johan Alm)
b167951 [rpc] Allow getrawtransaction to take optional blockhash to fetch transaction from a block directly. (Karl-Johan Alm)
a5f5a2c [rpc] Fix fVerbose parsing (remove excess if cases). (Karl-Johan Alm)
Pull request description:
[Reviewer hint: use [?w=1](https://github.com/bitcoin/bitcoin/pull/10275/files?w=1) to avoid seeing a bunch of indentation changes.]
Presuming a user knows the block hash of the block containing a given transaction, this PR allows them to fetch the raw transaction, even without `-txindex`. It also enables support for getting transactions that are in orphaned blocks.
Note that supplying a block hash will override mempool and txindex support in `GetTransaction`. The rationale behind this is that a transaction may be in multiple places (orphaned blocks) and if the user supplies an explicit block hash it should be adhered to.
```Bash
$ # a41.. is a tx inside an orphan block ..3c6f.. -- first try getting it normally
$ ./bitcoin-cli getrawtransaction a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79 1
error code: -5
error message:
No such mempool transaction. Use -txindex to enable blockchain transaction queries. Use gettransaction for wallet transactions.
$ # now try with block hash
$ ./bitcoin-cli getrawtransaction a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79 1 0000000000000000003c6fe479122bfa4a9187493937af1734e1e5cd9f198ec7
{
"hex": "01000000014e7e81144e42f6d65550e59b715d470c9301fd7ac189[...]90488ac00000000",
"inMainChain": false,
"txid": "a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79",
"hash": "a41e66ee1341aa9fb9475b98cfdc1fe1261faa56c0a49254f33065ec90f7cd79",
"size": 225,
[...]
}
$ # another tx 6c66... in block 462000
$ ./bitcoin-cli getrawtransaction 6c66b98191e9d6cc671f6817142152ebf6c5cab2ef008397b5a71ac13255a735 1 00000000000000000217f2c12922e321f6d4aa933ce88005a9a493c503054a40
{
"hex": "0200000004d157[...]88acaf0c0700",
"inMainChain": true,
"txid": "6c66b98191e9d6cc671f6817142152ebf6c5cab2ef008397b5a71ac13255a735",
"hash": "6c66b98191e9d6cc671f6817142152ebf6c5cab2ef008397b5a71ac13255a735",
"size": 666,
[...]
}
$
```
Tree-SHA512: 279be3818141edd3cc194a9ee65929331920afb30297ab2d6da07293a2d7311afee5c8b00c6457477d9f1f86e86786a9b56878ea3ee19fa2629b829d042d0cda
2017-12-06 12:10:16 +01:00
2020-11-02 16:54:06 +01:00
if not self . options . descriptors :
# The traditional multisig workflow does not work with descriptor wallets so these are legacy only.
# The multisig workflow with descriptor wallets uses PSBTs and is tested elsewhere, no need to do them here.
#########################
# RAW TX MULTISIG TESTS #
#########################
# 2of2 test
addr1 = self . nodes [ 2 ] . getnewaddress ( )
addr2 = self . nodes [ 2 ] . getnewaddress ( )
addr1Obj = self . nodes [ 2 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 2 ] . getaddressinfo ( addr2 )
# Tests for createmultisig and addmultisigaddress
assert_raises_rpc_error ( - 5 , " Invalid public key " , self . nodes [ 0 ] . createmultisig , 1 , [ " 01020304 " ] )
self . nodes [ 0 ] . createmultisig ( 2 , [ addr1Obj [ ' pubkey ' ] , addr2Obj [ ' pubkey ' ] ] ) # createmultisig can only take public keys
assert_raises_rpc_error ( - 5 , " Invalid public key " , self . nodes [ 0 ] . createmultisig , 2 , [ addr1Obj [ ' pubkey ' ] , addr1 ] ) # addmultisigaddress can take both pubkeys and addresses so long as they are in the wallet, which is tested here.
mSigObj = self . nodes [ 2 ] . addmultisigaddress ( 2 , [ addr1Obj [ ' pubkey ' ] , addr1 ] ) [ ' address ' ]
#use balance deltas instead of absolute values
bal = self . nodes [ 2 ] . getbalance ( )
# send 1.2 BTC to msig adr
txId = self . nodes [ 0 ] . sendtoaddress ( mSigObj , 1.2 )
self . sync_all ( )
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , bal + Decimal ( ' 1.20000000 ' ) ) #node2 has both keys of the 2of2 ms addr., tx should affect the balance
# 2of3 test from different nodes
bal = self . nodes [ 2 ] . getbalance ( )
addr1 = self . nodes [ 1 ] . getnewaddress ( )
addr2 = self . nodes [ 2 ] . getnewaddress ( )
addr3 = self . nodes [ 2 ] . getnewaddress ( )
addr1Obj = self . nodes [ 1 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 2 ] . getaddressinfo ( addr2 )
addr3Obj = self . nodes [ 2 ] . getaddressinfo ( addr3 )
mSigObj = self . nodes [ 2 ] . addmultisigaddress ( 2 , [ addr1Obj [ ' pubkey ' ] , addr2Obj [ ' pubkey ' ] , addr3Obj [ ' pubkey ' ] ] ) [ ' address ' ]
txId = self . nodes [ 0 ] . sendtoaddress ( mSigObj , 2.2 )
decTx = self . nodes [ 0 ] . gettransaction ( txId )
rawTx = self . nodes [ 0 ] . decoderawtransaction ( decTx [ ' hex ' ] )
self . sync_all ( )
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
#THIS IS AN INCOMPLETE FEATURE
#NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , bal ) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable
txDetails = self . nodes [ 0 ] . gettransaction ( txId , True )
rawTx = self . nodes [ 0 ] . decoderawtransaction ( txDetails [ ' hex ' ] )
vout = next ( o for o in rawTx [ ' vout ' ] if o [ ' value ' ] == Decimal ( ' 2.20000000 ' ) )
bal = self . nodes [ 0 ] . getbalance ( )
inputs = [ { " txid " : txId , " vout " : vout [ ' n ' ] , " scriptPubKey " : vout [ ' scriptPubKey ' ] [ ' hex ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 2.19 }
rawTx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
rawTxPartialSigned = self . nodes [ 1 ] . signrawtransactionwithwallet ( rawTx , inputs )
assert_equal ( rawTxPartialSigned [ ' complete ' ] , False ) #node1 only has one key, can't comp. sign the tx
rawTxSigned = self . nodes [ 2 ] . signrawtransactionwithwallet ( rawTx , inputs )
assert_equal ( rawTxSigned [ ' complete ' ] , True ) #node2 can sign the tx compl., own two of three keys
self . nodes [ 2 ] . sendrawtransaction ( rawTxSigned [ ' hex ' ] )
rawTx = self . nodes [ 0 ] . decoderawtransaction ( rawTxSigned [ ' hex ' ] )
self . sync_all ( )
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , bal + Decimal ( ' 500.00000000 ' ) + Decimal ( ' 2.19000000 ' ) ) #block reward + tx
# 2of2 test for combining transactions
bal = self . nodes [ 2 ] . getbalance ( )
addr1 = self . nodes [ 1 ] . getnewaddress ( )
addr2 = self . nodes [ 2 ] . getnewaddress ( )
addr1Obj = self . nodes [ 1 ] . getaddressinfo ( addr1 )
addr2Obj = self . nodes [ 2 ] . getaddressinfo ( addr2 )
self . nodes [ 1 ] . addmultisigaddress ( 2 , [ addr1Obj [ ' pubkey ' ] , addr2Obj [ ' pubkey ' ] ] ) [ ' address ' ]
mSigObj = self . nodes [ 2 ] . addmultisigaddress ( 2 , [ addr1Obj [ ' pubkey ' ] , addr2Obj [ ' pubkey ' ] ] ) [ ' address ' ]
mSigObjValid = self . nodes [ 2 ] . getaddressinfo ( mSigObj )
txId = self . nodes [ 0 ] . sendtoaddress ( mSigObj , 2.2 )
decTx = self . nodes [ 0 ] . gettransaction ( txId )
rawTx2 = self . nodes [ 0 ] . decoderawtransaction ( decTx [ ' hex ' ] )
self . sync_all ( )
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
assert_equal ( self . nodes [ 2 ] . getbalance ( ) , bal ) # the funds of a 2of2 multisig tx should not be marked as spendable
txDetails = self . nodes [ 0 ] . gettransaction ( txId , True )
rawTx2 = self . nodes [ 0 ] . decoderawtransaction ( txDetails [ ' hex ' ] )
vout = next ( o for o in rawTx2 [ ' vout ' ] if o [ ' value ' ] == Decimal ( ' 2.20000000 ' ) )
bal = self . nodes [ 0 ] . getbalance ( )
inputs = [ { " txid " : txId , " vout " : vout [ ' n ' ] , " scriptPubKey " : vout [ ' scriptPubKey ' ] [ ' hex ' ] , " redeemScript " : mSigObjValid [ ' hex ' ] } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 2.19 }
rawTx2 = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
rawTxPartialSigned1 = self . nodes [ 1 ] . signrawtransactionwithwallet ( rawTx2 , inputs )
self . log . debug ( rawTxPartialSigned1 )
assert_equal ( rawTxPartialSigned1 [ ' complete ' ] , False ) #node1 only has one key, can't comp. sign the tx
rawTxPartialSigned2 = self . nodes [ 2 ] . signrawtransactionwithwallet ( rawTx2 , inputs )
self . log . debug ( rawTxPartialSigned2 )
assert_equal ( rawTxPartialSigned2 [ ' complete ' ] , False ) #node2 only has one key, can't comp. sign the tx
rawTxComb = self . nodes [ 2 ] . combinerawtransaction ( [ rawTxPartialSigned1 [ ' hex ' ] , rawTxPartialSigned2 [ ' hex ' ] ] )
self . log . debug ( rawTxComb )
self . nodes [ 2 ] . sendrawtransaction ( rawTxComb )
rawTx2 = self . nodes [ 0 ] . decoderawtransaction ( rawTxComb )
self . sync_all ( )
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
assert_equal ( self . nodes [ 0 ] . getbalance ( ) , bal + Decimal ( ' 500.00000000 ' ) + Decimal ( ' 2.19000000 ' ) ) #block reward + tx
# Basic signrawtransaction test
addr = self . nodes [ 1 ] . getnewaddress ( )
txid = self . nodes [ 0 ] . sendtoaddress ( addr , 10 )
2015-04-01 15:08:00 +02:00
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
2020-11-02 16:54:06 +01:00
vout = find_vout_for_address ( self . nodes [ 1 ] , txid , addr )
rawTx = self . nodes [ 1 ] . createrawtransaction ( [ { ' txid ' : txid , ' vout ' : vout } ] , { self . nodes [ 1 ] . getnewaddress ( ) : 9.999 } )
rawTxSigned = self . nodes [ 1 ] . signrawtransactionwithwallet ( rawTx )
txId = self . nodes [ 1 ] . sendrawtransaction ( rawTxSigned [ ' hex ' ] )
2017-07-20 16:32:47 +02:00
self . nodes [ 0 ] . generate ( 1 )
self . sync_all ( )
2016-11-23 07:13:35 +01:00
# getrawtransaction tests
# 1. valid parameters - only supply txid
2019-05-24 12:46:28 +02:00
assert_equal ( self . nodes [ 0 ] . getrawtransaction ( txId ) , rawTxSigned [ ' hex ' ] )
2024-01-23 02:33:24 +01:00
assert_equal ( self . nodes [ 0 ] . getrawtransactionmulti ( { " 0 " : [ txId ] } ) [ txId ] , rawTxSigned [ ' hex ' ] )
2016-11-23 07:13:35 +01:00
# 2. valid parameters - supply txid and 0 for non-verbose
2019-05-24 12:46:28 +02:00
assert_equal ( self . nodes [ 0 ] . getrawtransaction ( txId , 0 ) , rawTxSigned [ ' hex ' ] )
2024-01-23 02:33:24 +01:00
assert_equal ( self . nodes [ 0 ] . getrawtransactionmulti ( { " 0 " : [ txId ] } , 0 ) [ txId ] , rawTxSigned [ ' hex ' ] )
2016-11-23 07:13:35 +01:00
# 3. valid parameters - supply txid and False for non-verbose
2019-05-24 12:46:28 +02:00
assert_equal ( self . nodes [ 0 ] . getrawtransaction ( txId , False ) , rawTxSigned [ ' hex ' ] )
2024-01-23 02:33:24 +01:00
assert_equal ( self . nodes [ 0 ] . getrawtransactionmulti ( { " 0 " : [ txId ] } , False ) [ txId ] , rawTxSigned [ ' hex ' ] )
2016-11-23 07:13:35 +01:00
# 4. valid parameters - supply txid and 1 for verbose.
# We only check the "hex" field of the output so we don't need to update this test every time the output format changes.
2019-05-24 12:46:28 +02:00
assert_equal ( self . nodes [ 0 ] . getrawtransaction ( txId , 1 ) [ " hex " ] , rawTxSigned [ ' hex ' ] )
2024-01-23 02:33:24 +01:00
assert_equal ( self . nodes [ 0 ] . getrawtransactionmulti ( { " 0 " : [ txId ] } , 1 ) [ txId ] [ ' hex ' ] , rawTxSigned [ ' hex ' ] )
2016-11-23 07:13:35 +01:00
# 5. valid parameters - supply txid and True for non-verbose
2019-05-24 12:46:28 +02:00
assert_equal ( self . nodes [ 0 ] . getrawtransaction ( txId , True ) [ " hex " ] , rawTxSigned [ ' hex ' ] )
2024-02-26 16:38:48 +01:00
assert_equal ( self . nodes [ 0 ] . getrawtransactionmulti ( verbose = True , transactions = { " 0 " : [ txId ] } ) [ txId ] [ ' hex ' ] , rawTxSigned [ ' hex ' ] )
2016-11-23 07:13:35 +01:00
# 6. invalid parameters - supply txid and string "Flase"
2019-05-24 12:46:28 +02:00
assert_raises_rpc_error ( - 1 , " not a boolean " , self . nodes [ 0 ] . getrawtransaction , txId , " Flase " )
2016-11-23 07:13:35 +01:00
# 7. invalid parameters - supply txid and empty array
2019-05-24 12:46:28 +02:00
assert_raises_rpc_error ( - 1 , " not a boolean " , self . nodes [ 0 ] . getrawtransaction , txId , [ ] )
2016-11-23 07:13:35 +01:00
# 8. invalid parameters - supply txid and empty dict
2019-05-24 12:46:28 +02:00
assert_raises_rpc_error ( - 1 , " not a boolean " , self . nodes [ 0 ] . getrawtransaction , txId , { } )
2016-11-23 07:13:35 +01:00
2016-06-07 18:25:10 +02:00
inputs = [ { ' txid ' : " 1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000 " , ' vout ' : 1 , ' sequence ' : 1000 } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1 }
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
decrawtx = self . nodes [ 0 ] . decoderawtransaction ( rawtx )
assert_equal ( decrawtx [ ' vin ' ] [ 0 ] [ ' sequence ' ] , 1000 )
2017-07-20 16:32:47 +02:00
2017-03-09 10:02:13 +01:00
# 9. invalid parameters - sequence number out of range
2016-06-16 11:05:32 +02:00
inputs = [ { ' txid ' : " 1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000 " , ' vout ' : 1 , ' sequence ' : - 1 } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1 }
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 8 , ' Invalid parameter, sequence number is out of range ' , self . nodes [ 0 ] . createrawtransaction , inputs , outputs )
2017-07-20 16:32:47 +02:00
2017-03-09 10:02:13 +01:00
# 10. invalid parameters - sequence number out of range
2016-06-16 11:05:32 +02:00
inputs = [ { ' txid ' : " 1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000 " , ' vout ' : 1 , ' sequence ' : 4294967296 } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1 }
2019-09-25 11:34:51 +02:00
assert_raises_rpc_error ( - 8 , ' Invalid parameter, sequence number is out of range ' , self . nodes [ 0 ] . createrawtransaction , inputs , outputs )
2017-07-20 16:32:47 +02:00
2016-06-16 11:05:32 +02:00
inputs = [ { ' txid ' : " 1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000 " , ' vout ' : 1 , ' sequence ' : 4294967294 } ]
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : 1 }
rawtx = self . nodes [ 0 ] . createrawtransaction ( inputs , outputs )
decrawtx = self . nodes [ 0 ] . decoderawtransaction ( rawtx )
assert_equal ( decrawtx [ ' vin ' ] [ 0 ] [ ' sequence ' ] , 4294967294 )
2016-06-07 18:25:10 +02:00
2018-04-24 11:56:32 +02:00
####################################
# TRANSACTION VERSION NUMBER TESTS #
####################################
2020-11-10 04:59:41 +01:00
# Test the minimum transaction version number that fits in a signed 16-bit integer.
# Note, this is different to bitcoin. Bitcoin has a 32 bit integer
# representing the version, we have 16 bits of version and 16 bits of
# type.
2020-07-16 21:33:13 +02:00
# As transaction version is unsigned, this should convert to its unsigned equivalent.
2018-04-24 11:56:32 +02:00
tx = CTransaction ( )
2020-11-10 04:59:41 +01:00
tx . nVersion = - 0x8000
2021-06-24 12:47:04 +02:00
rawtx = tx . serialize ( ) . hex ( )
2018-04-24 11:56:32 +02:00
decrawtx = self . nodes [ 0 ] . decoderawtransaction ( rawtx )
2020-07-16 21:33:13 +02:00
assert_equal ( decrawtx [ ' version ' ] , 0x8000 )
2018-04-24 11:56:32 +02:00
# Test the maximum transaction version number that fits in a signed 32-bit integer.
tx = CTransaction ( )
2020-11-10 04:59:41 +01:00
tx . nVersion = 0x7fff
2021-06-24 12:47:04 +02:00
rawtx = tx . serialize ( ) . hex ( )
2018-04-24 11:56:32 +02:00
decrawtx = self . nodes [ 0 ] . decoderawtransaction ( rawtx )
2020-11-10 04:59:41 +01:00
assert_equal ( decrawtx [ ' version ' ] , 0x7fff )
2018-04-24 11:56:32 +02:00
2021-12-12 07:27:44 +01:00
self . log . info ( ' sendrawtransaction/testmempoolaccept with maxfeerate ' )
2019-09-25 11:51:26 +02:00
# Test a transaction with a small fee.
2021-12-12 07:27:44 +01:00
txId = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 1.0 )
rawTx = self . nodes [ 0 ] . getrawtransaction ( txId , True )
vout = next ( o for o in rawTx [ ' vout ' ] if o [ ' value ' ] == Decimal ( ' 1.00000000 ' ) )
self . sync_all ( )
inputs = [ { " txid " : txId , " vout " : vout [ ' n ' ] } ]
2019-09-25 11:51:26 +02:00
# Fee 10,000 satoshis, (1 - (10000 sat * 0.00000001 BTC/sat)) = 0.9999
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( " 0.99990000 " ) }
2021-12-12 07:27:44 +01:00
rawTx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
rawTxSigned = self . nodes [ 2 ] . signrawtransactionwithwallet ( rawTx )
assert_equal ( rawTxSigned [ ' complete ' ] , True )
2019-09-25 11:51:26 +02:00
# Fee 10,000 satoshis, ~200 b transaction, fee rate should land around 50 sat/byte = 0.00500000 BTC/kB
2021-12-12 07:27:44 +01:00
# Thus, testmempoolaccept should reject
testres = self . nodes [ 2 ] . testmempoolaccept ( [ rawTxSigned [ ' hex ' ] ] , 0.00001000 ) [ 0 ]
assert_equal ( testres [ ' allowed ' ] , False )
2024-01-25 07:53:18 +01:00
assert_equal ( testres [ ' reject-reason ' ] , ' max-fee-exceeded ' )
2021-12-12 07:27:44 +01:00
# and sendrawtransaction should throw
2024-01-25 07:53:18 +01:00
assert_raises_rpc_error ( - 25 , ' Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate) ' , self . nodes [ 2 ] . sendrawtransaction , rawTxSigned [ ' hex ' ] , 0.00001000 )
2019-09-25 11:51:26 +02:00
# and the following calls should both succeed
2019-09-18 16:47:23 +02:00
testres = self . nodes [ 2 ] . testmempoolaccept ( rawtxs = [ rawTxSigned [ ' hex ' ] ] ) [ 0 ]
2021-12-12 07:27:44 +01:00
assert_equal ( testres [ ' allowed ' ] , True )
2019-09-18 16:47:23 +02:00
self . nodes [ 2 ] . sendrawtransaction ( hexstring = rawTxSigned [ ' hex ' ] )
2021-12-12 07:27:44 +01:00
2019-09-25 11:51:26 +02:00
# Test a transaction with a large fee.
2019-09-18 16:47:23 +02:00
txId = self . nodes [ 0 ] . sendtoaddress ( self . nodes [ 2 ] . getnewaddress ( ) , 1.0 )
rawTx = self . nodes [ 0 ] . getrawtransaction ( txId , True )
vout = next ( o for o in rawTx [ ' vout ' ] if o [ ' value ' ] == Decimal ( ' 1.00000000 ' ) )
self . sync_all ( )
inputs = [ { " txid " : txId , " vout " : vout [ ' n ' ] } ]
2019-09-25 11:51:26 +02:00
# Fee 2,000,000 satoshis, (1 - (2000000 sat * 0.00000001 BTC/sat)) = 0.98
outputs = { self . nodes [ 0 ] . getnewaddress ( ) : Decimal ( " 0.98000000 " ) }
2019-09-18 16:47:23 +02:00
rawTx = self . nodes [ 2 ] . createrawtransaction ( inputs , outputs )
rawTxSigned = self . nodes [ 2 ] . signrawtransactionwithwallet ( rawTx )
assert_equal ( rawTxSigned [ ' complete ' ] , True )
2019-09-25 11:51:26 +02:00
# Fee 2,000,000 satoshis, ~100 b transaction, fee rate should land around 20,000 sat/byte = 0.20000000 BTC/kB
2019-09-18 16:47:23 +02:00
# Thus, testmempoolaccept should reject
testres = self . nodes [ 2 ] . testmempoolaccept ( [ rawTxSigned [ ' hex ' ] ] ) [ 0 ]
assert_equal ( testres [ ' allowed ' ] , False )
2024-01-25 07:53:18 +01:00
assert_equal ( testres [ ' reject-reason ' ] , ' max-fee-exceeded ' )
2019-09-18 16:47:23 +02:00
# and sendrawtransaction should throw
2024-01-25 07:53:18 +01:00
assert_raises_rpc_error ( - 25 , ' Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate) ' , self . nodes [ 2 ] . sendrawtransaction , rawTxSigned [ ' hex ' ] )
2019-09-25 11:51:26 +02:00
# and the following calls should both succeed
2019-09-18 16:47:23 +02:00
testres = self . nodes [ 2 ] . testmempoolaccept ( rawtxs = [ rawTxSigned [ ' hex ' ] ] , maxfeerate = ' 0.20000000 ' ) [ 0 ]
assert_equal ( testres [ ' allowed ' ] , True )
self . nodes [ 2 ] . sendrawtransaction ( hexstring = rawTxSigned [ ' hex ' ] , maxfeerate = ' 0.20000000 ' )
2021-12-12 07:27:44 +01:00
2021-07-21 11:10:25 +02:00
self . log . info ( ' sendrawtransaction/testmempoolaccept with tx that is already in the chain ' )
self . nodes [ 2 ] . generate ( 1 )
self . sync_blocks ( )
for node in self . nodes :
testres = node . testmempoolaccept ( [ rawTxSigned [ ' hex ' ] ] ) [ 0 ]
assert_equal ( testres [ ' allowed ' ] , False )
assert_equal ( testres [ ' reject-reason ' ] , ' txn-already-known ' )
assert_raises_rpc_error ( - 27 , ' Transaction already in block chain ' , node . sendrawtransaction , rawTxSigned [ ' hex ' ] )
2014-12-29 20:17:21 +01:00
if __name__ == ' __main__ ' :
RawTransactionsTest ( ) . main ( )