aboutsummaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa')
-rwxr-xr-xqa/pull-tester/rpc-tests.py1
-rwxr-xr-xqa/rpc-tests/abandonconflict.py153
-rwxr-xr-xqa/rpc-tests/fundrawtransaction.py9
-rwxr-xr-xqa/rpc-tests/mempool_limit.py2
-rwxr-xr-xqa/rpc-tests/sendheaders.py18
-rwxr-xr-xqa/rpc-tests/test_framework/mininode.py15
6 files changed, 189 insertions, 9 deletions
diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py
index 669c508ccd..e7173fda08 100755
--- a/qa/pull-tester/rpc-tests.py
+++ b/qa/pull-tester/rpc-tests.py
@@ -105,6 +105,7 @@ testScripts = [
'prioritise_transaction.py',
'invalidblockrequest.py',
'invalidtxrequest.py',
+ 'abandonconflict.py',
]
testScriptsExt = [
'bip65-cltv.py',
diff --git a/qa/rpc-tests/abandonconflict.py b/qa/rpc-tests/abandonconflict.py
new file mode 100755
index 0000000000..38028df079
--- /dev/null
+++ b/qa/rpc-tests/abandonconflict.py
@@ -0,0 +1,153 @@
+#!/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.
+
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+try:
+ import urllib.parse as urlparse
+except ImportError:
+ import urlparse
+
+class AbandonConflictTest(BitcoinTestFramework):
+
+ def setup_network(self):
+ self.nodes = []
+ self.nodes.append(start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.00001"]))
+ self.nodes.append(start_node(1, self.options.tmpdir, ["-debug","-logtimemicros"]))
+ connect_nodes(self.nodes[0], 1)
+
+ def run_test(self):
+ self.nodes[1].generate(100)
+ sync_blocks(self.nodes)
+ balance = self.nodes[0].getbalance()
+ txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ txB = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ txC = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
+ sync_mempools(self.nodes)
+ self.nodes[1].generate(1)
+
+ sync_blocks(self.nodes)
+ newbalance = self.nodes[0].getbalance()
+ assert(balance - newbalance < Decimal("0.001")) #no more than fees lost
+ balance = newbalance
+
+ url = urlparse.urlparse(self.nodes[1].url)
+ self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1)))
+
+ # Identify the 10btc outputs
+ nA = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txA, 1)["vout"]) if vout["value"] == Decimal("10"))
+ nB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txB, 1)["vout"]) if vout["value"] == Decimal("10"))
+ nC = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txC, 1)["vout"]) if vout["value"] == Decimal("10"))
+
+ inputs =[]
+ # spend 10btc outputs from txA and txB
+ inputs.append({"txid":txA, "vout":nA})
+ inputs.append({"txid":txB, "vout":nB})
+ outputs = {}
+
+ outputs[self.nodes[0].getnewaddress()] = Decimal("14.99998")
+ outputs[self.nodes[1].getnewaddress()] = Decimal("5")
+ signed = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs))
+ txAB1 = self.nodes[0].sendrawtransaction(signed["hex"])
+
+ # Identify the 14.99998btc output
+ nAB = next(i for i, vout in enumerate(self.nodes[0].getrawtransaction(txAB1, 1)["vout"]) if vout["value"] == Decimal("14.99998"))
+
+ #Create a child tx spending AB1 and C
+ inputs = []
+ inputs.append({"txid":txAB1, "vout":nAB})
+ inputs.append({"txid":txC, "vout":nC})
+ outputs = {}
+ outputs[self.nodes[0].getnewaddress()] = Decimal("24.9996")
+ signed2 = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs))
+ txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"])
+
+ # In mempool txs from self should increase balance from change
+ newbalance = self.nodes[0].getbalance()
+ assert(newbalance == balance - Decimal("30") + Decimal("24.9996"))
+ balance = newbalance
+
+ # Restart the node with a higher min relay fee so the parent tx is no longer in mempool
+ # TODO: redo with eviction
+ # Note had to make sure tx did not have AllowFree priority
+ stop_node(self.nodes[0],0)
+ self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"])
+
+ # Verify txs no longer in mempool
+ assert(len(self.nodes[0].getrawmempool()) == 0)
+
+ # Not in mempool txs from self should only reduce balance
+ # inputs are still spent, but change not received
+ newbalance = self.nodes[0].getbalance()
+ assert(newbalance == balance - Decimal("24.9996"))
+ balance = newbalance
+
+ # Abandon original transaction and verify inputs are available again
+ # including that the child tx was also abandoned
+ self.nodes[0].abandontransaction(txAB1)
+ newbalance = self.nodes[0].getbalance()
+ assert(newbalance == balance + Decimal("30"))
+ balance = newbalance
+
+ # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
+ stop_node(self.nodes[0],0)
+ self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.00001"])
+ assert(len(self.nodes[0].getrawmempool()) == 0)
+ assert(self.nodes[0].getbalance() == balance)
+
+ # But if its received again then it is unabandoned
+ # And since now in mempool, the change is available
+ # But its child tx remains abandoned
+ self.nodes[0].sendrawtransaction(signed["hex"])
+ newbalance = self.nodes[0].getbalance()
+ assert(newbalance == balance - Decimal("20") + Decimal("14.99998"))
+ balance = newbalance
+
+ # Send child tx again so its unabandoned
+ self.nodes[0].sendrawtransaction(signed2["hex"])
+ newbalance = self.nodes[0].getbalance()
+ assert(newbalance == balance - Decimal("10") - Decimal("14.99998") + Decimal("24.9996"))
+ balance = newbalance
+
+ # Remove using high relay fee again
+ stop_node(self.nodes[0],0)
+ self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-logtimemicros","-minrelaytxfee=0.0001"])
+ assert(len(self.nodes[0].getrawmempool()) == 0)
+ newbalance = self.nodes[0].getbalance()
+ assert(newbalance == balance - Decimal("24.9996"))
+ balance = newbalance
+
+ # Create a double spend of AB1 by spending again from only A's 10 output
+ # Mine double spend from node 1
+ inputs =[]
+ inputs.append({"txid":txA, "vout":nA})
+ outputs = {}
+ outputs[self.nodes[1].getnewaddress()] = Decimal("9.9999")
+ tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ signed = self.nodes[0].signrawtransaction(tx)
+ self.nodes[1].sendrawtransaction(signed["hex"])
+ self.nodes[1].generate(1)
+
+ connect_nodes(self.nodes[0], 1)
+ sync_blocks(self.nodes)
+
+ # Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted
+ newbalance = self.nodes[0].getbalance()
+ assert(newbalance == balance + Decimal("20"))
+ balance = newbalance
+
+ # There is currently a minor bug around this and so this test doesn't work. See Issue #7315
+ # Invalidate the block with the double spend and B's 10 BTC output should no longer be available
+ # Don't think C's should either
+ self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
+ newbalance = self.nodes[0].getbalance()
+ #assert(newbalance == balance - Decimal("10"))
+ print "If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer"
+ print "conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315"
+ print balance , " -> " , newbalance , " ?"
+
+if __name__ == '__main__':
+ AbandonConflictTest().main()
diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py
index d6493dbb8a..dda9166151 100755
--- a/qa/rpc-tests/fundrawtransaction.py
+++ b/qa/rpc-tests/fundrawtransaction.py
@@ -30,6 +30,11 @@ class RawTransactionsTest(BitcoinTestFramework):
print "Mining blocks..."
min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
+ # This test is not meant to test fee estimation and we'd like
+ # to be sure all txs are sent at a consistent desired feerate
+ for node in self.nodes:
+ node.settxfee(min_relay_tx_fee)
+
# if the fee's positive delta is higher than this value tests will fail,
# neg. delta always fail the tests.
# The size of the signature of every input may be at most 2 bytes larger
@@ -447,6 +452,10 @@ class RawTransactionsTest(BitcoinTestFramework):
wait_bitcoinds()
self.nodes = start_nodes(4, self.options.tmpdir)
+ # This test is not meant to test fee estimation and we'd like
+ # to be sure all txs are sent at a consistent desired feerate
+ for node in self.nodes:
+ node.settxfee(min_relay_tx_fee)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
diff --git a/qa/rpc-tests/mempool_limit.py b/qa/rpc-tests/mempool_limit.py
index 3ba17ac4f1..a8cf6360ee 100755
--- a/qa/rpc-tests/mempool_limit.py
+++ b/qa/rpc-tests/mempool_limit.py
@@ -33,7 +33,9 @@ class MempoolLimitTest(BitcoinTestFramework):
inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}]
outputs = {self.nodes[0].getnewaddress() : 0.0001}
tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ self.nodes[0].settxfee(self.relayfee) # specifically fund this tx with low fee
txF = self.nodes[0].fundrawtransaction(tx)
+ self.nodes[0].settxfee(0) # return to automatic fee selection
txFS = self.nodes[0].signrawtransaction(txF['hex'])
txid = self.nodes[0].sendrawtransaction(txFS['hex'])
self.nodes[0].lockunspent(True, [us0])
diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py
index e6e26dbce3..7572bc2776 100755
--- a/qa/rpc-tests/sendheaders.py
+++ b/qa/rpc-tests/sendheaders.py
@@ -220,18 +220,20 @@ class SendHeadersTest(BitcoinTestFramework):
# mine count blocks and return the new tip
def mine_blocks(self, count):
+ # Clear out last block announcement from each p2p listener
+ [ x.clear_last_announcement() for x in self.p2p_connections ]
self.nodes[0].generate(count)
return int(self.nodes[0].getbestblockhash(), 16)
# mine a reorg that invalidates length blocks (replacing them with
# length+1 blocks).
- # peers is the p2p nodes we're using; we clear their state after the
+ # Note: we clear the state of our p2p connections after the
# to-be-reorged-out blocks are mined, so that we don't break later tests.
# return the list of block hashes newly mined
- def mine_reorg(self, length, peers):
+ def mine_reorg(self, length):
self.nodes[0].generate(length) # make sure all invalidated blocks are node0's
sync_blocks(self.nodes, wait=0.1)
- [x.clear_last_announcement() for x in peers]
+ [x.clear_last_announcement() for x in self.p2p_connections]
tip_height = self.nodes[1].getblockcount()
hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1))
@@ -245,6 +247,8 @@ class SendHeadersTest(BitcoinTestFramework):
inv_node = InvNode()
test_node = TestNode()
+ self.p2p_connections = [inv_node, test_node]
+
connections = []
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node))
# Set nServices to 0 for test_node, so no block download will occur outside of
@@ -303,7 +307,6 @@ class SendHeadersTest(BitcoinTestFramework):
prev_tip = int(self.nodes[0].getbestblockhash(), 16)
test_node.get_headers(locator=[prev_tip], hashstop=0L)
test_node.sync_with_ping()
- test_node.clear_last_announcement() # Clear out empty headers response
# Now that we've synced headers, headers announcements should work
tip = self.mine_blocks(1)
@@ -352,8 +355,6 @@ class SendHeadersTest(BitcoinTestFramework):
# broadcast it)
assert_equal(inv_node.last_inv, None)
assert_equal(inv_node.last_headers, None)
- inv_node.clear_last_announcement()
- test_node.clear_last_announcement()
tip = self.mine_blocks(1)
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
assert_equal(test_node.check_last_announcement(headers=[tip]), True)
@@ -368,7 +369,7 @@ class SendHeadersTest(BitcoinTestFramework):
# getheaders or inv from peer.
for j in xrange(2):
# First try mining a reorg that can propagate with header announcement
- new_block_hashes = self.mine_reorg(length=7, peers=[test_node, inv_node])
+ new_block_hashes = self.mine_reorg(length=7)
tip = new_block_hashes[-1]
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True)
@@ -376,7 +377,7 @@ class SendHeadersTest(BitcoinTestFramework):
block_time += 8
# Mine a too-large reorg, which should be announced with a single inv
- new_block_hashes = self.mine_reorg(length=8, peers=[test_node, inv_node])
+ new_block_hashes = self.mine_reorg(length=8)
tip = new_block_hashes[-1]
assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
assert_equal(test_node.check_last_announcement(inv=[tip]), True)
@@ -407,7 +408,6 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
test_node.get_data([tip])
test_node.wait_for_block(tip)
- test_node.clear_last_announcement()
elif i == 2:
test_node.get_data([tip])
test_node.wait_for_block(tip)
diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py
index 8e49b56565..ca65fb6e79 100755
--- a/qa/rpc-tests/test_framework/mininode.py
+++ b/qa/rpc-tests/test_framework/mininode.py
@@ -1004,6 +1004,18 @@ class msg_reject(object):
class NodeConnCB(object):
def __init__(self):
self.verack_received = False
+ # deliver_sleep_time is helpful for debugging race conditions in p2p
+ # tests; it causes message delivery to sleep for the specified time
+ # before acquiring the global lock and delivering the next message.
+ self.deliver_sleep_time = None
+
+ def set_deliver_sleep_time(self, value):
+ with mininode_lock:
+ self.deliver_sleep_time = value
+
+ def get_deliver_sleep_time(self):
+ with mininode_lock:
+ return self.deliver_sleep_time
# Spin until verack message is received from the node.
# Tests may want to use this as a signal that the test can begin.
@@ -1017,6 +1029,9 @@ class NodeConnCB(object):
time.sleep(0.05)
def deliver(self, conn, message):
+ deliver_sleep = self.get_deliver_sleep_time()
+ if deliver_sleep is not None:
+ time.sleep(deliver_sleep)
with mininode_lock:
try:
getattr(self, 'on_' + message.command)(conn, message)