aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README21
-rw-r--r--bitcoinrpc/.gitignore2
-rw-r--r--bitcoinrpc/__init__.py0
-rw-r--r--bitcoinrpc/authproxy.py140
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml2
-rw-r--r--contrib/gitian-descriptors/gitian-win.yml2
-rw-r--r--doc/build-osx.md44
-rw-r--r--doc/build-unix.md4
-rw-r--r--doc/release-process.md14
-rw-r--r--jsonrpc/__init__.py2
-rw-r--r--jsonrpc/authproxy.py3
-rw-r--r--jsonrpc/json.py9
-rw-r--r--jsonrpc/proxy.py1
-rw-r--r--qa/rpc-tests/.gitignore2
-rw-r--r--qa/rpc-tests/README.md37
-rwxr-xr-xqa/rpc-tests/conflictedbalance.sh143
-rwxr-xr-xqa/rpc-tests/listtransactions.py151
-rwxr-xr-xqa/rpc-tests/skeleton.py79
-rwxr-xr-xqa/rpc-tests/txnmall.sh58
-rw-r--r--qa/rpc-tests/util.py136
-rw-r--r--qa/rpc-tests/util.sh5
-rwxr-xr-xqa/rpc-tests/wallet.sh39
-rw-r--r--setup.py15
-rw-r--r--share/qt/Info.plist.in8
-rwxr-xr-xshare/qt/clean_mac_info_plist.py29
-rw-r--r--src/key.h4
-rw-r--r--src/main.cpp25
-rw-r--r--src/qt/coincontroldialog.cpp9
-rw-r--r--src/qt/transactionrecord.cpp59
-rw-r--r--src/qt/transactionrecord.h37
-rw-r--r--src/qt/transactiontablemodel.cpp143
-rw-r--r--src/qt/walletmodel.cpp6
-rw-r--r--src/qt/walletmodel.h1
-rw-r--r--src/rpcdump.cpp2
-rw-r--r--src/rpcwallet.cpp7
-rw-r--r--src/tinyformat.h2
-rw-r--r--src/txmempool.cpp17
-rw-r--r--src/txmempool.h6
-rw-r--r--src/wallet.cpp270
-rw-r--r--src/wallet.h125
40 files changed, 1100 insertions, 559 deletions
diff --git a/README b/README
new file mode 100644
index 0000000000..9edd5a2121
--- /dev/null
+++ b/README
@@ -0,0 +1,21 @@
+AuthServiceProxy is an improved version of python-jsonrpc.
+
+It includes the following generic improvements:
+
+- HTTP connections persist for the life of the AuthServiceProxy object
+- sends protocol 'version', per JSON-RPC 1.1
+- sends proper, incrementing 'id'
+- uses standard Python json lib
+
+It also includes the following bitcoin-specific details:
+
+- sends Basic HTTP authentication headers
+- parses all JSON numbers that look like floats as Decimal
+
+Installation:
+
+- change the first line of setup.py to point to the directory of your installation of python 2.*
+- run setup.py
+
+Note: This will only install bitcoinrpc. If you also want to install jsonrpc to preserve
+backwards compatibility, you have to replace 'bitcoinrpc' with 'jsonrpc' in setup.py and run it again.
diff --git a/bitcoinrpc/.gitignore b/bitcoinrpc/.gitignore
new file mode 100644
index 0000000000..2f78cf5b66
--- /dev/null
+++ b/bitcoinrpc/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+
diff --git a/bitcoinrpc/__init__.py b/bitcoinrpc/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/bitcoinrpc/__init__.py
diff --git a/bitcoinrpc/authproxy.py b/bitcoinrpc/authproxy.py
new file mode 100644
index 0000000000..2914477170
--- /dev/null
+++ b/bitcoinrpc/authproxy.py
@@ -0,0 +1,140 @@
+
+"""
+ Copyright 2011 Jeff Garzik
+
+ AuthServiceProxy has the following improvements over python-jsonrpc's
+ ServiceProxy class:
+
+ - HTTP connections persist for the life of the AuthServiceProxy object
+ (if server supports HTTP/1.1)
+ - sends protocol 'version', per JSON-RPC 1.1
+ - sends proper, incrementing 'id'
+ - sends Basic HTTP authentication headers
+ - parses all JSON numbers that look like floats as Decimal
+ - uses standard Python json lib
+
+ Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
+
+ Copyright (c) 2007 Jan-Klaas Kollhof
+
+ This file is part of jsonrpc.
+
+ jsonrpc is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this software; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+try:
+ import http.client as httplib
+except ImportError:
+ import httplib
+import base64
+import json
+import decimal
+try:
+ import urllib.parse as urlparse
+except ImportError:
+ import urlparse
+
+USER_AGENT = "AuthServiceProxy/0.1"
+
+HTTP_TIMEOUT = 30
+
+
+class JSONRPCException(Exception):
+ def __init__(self, rpc_error):
+ Exception.__init__(self)
+ self.error = rpc_error
+
+
+class AuthServiceProxy(object):
+ def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None):
+ self.__service_url = service_url
+ self.__service_name = service_name
+ self.__url = urlparse.urlparse(service_url)
+ if self.__url.port is None:
+ port = 80
+ else:
+ port = self.__url.port
+ self.__id_count = 0
+ (user, passwd) = (self.__url.username, self.__url.password)
+ try:
+ user = user.encode('utf8')
+ except AttributeError:
+ pass
+ try:
+ passwd = passwd.encode('utf8')
+ except AttributeError:
+ pass
+ authpair = user + b':' + passwd
+ self.__auth_header = b'Basic ' + base64.b64encode(authpair)
+
+ if connection:
+ # Callables re-use the connection of the original proxy
+ self.__conn = connection
+ elif self.__url.scheme == 'https':
+ self.__conn = httplib.HTTPSConnection(self.__url.hostname, port,
+ None, None, False,
+ timeout)
+ else:
+ self.__conn = httplib.HTTPConnection(self.__url.hostname, port,
+ False, timeout)
+
+ def __getattr__(self, name):
+ if name.startswith('__') and name.endswith('__'):
+ # Python internal stuff
+ raise AttributeError
+ if self.__service_name is not None:
+ name = "%s.%s" % (self.__service_name, name)
+ return AuthServiceProxy(self.__service_url, name, connection=self.__conn)
+
+ def __call__(self, *args):
+ self.__id_count += 1
+
+ postdata = json.dumps({'version': '1.1',
+ 'method': self.__service_name,
+ 'params': args,
+ 'id': self.__id_count})
+ self.__conn.request('POST', self.__url.path, postdata,
+ {'Host': self.__url.hostname,
+ 'User-Agent': USER_AGENT,
+ 'Authorization': self.__auth_header,
+ 'Content-type': 'application/json'})
+
+ response = self._get_response()
+ if response['error'] is not None:
+ raise JSONRPCException(response['error'])
+ elif 'result' not in response:
+ raise JSONRPCException({
+ 'code': -343, 'message': 'missing JSON-RPC result'})
+ else:
+ return response['result']
+
+ def _batch(self, rpc_call_list):
+ postdata = json.dumps(list(rpc_call_list))
+ self.__conn.request('POST', self.__url.path, postdata,
+ {'Host': self.__url.hostname,
+ 'User-Agent': USER_AGENT,
+ 'Authorization': self.__auth_header,
+ 'Content-type': 'application/json'})
+
+ return self._get_response()
+
+ def _get_response(self):
+ http_response = self.__conn.getresponse()
+ if http_response is None:
+ raise JSONRPCException({
+ 'code': -342, 'message': 'missing HTTP response from server'})
+
+ return json.loads(http_response.read().decode('utf8'),
+ parse_float=decimal.Decimal)
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index bf355fd828..01a5c3c0bf 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -48,7 +48,7 @@ script: |
mkdir -p distsrc
cd distsrc
tar --strip-components=1 -xf ../$DISTNAME
- ./configure --prefix=$STAGING --bindir=$BINDIR --with-protoc-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --disable-dependency-tracking PKG_CONFIG_PATH="$STAGING/lib/pkgconfig" CPPFLAGS="-I$STAGING/include ${OPTFLAGS}" LDFLAGS="-L$STAGING/lib ${OPTFLAGS}" CXXFLAGS="-frandom-seed=bitcoin ${OPTFLAGS}" BOOST_CHRONO_EXTRALIBS="-lrt"
+ ./configure --enable-upnp-default --prefix=$STAGING --bindir=$BINDIR --with-protoc-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --disable-dependency-tracking PKG_CONFIG_PATH="$STAGING/lib/pkgconfig" CPPFLAGS="-I$STAGING/include ${OPTFLAGS}" LDFLAGS="-L$STAGING/lib ${OPTFLAGS}" CXXFLAGS="-frandom-seed=bitcoin ${OPTFLAGS}" BOOST_CHRONO_EXTRALIBS="-lrt"
make $MAKEOPTS
make $MAKEOPTS install-strip
diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml
index 6e43c21823..ecda73e44e 100644
--- a/contrib/gitian-descriptors/gitian-win.yml
+++ b/contrib/gitian-descriptors/gitian-win.yml
@@ -77,7 +77,7 @@ script: |
mkdir -p distsrc
cd distsrc
tar --strip-components=1 -xf $HOME/build/bitcoin/$DISTNAME
- ./configure --bindir=$BINDIR --prefix=$STAGING --host=$HOST --with-qt-plugindir=$STAGING/plugins --with-qt-incdir=$STAGING/include --with-qt-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --with-protoc-bindir=$STAGING/host/bin --disable-dependency-tracking CPPFLAGS="-I$STAGING/include ${OPTFLAGS}" LDFLAGS="-L$STAGING/lib ${OPTFLAGS}" CXXFLAGS="-frandom-seed=bitcoin ${OPTFLAGS}"
+ ./configure --enable-upnp-default --bindir=$BINDIR --prefix=$STAGING --host=$HOST --with-qt-plugindir=$STAGING/plugins --with-qt-incdir=$STAGING/include --with-qt-bindir=$STAGING/host/bin --with-boost=$STAGING --disable-maintainer-mode --with-protoc-bindir=$STAGING/host/bin --disable-dependency-tracking CPPFLAGS="-I$STAGING/include ${OPTFLAGS}" LDFLAGS="-L$STAGING/lib ${OPTFLAGS}" CXXFLAGS="-frandom-seed=bitcoin ${OPTFLAGS}"
export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1
export FAKETIME=$REFERENCE_DATETIME
make $MAKEOPTS
diff --git a/doc/build-osx.md b/doc/build-osx.md
index fcb9a37f3d..faa31cb023 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -5,8 +5,9 @@ This guide will show you how to build bitcoind(headless client) for OSX.
Notes
-----
-* Tested on OS X 10.5 through 10.9 on Intel processors only. PPC is not
-supported because it is big-endian.
+* Tested on OS X 10.6 through 10.9 on 64-bit Intel processors only.
+Older OSX releases or 32-bit processors are no longer supported.
+
* All of the commands should be executed in a Terminal application. The
built-in one is located in `/Applications/Utilities`.
@@ -47,6 +48,10 @@ Installing the dependencies using MacPorts is very straightforward.
sudo port install boost db48@+no_java openssl miniupnpc autoconf pkgconfig automake
+Optional: install Qt4
+
+ sudo port install qt4-mac qrencode protobuf-cpp
+
### Building `bitcoind`
1. Clone the github tree to get the source code and go into the directory.
@@ -54,7 +59,7 @@ Installing the dependencies using MacPorts is very straightforward.
git clone git@github.com:bitcoin/bitcoin.git bitcoin
cd bitcoin
-2. Build bitcoind:
+2. Build bitcoind (and Bitcoin-Qt, if configured):
./autogen.sh
./configure
@@ -102,35 +107,33 @@ Rerunning "openssl version" should now return the correct version.
Creating a release build
------------------------
+You can ignore this section if you are building `bitcoind` for your own use.
-A bitcoind binary is not included in the Bitcoin-Qt.app bundle. You can ignore
-this section if you are building `bitcoind` for your own use.
+bitcoind/bitcoin-cli binaries are not included in the Bitcoin-Qt.app bundle.
-If you are building `bitcoind` for others, your build machine should be set up
+If you are building `bitcoind` or `Bitcoin-Qt` for others, your build machine should be set up
as follows for maximum compatibility:
All dependencies should be compiled with these flags:
- -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk
+ -mmacosx-version-min=10.6
+ -arch x86_64
+ -isysroot $(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk
For MacPorts, that means editing your macports.conf and setting
`macosx_deployment_target` and `build_arch`:
- macosx_deployment_target=10.5
- build_arch=i386
+ macosx_deployment_target=10.6
+ build_arch=x86_64
... and then uninstalling and re-installing, or simply rebuilding, all ports.
As of December 2012, the `boost` port does not obey `macosx_deployment_target`.
Download `http://gavinandresen-bitcoin.s3.amazonaws.com/boost_macports_fix.zip`
-for a fix. Some ports also seem to obey either `build_arch` or
-`macosx_deployment_target`, but not both at the same time. For example, building
-on an OS X 10.6 64-bit machine fails. Official release builds of Bitcoin-Qt are
-compiled on an OS X 10.6 32-bit machine to workaround that problem.
-
-Once dependencies are compiled, creating `Bitcoin-Qt.app` is easy:
+for a fix.
- make -f Makefile.osx RELEASE=1
+Once dependencies are compiled, see release-process.md for how the Bitcoin-Qt.app
+bundle is packaged and signed to create the .dmg disk image that is distributed.
Running
-------
@@ -145,10 +148,13 @@ commands:
chmod 600 "/Users/${USER}/Library/Application Support/Bitcoin/bitcoin.conf"
When next you run it, it will start downloading the blockchain, but it won't
-output anything while it's doing this. This process may take several hours.
+output anything while it's doing this. This process may take several hours;
+you can monitor its process by looking at the debug.log file, like this:
+
+ tail -f $HOME/Library/Application\ Support/Bitcoin/debug.log
Other commands:
- ./bitcoind --help # for a list of command-line options.
./bitcoind -daemon # to start the bitcoin daemon.
- ./bitcoind help # When the daemon is running, to get a list of RPC commands
+ ./bitcoin-cli --help # for a list of command-line options.
+ ./bitcoin-cli help # When the daemon is running, to get a list of RPC commands
diff --git a/doc/build-unix.md b/doc/build-unix.md
index 6f7a022817..c11c0138a4 100644
--- a/doc/build-unix.md
+++ b/doc/build-unix.md
@@ -65,6 +65,10 @@ for Ubuntu 12.04 and later:
sudo apt-get install libboost-all-dev
db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin).
+ You can add the repository using the following command:
+
+ sudo add-apt-repository ppa:bitcoin/bitcoin
+ sudo apt-get update
Ubuntu 12.04 and later have packages for libdb5.1-dev and libdb5.1++-dev,
but using these will break binary wallet compatibility, and is not recommended.
diff --git a/doc/release-process.md b/doc/release-process.md
index 459819e596..095d20f9ea 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -103,21 +103,23 @@ repackage gitian builds for release as stand-alone zip/tar/installer exe
**Perform Mac build:**
- OSX binaries are created by Gavin Andresen on a 32-bit, OSX 10.6 machine.
+ OSX binaries are created by Gavin Andresen on a 64-bit, OSX 10.6 machine.
- qmake RELEASE=1 USE_UPNP=1 USE_QRCODE=1 bitcoin-qt.pro
+ ./autogen.sh
+ SDK=$(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.6.sdk
+ CXXFLAGS="-mmacosx-version-min=10.6 -isysroot $SDK" ./configure --enable-upnp-default
make
export QTDIR=/opt/local/share/qt4 # needed to find translations/qt_*.qm files
T=$(contrib/qt_translations.py $QTDIR/translations src/qt/locale)
- python2.7 share/qt/clean_mac_info_plist.py
- python2.7 contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -add-qt-tr $T -dmg -fancy contrib/macdeploy/fancy.plist
+ export CODESIGNARGS='--keychain ...path_to_keychain --sign "Developer ID Application: BITCOIN FOUNDATION, INC., THE"'
+ python2.7 contrib/macdeploy/macdeployqtplus Bitcoin-Qt.app -sign -add-qt-tr $T -dmg -fancy contrib/macdeploy/fancy.plist
Build output expected: Bitcoin-Qt.dmg
###Next steps:
-* Code-sign Windows -setup.exe (in a Windows virtual machine) and
- OSX Bitcoin-Qt.app (Note: only Gavin has the code-signing keys currently)
+* Code-sign Windows -setup.exe (in a Windows virtual machine using signtool)
+ Note: only Gavin has the code-signing keys currently.
* upload builds to SourceForge
diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py
new file mode 100644
index 0000000000..8441fa3120
--- /dev/null
+++ b/jsonrpc/__init__.py
@@ -0,0 +1,2 @@
+from .json import loads, dumps, JSONEncodeException, JSONDecodeException
+from jsonrpc.proxy import ServiceProxy, JSONRPCException
diff --git a/jsonrpc/authproxy.py b/jsonrpc/authproxy.py
new file mode 100644
index 0000000000..e90ef361d0
--- /dev/null
+++ b/jsonrpc/authproxy.py
@@ -0,0 +1,3 @@
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+
+__all__ = ['AuthServiceProxy', 'JSONRPCException']
diff --git a/jsonrpc/json.py b/jsonrpc/json.py
new file mode 100644
index 0000000000..95398630f7
--- /dev/null
+++ b/jsonrpc/json.py
@@ -0,0 +1,9 @@
+_json = __import__('json')
+loads = _json.loads
+dumps = _json.dumps
+if hasattr(_json, 'JSONEncodeException'):
+ JSONEncodeException = _json.JSONEncodeException
+ JSONDecodeException = _json.JSONDecodeException
+else:
+ JSONEncodeException = TypeError
+ JSONDecodeException = ValueError
diff --git a/jsonrpc/proxy.py b/jsonrpc/proxy.py
new file mode 100644
index 0000000000..0d2be1e93b
--- /dev/null
+++ b/jsonrpc/proxy.py
@@ -0,0 +1 @@
+from bitcoinrpc.authproxy import AuthServiceProxy as ServiceProxy, JSONRPCException
diff --git a/qa/rpc-tests/.gitignore b/qa/rpc-tests/.gitignore
new file mode 100644
index 0000000000..cb41d94423
--- /dev/null
+++ b/qa/rpc-tests/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+cache
diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md
index ee9e8b35ca..835ff11057 100644
--- a/qa/rpc-tests/README.md
+++ b/qa/rpc-tests/README.md
@@ -1,25 +1,36 @@
Regression tests of RPC interface
=================================
-wallet.sh : Exercise wallet send/receive code.
+python-bitcoinrpc: git subtree of https://github.com/jgarzik/python-bitcoinrpc
+Changes to python-bitcoinrpc should be made upstream, and then
+pulled here using git subtree
-walletbackup.sh : Exercise wallet backup / dump / import
+skeleton.py : Copy this to create new regression tests.
-txnmall.sh : Test proper accounting of malleable transactions
+listtransactions.py : Tests for the listtransactions RPC call
+
+util.py : generally useful functions
+Bash-based tests, to be ported to Python:
+-----------------------------------------
+wallet.sh : Exercise wallet send/receive code.
+walletbackup.sh : Exercise wallet backup / dump / import
+txnmall.sh : Test proper accounting of malleable transactions
conflictedbalance.sh : More testing of malleable transaction handling
-util.sh : useful re-usable bash functions
+Notes
+=====
+A 200-block -regtest blockchain and wallets for four nodes
+is created the first time a regression test is run and
+is stored in the cache/ directory. Each node has 25 mature
+blocks (25*50=1250 BTC) in their wallet.
-Tips for creating new tests
-===========================
+After the first run, the cache/ blockchain and wallets are
+copied into a temporary directory and used as the initial
+test state.
-To cleanup after a failed or interrupted test:
+If you get into a bad state, you should be able
+to recover with:
+ rm -rf cache
killall bitcoind
- rm -rf test.*
-
-The most difficult part of writing reproducible tests is
-keeping multiple nodes in sync. See WaitBlocks,
-WaitPeers, and WaitMemPools for how other tests
-deal with this.
diff --git a/qa/rpc-tests/conflictedbalance.sh b/qa/rpc-tests/conflictedbalance.sh
new file mode 100755
index 0000000000..9d854d2d87
--- /dev/null
+++ b/qa/rpc-tests/conflictedbalance.sh
@@ -0,0 +1,143 @@
+#!/usr/bin/env bash
+
+# Test marking of spent outputs
+
+# Create a transaction graph with four transactions,
+# A/B/C/D
+# C spends A
+# D spends B and C
+
+# Then simulate C being mutated, to create C'
+# that is mined.
+# A is still (correctly) considered spent.
+# B should be treated as unspent
+
+if [ $# -lt 1 ]; then
+ echo "Usage: $0 path_to_binaries"
+ echo "e.g. $0 ../../src"
+ exit 1
+fi
+
+set -f
+
+BITCOIND=${1}/bitcoind
+CLI=${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 -debug=mempool"
+$BITCOIND $B1ARGS &
+B1PID=$!
+
+D2=${D}/node2
+CreateDataDir $D2 port=11010 rpcport=11011
+B2ARGS="-datadir=$D2 -debug=mempool"
+$BITCOIND $B2ARGS &
+B2PID=$!
+
+# Wait until all four 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
+
+# 2 block, 50 XBT each == 100 XBT
+# These will be transactions "A" and "B"
+$CLI $B1ARGS setgenerate true 2
+
+WaitBlocks
+# 100 blocks, 0 mature == 0 XBT
+$CLI $B2ARGS setgenerate true 100
+WaitBlocks
+
+CheckBalance "$B1ARGS" 100
+CheckBalance "$B2ARGS" 0
+
+# restart B2 with no connection
+$CLI $B2ARGS stop > /dev/null 2>&1
+wait $B2PID
+$BITCOIND $B2ARGS &
+B2PID=$!
+
+B1ADDRESS=$( $CLI $B1ARGS getnewaddress )
+B2ADDRESS=$( $CLI $B2ARGS getnewaddress )
+
+# Transaction C: send-to-self, spend A
+TXID_C=$( $CLI $B1ARGS sendtoaddress $B1ADDRESS 50.0)
+
+# Transaction D: spends B and C
+TXID_D=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 100.0)
+
+CheckBalance "$B1ARGS" 0
+
+# Mutate TXID_C and add it to B2's memory pool:
+RAWTX_C=$( $CLI $B1ARGS getrawtransaction $TXID_C )
+
+# ... mutate C to create C'
+L=${RAWTX_C:82:2}
+NEWLEN=$( printf "%x" $(( 16#$L + 1 )) )
+MUTATEDTX_C=${RAWTX_C:0:82}${NEWLEN}4c${RAWTX_C:84}
+# ... give mutated tx1 to B2:
+MUTATEDTXID=$( $CLI $B2ARGS sendrawtransaction $MUTATEDTX_C )
+
+echo "TXID_C: " $TXID_C
+echo "Mutated: " $MUTATEDTXID
+
+# Re-connect nodes, and have both nodes mine some blocks:
+$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
+WaitPeers "$B1ARGS" 1
+
+# Having B2 mine the next block puts the mutated
+# transaction C in the chain:
+$CLI $B2ARGS setgenerate true 1
+WaitBlocks
+
+# B1 should still be able to spend 100, because D is conflicted
+# so does not count as a spend of B
+CheckBalance "$B1ARGS" 100
+
+$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
diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py
new file mode 100755
index 0000000000..fec3acfbb3
--- /dev/null
+++ b/qa/rpc-tests/listtransactions.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+
+# Exercise the listtransactions API
+
+# Add python-bitcoinrpc to module search path:
+import os
+import sys
+sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
+
+import json
+import shutil
+import subprocess
+import tempfile
+import traceback
+
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+from util import *
+
+
+def check_array_result(object_array, to_match, expected):
+ """
+ Pass in array of JSON objects, a dictionary with key/value pairs
+ to match against, and another dictionary with expected key/value
+ pairs.
+ """
+ num_matched = 0
+ for item in object_array:
+ all_match = True
+ for key,value in to_match.items():
+ if item[key] != value:
+ all_match = False
+ if not all_match:
+ continue
+ for key,value in expected.items():
+ if item[key] != value:
+ raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
+ num_matched = num_matched+1
+ if num_matched == 0:
+ raise AssertionError("No objects matched %s"%(str(to_match)))
+
+def run_test(nodes):
+ # Simple send, 0 to 1:
+ txid = nodes[0].sendtoaddress(nodes[1].getnewaddress(), 0.1)
+ sync_mempools(nodes)
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid},
+ {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0})
+ check_array_result(nodes[1].listtransactions(),
+ {"txid":txid},
+ {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0})
+ # mine a block, confirmations should change:
+ nodes[0].setgenerate(True, 1)
+ sync_blocks(nodes)
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid},
+ {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1})
+ check_array_result(nodes[1].listtransactions(),
+ {"txid":txid},
+ {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1})
+
+ # send-to-self:
+ txid = nodes[0].sendtoaddress(nodes[0].getnewaddress(), 0.2)
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid, "category":"send"},
+ {"amount":Decimal("-0.2")})
+ check_array_result(nodes[0].listtransactions(),
+ {"txid":txid, "category":"receive"},
+ {"amount":Decimal("0.2")})
+
+ # sendmany from node1: twice to self, twice to node2:
+ send_to = { nodes[0].getnewaddress() : 0.11, nodes[1].getnewaddress() : 0.22,
+ nodes[0].getaccountaddress("from1") : 0.33, nodes[1].getaccountaddress("toself") : 0.44 }
+ txid = nodes[1].sendmany("", send_to)
+ sync_mempools(nodes)
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.11")},
+ {"txid":txid} )
+ check_array_result(nodes[0].listtransactions(),
+ {"category":"receive","amount":Decimal("0.11")},
+ {"txid":txid} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.22")},
+ {"txid":txid} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"receive","amount":Decimal("0.22")},
+ {"txid":txid} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.33")},
+ {"txid":txid} )
+ check_array_result(nodes[0].listtransactions(),
+ {"category":"receive","amount":Decimal("0.33")},
+ {"txid":txid, "account" : "from1"} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"send","amount":Decimal("-0.44")},
+ {"txid":txid, "account" : ""} )
+ check_array_result(nodes[1].listtransactions(),
+ {"category":"receive","amount":Decimal("0.44")},
+ {"txid":txid, "account" : "toself"} )
+
+
+def main():
+ import optparse
+
+ parser = optparse.OptionParser(usage="%prog [options]")
+ parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
+ help="Leave bitcoinds and test.* datadir on exit or error")
+ parser.add_option("--srcdir", dest="srcdir", default="../../src",
+ help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
+ parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
+ help="Root directory for datadirs")
+ (options, args) = parser.parse_args()
+
+ os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
+
+ check_json_precision()
+
+ success = False
+ try:
+ print("Initializing test directory "+options.tmpdir)
+ if not os.path.isdir(options.tmpdir):
+ os.makedirs(options.tmpdir)
+ initialize_chain(options.tmpdir)
+
+ nodes = start_nodes(2, options.tmpdir)
+ connect_nodes(nodes[1], 0)
+ sync_blocks(nodes)
+ run_test(nodes)
+
+ success = True
+
+ except AssertionError as e:
+ print("Assertion failed: "+e.message)
+ except Exception as e:
+ print("Unexpected exception caught during testing: "+str(e))
+ stack = traceback.extract_tb(sys.exc_info()[2])
+ print(stack[-1])
+
+ if not options.nocleanup:
+ print("Cleaning up")
+ stop_nodes()
+ shutil.rmtree(options.tmpdir)
+
+ if success:
+ print("Tests successful")
+ sys.exit(0)
+ else:
+ print("Failed")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/qa/rpc-tests/skeleton.py b/qa/rpc-tests/skeleton.py
new file mode 100755
index 0000000000..0bace6f4eb
--- /dev/null
+++ b/qa/rpc-tests/skeleton.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+# Skeleton for python-based regression tests using
+# JSON-RPC
+
+
+# Add python-bitcoinrpc to module search path:
+import os
+import sys
+sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
+
+import json
+import shutil
+import subprocess
+import tempfile
+import traceback
+
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+from util import *
+
+
+def run_test(nodes):
+ # Replace this as appropriate
+ for node in nodes:
+ assert_equal(node.getblockcount(), 200)
+ assert_equal(node.getbalance(), 25*50)
+
+def main():
+ import optparse
+
+ parser = optparse.OptionParser(usage="%prog [options]")
+ parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
+ help="Leave bitcoinds and test.* datadir on exit or error")
+ parser.add_option("--srcdir", dest="srcdir", default="../../src",
+ help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
+ parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
+ help="Root directory for datadirs")
+ (options, args) = parser.parse_args()
+
+ os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
+
+ check_json_precision()
+
+ success = False
+ try:
+ print("Initializing test directory "+options.tmpdir)
+ if not os.path.isdir(options.tmpdir):
+ os.makedirs(options.tmpdir)
+ initialize_chain(options.tmpdir)
+
+ nodes = start_nodes(2, options.tmpdir)
+ connect_nodes(nodes[1], 0)
+ sync_blocks(nodes)
+
+ run_test(nodes)
+
+ success = True
+
+ except AssertionError as e:
+ print("Assertion failed: "+e.message)
+ except Exception as e:
+ print("Unexpected exception caught during testing: "+str(e))
+ stack = traceback.extract_tb(sys.exc_info()[2])
+ print(stack[-1])
+
+ if not options.nocleanup:
+ print("Cleaning up")
+ stop_nodes()
+ shutil.rmtree(options.tmpdir)
+
+ if success:
+ print("Tests successful")
+ sys.exit(0)
+ else:
+ print("Failed")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/qa/rpc-tests/txnmall.sh b/qa/rpc-tests/txnmall.sh
index 06e4f7102d..11e0276494 100755
--- a/qa/rpc-tests/txnmall.sh
+++ b/qa/rpc-tests/txnmall.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-# Test block generation and basic wallet sending
+# Test proper accounting with malleable transactions
if [ $# -lt 1 ]; then
echo "Usage: $0 path_to_binaries"
@@ -35,16 +35,14 @@ B2ARGS="-datadir=$D2"
$BITCOIND $B2ARGS &
B2PID=$!
-trap "kill -9 $B1PID $B2PID; rm -rf $D" EXIT
-
-# Wait until all four nodes are at the same block number
+# Wait until both nodes are at the same block number
function WaitBlocks {
while :
do
sleep 1
- BLOCKS1=$( GetBlocks $B1ARGS )
- BLOCKS2=$( GetBlocks $B2ARGS )
- if (( $BLOCKS1 == $BLOCKS2 ))
+ declare -i BLOCKS1=$( GetBlocks $B1ARGS )
+ declare -i BLOCKS2=$( GetBlocks $B2ARGS )
+ if (( BLOCKS1 == BLOCKS2 ))
then
break
fi
@@ -55,8 +53,8 @@ function WaitBlocks {
function WaitPeers {
while :
do
- PEERS=$( $CLI $1 getconnectioncount )
- if (( "$PEERS" == $2 ))
+ declare -i PEERS=$( $CLI $1 getconnectioncount )
+ if (( PEERS == "$2" ))
then
break
fi
@@ -64,6 +62,8 @@ function WaitPeers {
done
}
+echo "Generating test blockchain..."
+
# Start with B2 connected to B1:
$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
WaitPeers "$B1ARGS" 1
@@ -76,8 +76,8 @@ WaitBlocks
$CLI $B2ARGS setgenerate true 100
WaitBlocks
-CheckBalance $B1ARGS 50
-CheckBalance $B2ARGS 0
+CheckBalance "$B1ARGS" 50
+CheckBalance "$B2ARGS" 0
# restart B2 with no connection
$CLI $B2ARGS stop > /dev/null 2>&1
@@ -85,20 +85,18 @@ wait $B2PID
$BITCOIND $B2ARGS &
B2PID=$!
-B2ADDRESS=$( $CLI $B2ARGS getnewaddress )
+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
-$CLI $B1ARGS move "" "bar" 10.0
+$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 )
-RAWTX2=$( $CLI $B1ARGS getrawtransaction $TXID2 )
-# ... mutate RAWTX1:
# 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),
@@ -123,28 +121,28 @@ 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
-WaitPeers "$B1ARGS" 1
-
-$CLI $B2ARGS setgenerate true 3
-WaitBlocks
-$CLI $B1ARGS setgenerate true 3
+$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
-trap "" EXIT
-
-echo "Done, bitcoind's shut down. To rerun/poke around:"
-echo "${1}/bitcoind -datadir=$D1 -daemon"
-echo "${1}/bitcoind -datadir=$D2 -daemon -connect=127.0.0.1:11000"
-echo "To cleanup:"
-echo "killall bitcoind; rm -rf test.*"
-exit 0
-
echo "Tests successful, cleaning up"
rm -rf $D
exit 0
diff --git a/qa/rpc-tests/util.py b/qa/rpc-tests/util.py
new file mode 100644
index 0000000000..fbb27ae2df
--- /dev/null
+++ b/qa/rpc-tests/util.py
@@ -0,0 +1,136 @@
+#
+# Helpful routines for regression testing
+#
+
+# Add python-bitcoinrpc to module search path:
+import os
+import sys
+sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc"))
+
+from decimal import Decimal
+import json
+import shutil
+import subprocess
+import time
+
+from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
+from util import *
+
+START_P2P_PORT=11000
+START_RPC_PORT=11100
+
+def check_json_precision():
+ """Make sure json library being used does not lose precision converting BTC values"""
+ n = Decimal("20000000.00000003")
+ satoshis = int(json.loads(json.dumps(float(n)))*1.0e8)
+ if satoshis != 2000000000000003:
+ raise RuntimeError("JSON encode/decode loses precision")
+
+def sync_blocks(rpc_connections):
+ """
+ Wait until everybody has the same block count
+ """
+ while True:
+ counts = [ x.getblockcount() for x in rpc_connections ]
+ if counts == [ counts[0] ]*len(counts):
+ break
+ time.sleep(1)
+
+def sync_mempools(rpc_connections):
+ """
+ Wait until everybody has the same transactions in their memory
+ pools
+ """
+ while True:
+ pool = set(rpc_connections[0].getrawmempool())
+ num_match = 1
+ for i in range(1, len(rpc_connections)):
+ if set(rpc_connections[i].getrawmempool()) == pool:
+ num_match = num_match+1
+ if num_match == len(rpc_connections):
+ break
+ time.sleep(1)
+
+
+def initialize_chain(test_dir):
+ """
+ Create (or copy from cache) a 200-block-long chain and
+ 4 wallets.
+ bitcoind and bitcoin-cli must be in search path.
+ """
+
+ if not os.path.isdir(os.path.join("cache", "node0")):
+ # Create cache directories, run bitcoinds:
+ bitcoinds = []
+ for i in range(4):
+ datadir = os.path.join("cache", "node"+str(i))
+ os.makedirs(datadir)
+ with open(os.path.join(datadir, "bitcoin.conf"), 'w') as f:
+ f.write("regtest=1\n");
+ f.write("rpcuser=rt\n");
+ f.write("rpcpassword=rt\n");
+ f.write("port="+str(START_P2P_PORT+i)+"\n");
+ f.write("rpcport="+str(START_RPC_PORT+i)+"\n");
+ args = [ "bitcoind", "-keypool=1", "-datadir="+datadir ]
+ if i > 0:
+ args.append("-connect=127.0.0.1:"+str(START_P2P_PORT))
+ bitcoinds.append(subprocess.Popen(args))
+ subprocess.check_output([ "bitcoin-cli", "-datadir="+datadir,
+ "-rpcwait", "getblockcount"])
+
+ rpcs = []
+ for i in range(4):
+ try:
+ url = "http://rt:rt@127.0.0.1:%d"%(START_RPC_PORT+i,)
+ rpcs.append(AuthServiceProxy(url))
+ except:
+ sys.stderr.write("Error connecting to "+url+"\n")
+ sys.exit(1)
+
+ import pdb; pdb.set_trace()
+
+ # Create a 200-block-long chain; each of the 4 nodes
+ # gets 25 mature blocks and 25 immature.
+ for i in range(4):
+ rpcs[i].setgenerate(True, 25)
+ sync_blocks(rpcs)
+ for i in range(4):
+ rpcs[i].setgenerate(True, 25)
+ sync_blocks(rpcs)
+ # Shut them down
+ for i in range(4):
+ rpcs[i].stop()
+
+ for i in range(4):
+ from_dir = os.path.join("cache", "node"+str(i))
+ to_dir = os.path.join(test_dir, "node"+str(i))
+ shutil.copytree(from_dir, to_dir)
+
+bitcoind_processes = []
+
+def start_nodes(num_nodes, dir):
+ # Start bitcoinds, and wait for RPC interface to be up and running:
+ for i in range(num_nodes):
+ datadir = os.path.join(dir, "node"+str(i))
+ args = [ "bitcoind", "-datadir="+datadir ]
+ bitcoind_processes.append(subprocess.Popen(args))
+ subprocess.check_output([ "bitcoin-cli", "-datadir="+datadir,
+ "-rpcwait", "getblockcount"])
+ # Create&return JSON-RPC connections
+ rpc_connections = []
+ for i in range(num_nodes):
+ url = "http://rt:rt@127.0.0.1:%d"%(START_RPC_PORT+i,)
+ rpc_connections.append(AuthServiceProxy(url))
+ return rpc_connections
+
+def stop_nodes():
+ for process in bitcoind_processes:
+ process.kill()
+
+def connect_nodes(from_connection, node_num):
+ ip_port = "127.0.0.1:"+str(START_P2P_PORT+node_num)
+ from_connection.addnode(ip_port, "onetry")
+
+def assert_equal(thing1, thing2):
+ if thing1 != thing2:
+ raise AssertionError("%s != %s"%(str(thing1),str(thing2)))
diff --git a/qa/rpc-tests/util.sh b/qa/rpc-tests/util.sh
index d1e4c941cc..9001c42fbc 100644
--- a/qa/rpc-tests/util.sh
+++ b/qa/rpc-tests/util.sh
@@ -41,8 +41,9 @@ function AssertEqual {
# CheckBalance -datadir=... amount account minconf
function CheckBalance {
+ declare -i EXPECT="$2"
B=$( $CLI $1 getbalance $3 $4 )
- if (( $( echo "$B == $2" | bc ) == 0 ))
+ if (( $( echo "$B == $EXPECT" | bc ) == 0 ))
then
echoerr "bad balance: $B (expected $2)"
exit 1
@@ -87,5 +88,5 @@ function SendRawTxn {
# Use: GetBlocks <datadir>
# returns number of blocks from getinfo
function GetBlocks {
- ExtractKey blocks "$( $CLI $1 getinfo )"
+ $CLI $1 getblockcount
}
diff --git a/qa/rpc-tests/wallet.sh b/qa/rpc-tests/wallet.sh
index 8d5a6cdc78..2940566af9 100755
--- a/qa/rpc-tests/wallet.sh
+++ b/qa/rpc-tests/wallet.sh
@@ -8,6 +8,8 @@ if [ $# -lt 1 ]; then
exit 1
fi
+set -f
+
BITCOIND=${1}/bitcoind
CLI=${1}/bitcoin-cli
@@ -19,40 +21,40 @@ if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
D=$(mktemp -d test.XXXXX)
D1=${D}/node1
-CreateDataDir $D1 port=11000 rpcport=11001
+CreateDataDir "$D1" port=11000 rpcport=11001
B1ARGS="-datadir=$D1"
$BITCOIND $B1ARGS &
B1PID=$!
D2=${D}/node2
-CreateDataDir $D2 port=11010 rpcport=11011 connect=127.0.0.1:11000
+CreateDataDir "$D2" port=11010 rpcport=11011 connect=127.0.0.1:11000
B2ARGS="-datadir=$D2"
$BITCOIND $B2ARGS &
B2PID=$!
D3=${D}/node3
-CreateDataDir $D3 port=11020 rpcport=11021 connect=127.0.0.1:11000
+CreateDataDir "$D3" port=11020 rpcport=11021 connect=127.0.0.1:11000
B3ARGS="-datadir=$D3"
$BITCOIND $BITCOINDARGS $B3ARGS &
B3PID=$!
-trap "kill -9 $B1PID $B2PID $B3PID; rm -rf $D" EXIT
-
# Wait until all three nodes are at the same block number
function WaitBlocks {
while :
do
sleep 1
- BLOCKS1=$( GetBlocks $B1ARGS )
- BLOCKS2=$( GetBlocks $B2ARGS )
- BLOCKS3=$( GetBlocks $B3ARGS )
- if (( $BLOCKS1 == $BLOCKS2 && $BLOCKS2 == $BLOCKS3 ))
+ declare -i BLOCKS1=$( GetBlocks $B1ARGS )
+ declare -i BLOCKS2=$( GetBlocks $B2ARGS )
+ declare -i BLOCKS3=$( GetBlocks $B3ARGS )
+ if (( BLOCKS1 == BLOCKS2 && BLOCKS2 == BLOCKS3 ))
then
break
fi
done
}
+echo "Generating test blockchain..."
+
# 1 block, 50 XBT each == 50 XBT
$CLI $B1ARGS setgenerate true 1
WaitBlocks
@@ -60,8 +62,8 @@ WaitBlocks
$CLI $B2ARGS setgenerate true 101
WaitBlocks
-CheckBalance $B1ARGS 50
-CheckBalance $B2ARGS 50
+CheckBalance "$B1ARGS" 50
+CheckBalance "$B2ARGS" 50
# Send 21 XBT from 1 to 3. Second
# transaction will be child of first, and
@@ -80,25 +82,25 @@ WaitBlocks
# B1 should end up with 100 XBT in block rewards plus fees,
# minus the 21 XBT sent to B3:
-CheckBalance $B1ARGS "100-21"
-CheckBalance $B3ARGS "21"
+CheckBalance "$B1ARGS" "100-21"
+CheckBalance "$B3ARGS" "21"
# B1 should have two unspent outputs; create a couple
# of raw transactions to send them to B3, submit them through
# B2, and make sure both B1 and B3 pick them up properly:
RAW1=$(CreateTxn1 $B1ARGS 1 $(Address $B3ARGS "from1" ) )
RAW2=$(CreateTxn1 $B1ARGS 2 $(Address $B3ARGS "from1" ) )
-RAWTXID1=$(SendRawTxn $B2ARGS $RAW1)
-RAWTXID2=$(SendRawTxn $B2ARGS $RAW2)
+RAWTXID1=$(SendRawTxn "$B2ARGS" $RAW1)
+RAWTXID2=$(SendRawTxn "$B2ARGS" $RAW2)
# Have B2 mine a block to confirm transactions:
$CLI $B2ARGS setgenerate true 1
WaitBlocks
# Check balances after confirmation
-CheckBalance $B1ARGS 0
-CheckBalance $B3ARGS 100
-CheckBalance $B3ARGS "100-21" "from1"
+CheckBalance "$B1ARGS" 0
+CheckBalance "$B3ARGS" 100
+CheckBalance "$B3ARGS" "100-21" "from1"
$CLI $B3ARGS stop > /dev/null 2>&1
wait $B3PID
@@ -108,6 +110,5 @@ $CLI $B1ARGS stop > /dev/null 2>&1
wait $B1PID
echo "Tests successful, cleaning up"
-trap "" EXIT
rm -rf $D
exit 0
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000..b5a217bf93
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+setup(name='python-bitcoinrpc',
+ version='0.1',
+ description='Enhanced version of python-jsonrpc for use with Bitcoin',
+ long_description=open('README').read(),
+ author='Jeff Garzik',
+ author_email='<jgarzik@exmulti.com>',
+ maintainer='Jeff Garzik',
+ maintainer_email='<jgarzik@exmulti.com>',
+ url='http://www.github.com/jgarzik/python-bitcoinrpc',
+ packages=['bitcoinrpc'],
+ classifiers=['License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Operating System :: OS Independent'])
diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in
index b1c2dcb462..4ed1f1be50 100644
--- a/share/qt/Info.plist.in
+++ b/share/qt/Info.plist.in
@@ -2,6 +2,14 @@
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.6.0</string>
+
+ <key>LSArchitecturePriority</key>
+ <array>
+ <string>x86_64</string>
+ </array>
+
<key>CFBundleIconFile</key>
<string>bitcoin.icns</string>
diff --git a/share/qt/clean_mac_info_plist.py b/share/qt/clean_mac_info_plist.py
deleted file mode 100755
index df677f50b7..0000000000
--- a/share/qt/clean_mac_info_plist.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python
-# Jonas Schnelli, 2013
-# make sure the Bitcoin-Qt.app contains the right plist (including the right version)
-# fix made because of serval bugs in Qt mac deployment (https://bugreports.qt-project.org/browse/QTBUG-21267)
-
-from string import Template
-from datetime import date
-
-bitcoinDir = "./";
-
-inFile = bitcoinDir+"/share/qt/Info.plist"
-outFile = "Bitcoin-Qt.app/Contents/Info.plist"
-version = "unknown";
-
-fileForGrabbingVersion = bitcoinDir+"bitcoin-qt.pro"
-for line in open(fileForGrabbingVersion):
- lineArr = line.replace(" ", "").split("=");
- if lineArr[0].startswith("VERSION"):
- version = lineArr[1].replace("\n", "");
-
-fIn = open(inFile, "r")
-fileContent = fIn.read()
-s = Template(fileContent)
-newFileContent = s.substitute(VERSION=version,YEAR=date.today().year)
-
-fOut = open(outFile, "w");
-fOut.write(newFileContent);
-
-print "Info.plist fresh created" \ No newline at end of file
diff --git a/src/key.h b/src/key.h
index bb3777e384..cf1165d3d0 100644
--- a/src/key.h
+++ b/src/key.h
@@ -137,7 +137,9 @@ public:
return Hash(vch, vch+size());
}
- // just check syntactic correctness.
+ // Check syntactic correctness.
+ //
+ // Note that this is consensus critical as CheckSig() calls it!
bool IsValid() const {
return size() > 0;
}
diff --git a/src/main.cpp b/src/main.cpp
index 8a5b659e7c..7afaa9e7e2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1866,17 +1866,23 @@ bool static DisconnectTip(CValidationState &state) {
// Write the chain state to disk, if necessary.
if (!WriteChainState(state))
return false;
- // Ressurect mempool transactions from the disconnected block.
+ // Resurrect mempool transactions from the disconnected block.
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
// ignore validation errors in resurrected transactions
+ list<CTransaction> removed;
CValidationState stateDummy;
if (!tx.IsCoinBase())
if (!AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL))
- mempool.remove(tx, true);
+ mempool.remove(tx, removed, true);
}
mempool.check(pcoinsTip);
// Update chainActive and related variables.
UpdateTip(pindexDelete->pprev);
+ // Let wallets know transactions went from 1-confirmed to
+ // 0-confirmed or conflicted:
+ BOOST_FOREACH(const CTransaction &tx, block.vtx) {
+ SyncWithWallets(tx.GetHash(), tx, NULL);
+ }
return true;
}
@@ -1907,13 +1913,24 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) {
if (!WriteChainState(state))
return false;
// Remove conflicting transactions from the mempool.
+ list<CTransaction> txConflicted;
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
- mempool.remove(tx);
- mempool.removeConflicts(tx);
+ list<CTransaction> unused;
+ mempool.remove(tx, unused);
+ mempool.removeConflicts(tx, txConflicted);
}
mempool.check(pcoinsTip);
// Update chainActive & related variables.
UpdateTip(pindexNew);
+ // Tell wallet about transactions that went from mempool
+ // to conflicted:
+ BOOST_FOREACH(const CTransaction &tx, txConflicted) {
+ SyncWithWallets(tx.GetHash(), tx, NULL);
+ }
+ // ... and about transactions that got confirmed:
+ BOOST_FOREACH(const CTransaction &tx, block.vtx) {
+ SyncWithWallets(tx.GetHash(), tx, &block);
+ }
return true;
}
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index e1a9140f45..2d8fcd7a52 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -468,11 +468,12 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
BOOST_FOREACH(const COutput& out, vOutputs)
{
- // unselect already spent, very unlikely scenario, this could happen when selected are spent elsewhere, like rpc or another computer
- if (out.tx->IsSpent(out.i))
+ // unselect already spent, very unlikely scenario, this could happen
+ // when selected are spent elsewhere, like rpc or another computer
+ uint256 txhash = out.tx->GetHash();
+ COutPoint outpt(txhash, out.i);
+ if (model->isSpent(outpt))
{
- uint256 txhash = out.tx->GetHash();
- COutPoint outpt(txhash, out.i);
coinControl->UnSelect(outpt);
continue;
}
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 8cfaed27c7..703a2b4e79 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -164,7 +164,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
(wtx.IsCoinBase() ? 1 : 0),
wtx.nTimeReceived,
idx);
- status.confirmed = wtx.IsTrusted();
+ status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
status.depth = wtx.GetDepthInMainChain();
status.cur_num_blocks = chainActive.Height();
@@ -181,33 +181,12 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
status.open_for = wtx.nLockTime;
}
}
- else
- {
- if (status.depth < 0)
- {
- status.status = TransactionStatus::Conflicted;
- }
- else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
- {
- status.status = TransactionStatus::Offline;
- }
- else if (status.depth < NumConfirmations)
- {
- status.status = TransactionStatus::Unconfirmed;
- }
- else
- {
- status.status = TransactionStatus::HaveConfirmations;
- }
- }
-
// For generated transactions, determine maturity
- if(type == TransactionRecord::Generated)
+ else if(type == TransactionRecord::Generated)
{
- int64_t nCredit = wtx.GetCredit(true);
- if (nCredit == 0)
+ if (wtx.GetBlocksToMaturity() > 0)
{
- status.maturity = TransactionStatus::Immature;
+ status.status = TransactionStatus::Immature;
if (wtx.IsInMainChain())
{
@@ -215,18 +194,42 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
// Check if the block was requested by anyone
if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
- status.maturity = TransactionStatus::MaturesWarning;
+ status.status = TransactionStatus::MaturesWarning;
}
else
{
- status.maturity = TransactionStatus::NotAccepted;
+ status.status = TransactionStatus::NotAccepted;
}
}
else
{
- status.maturity = TransactionStatus::Mature;
+ status.status = TransactionStatus::Confirmed;
+ }
+ }
+ else
+ {
+ if (status.depth < 0)
+ {
+ status.status = TransactionStatus::Conflicted;
+ }
+ else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
+ {
+ status.status = TransactionStatus::Offline;
+ }
+ else if (status.depth == 0)
+ {
+ status.status = TransactionStatus::Unconfirmed;
+ }
+ else if (status.depth < RecommendedNumConfirmations)
+ {
+ status.status = TransactionStatus::Confirming;
+ }
+ else
+ {
+ status.status = TransactionStatus::Confirmed;
}
}
+
}
bool TransactionRecord::statusUpdateNeeded()
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index d7be0bc438..af6fd403b3 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -19,33 +19,32 @@ class TransactionStatus
{
public:
TransactionStatus():
- confirmed(false), sortKey(""), maturity(Mature),
+ countsForBalance(false), sortKey(""),
matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1)
{ }
- enum Maturity
- {
- Immature,
- Mature,
- MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
- NotAccepted
- };
-
enum Status {
- OpenUntilDate,
- OpenUntilBlock,
- Offline,
- Unconfirmed,
- HaveConfirmations,
- Conflicted
+ Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/
+ /// Normal (sent/received) transactions
+ OpenUntilDate, /**< Transaction not yet final, waiting for date */
+ OpenUntilBlock, /**< Transaction not yet final, waiting for block */
+ Offline, /**< Not sent to any other nodes **/
+ Unconfirmed, /**< Not yet mined into a block **/
+ Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
+ Conflicted, /**< Conflicts with other transaction or mempool **/
+ /// Generated (mined) transactions
+ Immature, /**< Mined but waiting for maturity */
+ MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */
+ NotAccepted /**< Mined but not accepted */
};
- bool confirmed;
+ /// Transaction counts towards available balance
+ bool countsForBalance;
+ /// Sorting key based on status
std::string sortKey;
/** @name Generated (mined) transactions
@{*/
- Maturity maturity;
int matures_in;
/**@}*/
@@ -79,8 +78,8 @@ public:
SendToSelf
};
- /** Number of confirmation needed for transaction */
- static const int NumConfirmations = 6;
+ /** Number of confirmation recommended for accepting a transaction */
+ static const int RecommendedNumConfirmations = 6;
TransactionRecord():
hash(), time(0), type(Other), address(""), debit(0), credit(0), idx(0)
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 7d76204ba4..959987461f 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -285,45 +285,38 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
{
QString status;
- if(wtx->type == TransactionRecord::Generated)
+ switch(wtx->status.status)
{
- switch(wtx->status.maturity)
- {
- case TransactionStatus::Immature:
- status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
- break;
- case TransactionStatus::Mature:
- status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
- break;
- case TransactionStatus::MaturesWarning:
- status = tr("This block was not received by any other nodes and will probably not be accepted!");
- break;
- case TransactionStatus::NotAccepted:
- status = tr("Generated but not accepted");
- break;
- }
- } else {
- switch(wtx->status.status)
- {
- case TransactionStatus::OpenUntilBlock:
- status = tr("Open for %n more block(s)","",wtx->status.open_for);
- break;
- case TransactionStatus::OpenUntilDate:
- status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
- break;
- case TransactionStatus::Offline:
- status = tr("Offline");
- break;
- case TransactionStatus::Unconfirmed:
- status = tr("Unconfirmed (%1 of %2 confirmations)").arg(wtx->status.depth).arg(TransactionRecord::NumConfirmations);
- break;
- case TransactionStatus::HaveConfirmations:
- status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
- break;
- case TransactionStatus::Conflicted:
- status = tr("Conflicted");
- break;
- }
+ case TransactionStatus::OpenUntilBlock:
+ status = tr("Open for %n more block(s)","",wtx->status.open_for);
+ break;
+ case TransactionStatus::OpenUntilDate:
+ status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
+ break;
+ case TransactionStatus::Offline:
+ status = tr("Offline");
+ break;
+ case TransactionStatus::Unconfirmed:
+ status = tr("Unconfirmed");
+ break;
+ case TransactionStatus::Confirming:
+ status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations);
+ break;
+ case TransactionStatus::Confirmed:
+ status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
+ break;
+ case TransactionStatus::Conflicted:
+ status = tr("Conflicted");
+ break;
+ case TransactionStatus::Immature:
+ status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
+ break;
+ case TransactionStatus::MaturesWarning:
+ status = tr("This block was not received by any other nodes and will probably not be accepted!");
+ break;
+ case TransactionStatus::NotAccepted:
+ status = tr("Generated but not accepted");
+ break;
}
return status;
@@ -441,7 +434,7 @@ QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool
QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit);
if(showUnconfirmed)
{
- if(!wtx->status.confirmed || wtx->status.maturity != TransactionStatus::Mature)
+ if(!wtx->status.countsForBalance)
{
str = QString("[") + str + QString("]");
}
@@ -451,46 +444,36 @@ QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool
QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const
{
- if(wtx->type == TransactionRecord::Generated)
- {
- switch(wtx->status.maturity)
- {
- case TransactionStatus::Immature: {
- int total = wtx->status.depth + wtx->status.matures_in;
- int part = (wtx->status.depth * 4 / total) + 1;
- return QIcon(QString(":/icons/transaction_%1").arg(part));
- }
- case TransactionStatus::Mature:
- return QIcon(":/icons/transaction_confirmed");
- case TransactionStatus::MaturesWarning:
- case TransactionStatus::NotAccepted:
- return QIcon(":/icons/transaction_0");
- }
- }
- else
+ switch(wtx->status.status)
{
- switch(wtx->status.status)
+ case TransactionStatus::OpenUntilBlock:
+ case TransactionStatus::OpenUntilDate:
+ return QColor(64,64,255);
+ case TransactionStatus::Offline:
+ return QColor(192,192,192);
+ case TransactionStatus::Unconfirmed:
+ return QIcon(":/icons/transaction_0");
+ case TransactionStatus::Confirming:
+ switch(wtx->status.depth)
{
- case TransactionStatus::OpenUntilBlock:
- case TransactionStatus::OpenUntilDate:
- return QColor(64,64,255);
- case TransactionStatus::Offline:
- return QColor(192,192,192);
- case TransactionStatus::Unconfirmed:
- switch(wtx->status.depth)
- {
- case 0: return QIcon(":/icons/transaction_0");
- case 1: return QIcon(":/icons/transaction_1");
- case 2: return QIcon(":/icons/transaction_2");
- case 3: return QIcon(":/icons/transaction_3");
- case 4: return QIcon(":/icons/transaction_4");
- default: return QIcon(":/icons/transaction_5");
- };
- case TransactionStatus::HaveConfirmations:
- return QIcon(":/icons/transaction_confirmed");
- case TransactionStatus::Conflicted:
- return QIcon(":/icons/transaction_conflicted");
+ case 1: return QIcon(":/icons/transaction_1");
+ case 2: return QIcon(":/icons/transaction_2");
+ case 3: return QIcon(":/icons/transaction_3");
+ case 4: return QIcon(":/icons/transaction_4");
+ default: return QIcon(":/icons/transaction_5");
+ };
+ case TransactionStatus::Confirmed:
+ return QIcon(":/icons/transaction_confirmed");
+ case TransactionStatus::Conflicted:
+ return QIcon(":/icons/transaction_conflicted");
+ case TransactionStatus::Immature: {
+ int total = wtx->status.depth + wtx->status.matures_in;
+ int part = (wtx->status.depth * 4 / total) + 1;
+ return QIcon(QString(":/icons/transaction_%1").arg(part));
}
+ case TransactionStatus::MaturesWarning:
+ case TransactionStatus::NotAccepted:
+ return QIcon(":/icons/transaction_0");
}
return QColor(0,0,0);
}
@@ -557,8 +540,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case Qt::TextAlignmentRole:
return column_alignments[index.column()];
case Qt::ForegroundRole:
- // Non-confirmed transactions are grey
- if(!rec->status.confirmed)
+ // Non-confirmed (but not immature) as transactions are grey
+ if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature)
{
return COLOR_UNCONFIRMED;
}
@@ -586,9 +569,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case TxIDRole:
return rec->getTxID();
case ConfirmedRole:
- // Return True if transaction counts for balance
- return rec->status.confirmed && !(rec->type == TransactionRecord::Generated &&
- rec->status.maturity != TransactionStatus::Mature);
+ return rec->status.countsForBalance;
case FormattedAmountRole:
return formatTxAmount(rec, false);
case StatusRole:
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 3549cd49f0..eae448fee4 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -501,6 +501,12 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect
}
}
+bool WalletModel::isSpent(const COutPoint& outpoint) const
+{
+ LOCK(wallet->cs_wallet);
+ return wallet->IsSpent(outpoint.hash, outpoint.n);
+}
+
// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
{
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 91a6fba222..28a9169e27 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -180,6 +180,7 @@ public:
bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs);
+ bool isSpent(const COutPoint& outpoint) const;
void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const;
bool isLockedCoin(uint256 hash, unsigned int n) const;
diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp
index 9e1d47846e..635d4ac19b 100644
--- a/src/rpcdump.cpp
+++ b/src/rpcdump.cpp
@@ -128,7 +128,6 @@ Value importprivkey(const Array& params, bool fHelp)
if (fRescan) {
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
- pwalletMain->ReacceptWalletTransactions();
}
}
@@ -216,7 +215,6 @@ Value importwallet(const Array& params, bool fHelp)
LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1);
pwalletMain->ScanForWalletTransactions(pindex);
- pwalletMain->ReacceptWalletTransactions();
pwalletMain->MarkDirty();
if (!fGood)
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 7b605af589..d3b6c349a7 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -560,7 +560,7 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (!IsFinalTx(wtx))
+ if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0)
continue;
int64_t nReceived, nSent, nFee;
@@ -1324,13 +1324,14 @@ Value listaccounts(const Array& params, bool fHelp)
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
- if (wtx.GetBlocksToMaturity() > 0)
+ int nDepth = wtx.GetDepthInMainChain();
+ if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0)
continue;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
mapAccountBalances[strSentAccount] -= nFee;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
mapAccountBalances[strSentAccount] -= s.second;
- if (wtx.GetDepthInMainChain() >= nMinDepth)
+ if (nDepth >= nMinDepth)
{
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived)
if (pwalletMain->mapAddressBook.count(r.first))
diff --git a/src/tinyformat.h b/src/tinyformat.h
index 04b51f0adc..b6113029f5 100644
--- a/src/tinyformat.h
+++ b/src/tinyformat.h
@@ -109,7 +109,7 @@ namespace tinyformat {}
namespace tfm = tinyformat;
// Error handling; calls assert() by default.
-// #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString)
+#define TINYFORMAT_ERROR(reasonString) throw std::runtime_error(reasonString)
// Define for C++11 variadic templates which make the code shorter & more
// general. If you don't define this, C++11 support is autodetected below.
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index be251d1d64..64c9eac73d 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -86,7 +86,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry)
}
-bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive)
+void CTxMemPool::remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive)
{
// Remove transaction from memory pool
{
@@ -95,34 +95,37 @@ bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive)
if (fRecursive) {
for (unsigned int i = 0; i < tx.vout.size(); i++) {
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));
- if (it != mapNextTx.end())
- remove(*it->second.ptx, true);
+ if (it == mapNextTx.end())
+ continue;
+ remove(*it->second.ptx, removed, true);
}
}
if (mapTx.count(hash))
{
+ removed.push_front(tx);
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout);
mapTx.erase(hash);
nTransactionsUpdated++;
}
}
- return true;
}
-bool CTxMemPool::removeConflicts(const CTransaction &tx)
+void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed)
{
// Remove transactions which depend on inputs of tx, recursively
+ list<CTransaction> result;
LOCK(cs);
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout);
if (it != mapNextTx.end()) {
const CTransaction &txConflict = *it->second.ptx;
if (txConflict != tx)
- remove(txConflict, true);
+ {
+ remove(txConflict, removed, true);
+ }
}
}
- return true;
}
void CTxMemPool::clear()
diff --git a/src/txmempool.h b/src/txmempool.h
index a652c424a4..4509e95778 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_TXMEMPOOL_H
#define BITCOIN_TXMEMPOOL_H
+#include <list>
+
#include "coins.h"
#include "core.h"
#include "sync.h"
@@ -72,8 +74,8 @@ public:
void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; }
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry);
- bool remove(const CTransaction &tx, bool fRecursive = false);
- bool removeConflicts(const CTransaction &tx);
+ void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false);
+ void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
void clear();
void queryHashes(std::vector<uint256>& vtxid);
void pruneSpent(const uint256& hash, CCoins &coins);
diff --git a/src/wallet.cpp b/src/wallet.cpp
index eaf0b98467..3ecd994e9d 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -32,6 +32,15 @@ struct CompareValueOnly
}
};
+const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
+{
+ LOCK(cs_wallet);
+ std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
+ if (it == mapWallet.end())
+ return NULL;
+ return &(it->second);
+}
+
CPubKey CWallet::GenerateNewKey()
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
@@ -239,18 +248,20 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
return result;
const CWalletTx& wtx = it->second;
- std::pair<TxConflicts::const_iterator, TxConflicts::const_iterator> range;
+ std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
{
- range = mapTxConflicts.equal_range(txin.prevout);
- for (TxConflicts::const_iterator it = range.first; it != range.second; ++it)
+ if (mapTxSpends.count(txin.prevout) <= 1)
+ continue; // No conflict if zero or one spends
+ range = mapTxSpends.equal_range(txin.prevout);
+ for (TxSpends::const_iterator it = range.first; it != range.second; ++it)
result.insert(it->second);
}
return result;
}
-void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> range)
+void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range)
{
// We want all the wallet transactions in range to have the same metadata as
// the oldest (smallest nOrderPos).
@@ -258,7 +269,7 @@ void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> ra
int nMinOrderPos = std::numeric_limits<int>::max();
const CWalletTx* copyFrom = NULL;
- for (TxConflicts::iterator it = range.first; it != range.second; ++it)
+ for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
int n = mapWallet[hash].nOrderPos;
@@ -269,7 +280,7 @@ void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> ra
}
}
// Now copy data from copyFrom to rest:
- for (TxConflicts::iterator it = range.first; it != range.second; ++it)
+ for (TxSpends::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
CWalletTx* copyTo = &mapWallet[hash];
@@ -281,28 +292,48 @@ void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> ra
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
- // vfSpent not copied on purpose
// nOrderPos not copied on purpose
// cached members not copied on purpose
}
}
-void CWallet::AddToConflicts(const uint256& wtxhash)
+// Outpoint is spent if any non-conflicted transaction
+// spends it:
+bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
{
- assert(mapWallet.count(wtxhash));
- CWalletTx& thisTx = mapWallet[wtxhash];
- if (thisTx.IsCoinBase())
- return;
+ const COutPoint outpoint(hash, n);
+ pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
+ range = mapTxSpends.equal_range(outpoint);
- BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
+ for (TxSpends::const_iterator it = range.first; it != range.second; ++it)
{
- mapTxConflicts.insert(make_pair(txin.prevout, wtxhash));
-
- pair<TxConflicts::iterator, TxConflicts::iterator> range;
- range = mapTxConflicts.equal_range(txin.prevout);
- if (range.first != range.second)
- SyncMetaData(range);
+ const uint256& wtxid = it->second;
+ std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
+ if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0)
+ return true; // Spent
}
+ return false;
+}
+
+void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
+{
+ mapTxSpends.insert(make_pair(outpoint, wtxid));
+
+ pair<TxSpends::iterator, TxSpends::iterator> range;
+ range = mapTxSpends.equal_range(outpoint);
+ SyncMetaData(range);
+}
+
+
+void CWallet::AddToSpends(const uint256& wtxid)
+{
+ assert(mapWallet.count(wtxid));
+ CWalletTx& thisTx = mapWallet[wtxid];
+ if (thisTx.IsCoinBase()) // Coinbases don't spend anything!
+ return;
+
+ BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
+ AddToSpends(txin.prevout, wtxid);
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
@@ -423,33 +454,6 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries,
return txOrdered;
}
-void CWallet::WalletUpdateSpent(const CTransaction &tx)
-{
- // Anytime a signature is successfully verified, it's proof the outpoint is spent.
- // Update the wallet spent flag if it doesn't know due to wallet.dat being
- // restored from backup or the user making copies of wallet.dat.
- {
- LOCK(cs_wallet);
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- {
- map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
- if (mi != mapWallet.end())
- {
- CWalletTx& wtx = (*mi).second;
- if (txin.prevout.n >= wtx.vout.size())
- LogPrintf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString());
- else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
- {
- LogPrintf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString());
- wtx.MarkSpent(txin.prevout.n);
- wtx.WriteToDisk();
- NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
- }
- }
- }
- }
-}
-
void CWallet::MarkDirty()
{
{
@@ -466,7 +470,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
if (fFromLoadWallet)
{
mapWallet[hash] = wtxIn;
- AddToConflicts(hash);
+ AddToSpends(hash);
}
else
{
@@ -526,7 +530,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
wtxIn.GetHash().ToString(),
wtxIn.hashBlock.ToString());
}
- AddToConflicts(hash);
+ AddToSpends(hash);
}
bool fUpdated = false;
@@ -549,7 +553,6 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true;
}
- fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent);
}
//// debug print
@@ -560,8 +563,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
if (!wtx.WriteToDisk())
return false;
- // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
- WalletUpdateSpent(wtx);
+ // Break debit/credit balance caches:
+ wtx.MarkDirty();
// Notify UI of new or updated transaction
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
@@ -596,14 +599,25 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction&
wtx.SetMerkleBranch(pblock);
return AddToWallet(wtx);
}
- else
- WalletUpdateSpent(tx);
}
return false;
}
-void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) {
+void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock)
+{
AddToWalletIfInvolvingMe(hash, tx, pblock, true);
+
+ if (mapWallet.count(hash) == 0)
+ return; // Not one of ours
+
+ // If a transaction changes 'conflicted' state, that changes the balance
+ // available of the outputs it spends. So force those to be
+ // recomputed, also:
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ if (mapWallet.count(txin.prevout.hash))
+ mapWallet[txin.prevout.hash].MarkDirty();
+ }
}
void CWallet::EraseFromWallet(const uint256 &hash)
@@ -804,78 +818,6 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived,
}
}
-void CWalletTx::AddSupportingTransactions()
-{
- vtxPrev.clear();
-
- const int COPY_DEPTH = 3;
- if (SetMerkleBranch() < COPY_DEPTH)
- {
- vector<uint256> vWorkQueue;
- BOOST_FOREACH(const CTxIn& txin, vin)
- vWorkQueue.push_back(txin.prevout.hash);
-
- {
- LOCK(pwallet->cs_wallet);
- map<uint256, const CMerkleTx*> mapWalletPrev;
- set<uint256> setAlreadyDone;
- for (unsigned int i = 0; i < vWorkQueue.size(); i++)
- {
- uint256 hash = vWorkQueue[i];
- if (setAlreadyDone.count(hash))
- continue;
- setAlreadyDone.insert(hash);
-
- CMerkleTx tx;
- map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash);
- if (mi != pwallet->mapWallet.end())
- {
- tx = (*mi).second;
- BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev)
- mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
- }
- else if (mapWalletPrev.count(hash))
- {
- tx = *mapWalletPrev[hash];
- }
- else
- {
- continue;
- }
-
- int nDepth = tx.SetMerkleBranch();
- vtxPrev.push_back(tx);
-
- if (nDepth < COPY_DEPTH)
- {
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- vWorkQueue.push_back(txin.prevout.hash);
- }
- }
- }
- }
-
- reverse(vtxPrev.begin(), vtxPrev.end());
-}
-
-bool CWalletTx::AcceptWalletTransaction()
-{
- {
- LOCK(mempool.cs);
- // Add previous supporting transactions first
- BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
- {
- if (!tx.IsCoinBase())
- {
- uint256 hash = tx.GetHash();
- if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash))
- tx.AcceptToMemoryPool(false);
- }
- }
- return AcceptToMemoryPool(false);
- }
- return false;
-}
bool CWalletTx::WriteToDisk()
{
@@ -916,69 +858,26 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
void CWallet::ReacceptWalletTransactions()
{
- bool fRepeat = true;
- while (fRepeat)
+ LOCK(cs_wallet);
+ BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
- LOCK(cs_wallet);
- fRepeat = false;
- bool fMissing = false;
- BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
- {
- CWalletTx& wtx = item.second;
- if (wtx.IsCoinBase() && wtx.IsSpent(0))
- continue;
+ const uint256& wtxid = item.first;
+ CWalletTx& wtx = item.second;
+ assert(wtx.GetHash() == wtxid);
- CCoins coins;
- bool fUpdated = false;
- bool fFound = pcoinsTip->GetCoins(wtx.GetHash(), coins);
- if (fFound || wtx.GetDepthInMainChain() > 0)
- {
- // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
- for (unsigned int i = 0; i < wtx.vout.size(); i++)
- {
- if (wtx.IsSpent(i))
- continue;
- if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i]))
- {
- wtx.MarkSpent(i);
- fUpdated = true;
- fMissing = true;
- }
- }
- if (fUpdated)
- {
- LogPrintf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()), wtx.GetHash().ToString());
- wtx.MarkDirty();
- wtx.WriteToDisk();
- }
- }
- else
- {
- // Re-accept any txes of ours that aren't already in a block
- if (!wtx.IsCoinBase())
- wtx.AcceptWalletTransaction();
- }
- }
- if (fMissing)
+ int nDepth = wtx.GetDepthInMainChain();
+
+ if (!wtx.IsCoinBase() && nDepth < 0)
{
- // TODO: optimize this to scan just part of the block chain?
- if (ScanForWalletTransactions(chainActive.Genesis()))
- fRepeat = true; // Found missing transactions: re-do re-accept.
+ // Try to add to memory pool
+ LOCK(mempool.cs);
+ wtx.AcceptToMemoryPool(false);
}
}
}
void CWalletTx::RelayWalletTransaction()
{
- BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
- {
- // Important: versions of bitcoin before 0.8.6 had a bug that inserted
- // empty transactions into the vtxPrev, which will cause the node to be
- // banned when retransmitted, hence the check for !tx.vin.empty()
- if (!tx.IsCoinBase() && !tx.vin.empty())
- if (tx.GetDepthInMainChain() == 0)
- RelayTransaction((CTransaction)tx, tx.GetHash());
- }
if (!IsCoinBase())
{
if (GetDepthInMainChain() == 0) {
@@ -1104,6 +1003,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
+ const uint256& wtxid = it->first;
const CWalletTx* pcoin = &(*it).second;
if (!IsFinalTx(*pcoin))
@@ -1120,7 +1020,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
- if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) &&
+ if (!(IsSpent(wtxid, i)) && IsMine(pcoin->vout[i]) &&
!IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 &&
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
vCoins.push_back(COutput(pcoin, i, nDepth));
@@ -1452,8 +1352,6 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend,
continue;
}
- // Fill vtxPrev by copying from previous transactions vtxPrev
- wtxNew.AddSupportingTransactions();
wtxNew.fTimeReceivedIsTxTime = true;
break;
@@ -1490,14 +1388,12 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
// otherwise just for transaction history.
AddToWallet(wtxNew);
- // Mark old coins as spent
+ // Notify that old coins are spent
set<CWalletTx*> setCoins;
BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
{
CWalletTx &coin = mapWallet[txin.prevout.hash];
coin.BindWallet(this);
- coin.MarkSpent(txin.prevout.n);
- coin.WriteToDisk();
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
}
@@ -1855,7 +1751,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances()
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
continue;
- int64_t n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue;
+ int64_t n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
diff --git a/src/wallet.h b/src/wallet.h
index eb192f1ca6..7feb86d294 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -108,11 +108,15 @@ private:
int64_t nNextResend;
int64_t nLastResend;
- // Used to detect and report conflicted transactions:
- typedef std::multimap<COutPoint, uint256> TxConflicts;
- TxConflicts mapTxConflicts;
- void AddToConflicts(const uint256& wtxhash);
- void SyncMetaData(std::pair<TxConflicts::iterator, TxConflicts::iterator>);
+ // Used to keep track of spent outpoints, and
+ // detect and report conflicts (double-spends or
+ // mutated transactions where the mutant gets mined).
+ typedef std::multimap<COutPoint, uint256> TxSpends;
+ TxSpends mapTxSpends;
+ void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
+ void AddToSpends(const uint256& wtxid);
+
+ void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
public:
/// Main wallet lock.
@@ -169,12 +173,16 @@ public:
int64_t nTimeFirstKey;
+ const CWalletTx* GetWalletTx(const uint256& hash) const;
+
// check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL) const;
bool SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const;
+ bool IsSpent(const uint256& hash, unsigned int n) const;
+
bool IsLockedCoin(uint256 hash, unsigned int n) const;
void LockCoin(COutPoint& output);
void UnlockCoin(COutPoint& output);
@@ -234,7 +242,6 @@ public:
void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock);
bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate);
void EraseFromWallet(const uint256 &hash);
- void WalletUpdateSpent(const CTransaction& prevout);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions();
@@ -439,7 +446,6 @@ private:
const CWallet* pwallet;
public:
- std::vector<CMerkleTx> vtxPrev;
mapValue_t mapValue;
std::vector<std::pair<std::string, std::string> > vOrderForm;
unsigned int fTimeReceivedIsTxTime;
@@ -447,7 +453,6 @@ public:
unsigned int nTimeSmart;
char fFromMe;
std::string strFromAccount;
- std::vector<char> vfSpent; // which outputs are already spent
int64_t nOrderPos; // position in ordered transaction list
// memory only
@@ -485,7 +490,6 @@ public:
void Init(const CWallet* pwalletIn)
{
pwallet = pwalletIn;
- vtxPrev.clear();
mapValue.clear();
vOrderForm.clear();
fTimeReceivedIsTxTime = false;
@@ -493,7 +497,6 @@ public:
nTimeSmart = 0;
fFromMe = false;
strFromAccount.clear();
- vfSpent.clear();
fDebitCached = false;
fCreditCached = false;
fImmatureCreditCached = false;
@@ -518,15 +521,6 @@ public:
{
pthis->mapValue["fromaccount"] = pthis->strFromAccount;
- std::string str;
- BOOST_FOREACH(char f, vfSpent)
- {
- str += (f ? '1' : '0');
- if (f)
- fSpent = true;
- }
- pthis->mapValue["spent"] = str;
-
WriteOrderPos(pthis->nOrderPos, pthis->mapValue);
if (nTimeSmart)
@@ -534,7 +528,8 @@ public:
}
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action);
- READWRITE(vtxPrev);
+ std::vector<CMerkleTx> vUnused; // Used to be vtxPrev
+ READWRITE(vUnused);
READWRITE(mapValue);
READWRITE(vOrderForm);
READWRITE(fTimeReceivedIsTxTime);
@@ -546,12 +541,6 @@ public:
{
pthis->strFromAccount = pthis->mapValue["fromaccount"];
- if (mapValue.count("spent"))
- BOOST_FOREACH(char c, pthis->mapValue["spent"])
- pthis->vfSpent.push_back(c != '0');
- else
- pthis->vfSpent.assign(vout.size(), fSpent);
-
ReadOrderPos(pthis->nOrderPos, pthis->mapValue);
pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0;
@@ -564,26 +553,6 @@ public:
pthis->mapValue.erase("timesmart");
)
- // marks certain txout's as spent
- // returns true if any update took place
- bool UpdateSpent(const std::vector<char>& vfNewSpent)
- {
- bool fReturn = false;
- for (unsigned int i = 0; i < vfNewSpent.size(); i++)
- {
- if (i == vfSpent.size())
- break;
-
- if (vfNewSpent[i] && !vfSpent[i])
- {
- vfSpent[i] = true;
- fReturn = true;
- fAvailableCreditCached = false;
- }
- }
- return fReturn;
- }
-
// make sure balances are recalculated
void MarkDirty()
{
@@ -599,27 +568,6 @@ public:
MarkDirty();
}
- void MarkSpent(unsigned int nOut)
- {
- if (nOut >= vout.size())
- throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range");
- vfSpent.resize(vout.size());
- if (!vfSpent[nOut])
- {
- vfSpent[nOut] = true;
- fAvailableCreditCached = false;
- }
- }
-
- bool IsSpent(unsigned int nOut) const
- {
- if (nOut >= vout.size())
- throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range");
- if (nOut >= vfSpent.size())
- return false;
- return (!!vfSpent[nOut]);
- }
-
int64_t GetDebit() const
{
if (vin.empty())
@@ -661,6 +609,9 @@ public:
int64_t GetAvailableCredit(bool fUseCache=true) const
{
+ if (pwallet == 0)
+ return 0;
+
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsCoinBase() && GetBlocksToMaturity() > 0)
return 0;
@@ -671,7 +622,7 @@ public:
int64_t nCredit = 0;
for (unsigned int i = 0; i < vout.size(); i++)
{
- if (!IsSpent(i))
+ if (!pwallet->IsSpent(GetHash(), i))
{
const CTxOut &txout = vout[i];
nCredit += pwallet->GetCredit(txout);
@@ -719,38 +670,14 @@ public:
if (!bSpendZeroConfChange || !IsFromMe()) // using wtx's cached debit
return false;
- // If no confirmations but it's from us, we can still
- // consider it confirmed if all dependencies are confirmed
- std::map<uint256, const CMerkleTx*> mapPrev;
- std::vector<const CMerkleTx*> vWorkQueue;
- vWorkQueue.reserve(vtxPrev.size()+1);
- vWorkQueue.push_back(this);
- for (unsigned int i = 0; i < vWorkQueue.size(); i++)
+ // Trusted if all inputs are from us and are in the mempool:
+ BOOST_FOREACH(const CTxIn& txin, vin)
{
- const CMerkleTx* ptx = vWorkQueue[i];
-
- if (!IsFinalTx(*ptx))
+ // Transactions not sent by us: not trusted
+ const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
+ const CTxOut& parentOut = parent->vout[txin.prevout.n];
+ if (parent == NULL || !pwallet->IsMine(parentOut))
return false;
- int nPDepth = ptx->GetDepthInMainChain();
- if (nPDepth >= 1)
- continue;
- if (nPDepth < 0)
- return false;
- if (!pwallet->IsFromMe(*ptx))
- return false;
-
- if (mapPrev.empty())
- {
- BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
- mapPrev[tx.GetHash()] = &tx;
- }
-
- BOOST_FOREACH(const CTxIn& txin, ptx->vin)
- {
- if (!mapPrev.count(txin.prevout.hash))
- return false;
- vWorkQueue.push_back(mapPrev[txin.prevout.hash]);
- }
}
return true;
}
@@ -760,8 +687,6 @@ public:
int64_t GetTxTime() const;
int GetRequestCount() const;
- void AddSupportingTransactions();
- bool AcceptWalletTransaction();
void RelayWalletTransaction();
std::set<uint256> GetConflicts() const;