Changeset View
Changeset View
Standalone View
Standalone View
test/functional/rpc_rawtransaction.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2014-2016 The Bitcoin Core developers | # Copyright (c) 2014-2017 The Bitcoin Core 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. | ||||
"""rawtranscation RPCs QA test. | """rawtranscation RPCs QA test. | ||||
# Tests the following RPCs: | # Tests the following RPCs: | ||||
# - createrawtransaction | # - createrawtransaction | ||||
# - signrawtransactionwithwallet | # - signrawtransactionwithwallet | ||||
# - sendrawtransaction | # - sendrawtransaction | ||||
# - decoderawtransaction | # - decoderawtransaction | ||||
# - getrawtransaction | # - getrawtransaction | ||||
""" | """ | ||||
from decimal import Decimal | from decimal import Decimal | ||||
from collections import OrderedDict | |||||
from io import BytesIO | |||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.txtools import pad_raw_tx | from test_framework.txtools import pad_raw_tx | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_equal, | assert_equal, | ||||
assert_greater_than, | assert_greater_than, | ||||
assert_raises_rpc_error, | assert_raises_rpc_error, | ||||
connect_nodes_bi, | connect_nodes_bi, | ||||
hex_str_to_bytes, | |||||
bytes_to_hex_str, | |||||
) | |||||
from test_framework.messages import ( | |||||
CTransaction, | |||||
) | ) | ||||
class multidict(dict): | class multidict(dict): | ||||
"""Dictionary that allows duplicate keys. | """Dictionary that allows duplicate keys. | ||||
Constructed with a list of (key, value) tuples. When dumped by the json module, | Constructed with a list of (key, value) tuples. When dumped by the json module, | ||||
will output invalid json with repeated keys, eg: | will output invalid json with repeated keys, eg: | ||||
>>> json.dumps(multidict([(1,2),(1,2)]) | >>> json.dumps(multidict([(1,2),(1,2)]) | ||||
Show All 14 Lines | def set_test_params(self): | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
self.num_nodes = 3 | self.num_nodes = 3 | ||||
def setup_network(self, split=False): | def setup_network(self, split=False): | ||||
super().setup_network() | super().setup_network() | ||||
connect_nodes_bi(self.nodes[0], self.nodes[2]) | connect_nodes_bi(self.nodes[0], self.nodes[2]) | ||||
def run_test(self): | def run_test(self): | ||||
# prepare some coins for multiple *rawtransaction commands | self.log.info( | ||||
'prepare some coins for multiple *rawtransaction commands') | |||||
self.nodes[2].generate(1) | self.nodes[2].generate(1) | ||||
self.sync_all() | self.sync_all() | ||||
self.nodes[0].generate(101) | self.nodes[0].generate(101) | ||||
self.sync_all() | self.sync_all() | ||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5) | 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(), 1.0) | ||||
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 5.0) | self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 5.0) | ||||
self.sync_all() | self.sync_all() | ||||
self.nodes[0].generate(5) | self.nodes[0].generate(5) | ||||
self.sync_all() | self.sync_all() | ||||
# Test getrawtransaction on genesis block coinbase returns an error | self.log.info( | ||||
'Test getrawtransaction on genesis block coinbase returns an error') | |||||
block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) | 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", | assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", | ||||
self.nodes[0].getrawtransaction, block['merkleroot']) | self.nodes[0].getrawtransaction, block['merkleroot']) | ||||
self.log.info( | |||||
'Check parameter types and required parameters of createrawtransaction') | |||||
# Test `createrawtransaction` required parameters | # Test `createrawtransaction` required parameters | ||||
assert_raises_rpc_error(-1, "createrawtransaction", | assert_raises_rpc_error(-1, "createrawtransaction", | ||||
self.nodes[0].createrawtransaction) | self.nodes[0].createrawtransaction) | ||||
assert_raises_rpc_error(-1, "createrawtransaction", | assert_raises_rpc_error(-1, "createrawtransaction", | ||||
self.nodes[0].createrawtransaction, []) | self.nodes[0].createrawtransaction, []) | ||||
# Test `createrawtransaction` invalid extra parameters | # Test `createrawtransaction` invalid extra parameters | ||||
assert_raises_rpc_error(-1, "createrawtransaction", | assert_raises_rpc_error(-1, "createrawtransaction", | ||||
Show All 15 Lines | def run_test(self): | ||||
self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {}) | self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {}) | ||||
assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", | assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", | ||||
self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {}) | self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {}) | ||||
assert_raises_rpc_error(-8, "Invalid parameter, sequence number is out of range", | assert_raises_rpc_error(-8, "Invalid parameter, sequence number is out of range", | ||||
self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 0, 'sequence': -1}], {}) | self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 0, 'sequence': -1}], {}) | ||||
# Test `createrawtransaction` invalid `outputs` | # Test `createrawtransaction` invalid `outputs` | ||||
address = self.nodes[0].getnewaddress() | address = self.nodes[0].getnewaddress() | ||||
assert_raises_rpc_error(-3, "Expected type object", | 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, [], 'foo') | ||||
# Should not throw for backwards compatibility | |||||
self.nodes[0].createrawtransaction(inputs=[], outputs={}) | |||||
self.nodes[0].createrawtransaction(inputs=[], outputs=[]) | |||||
assert_raises_rpc_error(-8, "Data must be hexadecimal string", | assert_raises_rpc_error(-8, "Data must be hexadecimal string", | ||||
self.nodes[0].createrawtransaction, [], {'data': 'foo'}) | self.nodes[0].createrawtransaction, [], {'data': 'foo'}) | ||||
assert_raises_rpc_error(-5, "Invalid Bitcoin address", | assert_raises_rpc_error(-5, "Invalid Bitcoin address", | ||||
self.nodes[0].createrawtransaction, [], {'foo': 0}) | self.nodes[0].createrawtransaction, [], {'foo': 0}) | ||||
assert_raises_rpc_error(-3, "Invalid amount", | assert_raises_rpc_error(-3, "Invalid amount", | ||||
self.nodes[0].createrawtransaction, [], {address: 'foo'}) | self.nodes[0].createrawtransaction, [], {address: 'foo'}) | ||||
assert_raises_rpc_error(-3, "Amount out of range", | assert_raises_rpc_error(-3, "Amount out of range", | ||||
self.nodes[0].createrawtransaction, [], {address: -1}) | self.nodes[0].createrawtransaction, [], {address: -1}) | ||||
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: {}".format( | assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: {}".format( | ||||
address), self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) | address), self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) | ||||
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: {}".format( | |||||
address), self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}]) | |||||
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']]) | |||||
# Test `createrawtransaction` invalid `locktime` | # Test `createrawtransaction` invalid `locktime` | ||||
assert_raises_rpc_error(-3, "Expected type number", | assert_raises_rpc_error(-3, "Expected type number", | ||||
self.nodes[0].createrawtransaction, [], {}, 'foo') | self.nodes[0].createrawtransaction, [], {}, 'foo') | ||||
assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", | assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", | ||||
self.nodes[0].createrawtransaction, [], {}, -1) | self.nodes[0].createrawtransaction, [], {}, -1) | ||||
assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", | assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", | ||||
self.nodes[0].createrawtransaction, [], {}, 4294967296) | self.nodes[0].createrawtransaction, [], {}, 4294967296) | ||||
# | self.log.info( | ||||
# sendrawtransaction with missing input # | 'Check that createrawtransaction accepts an array and object as outputs') | ||||
# | tx = CTransaction() | ||||
# One output | |||||
tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction( | |||||
inputs=[{'txid': txid, 'vout': 9}], outputs={address: 99})))) | |||||
assert_equal(len(tx.vout), 1) | |||||
assert_equal( | |||||
bytes_to_hex_str(tx.serialize()), | |||||
self.nodes[2].createrawtransaction( | |||||
inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}]), | |||||
) | |||||
# Two outputs | |||||
tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[ | |||||
{'txid': txid, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)]))))) | |||||
assert_equal(len(tx.vout), 2) | |||||
assert_equal( | |||||
bytes_to_hex_str(tx.serialize()), | |||||
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[ | |||||
{address: 99}, {address2: 99}]), | |||||
) | |||||
# Two data outputs | |||||
tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[ | |||||
{'txid': txid, 'vout': 9}], outputs=multidict([('data', '99'), ('data', '99')]))))) | |||||
assert_equal(len(tx.vout), 2) | |||||
assert_equal( | |||||
bytes_to_hex_str(tx.serialize()), | |||||
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[ | |||||
{'data': '99'}, {'data': '99'}]), | |||||
) | |||||
# Multiple mixed outputs | |||||
tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[ | |||||
{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), ('data', '99'), ('data', '99')]))))) | |||||
assert_equal(len(tx.vout), 3) | |||||
assert_equal( | |||||
bytes_to_hex_str(tx.serialize()), | |||||
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[ | |||||
{address: 99}, {'data': '99'}, {'data': '99'}]), | |||||
) | |||||
self.log.info('sendrawtransaction with missing input') | |||||
# won't exists | |||||
inputs = [ | inputs = [ | ||||
{'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1}] | {'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1}] | ||||
# won't exists | |||||
outputs = {self.nodes[0].getnewaddress(): 4.998} | outputs = {self.nodes[0].getnewaddress(): 4.998} | ||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs) | rawtx = self.nodes[2].createrawtransaction(inputs, outputs) | ||||
rawtx = pad_raw_tx(rawtx) | rawtx = pad_raw_tx(rawtx) | ||||
rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) | rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) | ||||
# This will raise an exception since there are missing inputs | # This will raise an exception since there are missing inputs | ||||
assert_raises_rpc_error( | assert_raises_rpc_error( | ||||
-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) | -25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) | ||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
bal = self.nodes[0].getbalance() | bal = self.nodes[0].getbalance() | ||||
inputs = [{"txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey'] | inputs = [{"txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey'] | ||||
['hex'], "redeemScript": mSigObjValid['hex'], "amount": vout['value']}] | ['hex'], "redeemScript": mSigObjValid['hex'], "amount": vout['value']}] | ||||
outputs = {self.nodes[0].getnewaddress(): 2.19} | outputs = {self.nodes[0].getnewaddress(): 2.19} | ||||
rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) | rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) | ||||
rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet( | rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet( | ||||
rawTx2, inputs) | rawTx2, inputs) | ||||
self.log.info(rawTxPartialSigned1) | self.log.debug(rawTxPartialSigned1) | ||||
# node1 only has one key, can't comp. sign the tx | # node1 only has one key, can't comp. sign the tx | ||||
assert_equal(rawTxPartialSigned['complete'], False) | assert_equal(rawTxPartialSigned['complete'], False) | ||||
rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet( | rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet( | ||||
rawTx2, inputs) | rawTx2, inputs) | ||||
self.log.info(rawTxPartialSigned2) | self.log.debug(rawTxPartialSigned2) | ||||
# node2 only has one key, can't comp. sign the tx | # node2 only has one key, can't comp. sign the tx | ||||
assert_equal(rawTxPartialSigned2['complete'], False) | assert_equal(rawTxPartialSigned2['complete'], False) | ||||
rawTxComb = self.nodes[2].combinerawtransaction( | rawTxComb = self.nodes[2].combinerawtransaction( | ||||
[rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) | [rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) | ||||
self.log.info(rawTxComb) | self.log.debug(rawTxComb) | ||||
self.nodes[2].sendrawtransaction(rawTxComb) | self.nodes[2].sendrawtransaction(rawTxComb) | ||||
rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) | rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) | ||||
self.sync_all() | self.sync_all() | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
self.sync_all() | self.sync_all() | ||||
assert_equal(self.nodes[0].getbalance( | assert_equal(self.nodes[0].getbalance( | ||||
), bal+Decimal('50.00000000')+Decimal('2.19000000')) # block reward + tx | ), bal+Decimal('50.00000000')+Decimal('2.19000000')) # block reward + tx | ||||
▲ Show 20 Lines • Show All 93 Lines • Show Last 20 Lines |