aboutsummaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa')
-rwxr-xr-xqa/pull-tester/rpc-tests.py10
-rwxr-xr-xqa/rpc-tests/bip65-cltv-p2p.py4
-rwxr-xr-xqa/rpc-tests/decodescript.py4
-rwxr-xr-xqa/rpc-tests/invalidblockrequest.py6
-rwxr-xr-xqa/rpc-tests/invalidtxrequest.py76
-rwxr-xr-xqa/rpc-tests/maxuploadtarget.py17
-rwxr-xr-xqa/rpc-tests/mempool_limit.py53
-rwxr-xr-xqa/rpc-tests/mempool_packages.py24
-rwxr-xr-xqa/rpc-tests/p2p-fullblocktest.py157
-rwxr-xr-xqa/rpc-tests/prioritise_transaction.py107
-rwxr-xr-xqa/rpc-tests/pruning.py19
-rwxr-xr-xqa/rpc-tests/replace-by-fee.py80
-rwxr-xr-xqa/rpc-tests/test_framework/comptool.py40
-rwxr-xr-xqa/rpc-tests/test_framework/mininode.py1
-rw-r--r--qa/rpc-tests/test_framework/script.py8
-rwxr-xr-xqa/rpc-tests/test_framework/test_framework.py2
-rw-r--r--qa/rpc-tests/test_framework/util.py68
17 files changed, 547 insertions, 129 deletions
diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py
index df71e44b60..44d7d71759 100755
--- a/qa/pull-tester/rpc-tests.py
+++ b/qa/pull-tester/rpc-tests.py
@@ -62,8 +62,10 @@ for arg in sys.argv[1:]:
#Set env vars
buildDir = BUILDDIR
-os.environ["BITCOIND"] = buildDir + '/src/bitcoind' + EXEEXT
-os.environ["BITCOINCLI"] = buildDir + '/src/bitcoin-cli' + EXEEXT
+if "BITCOIND" not in os.environ:
+ os.environ["BITCOIND"] = buildDir + '/src/bitcoind' + EXEEXT
+if "BITCOINCLI" not in os.environ:
+ os.environ["BITCOINCLI"] = buildDir + '/src/bitcoin-cli' + EXEEXT
#Disable Windows tests by default
if EXEEXT == ".exe" and "-win" not in opts:
@@ -83,6 +85,7 @@ testScripts = [
'rest.py',
'mempool_spendcoinbase.py',
'mempool_reorg.py',
+ 'mempool_limit.py',
'httpbasics.py',
'multi_rpc.py',
'zapwallettxes.py',
@@ -100,6 +103,8 @@ testScripts = [
'sendheaders.py',
'keypool.py',
'prioritise_transaction.py',
+ 'invalidblockrequest.py',
+ 'invalidtxrequest.py',
]
testScriptsExt = [
'bip65-cltv.py',
@@ -116,7 +121,6 @@ testScriptsExt = [
# 'rpcbind_test.py', #temporary, bug in libevent, see #6655
'smartfees.py',
'maxblocksinflight.py',
- 'invalidblockrequest.py',
'p2p-acceptblock.py',
'mempool_packages.py',
'maxuploadtarget.py',
diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py
index 9ca5c69f16..5bb41df1ad 100755
--- a/qa/rpc-tests/bip65-cltv-p2p.py
+++ b/qa/rpc-tests/bip65-cltv-p2p.py
@@ -9,7 +9,7 @@ from test_framework.util import *
from test_framework.mininode import CTransaction, NetworkThread
from test_framework.blocktools import create_coinbase, create_block
from test_framework.comptool import TestInstance, TestManager
-from test_framework.script import CScript, OP_1NEGATE, OP_NOP2, OP_DROP
+from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP
from binascii import hexlify, unhexlify
import cStringIO
import time
@@ -19,7 +19,7 @@ def cltv_invalidate(tx):
Prepends -1 CLTV DROP in the scriptSig itself.
'''
- tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_NOP2, OP_DROP] +
+ tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
list(CScript(tx.vin[0].scriptSig)))
'''
diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py
index 4bca623380..490808d49d 100755
--- a/qa/rpc-tests/decodescript.py
+++ b/qa/rpc-tests/decodescript.py
@@ -102,13 +102,13 @@ class DecodeScriptTest(BitcoinTestFramework):
# OP_IF
# <receiver-pubkey> OP_CHECKSIGVERIFY
# OP_ELSE
- # <lock-until> OP_NOP2 OP_DROP
+ # <lock-until> OP_CHECKLOCKTIMEVERIFY OP_DROP
# OP_ENDIF
# <sender-pubkey> OP_CHECKSIG
#
# lock until block 500,000
rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac')
- assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_NOP2 OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
+ assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
def decoderawtransaction_asm_sighashtype(self):
"""Tests decoding scripts via RPC command "decoderawtransaction".
diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py
index 6a7980cd45..a74ecb1288 100755
--- a/qa/rpc-tests/invalidblockrequest.py
+++ b/qa/rpc-tests/invalidblockrequest.py
@@ -6,7 +6,7 @@
from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
-from test_framework.comptool import TestManager, TestInstance
+from test_framework.comptool import TestManager, TestInstance, RejectResult
from test_framework.mininode import *
from test_framework.blocktools import *
import logging
@@ -97,7 +97,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework):
assert(block2_orig.vtx != block2.vtx)
self.tip = block2.sha256
- yield TestInstance([[block2, False], [block2_orig, True]])
+ yield TestInstance([[block2, RejectResult(16,'bad-txns-duplicate')], [block2_orig, True]])
height += 1
'''
@@ -112,7 +112,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework):
block3.rehash()
block3.solve()
- yield TestInstance([[block3, False]])
+ yield TestInstance([[block3, RejectResult(16,'bad-cb-amount')]])
if __name__ == '__main__':
diff --git a/qa/rpc-tests/invalidtxrequest.py b/qa/rpc-tests/invalidtxrequest.py
new file mode 100755
index 0000000000..d17b3d0980
--- /dev/null
+++ b/qa/rpc-tests/invalidtxrequest.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python2
+#
+# Distributed under the MIT/X11 software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+
+from test_framework.test_framework import ComparisonTestFramework
+from test_framework.util import *
+from test_framework.comptool import TestManager, TestInstance, RejectResult
+from test_framework.mininode import *
+from test_framework.blocktools import *
+import logging
+import copy
+import time
+
+
+'''
+In this test we connect to one node over p2p, and test tx requests.
+'''
+
+# Use the ComparisonTestFramework with 1 node: only use --testbinary.
+class InvalidTxRequestTest(ComparisonTestFramework):
+
+ ''' Can either run this test as 1 node with expected answers, or two and compare them.
+ Change the "outcome" variable from each TestInstance object to only do the comparison. '''
+ def __init__(self):
+ self.num_nodes = 1
+
+ def run_test(self):
+ test = TestManager(self, self.options.tmpdir)
+ test.add_all_connections(self.nodes)
+ self.tip = None
+ self.block_time = None
+ NetworkThread().start() # Start up network handling in another thread
+ test.run()
+
+ def get_tests(self):
+ if self.tip is None:
+ self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0)
+ self.block_time = int(time.time())+1
+
+ '''
+ Create a new block with an anyone-can-spend coinbase
+ '''
+ height = 1
+ block = create_block(self.tip, create_coinbase(height), self.block_time)
+ self.block_time += 1
+ block.solve()
+ # Save the coinbase for later
+ self.block1 = block
+ self.tip = block.sha256
+ height += 1
+ yield TestInstance([[block, True]])
+
+ '''
+ Now we need that block to mature so we can spend the coinbase.
+ '''
+ test = TestInstance(sync_every_block=False)
+ for i in xrange(100):
+ block = create_block(self.tip, create_coinbase(height), self.block_time)
+ block.solve()
+ self.tip = block.sha256
+ self.block_time += 1
+ test.blocks_and_transactions.append([block, True])
+ height += 1
+ yield test
+
+ # chr(100) is OP_NOTIF
+ # Transaction will be rejected with code 16 (REJECT_INVALID)
+ tx1 = create_transaction(self.block1.vtx[0], 0, chr(100), 50*100000000)
+ yield TestInstance([[tx1, RejectResult(16, 'mandatory-script-verify-flag-failed')]])
+
+ # TODO: test further transactions...
+
+if __name__ == '__main__':
+ InvalidTxRequestTest().main()
diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py
index 249663779c..4d6b343f77 100755
--- a/qa/rpc-tests/maxuploadtarget.py
+++ b/qa/rpc-tests/maxuploadtarget.py
@@ -84,22 +84,7 @@ class TestNode(NodeConnCB):
class MaxUploadTest(BitcoinTestFramework):
def __init__(self):
self.utxo = []
-
- # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create
- # So we have big transactions and full blocks to fill up our block files
- # create one script_pubkey
- script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes
- for i in xrange (512):
- script_pubkey = script_pubkey + "01"
- # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
- self.txouts = "81"
- for k in xrange(128):
- # add txout value
- self.txouts = self.txouts + "0000000000000000"
- # add length of script_pubkey
- self.txouts = self.txouts + "fd0402"
- # add script_pubkey
- self.txouts = self.txouts + script_pubkey
+ self.txouts = gen_return_txouts()
def add_options(self, parser):
parser.add_option("--testbinary", dest="testbinary",
diff --git a/qa/rpc-tests/mempool_limit.py b/qa/rpc-tests/mempool_limit.py
new file mode 100755
index 0000000000..3ba17ac4f1
--- /dev/null
+++ b/qa/rpc-tests/mempool_limit.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python2
+# Copyright (c) 2014-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+# Test mempool limiting together/eviction with the wallet
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+class MempoolLimitTest(BitcoinTestFramework):
+
+ def __init__(self):
+ self.txouts = gen_return_txouts()
+
+ def setup_network(self):
+ self.nodes = []
+ self.nodes.append(start_node(0, self.options.tmpdir, ["-maxmempool=5", "-spendzeroconfchange=0", "-debug"]))
+ self.is_network_split = False
+ self.sync_all()
+ self.relayfee = self.nodes[0].getnetworkinfo()['relayfee']
+
+ def setup_chain(self):
+ print("Initializing test directory "+self.options.tmpdir)
+ initialize_chain_clean(self.options.tmpdir, 2)
+
+ def run_test(self):
+ txids = []
+ utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 90)
+
+ #create a mempool tx that will be evicted
+ us0 = utxos.pop()
+ inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}]
+ outputs = {self.nodes[0].getnewaddress() : 0.0001}
+ tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ txF = self.nodes[0].fundrawtransaction(tx)
+ txFS = self.nodes[0].signrawtransaction(txF['hex'])
+ txid = self.nodes[0].sendrawtransaction(txFS['hex'])
+ self.nodes[0].lockunspent(True, [us0])
+
+ relayfee = self.nodes[0].getnetworkinfo()['relayfee']
+ base_fee = relayfee*100
+ for i in xrange (4):
+ txids.append([])
+ txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], (i+1)*base_fee)
+
+ # by now, the tx should be evicted, check confirmation state
+ assert(txid not in self.nodes[0].getrawmempool())
+ txdata = self.nodes[0].gettransaction(txid);
+ assert(txdata['confirmations'] == 0) #confirmation should still be 0
+
+if __name__ == '__main__':
+ MempoolLimitTest().main()
diff --git a/qa/rpc-tests/mempool_packages.py b/qa/rpc-tests/mempool_packages.py
index 34b316a6a3..063308d394 100755
--- a/qa/rpc-tests/mempool_packages.py
+++ b/qa/rpc-tests/mempool_packages.py
@@ -64,17 +64,41 @@ class MempoolPackagesTest(BitcoinTestFramework):
for x in reversed(chain):
assert_equal(mempool[x]['descendantcount'], descendant_count)
descendant_fees += mempool[x]['fee']
+ assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees)
descendant_size += mempool[x]['size']
assert_equal(mempool[x]['descendantsize'], descendant_size)
descendant_count += 1
+ # Check that descendant modified fees includes fee deltas from
+ # prioritisetransaction
+ self.nodes[0].prioritisetransaction(chain[-1], 0, 1000)
+ mempool = self.nodes[0].getrawmempool(True)
+
+ descendant_fees = 0
+ for x in reversed(chain):
+ descendant_fees += mempool[x]['fee']
+ assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees+1000)
+
# Adding one more transaction on to the chain should fail.
try:
self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
except JSONRPCException as e:
print "too-long-ancestor-chain successfully rejected"
+ # Check that prioritising a tx before it's added to the mempool works
+ self.nodes[0].generate(1)
+ self.nodes[0].prioritisetransaction(chain[-1], 0, 2000)
+ self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
+ mempool = self.nodes[0].getrawmempool(True)
+
+ descendant_fees = 0
+ for x in reversed(chain):
+ descendant_fees += mempool[x]['fee']
+ if (x == chain[-1]):
+ assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002))
+ assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees+2000)
+
# TODO: check that node1's mempool is as expected
# TODO: test ancestor size limits
diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py
index 9555940cec..a6525e6793 100755
--- a/qa/rpc-tests/p2p-fullblocktest.py
+++ b/qa/rpc-tests/p2p-fullblocktest.py
@@ -7,7 +7,7 @@
from test_framework.test_framework import ComparisonTestFramework
from test_framework.util import *
-from test_framework.comptool import TestManager, TestInstance
+from test_framework.comptool import TestManager, TestInstance, RejectResult
from test_framework.mininode import *
from test_framework.blocktools import *
import logging
@@ -15,7 +15,7 @@ import copy
import time
import numbers
from test_framework.key import CECKey
-from test_framework.script import CScript, CScriptOp, SignatureHash, SIGHASH_ALL, OP_TRUE
+from test_framework.script import CScript, CScriptOp, SignatureHash, SIGHASH_ALL, OP_TRUE, OP_FALSE
class PreviousSpendableOutput(object):
def __init__(self, tx = CTransaction(), n = -1):
@@ -122,13 +122,29 @@ class FullBlockTest(ComparisonTestFramework):
return TestInstance([[self.tip, True]])
# returns a test case that asserts that the current tip was rejected
- def rejected():
- return TestInstance([[self.tip, False]])
+ def rejected(reject = None):
+ if reject is None:
+ return TestInstance([[self.tip, False]])
+ else:
+ return TestInstance([[self.tip, reject]])
# move the tip back to a previous block
def tip(number):
self.tip = self.blocks[number]
+ # add transactions to a block produced by next_block
+ def update_block(block_number, new_transactions):
+ block = self.blocks[block_number]
+ old_hash = block.sha256
+ self.add_transactions_to_block(block, new_transactions)
+ block.solve()
+ # Update the internal state just like in next_block
+ self.tip = block
+ self.block_heights[block.sha256] = self.block_heights[old_hash]
+ del self.block_heights[old_hash]
+ self.blocks[block_number] = block
+ return block
+
# creates a new block and advances the tip to that block
block = self.next_block
@@ -141,14 +157,15 @@ class FullBlockTest(ComparisonTestFramework):
# Now we need that block to mature so we can spend the coinbase.
test = TestInstance(sync_every_block=False)
- for i in range(100):
+ for i in range(99):
block(1000 + i)
test.blocks_and_transactions.append([self.tip, True])
save_spendable_output()
yield test
- # Start by bulding a couple of blocks on top (which output is spent is in parentheses):
+ # Start by building a couple of blocks on top (which output is spent is
+ # in parentheses):
# genesis -> b1 (0) -> b2 (1)
out0 = get_spendable_output()
block(1, spend=out0)
@@ -156,8 +173,7 @@ class FullBlockTest(ComparisonTestFramework):
yield accepted()
out1 = get_spendable_output()
- block(2, spend=out1)
- # Inv again, then deliver twice (shouldn't break anything).
+ b2 = block(2, spend=out1)
yield accepted()
@@ -168,8 +184,8 @@ class FullBlockTest(ComparisonTestFramework):
#
# Nothing should happen at this point. We saw b2 first so it takes priority.
tip(1)
- block(3, spend=out1)
- # Deliver twice (should still not break anything)
+ b3 = block(3, spend=out1)
+ txout_b3 = PreviousSpendableOutput(b3.vtx[1], 1)
yield rejected()
@@ -214,7 +230,7 @@ class FullBlockTest(ComparisonTestFramework):
# \-> b3 (1) -> b4 (2)
tip(6)
block(9, spend=out4, additional_coinbase_value=1)
- yield rejected()
+ yield rejected(RejectResult(16, 'bad-cb-amount'))
# Create a fork that ends in a block with too much fee (the one that causes the reorg)
@@ -226,7 +242,7 @@ class FullBlockTest(ComparisonTestFramework):
yield rejected()
block(11, spend=out4, additional_coinbase_value=1)
- yield rejected()
+ yield rejected(RejectResult(16, 'bad-cb-amount'))
# Try again, but with a valid fork first
@@ -252,6 +268,10 @@ class FullBlockTest(ComparisonTestFramework):
yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13.
+ # Add a block with MAX_BLOCK_SIGOPS and one with one more sigop
+ # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
+ # \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6)
+ # \-> b3 (1) -> b4 (2)
# Test that a block with a lot of checksigs is okay
lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50 - 1))
@@ -264,8 +284,121 @@ class FullBlockTest(ComparisonTestFramework):
out6 = get_spendable_output()
too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 / 50))
block(16, spend=out6, script=too_many_checksigs)
+ yield rejected(RejectResult(16, 'bad-blk-sigops'))
+
+
+ # Attempt to spend a transaction created on a different fork
+ # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
+ # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1])
+ # \-> b3 (1) -> b4 (2)
+ tip(15)
+ block(17, spend=txout_b3)
+ yield rejected(RejectResult(16, 'bad-txns-inputs-missingorspent'))
+
+ # Attempt to spend a transaction created on a different fork (on a fork this time)
+ # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
+ # \-> b12 (3) -> b13 (4) -> b15 (5)
+ # \-> b18 (b3.vtx[1]) -> b19 (6)
+ # \-> b3 (1) -> b4 (2)
+ tip(13)
+ block(18, spend=txout_b3)
+ yield rejected()
+
+ block(19, spend=out6)
yield rejected()
+ # Attempt to spend a coinbase at depth too low
+ # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
+ # \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7)
+ # \-> b3 (1) -> b4 (2)
+ tip(15)
+ out7 = get_spendable_output()
+ block(20, spend=out7)
+ yield rejected(RejectResult(16, 'bad-txns-premature-spend-of-coinbase'))
+
+ # Attempt to spend a coinbase at depth too low (on a fork this time)
+ # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
+ # \-> b12 (3) -> b13 (4) -> b15 (5)
+ # \-> b21 (6) -> b22 (5)
+ # \-> b3 (1) -> b4 (2)
+ tip(13)
+ block(21, spend=out6)
+ yield rejected()
+
+ block(22, spend=out5)
+ yield rejected()
+
+ # Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected
+ # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
+ # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
+ # \-> b24 (6) -> b25 (7)
+ # \-> b3 (1) -> b4 (2)
+ tip(15)
+ b23 = block(23, spend=out6)
+ old_hash = b23.sha256
+ tx = CTransaction()
+ script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69
+ script_output = CScript([chr(0)*script_length])
+ tx.vout.append(CTxOut(0, script_output))
+ tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 1)))
+ b23 = update_block(23, [tx])
+ # Make sure the math above worked out to produce a max-sized block
+ assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE)
+ yield accepted()
+
+ # Make the next block one byte bigger and check that it fails
+ tip(15)
+ b24 = block(24, spend=out6)
+ script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69
+ script_output = CScript([chr(0)*(script_length+1)])
+ tx.vout = [CTxOut(0, script_output)]
+ b24 = update_block(24, [tx])
+ assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1)
+ yield rejected(RejectResult(16, 'bad-blk-length'))
+
+ b25 = block(25, spend=out7)
+ yield rejected()
+
+ # Create blocks with a coinbase input script size out of range
+ # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
+ # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7)
+ # \-> ... (6) -> ... (7)
+ # \-> b3 (1) -> b4 (2)
+ tip(15)
+ b26 = block(26, spend=out6)
+ b26.vtx[0].vin[0].scriptSig = chr(0)
+ b26.vtx[0].rehash()
+ # update_block causes the merkle root to get updated, even with no new
+ # transactions, and updates the required state.
+ b26 = update_block(26, [])
+ yield rejected(RejectResult(16, 'bad-cb-length'))
+
+ # Extend the b26 chain to make sure bitcoind isn't accepting b26
+ b27 = block(27, spend=out7)
+ yield rejected()
+
+ # Now try a too-large-coinbase script
+ tip(15)
+ b28 = block(28, spend=out6)
+ b28.vtx[0].vin[0].scriptSig = chr(0)*101
+ b28.vtx[0].rehash()
+ b28 = update_block(28, [])
+ yield rejected(RejectResult(16, 'bad-cb-length'))
+
+ # Extend the b28 chain to make sure bitcoind isn't accepted b28
+ b29 = block(29, spend=out7)
+ # TODO: Should get a reject message back with "bad-prevblk", except
+ # there's a bug that prevents this from being detected. Just note
+ # failure for now, and add the reject result later.
+ yield rejected()
+
+ # b30 has a max-sized coinbase scriptSig.
+ tip(23)
+ b30 = block(30)
+ b30.vtx[0].vin[0].scriptSig = chr(0)*100
+ b30.vtx[0].rehash()
+ b30 = update_block(30, [])
+ yield accepted()
if __name__ == '__main__':
diff --git a/qa/rpc-tests/prioritise_transaction.py b/qa/rpc-tests/prioritise_transaction.py
index f376ceee5e..4a79d38da0 100755
--- a/qa/rpc-tests/prioritise_transaction.py
+++ b/qa/rpc-tests/prioritise_transaction.py
@@ -15,21 +15,7 @@ COIN = 100000000
class PrioritiseTransactionTest(BitcoinTestFramework):
def __init__(self):
- # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create
- # So we have big transactions (and therefore can't fit very many into each block)
- # create one script_pubkey
- script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes
- for i in xrange (512):
- script_pubkey = script_pubkey + "01"
- # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
- self.txouts = "81"
- for k in xrange(128):
- # add txout value
- self.txouts = self.txouts + "0000000000000000"
- # add length of script_pubkey
- self.txouts = self.txouts + "fd0402"
- # add script_pubkey
- self.txouts = self.txouts + script_pubkey
+ self.txouts = gen_return_txouts()
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
@@ -42,62 +28,15 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-printpriority=1"]))
self.relayfee = self.nodes[0].getnetworkinfo()['relayfee']
- def create_confirmed_utxos(self, count):
- self.nodes[0].generate(int(0.5*count)+101)
- utxos = self.nodes[0].listunspent()
- iterations = count - len(utxos)
- addr1 = self.nodes[0].getnewaddress()
- addr2 = self.nodes[0].getnewaddress()
- if iterations <= 0:
- return utxos
- for i in xrange(iterations):
- t = utxos.pop()
- fee = self.relayfee
- inputs = []
- inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
- outputs = {}
- send_value = t['amount'] - fee
- outputs[addr1] = satoshi_round(send_value/2)
- outputs[addr2] = satoshi_round(send_value/2)
- raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
- signed_tx = self.nodes[0].signrawtransaction(raw_tx)["hex"]
- txid = self.nodes[0].sendrawtransaction(signed_tx)
-
- while (self.nodes[0].getmempoolinfo()['size'] > 0):
- self.nodes[0].generate(1)
-
- utxos = self.nodes[0].listunspent()
- assert(len(utxos) >= count)
- return utxos
-
- def create_lots_of_big_transactions(self, utxos, fee):
- addr = self.nodes[0].getnewaddress()
- txids = []
- for i in xrange(len(utxos)):
- t = utxos.pop()
- inputs = []
- inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
- outputs = {}
- send_value = t['amount'] - fee
- outputs[addr] = satoshi_round(send_value)
- rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
- newtx = rawtx[0:92]
- newtx = newtx + self.txouts
- newtx = newtx + rawtx[94:]
- signresult = self.nodes[0].signrawtransaction(newtx, None, None, "NONE")
- txid = self.nodes[0].sendrawtransaction(signresult["hex"], True)
- txids.append(txid)
- return txids
-
def run_test(self):
- utxos = self.create_confirmed_utxos(90)
+ utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 90)
base_fee = self.relayfee*100 # our transactions are smaller than 100kb
txids = []
# Create 3 batches of transactions at 3 different fee rate levels
for i in xrange(3):
txids.append([])
- txids[i] = self.create_lots_of_big_transactions(utxos[30*i:30*i+30], (i+1)*base_fee)
+ txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], (i+1)*base_fee)
# add a fee delta to something in the cheapest bucket and make sure it gets mined
# also check that a different entry in the cheapest bucket is NOT mined (lower
@@ -143,5 +82,45 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
if (x != high_fee_tx):
assert(x not in mempool)
+ # Create a free, low priority transaction. Should be rejected.
+ utxo_list = self.nodes[0].listunspent()
+ assert(len(utxo_list) > 0)
+ utxo = utxo_list[0]
+
+ inputs = []
+ outputs = {}
+ inputs.append({"txid" : utxo["txid"], "vout" : utxo["vout"]})
+ outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee
+ raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ tx_hex = self.nodes[0].signrawtransaction(raw_tx)["hex"]
+ txid = self.nodes[0].sendrawtransaction(tx_hex)
+
+ # A tx that spends an in-mempool tx has 0 priority, so we can use it to
+ # test the effect of using prioritise transaction for mempool acceptance
+ inputs = []
+ inputs.append({"txid": txid, "vout": 0})
+ outputs = {}
+ outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee
+ raw_tx2 = self.nodes[0].createrawtransaction(inputs, outputs)
+ tx2_hex = self.nodes[0].signrawtransaction(raw_tx2)["hex"]
+ tx2_id = self.nodes[0].decoderawtransaction(tx2_hex)["txid"]
+
+ try:
+ self.nodes[0].sendrawtransaction(tx2_hex)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26) # insufficient fee
+ assert(tx2_id not in self.nodes[0].getrawmempool())
+ else:
+ assert(False)
+
+ # This is a less than 1000-byte transaction, so just set the fee
+ # to be the minimum for a 1000 byte transaction and check that it is
+ # accepted.
+ self.nodes[0].prioritisetransaction(tx2_id, 0, int(self.relayfee*COIN))
+
+ print "Assert that prioritised free transaction is accepted to mempool"
+ assert_equal(self.nodes[0].sendrawtransaction(tx2_hex), tx2_id)
+ assert(tx2_id in self.nodes[0].getrawmempool())
+
if __name__ == '__main__':
PrioritiseTransactionTest().main()
diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py
index 21f8d69382..fbf40f24c3 100755
--- a/qa/rpc-tests/pruning.py
+++ b/qa/rpc-tests/pruning.py
@@ -23,24 +23,7 @@ class PruneTest(BitcoinTestFramework):
def __init__(self):
self.utxo = []
self.address = ["",""]
-
- # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create
- # So we have big transactions and full blocks to fill up our block files
-
- # create one script_pubkey
- script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes
- for i in xrange (512):
- script_pubkey = script_pubkey + "01"
- # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
- self.txouts = "81"
- for k in xrange(128):
- # add txout value
- self.txouts = self.txouts + "0000000000000000"
- # add length of script_pubkey
- self.txouts = self.txouts + "fd0402"
- # add script_pubkey
- self.txouts = self.txouts + script_pubkey
-
+ self.txouts = gen_return_txouts()
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
diff --git a/qa/rpc-tests/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py
index 6e9e0b304c..734db33b51 100755
--- a/qa/rpc-tests/replace-by-fee.py
+++ b/qa/rpc-tests/replace-by-fee.py
@@ -63,8 +63,14 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
# If requested, ensure txouts are confirmed.
if confirmed:
- while len(node.getrawmempool()):
+ mempool_size = len(node.getrawmempool())
+ while mempool_size > 0:
node.generate(1)
+ new_size = len(node.getrawmempool())
+ # Error out if we have something stuck in the mempool, as this
+ # would likely be a bug.
+ assert(new_size < mempool_size)
+ mempool_size = new_size
return COutPoint(int(txid, 16), 0)
@@ -72,7 +78,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def setup_network(self):
self.nodes = []
- self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000",
+ self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug",
"-relaypriority=0", "-whitelist=127.0.0.1",
"-limitancestorcount=50",
"-limitancestorsize=101",
@@ -108,6 +114,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
print "Running test opt-in..."
self.test_opt_in()
+ print "Running test prioritised transactions..."
+ self.test_prioritised_transactions()
+
print "Passed\n"
def test_simple_doublespend(self):
@@ -513,5 +522,72 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# but make sure it is accepted anyway
self.nodes[0].sendrawtransaction(tx3c_hex, True)
+ def test_prioritised_transactions(self):
+ # Ensure that fee deltas used via prioritisetransaction are
+ # correctly used by replacement logic
+
+ # 1. Check that feeperkb uses modified fees
+ tx0_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
+
+ tx1a = CTransaction()
+ tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a_hex = txToHex(tx1a)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+
+ # Higher fee, but the actual fee per KB is much lower.
+ tx1b = CTransaction()
+ tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1b.vout = [CTxOut(0.001*COIN, CScript([b'a'*740000]))]
+ tx1b_hex = txToHex(tx1b)
+
+ # Verify tx1b cannot replace tx1a.
+ try:
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ else:
+ assert(False)
+
+ # Use prioritisetransaction to set tx1a's fee to 0.
+ self.nodes[0].prioritisetransaction(tx1a_txid, 0, int(-0.1*COIN))
+
+ # Now tx1b should be able to replace tx1a
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+
+ assert(tx1b_txid in self.nodes[0].getrawmempool())
+
+ # 2. Check that absolute fee checks use modified fee.
+ tx1_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
+
+ tx2a = CTransaction()
+ tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)]
+ tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx2a_hex = txToHex(tx2a)
+ tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True)
+
+ # Lower fee, but we'll prioritise it
+ tx2b = CTransaction()
+ tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
+ tx2b.vout = [CTxOut(1.01*COIN, CScript([b'a']))]
+ tx2b.rehash()
+ tx2b_hex = txToHex(tx2b)
+
+ # Verify tx2b cannot replace tx2a.
+ try:
+ tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ else:
+ assert(False)
+
+ # Now prioritise tx2b to have a higher modified fee
+ self.nodes[0].prioritisetransaction(tx2b.hash, 0, int(0.1*COIN))
+
+ # tx2b should now be accepted
+ tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
+
+ assert(tx2b_txid in self.nodes[0].getrawmempool())
+
if __name__ == '__main__':
ReplaceByFeeTest().main()
diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py
index 9444424dcf..badbc0a1fb 100755
--- a/qa/rpc-tests/test_framework/comptool.py
+++ b/qa/rpc-tests/test_framework/comptool.py
@@ -41,6 +41,20 @@ def wait_until(predicate, attempts=float('inf'), timeout=float('inf')):
return False
+class RejectResult(object):
+ '''
+ Outcome that expects rejection of a transaction or block.
+ '''
+ def __init__(self, code, reason=''):
+ self.code = code
+ self.reason = reason
+ def match(self, other):
+ if self.code != other.code:
+ return False
+ return other.reason.startswith(self.reason)
+ def __repr__(self):
+ return '%i:%s' % (self.code,self.reason or '*')
+
class TestNode(NodeConnCB):
def __init__(self, block_store, tx_store):
@@ -51,6 +65,8 @@ class TestNode(NodeConnCB):
self.block_request_map = {}
self.tx_store = tx_store
self.tx_request_map = {}
+ self.block_reject_map = {}
+ self.tx_reject_map = {}
# When the pingmap is non-empty we're waiting for
# a response
@@ -94,6 +110,12 @@ class TestNode(NodeConnCB):
except KeyError:
raise AssertionError("Got pong for unknown ping [%s]" % repr(message))
+ def on_reject(self, conn, message):
+ if message.message == 'tx':
+ self.tx_reject_map[message.data] = RejectResult(message.code, message.reason)
+ if message.message == 'block':
+ self.block_reject_map[message.data] = RejectResult(message.code, message.reason)
+
def send_inv(self, obj):
mtype = 2 if isinstance(obj, CBlock) else 1
self.conn.send_message(msg_inv([CInv(mtype, obj.sha256)]))
@@ -243,6 +265,15 @@ class TestManager(object):
if outcome is None:
if c.cb.bestblockhash != self.connections[0].cb.bestblockhash:
return False
+ elif isinstance(outcome, RejectResult): # Check that block was rejected w/ code
+ if c.cb.bestblockhash == blockhash:
+ return False
+ if blockhash not in c.cb.block_reject_map:
+ print 'Block not in reject map: %064x' % (blockhash)
+ return False
+ if not outcome.match(c.cb.block_reject_map[blockhash]):
+ print 'Block rejected with %s instead of expected %s: %064x' % (c.cb.block_reject_map[blockhash], outcome, blockhash)
+ return False
elif ((c.cb.bestblockhash == blockhash) != outcome):
# print c.cb.bestblockhash, blockhash, outcome
return False
@@ -262,6 +293,15 @@ class TestManager(object):
if c.cb.lastInv != self.connections[0].cb.lastInv:
# print c.rpc.getrawmempool()
return False
+ elif isinstance(outcome, RejectResult): # Check that tx was rejected w/ code
+ if txhash in c.cb.lastInv:
+ return False
+ if txhash not in c.cb.tx_reject_map:
+ print 'Tx not in reject map: %064x' % (txhash)
+ return False
+ if not outcome.match(c.cb.tx_reject_map[txhash]):
+ print 'Tx rejected with %s instead of expected %s: %064x' % (c.cb.tx_reject_map[txhash], outcome, txhash)
+ return False
elif ((txhash in c.cb.lastInv) != outcome):
# print c.rpc.getrawmempool(), c.cb.lastInv
return False
diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py
index 9d0fb713a1..8e49b56565 100755
--- a/qa/rpc-tests/test_framework/mininode.py
+++ b/qa/rpc-tests/test_framework/mininode.py
@@ -36,6 +36,7 @@ MY_VERSION = 60001 # past bip-31 for ping/pong
MY_SUBVERSION = "/python-mininode-tester:0.0.1/"
MAX_INV_SZ = 50000
+MAX_BLOCK_SIZE = 1000000
# Keep our own socket map for asyncore, so that we can track disconnects
# ourselves (to workaround an issue with closing an asyncore socket when
diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py
index 0a78cf6fb1..0088876028 100644
--- a/qa/rpc-tests/test_framework/script.py
+++ b/qa/rpc-tests/test_framework/script.py
@@ -226,7 +226,7 @@ OP_CHECKMULTISIGVERIFY = CScriptOp(0xaf)
# expansion
OP_NOP1 = CScriptOp(0xb0)
-OP_NOP2 = CScriptOp(0xb1)
+OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xb1)
OP_NOP3 = CScriptOp(0xb2)
OP_NOP4 = CScriptOp(0xb3)
OP_NOP5 = CScriptOp(0xb4)
@@ -353,7 +353,7 @@ VALID_OPCODES = {
OP_CHECKMULTISIGVERIFY,
OP_NOP1,
- OP_NOP2,
+ OP_CHECKLOCKTIMEVERIFY,
OP_NOP3,
OP_NOP4,
OP_NOP5,
@@ -472,7 +472,7 @@ OPCODE_NAMES.update({
OP_CHECKMULTISIG : 'OP_CHECKMULTISIG',
OP_CHECKMULTISIGVERIFY : 'OP_CHECKMULTISIGVERIFY',
OP_NOP1 : 'OP_NOP1',
- OP_NOP2 : 'OP_NOP2',
+ OP_CHECKLOCKTIMEVERIFY : 'OP_CHECKLOCKTIMEVERIFY',
OP_NOP3 : 'OP_NOP3',
OP_NOP4 : 'OP_NOP4',
OP_NOP5 : 'OP_NOP5',
@@ -591,7 +591,7 @@ OPCODES_BY_NAME = {
'OP_CHECKMULTISIG' : OP_CHECKMULTISIG,
'OP_CHECKMULTISIGVERIFY' : OP_CHECKMULTISIGVERIFY,
'OP_NOP1' : OP_NOP1,
- 'OP_NOP2' : OP_NOP2,
+ 'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY,
'OP_NOP3' : OP_NOP3,
'OP_NOP4' : OP_NOP4,
'OP_NOP5' : OP_NOP5,
diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py
index ae2d91ab60..86d2f06df7 100755
--- a/qa/rpc-tests/test_framework/test_framework.py
+++ b/qa/rpc-tests/test_framework/test_framework.py
@@ -120,7 +120,7 @@ class BitcoinTestFramework(object):
if self.options.coveragedir:
enable_coverage(self.options.coveragedir)
- os.environ['PATH'] = self.options.srcdir+":"+os.environ['PATH']
+ os.environ['PATH'] = self.options.srcdir+":"+self.options.srcdir+"/qt:"+os.environ['PATH']
check_json_precision()
diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py
index 72df3ae685..83216fc737 100644
--- a/qa/rpc-tests/test_framework/util.py
+++ b/qa/rpc-tests/test_framework/util.py
@@ -131,7 +131,7 @@ def initialize_chain(test_dir):
# Create cache directories, run bitcoinds:
for i in range(4):
datadir=initialize_datadir("cache", i)
- args = [ os.getenv("BITCOIND", "bitcoind"), "-keypool=1", "-datadir="+datadir, "-discover=0" ]
+ args = [ os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ]
if i > 0:
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
bitcoind_processes[i] = subprocess.Popen(args)
@@ -219,7 +219,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
if binary is None:
binary = os.getenv("BITCOIND", "bitcoind")
# RPC tests still depend on free transactions
- args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest", "-blockprioritysize=50000" ]
+ args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-blockprioritysize=50000" ]
if extra_args is not None: args.extend(extra_args)
bitcoind_processes[i] = subprocess.Popen(args)
devnull = open(os.devnull, "w")
@@ -409,3 +409,67 @@ def assert_raises(exc, fun, *args, **kwds):
def satoshi_round(amount):
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
+
+def create_confirmed_utxos(fee, node, count):
+ node.generate(int(0.5*count)+101)
+ utxos = node.listunspent()
+ iterations = count - len(utxos)
+ addr1 = node.getnewaddress()
+ addr2 = node.getnewaddress()
+ if iterations <= 0:
+ return utxos
+ for i in xrange(iterations):
+ t = utxos.pop()
+ inputs = []
+ inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
+ outputs = {}
+ send_value = t['amount'] - fee
+ outputs[addr1] = satoshi_round(send_value/2)
+ outputs[addr2] = satoshi_round(send_value/2)
+ raw_tx = node.createrawtransaction(inputs, outputs)
+ signed_tx = node.signrawtransaction(raw_tx)["hex"]
+ txid = node.sendrawtransaction(signed_tx)
+
+ while (node.getmempoolinfo()['size'] > 0):
+ node.generate(1)
+
+ utxos = node.listunspent()
+ assert(len(utxos) >= count)
+ return utxos
+
+def gen_return_txouts():
+ # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create
+ # So we have big transactions (and therefore can't fit very many into each block)
+ # create one script_pubkey
+ script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes
+ for i in xrange (512):
+ script_pubkey = script_pubkey + "01"
+ # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
+ txouts = "81"
+ for k in xrange(128):
+ # add txout value
+ txouts = txouts + "0000000000000000"
+ # add length of script_pubkey
+ txouts = txouts + "fd0402"
+ # add script_pubkey
+ txouts = txouts + script_pubkey
+ return txouts
+
+def create_lots_of_big_transactions(node, txouts, utxos, fee):
+ addr = node.getnewaddress()
+ txids = []
+ for i in xrange(len(utxos)):
+ t = utxos.pop()
+ inputs = []
+ inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
+ outputs = {}
+ send_value = t['amount'] - fee
+ outputs[addr] = satoshi_round(send_value)
+ rawtx = node.createrawtransaction(inputs, outputs)
+ newtx = rawtx[0:92]
+ newtx = newtx + txouts
+ newtx = newtx + rawtx[94:]
+ signresult = node.signrawtransaction(newtx, None, None, "NONE")
+ txid = node.sendrawtransaction(signresult["hex"], True)
+ txids.append(txid)
+ return txids