aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xqa/pull-tester/rpc-tests.sh2
-rwxr-xr-xqa/rpc-tests/txn_doublespend.py120
-rwxr-xr-xqa/rpc-tests/txnmall.sh152
3 files changed, 122 insertions, 152 deletions
diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh
index 0b5ad20642..cc24a67c08 100755
--- a/qa/pull-tester/rpc-tests.sh
+++ b/qa/pull-tester/rpc-tests.sh
@@ -18,6 +18,8 @@ fi
if [ "x${ENABLE_BITCOIND}${ENABLE_UTILS}${ENABLE_WALLET}" = "x111" ]; then
${BUILDDIR}/qa/rpc-tests/wallet.sh "${BUILDDIR}/src"
${BUILDDIR}/qa/rpc-tests/listtransactions.py --srcdir "${BUILDDIR}/src"
+ ${BUILDDIR}/qa/rpc-tests/txn_doublespend.py --srcdir "${BUILDDIR}/src"
+ ${BUILDDIR}/qa/rpc-tests/txn_doublespend.py --mineblock --srcdir "${BUILDDIR}/src"
#${BUILDDIR}/qa/rpc-tests/forknotify.py --srcdir "${BUILDDIR}/src"
else
echo "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled"
diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py
new file mode 100755
index 0000000000..6125147ebc
--- /dev/null
+++ b/qa/rpc-tests/txn_doublespend.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 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 proper accounting with malleable transactions
+#
+
+from test_framework import BitcoinTestFramework
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+from decimal import Decimal
+from util import *
+import os
+import shutil
+
+class TxnMallTest(BitcoinTestFramework):
+
+ def add_options(self, parser):
+ parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true",
+ help="Test double-spend of 1-confirmed transaction")
+
+ def setup_network(self):
+ # Start with split network:
+ return super(TxnMallTest, self).setup_network(True)
+
+ def run_test(self):
+ # All nodes should start with 1,250 BTC:
+ starting_balance = 1250
+ for i in range(4):
+ assert_equal(self.nodes[i].getbalance(), starting_balance)
+ self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
+
+ # Assign coins to foo and bar accounts:
+ self.nodes[0].move("", "foo", 1220)
+ self.nodes[0].move("", "bar", 30)
+ assert_equal(self.nodes[0].getbalance(""), 0)
+
+ # Coins are sent to node1_address
+ node1_address = self.nodes[1].getnewaddress("from0")
+
+ # First: use raw transaction API to send 1210 BTC to node1_address,
+ # but don't broadcast:
+ (total_in, inputs) = gather_inputs(self.nodes[0], 1210)
+ change_address = self.nodes[0].getnewaddress("foo")
+ outputs = {}
+ outputs[change_address] = 40
+ outputs[node1_address] = 1210
+ rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
+ doublespend = self.nodes[0].signrawtransaction(rawtx)
+ assert_equal(doublespend["complete"], True)
+
+ # Create two transaction from node[0] to node[1]; the
+ # second must spend change from the first because the first
+ # spends all mature inputs:
+ txid1 = self.nodes[0].sendfrom("foo", node1_address, 1210, 0)
+ txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0)
+
+ # Have node0 mine a block:
+ if (self.options.mine_block):
+ self.nodes[0].setgenerate(True, 1)
+ sync_blocks(self.nodes[0:2])
+
+ tx1 = self.nodes[0].gettransaction(txid1)
+ tx2 = self.nodes[0].gettransaction(txid2)
+
+ # Node0's balance should be starting balance, plus 50BTC for another
+ # matured block, minus 1210, minus 20, and minus transaction fees:
+ expected = starting_balance
+ if self.options.mine_block: expected += 50
+ expected += tx1["amount"] + tx1["fee"]
+ expected += tx2["amount"] + tx2["fee"]
+ assert_equal(self.nodes[0].getbalance(), expected)
+
+ # foo and bar accounts should be debited:
+ assert_equal(self.nodes[0].getbalance("foo"), 1220+tx1["amount"]+tx1["fee"])
+ assert_equal(self.nodes[0].getbalance("bar"), 30+tx2["amount"]+tx2["fee"])
+
+ if self.options.mine_block:
+ assert_equal(tx1["confirmations"], 1)
+ assert_equal(tx2["confirmations"], 1)
+ # Node1's "from0" balance should be both transaction amounts:
+ assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"]+tx2["amount"]))
+ else:
+ assert_equal(tx1["confirmations"], 0)
+ assert_equal(tx2["confirmations"], 0)
+
+ # Now give doublespend to miner:
+ mutated_txid = self.nodes[2].sendrawtransaction(doublespend["hex"])
+ # ... mine a block...
+ self.nodes[2].setgenerate(True, 1)
+
+ # Reconnect the split network, and sync chain:
+ connect_nodes(self.nodes[1], 2)
+ self.nodes[2].setgenerate(True, 1) # Mine another block to make sure we sync
+ sync_blocks(self.nodes)
+
+ # Re-fetch transaction info:
+ tx1 = self.nodes[0].gettransaction(txid1)
+ tx2 = self.nodes[0].gettransaction(txid2)
+
+ # Both transactions should be conflicted
+ assert_equal(tx1["confirmations"], -1)
+ assert_equal(tx2["confirmations"], -1)
+
+ # Node0's total balance should be starting balance, plus 100BTC for
+ # two more matured blocks, minus 1210 for the double-spend:
+ expected = starting_balance + 100 - 1210
+ assert_equal(self.nodes[0].getbalance(), expected)
+ assert_equal(self.nodes[0].getbalance("*"), expected)
+
+ # foo account should be debited, but bar account should not:
+ assert_equal(self.nodes[0].getbalance("foo"), 1220-1210)
+ assert_equal(self.nodes[0].getbalance("bar"), 30)
+
+ # Node1's "from" account balance should be just the mutated send:
+ assert_equal(self.nodes[1].getbalance("from0"), 1210)
+
+if __name__ == '__main__':
+ TxnMallTest().main()
diff --git a/qa/rpc-tests/txnmall.sh b/qa/rpc-tests/txnmall.sh
deleted file mode 100755
index 1296d54d92..0000000000
--- a/qa/rpc-tests/txnmall.sh
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2014 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 proper accounting with malleable transactions
-
-if [ $# -lt 1 ]; then
- echo "Usage: $0 path_to_binaries"
- echo "e.g. $0 ../../src"
- echo "Env vars BITCOIND and BITCOINCLI may be used to specify the exact binaries used"
- exit 1
-fi
-
-set -f
-
-BITCOIND=${BITCOIND:-${1}/bitcoind}
-CLI=${BITCOINCLI:-${1}/bitcoin-cli}
-
-DIR="${BASH_SOURCE%/*}"
-SENDANDWAIT="${DIR}/send.sh"
-if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
-. "$DIR/util.sh"
-
-D=$(mktemp -d test.XXXXX)
-
-# Two nodes; one will play the part of merchant, the
-# other an evil transaction-mutating miner.
-
-D1=${D}/node1
-CreateDataDir $D1 port=11000 rpcport=11001
-B1ARGS="-datadir=$D1"
-$BITCOIND $B1ARGS &
-B1PID=$!
-
-D2=${D}/node2
-CreateDataDir $D2 port=11010 rpcport=11011
-B2ARGS="-datadir=$D2"
-$BITCOIND $B2ARGS &
-B2PID=$!
-
-# Wait until both nodes are at the same block number
-function WaitBlocks {
- while :
- do
- sleep 1
- declare -i BLOCKS1=$( GetBlocks $B1ARGS )
- declare -i BLOCKS2=$( GetBlocks $B2ARGS )
- if (( BLOCKS1 == BLOCKS2 ))
- then
- break
- fi
- done
-}
-
-# Wait until node has $N peers
-function WaitPeers {
- while :
- do
- declare -i PEERS=$( $CLI $1 getconnectioncount )
- if (( PEERS == "$2" ))
- then
- break
- fi
- sleep 1
- done
-}
-
-echo "Generating test blockchain..."
-
-# Start with B2 connected to B1:
-$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
-WaitPeers "$B1ARGS" 1
-
-# 1 block, 50 XBT each == 50 XBT
-$CLI $B1ARGS setgenerate true 1
-
-WaitBlocks
-# 100 blocks, 0 mature == 0 XBT
-$CLI $B2ARGS setgenerate true 100
-WaitBlocks
-
-CheckBalance "$B1ARGS" 50
-CheckBalance "$B2ARGS" 0
-
-# restart B2 with no connection
-$CLI $B2ARGS stop > /dev/null 2>&1
-wait $B2PID
-$BITCOIND $B2ARGS &
-B2PID=$!
-
-B2ADDRESS=$( $CLI $B2ARGS getaccountaddress "from1" )
-
-# Have B1 create two transactions; second will
-# spend change from first, since B1 starts with only a single
-# 50 bitcoin output:
-$CLI $B1ARGS move "" "foo" 10.0 > /dev/null
-$CLI $B1ARGS move "" "bar" 10.0 > /dev/null
-TXID1=$( $CLI $B1ARGS sendfrom foo $B2ADDRESS 1.0 0)
-TXID2=$( $CLI $B1ARGS sendfrom bar $B2ADDRESS 2.0 0)
-
-# Mutate TXID1 and add it to B2's memory pool:
-RAWTX1=$( $CLI $B1ARGS getrawtransaction $TXID1 )
-# RAWTX1 is hex-encoded, serialized transaction. So each
-# byte is two characters; we'll prepend the first
-# "push" in the scriptsig with OP_PUSHDATA1 (0x4c),
-# and add one to the length of the signature.
-# Fields are fixed; from the beginning:
-# 4-byte version
-# 1-byte varint number-of inputs (one in this case)
-# 32-byte previous txid
-# 4-byte previous output
-# 1-byte varint length-of-scriptsig
-# 1-byte PUSH this many bytes onto stack
-# ... etc
-# So: to mutate, we want to get byte 41 (hex characters 82-83),
-# increment it, and insert 0x4c after it.
-L=${RAWTX1:82:2}
-NEWLEN=$( printf "%x" $(( 16#$L + 1 )) )
-MUTATEDTX1=${RAWTX1:0:82}${NEWLEN}4c${RAWTX1:84}
-# ... give mutated tx1 to B2:
-MUTATEDTXID=$( $CLI $B2ARGS sendrawtransaction $MUTATEDTX1 )
-
-echo "TXID1: " $TXID1
-echo "Mutated: " $MUTATEDTXID
-
-# Re-connect nodes, and have B2 mine a block
-# containing the mutant:
-$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
-$CLI $B2ARGS setgenerate true 1
-WaitBlocks
-
-# B1 should have 49 BTC; the 2 BTC send is
-# conflicted, and should not count in
-# balances.
-CheckBalance "$B1ARGS" 49
-CheckBalance "$B1ARGS" 49 "*"
-CheckBalance "$B1ARGS" 9 "foo"
-CheckBalance "$B1ARGS" 10 "bar"
-
-# B2 should have 51 BTC
-CheckBalance "$B2ARGS" 51
-CheckBalance "$B2ARGS" 1 "from1"
-
-$CLI $B2ARGS stop > /dev/null 2>&1
-wait $B2PID
-$CLI $B1ARGS stop > /dev/null 2>&1
-wait $B1PID
-
-echo "Tests successful, cleaning up"
-rm -rf $D
-exit 0