diff options
141 files changed, 2422 insertions, 1287 deletions
diff --git a/autogen.sh b/autogen.sh index 4e7011a596..5b883a6a4c 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1 +1,5 @@ -autoreconf -vif +#!/bin/sh +set -e +srcdir="$(dirname $0)" +cd "$srcdir" +autoreconf --install --force diff --git a/configure.ac b/configure.ac index df94438954..af230c355d 100644 --- a/configure.ac +++ b/configure.ac @@ -37,6 +37,13 @@ AM_MAINTAINER_MODE([enable]) dnl make the compilation flags quiet unless V=1 is used m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +# Enable wallet +AC_ARG_ENABLE([wallet], + [AS_HELP_STRING([--enable-wallet], + [enable wallet (default is yes)])], + [enable_wallet=$enableval], + [enable_wallet=yes]) + AC_ARG_WITH([miniupnpc], [AS_HELP_STRING([--with-miniupnpc], [enable UPNP (default is yes if libminiupnpc is found)])], @@ -362,12 +369,18 @@ AC_TRY_COMPILE([#include <sys/socket.h>], [ AC_MSG_RESULT(no)] ) -dnl Check for libdb_cxx -BITCOIN_FIND_BDB48 +if test x$enable_wallet != xno; then + dnl Check for libdb_cxx only if wallet enabled + BITCOIN_FIND_BDB48 +fi dnl Check for libminiupnpc (optional) if test x$use_upnp != xno; then - AC_CHECK_LIB([miniupnpc], [main],, [have_miniupnpc=no]) + AC_CHECK_HEADERS( + [miniupnpc/miniwget.h miniupnpc/miniupnpc.h miniupnpc/upnpcommands.h miniupnpc/upnperrors.h], + [AC_CHECK_LIB([miniupnpc], [main],, [have_miniupnpc=no])], + [have_miniupnpc=no] + ) fi dnl Check for boost libs @@ -518,10 +531,9 @@ else CPPFLAGS="$CPPFLAGS $QT_INCLUDES" fi ]) - - BITCOIN_QT_CHECK(AC_CHECK_HEADER([QtPlugin],,BITCOIN_QT_FAIL(QtCore headers missing),)) - BITCOIN_QT_CHECK(AC_CHECK_HEADER([QApplication],, BITCOIN_QT_FAIL(QtGui headers missing),)) - BITCOIN_QT_CHECK(AC_CHECK_HEADER([QLocalSocket],, BITCOIN_QT_FAIL(QtNetwork headers missing),)) + BITCOIN_QT_CHECK([AC_CHECK_HEADER([QtPlugin],,BITCOIN_QT_FAIL(QtCore headers missing))]) + BITCOIN_QT_CHECK([AC_CHECK_HEADER([QApplication],, BITCOIN_QT_FAIL(QtGui headers missing))]) + BITCOIN_QT_CHECK([AC_CHECK_HEADER([QLocalSocket],, BITCOIN_QT_FAIL(QtNetwork headers missing))]) BITCOIN_QT_CHECK([ if test x$use_tests = xyes; then @@ -593,6 +605,20 @@ if test "x$use_ccache" != "xno"; then AC_MSG_RESULT($use_ccache) fi +dnl enable wallet +AC_MSG_CHECKING([if wallet should be enabled]) +if test x$enable_wallet != xno; then + AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED([ENABLE_WALLET],[1],[Define to 1 to enable wallet functions]) + +else + AC_MSG_RESULT(no) + + if test "x$use_qt" != "xno"; then + AC_MSG_ERROR([Cannot currently build Qt GUI with wallet disabled. Use --without-qt.]) + fi +fi + dnl enable ipv6 support AC_MSG_CHECKING([if ipv6 should be enabled]) if test x$have_ipv6 = xno; then @@ -705,6 +731,7 @@ fi AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows]) +AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet == xyes]) AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov == xyes]) AM_CONDITIONAL([USE_COMPARISON_TOOL],[test x$use_comparison_tool != xno]) diff --git a/contrib/gitian-descriptors/deps-win32.yml b/contrib/gitian-descriptors/deps-win32.yml index 3f4e4e27f2..addb11d726 100644 --- a/contrib/gitian-descriptors/deps-win32.yml +++ b/contrib/gitian-descriptors/deps-win32.yml @@ -29,8 +29,8 @@ script: | export HOST=i686-w64-mingw32 # Input Integrity Check echo "2a9eb3cd4e8b114eb9179c0d3884d61658e7d8e8bf4984798a5f5bd48e325ebe openssl-1.0.1c.tar.gz" | sha256sum -c - echo "b75dae26151f9b031062c8d2f577a094b08da0ae44fe8c11175d0b9ff434cc02 libpng-1.5.9.tar.gz" | sha256sum -c echo "12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef db-4.8.30.NC.tar.gz" | sha256sum -c + echo "bbd6b756e6af44b5a5b0f9b93eada3fb8922ed1d6451b7d6f184d0ae0c813994 miniupnpc-1.6.tar.gz" | sha256sum -c echo "21235e08552e6feba09ea5e8d750805b3391c62fb81c71a235c0044dc7a8a61b zlib-1.2.6.tar.gz" | sha256sum -c echo "b75dae26151f9b031062c8d2f577a094b08da0ae44fe8c11175d0b9ff434cc02 libpng-1.5.9.tar.gz" | sha256sum -c echo "03c4bc7cd9a75747c3815d509bbe061907d615764f2357923f0db948c567068f qrencode-3.2.0.tar.bz2" | sha256sum -c diff --git a/doc/bitcoin_logo_doxygen.png b/doc/bitcoin_logo_doxygen.png Binary files differindex 2d62a8dc8a..258be86ede 100644 --- a/doc/bitcoin_logo_doxygen.png +++ b/doc/bitcoin_logo_doxygen.png diff --git a/doc/build-osx.md b/doc/build-osx.md index f70da685e9..6268a440a8 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -62,7 +62,7 @@ Installing the dependencies using MacPorts is very straightforward. 3. It is a good idea to build and run the unit tests, too: - make test + make check Instructions: HomeBrew ---------------------- @@ -87,7 +87,7 @@ Rerunning "openssl version" should now return the correct version. 1. Clone the github tree to get the source code and go into the directory. - git clone git@github.com:bitcoin/bitcoin.git bitcoin + git clone https://github.com/bitcoin/bitcoin.git cd bitcoin 2. Build bitcoind: @@ -98,7 +98,7 @@ Rerunning "openssl version" should now return the correct version. 3. It is a good idea to build and run the unit tests, too: - make test + make check Creating a release build ------------------------ diff --git a/doc/build-unix.md b/doc/build-unix.md index 27228cdc1b..704bc1f5c7 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -11,18 +11,13 @@ To Build This will build bitcoin-qt as well if the dependencies are met. -**Note:** on Ubuntu 13.10 (Saucy Salamander) the boost configuration script doesn't look in the -correct directory and an error about boost-system will appear. For now you need to do - - ./configure --with-boost-libdir=/usr/lib/x86_64-linux-gnu - Dependencies --------------------- Library Purpose Description ------- ------- ----------- libssl SSL Support Secure communications - libdb4.8 Berkeley DB Blockchain & wallet storage + libdb4.8 Berkeley DB Wallet storage libboost Boost C++ Library miniupnpc UPnP Support Optional firewall-jumping support qt GUI GUI toolkit @@ -93,7 +88,7 @@ are installed. Qt 4 is currently necessary to build the GUI. To build with Qt 4 you need the following: - apt-get install libqt4-dev libprotobuf-dev + apt-get install libqt4-dev libprotobuf-dev protobuf-compiler libqrencode (optional) can be installed with: @@ -178,3 +173,16 @@ Hardening enables the following features: RW- R-- RW- The STK RW- means that the stack is readable and writeable but not executable. + +Disable-wallet mode +-------------------- +When the intention is to run only a P2P node without a wallet, bitcoin may be compiled in +disable-wallet mode with: + + ./configure --disable-wallet + +In this case there is no dependency on Berkeley DB 4.8. + +Mining is also possible in disable-wallet mode, but only using the `getblocktemplate` RPC +call not `getwork`. + diff --git a/doc/release-process.md b/doc/release-process.md index feadb3c1b0..2c0e2a3677 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -17,7 +17,7 @@ Release Process ###tag version in git - git tag -a v(new version, e.g. 0.8.0) + git tag -s v(new version, e.g. 0.8.0) ###write release notes. git shortlog helps a lot, for example: @@ -172,4 +172,15 @@ From a directory containing bitcoin source, gitian.sigs and gitian zips popd - Upload gitian zips to SourceForge + +- Announce the release: + + - Add the release to bitcoin.org: https://github.com/bitcoin/bitcoin.org/tree/master/_releases + + - Release sticky on bitcointalk: https://bitcointalk.org/index.php?board=1.0 + + - Bitcoin-development mailing list + + - Optionally reddit /r/Bitcoin, ... + - Celebrate diff --git a/qa/pull-tester/build-tests.sh.in b/qa/pull-tester/build-tests.sh.in index 82ae60fdf1..86d4d9d0e9 100755 --- a/qa/pull-tester/build-tests.sh.in +++ b/qa/pull-tester/build-tests.sh.in @@ -68,6 +68,9 @@ fi cd @abs_top_srcdir@/linux-build make check +# Run RPC integration test on Linux: +@abs_top_srcdir@/qa/rpc-tests/wallet.sh @abs_top_srcdir@/linux-build/src + if [ $RUN_EXPENSIVE_TESTS = 1 ]; then # Run unit tests and blockchain-tester on Windows: cd @abs_top_srcdir@/win32-build diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md new file mode 100644 index 0000000000..c8537247d9 --- /dev/null +++ b/qa/rpc-tests/README.md @@ -0,0 +1,6 @@ +Regression tests of RPC interface +================================= + +wallet.sh : Test wallet send/receive code (see comments for details) + +util.sh : useful re-usable functions diff --git a/qa/rpc-tests/util.sh b/qa/rpc-tests/util.sh new file mode 100644 index 0000000000..dc2a319970 --- /dev/null +++ b/qa/rpc-tests/util.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# Functions used by more than one test + +function echoerr { + echo "$@" 1>&2; +} + +# Usage: ExtractKey <key> "<json_object_string>" +# Warning: this will only work for the very-well-behaved +# JSON produced by bitcoind, do NOT use it to try to +# parse arbitrary/nested/etc JSON. +function ExtractKey { + echo $2 | tr -d ' "{}\n' | awk -v RS=',' -F: "\$1 ~ /$1/ { print \$2}" +} + +function CreateDataDir { + DIR=$1 + mkdir -p $DIR + CONF=$DIR/bitcoin.conf + echo "regtest=1" >> $CONF + echo "keypool=2" >> $CONF + echo "rpcuser=rt" >> $CONF + echo "rpcpassword=rt" >> $CONF + echo "rpcwait=1" >> $CONF + shift + while (( "$#" )); do + echo $1 >> $CONF + shift + done +} + +function AssertEqual { + if (( $( echo "$1 == $2" | bc ) == 0 )) + then + echoerr "AssertEqual: $1 != $2" + exit 1 + fi +} + +# CheckBalance -datadir=... amount account minconf +function CheckBalance { + B=$( $CLI $1 getbalance $3 $4 ) + if (( $( echo "$B == $2" | bc ) == 0 )) + then + echoerr "bad balance: $B (expected $2)" + exit 1 + fi +} + +# Use: Address <datadir> [account] +function Address { + $CLI $1 getnewaddress $2 +} + +# Send from to amount +function Send { + from=$1 + to=$2 + amount=$3 + address=$(Address $to) + txid=$( $CLI $from sendtoaddress $address $amount ) +} + +# Use: Unspent <datadir> <n'th-last-unspent> <var> +function Unspent { + local r=$( $CLI $1 listunspent | awk -F'[ |:,"]+' "\$2 ~ /$3/ { print \$3 }" | tail -n $2 | head -n 1) + echo $r +} + +# Use: CreateTxn1 <datadir> <n'th-last-unspent> <destaddress> +# produces hex from signrawtransaction +function CreateTxn1 { + TXID=$(Unspent $1 $2 txid) + AMOUNT=$(Unspent $1 $2 amount) + VOUT=$(Unspent $1 $2 vout) + RAWTXN=$( $CLI $1 createrawtransaction "[{\"txid\":\"$TXID\",\"vout\":$VOUT}]" "{\"$3\":$AMOUNT}") + ExtractKey hex "$( $CLI $1 signrawtransaction $RAWTXN )" +} + +# Use: SendRawTxn <datadir> <hex_txn_data> +function SendRawTxn { + $CLI $1 sendrawtransaction $2 +} diff --git a/qa/rpc-tests/wallet.sh b/qa/rpc-tests/wallet.sh new file mode 100755 index 0000000000..dd511782d0 --- /dev/null +++ b/qa/rpc-tests/wallet.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash + +# Test block generation and basic wallet sending + +if [ $# -lt 1 ]; then + echo "Usage: $0 path_to_binaries" + echo "e.g. $0 ../../src" + exit 1 +fi + +BITCOIND=${1}/bitcoind +CLI=${1}/bitcoin-cli + +DIR="${BASH_SOURCE%/*}" +if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +. "$DIR/util.sh" + +D=$(mktemp -d test.XXXXX) + +D1=${D}/node1 +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 +B2ARGS="-datadir=$D2" +$BITCOIND $B2ARGS & +B2PID=$! + +D3=${D}/node3 +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 + +# 1 block, 50 XBT each == 50 XBT +$CLI $B1ARGS setgenerate true 1 +sleep 1 # sleep is necessary to let block broadcast +# 101 blocks, 1 mature == 50 XBT +$CLI $B2ARGS setgenerate true 101 +sleep 1 + +CheckBalance $B1ARGS 50 +CheckBalance $B2ARGS 50 + +# Send 21 XBT from 1 to 3. Second +# transaction will be child of first, and +# will require a fee +Send $B1ARGS $B3ARGS 11 +Send $B1ARGS $B3ARGS 10 + +# Have B1 mine a new block, and mature it +# to recover transaction fees +$CLI $B1ARGS setgenerate true 1 +sleep 1 + +# Have B2 mine 100 blocks so B1's block is mature: +$CLI $B2ARGS setgenerate true 100 +sleep 1 + +# 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" + +# 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) + +# Have B2 mine a block to confirm transactions: +$CLI $B2ARGS setgenerate true 1 +sleep 1 # allow time for block to be relayed + +# Check balances after confirmation +CheckBalance $B1ARGS 0 +CheckBalance $B3ARGS 100 +CheckBalance $B3ARGS "100-21" "from1" + +$CLI $B3ARGS stop > /dev/null 2>&1 +wait $B3PID +$CLI $B2ARGS stop > /dev/null 2>&1 +wait $B2PID +$CLI $B1ARGS stop > /dev/null 2>&1 +wait $B1PID + +echo "Tests successful, cleaning up" +trap "" EXIT +rm -rf $D +exit 0 diff --git a/share/pixmaps/bitcoin128.png b/share/pixmaps/bitcoin128.png Binary files differindex 04b56cf2a0..55039b1c92 100644 --- a/share/pixmaps/bitcoin128.png +++ b/share/pixmaps/bitcoin128.png diff --git a/share/pixmaps/bitcoin16.png b/share/pixmaps/bitcoin16.png Binary files differindex 16e240e79e..5537479095 100644 --- a/share/pixmaps/bitcoin16.png +++ b/share/pixmaps/bitcoin16.png diff --git a/share/pixmaps/bitcoin256.png b/share/pixmaps/bitcoin256.png Binary files differindex 0ba94fb46d..1d42116ef1 100644 --- a/share/pixmaps/bitcoin256.png +++ b/share/pixmaps/bitcoin256.png diff --git a/share/pixmaps/bitcoin32.png b/share/pixmaps/bitcoin32.png Binary files differindex 97a3f28029..367abfcc8e 100644 --- a/share/pixmaps/bitcoin32.png +++ b/share/pixmaps/bitcoin32.png diff --git a/share/pixmaps/bitcoin64.png b/share/pixmaps/bitcoin64.png Binary files differindex 8aedee811f..08c676ae4a 100644 --- a/share/pixmaps/bitcoin64.png +++ b/share/pixmaps/bitcoin64.png diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in index d0dd796561..54ced278f2 100644 --- a/share/qt/Info.plist.in +++ b/share/qt/Info.plist.in @@ -4,18 +4,25 @@ <dict> <key>CFBundleIconFile</key> <string>bitcoin.icns</string> + <key>CFBundlePackageType</key> <string>APPL</string> + <key>CFBundleGetInfoString</key> <string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@, Copyright © 2009-@COPYRIGHT_YEAR@ The Bitcoin developers</string> + <key>CFBundleShortVersionString</key> <string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@</string> + <key>CFBundleVersion</key> <string>@CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@</string> + <key>CFBundleSignature</key> <string>????</string> + <key>CFBundleExecutable</key> <string>Bitcoin-Qt</string> + <key>CFBundleIdentifier</key> <string>org.bitcoinfoundation.Bitcoin-Qt</string> @@ -69,7 +76,11 @@ <string>Owner</string> </dict> </array> + + <key>NSPrincipalClass</key> + <string>NSApplication</string> + <key>NSHighResolutionCapable</key> - <true/> + <string>True</string> </dict> </plist> diff --git a/src/Makefile.am b/src/Makefile.am index 561d2ca7ae..62dd63abef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,10 @@ include Makefile.include AM_CPPFLAGS += -I$(top_srcdir)/src/leveldb/helpers/memenv \ -I$(builddir) -noinst_LIBRARIES = libbitcoin.a +noinst_LIBRARIES = libbitcoin_server.a libbitcoin_common.a libbitcoin_cli.a +if ENABLE_WALLET +noinst_LIBRARIES += libbitcoin_wallet.a +endif bin_PROGRAMS = bitcoind bitcoin-cli @@ -33,24 +36,67 @@ obj/build.h: FORCE $(abs_top_srcdir) version.o: obj/build.h -libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp \ - rpcclient.cpp \ - rpcprotocol.cpp \ +libbitcoin_server_a_SOURCES = \ + addrman.cpp \ + alert.cpp \ rpcserver.cpp \ bloom.cpp \ - chainparams.cpp checkpoints.cpp core.cpp coins.cpp crypter.cpp db.cpp hash.cpp \ - init.cpp key.cpp keystore.cpp leveldbwrapper.cpp main.cpp miner.cpp \ - netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \ - rpcmining.cpp rpcnet.cpp rpcrawtransaction.cpp rpcwallet.cpp script.cpp \ - sync.cpp txdb.cpp txmempool.cpp util.cpp version.cpp wallet.cpp walletdb.cpp $(JSON_H) \ + chainparams.cpp \ + checkpoints.cpp \ + coins.cpp \ + init.cpp \ + keystore.cpp \ + leveldbwrapper.cpp \ + main.cpp \ + miner.cpp \ + net.cpp \ + noui.cpp \ + rpcblockchain.cpp \ + rpcmining.cpp \ + rpcnet.cpp \ + rpcrawtransaction.cpp \ + txdb.cpp \ + txmempool.cpp \ + $(JSON_H) \ $(BITCOIN_CORE_H) -nodist_libbitcoin_a_SOURCES = $(top_srcdir)/src/obj/build.h +libbitcoin_wallet_a_SOURCES = \ + db.cpp \ + crypter.cpp \ + rpcdump.cpp \ + rpcwallet.cpp \ + wallet.cpp \ + walletdb.cpp \ + $(BITCOIN_CORE_H) + +libbitcoin_common_a_SOURCES = \ + allocators.cpp \ + chainparams.cpp \ + core.cpp \ + hash.cpp \ + key.cpp \ + netbase.cpp \ + protocol.cpp \ + rpcprotocol.cpp \ + script.cpp \ + sync.cpp \ + util.cpp \ + version.cpp \ + $(BITCOIN_CORE_H) + +libbitcoin_cli_a_SOURCES = \ + rpcclient.cpp \ + $(BITCOIN_CORE_H) + +nodist_libbitcoin_common_a_SOURCES = $(top_srcdir)/src/obj/build.h # # bitcoind binary # -bitcoind_LDADD = libbitcoin.a leveldb/libleveldb.a leveldb/libmemenv.a \ +bitcoind_LDADD = libbitcoin_server.a libbitcoin_cli.a libbitcoin_common.a leveldb/libleveldb.a leveldb/libmemenv.a \ $(BOOST_LIBS) +if ENABLE_WALLET +bitcoind_LDADD += libbitcoin_wallet.a +endif bitcoind_SOURCES = bitcoind.cpp # @@ -62,15 +108,13 @@ AM_CPPFLAGS += $(BDB_CPPFLAGS) bitcoind_LDADD += $(BDB_LIBS) # bitcoin-cli binary # -bitcoin_cli_LDADD = libbitcoin.a leveldb/libleveldb.a leveldb/libmemenv.a \ - $(BOOST_LIBS) +bitcoin_cli_LDADD = libbitcoin_cli.a libbitcoin_common.a $(BOOST_LIBS) bitcoin_cli_SOURCES = bitcoin-cli.cpp # if TARGET_WINDOWS bitcoin_cli_SOURCES += bitcoin-cli-res.rc endif -bitcoin_cli_LDADD += $(BDB_LIBS) leveldb/libleveldb.a: leveldb/libmemenv.a @@ -79,7 +123,7 @@ leveldb/%.a: CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ OPT="$(CXXFLAGS) $(CPPFLAGS)" -qt/bitcoinstrings.cpp: $(libbitcoin_a_SOURCES) +qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_cli_a_SOURCES) @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" @cd $(top_srcdir); XGETTEXT=$(XGETTEXT) share/qt/extract_strings_qt.py @@ -91,5 +135,4 @@ EXTRA_DIST = leveldb Makefile.include clean-local: -$(MAKE) -C leveldb clean - rm -f leveldb/port/*.gcno leveldb/db/*.gcno leveldb/table/*.gcno leveldb/helpers/*.gcno - rm -f leveldb/util/*.gcno leveldb/helpers/memenv/*.gcno + rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno diff --git a/src/Makefile.include b/src/Makefile.include index 8c2274a287..13cffd29bc 100644 --- a/src/Makefile.include +++ b/src/Makefile.include @@ -5,7 +5,10 @@ AM_CPPFLAGS = $(INCLUDES) \ $(BOOST_INCLUDES) AM_LDFLAGS = $(PTHREAD_CFLAGS) -LIBBITCOIN=$(top_builddir)/src/libbitcoin.a +LIBBITCOIN_SERVER=$(top_builddir)/src/libbitcoin_server.a +LIBBITCOIN_WALLET=$(top_builddir)/src/libbitcoin_wallet.a +LIBBITCOIN_COMMON=$(top_builddir)/src/libbitcoin_common.a +LIBBITCOIN_CLI=$(top_builddir)/src/libbitcoin_cli.a LIBLEVELDB=$(top_builddir)/src/leveldb/libleveldb.a LIBMEMENV=$(top_builddir)/src/leveldb/libmemenv.a LIBBITCOINQT=$(top_builddir)/src/qt/libbitcoinqt.a diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index e4a648e980..04b75e7f1c 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -7,6 +7,7 @@ #include "init.h" #include "rpcclient.h" #include "ui_interface.h" /* for _(...) */ +#include "chainparams.h" #include <boost/filesystem/operations.hpp> @@ -26,6 +27,11 @@ static bool AppInitRPC(int argc, char* argv[]) return false; } ReadConfigFile(mapArgs, mapMultiArgs); + // Check for -testnet or -regtest parameter (TestNet() calls are only valid after this clause) + if (!SelectParamsFromCommandLine()) { + fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); + return false; + } if (argc<2 || mapArgs.count("-?") || mapArgs.count("--help")) { @@ -36,7 +42,7 @@ static bool AppInitRPC(int argc, char* argv[]) " bitcoin-cli [options] help " + _("List commands") + "\n" + " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; - strUsage += "\n" + HelpMessage(HMM_BITCOIN_CLI); + strUsage += "\n" + HelpMessageCli(true); fprintf(stdout, "%s", strUsage.c_str()); return false; diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index fdc66b8d65..e13d688158 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -87,6 +87,7 @@ bool AppInit(int argc, char* argv[]) " bitcoind [options] help <command> " + _("Get help for a command") + "\n"; strUsage += "\n" + HelpMessage(HMM_BITCOIND); + strUsage += "\n" + HelpMessageCli(false); fprintf(stdout, "%s", strUsage.c_str()); return false; diff --git a/src/coins.cpp b/src/coins.cpp index ed82c9df8b..86b2a6ef17 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -178,3 +178,19 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) } return true; } + +double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) +{ + if (tx.IsCoinBase()) + return 0.0; + double dResult = 0.0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + const CCoins &coins = GetCoins(txin.prevout.hash); + if (!coins.IsAvailable(txin.prevout.n)) continue; + if (coins.nHeight < nHeight) { + dResult += coins.vout[txin.prevout.n].nValue * (nHeight-coins.nHeight); + } + } + return tx.ComputePriority(dResult); +} diff --git a/src/coins.h b/src/coins.h index 2c72ee88e1..0ad28524a1 100644 --- a/src/coins.h +++ b/src/coins.h @@ -340,13 +340,15 @@ public: @param[in] tx transaction for which we are checking input total @return Sum of value of all inputs (scriptSigs) - @see CTransaction::FetchInputs */ int64_t GetValueIn(const CTransaction& tx); // Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx); + // Return priority of tx at height nHeight + double GetPriority(const CTransaction &tx, int nHeight); + const CTxOut &GetOutputFor(const CTxIn& input); private: diff --git a/src/compat.h b/src/compat.h index 1deef493c2..3924445b1a 100644 --- a/src/compat.h +++ b/src/compat.h @@ -11,7 +11,9 @@ #undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0501 +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 +#endif #ifndef NOMINMAX #define NOMINMAX #endif @@ -26,6 +28,7 @@ #include <windows.h> #include <ws2tcpip.h> #else +#include <sys/types.h> #include <arpa/inet.h> #include <ifaddrs.h> #include <limits.h> @@ -35,7 +38,6 @@ #include <sys/fcntl.h> #include <sys/mman.h> #include <sys/socket.h> -#include <sys/types.h> #include <unistd.h> #endif diff --git a/src/core.cpp b/src/core.cpp index 7a1c90e587..f41ea87fea 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -106,6 +106,37 @@ bool CTransaction::IsNewerThan(const CTransaction& old) const return fNewer; } +int64_t CTransaction::GetValueOut() const +{ + int64_t nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); + } + return nValueOut; +} + +double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const +{ + // In order to avoid disincentivizing cleaning up the UTXO set we don't count + // the constant overhead for each txin and up to 110 bytes of scriptSig (which + // is enough to cover a compressed pubkey p2sh redemption) for priority. + // Providing any more cleanup incentive than making additional inputs free would + // risk encouraging people to create junk outputs to redeem later. + if (nTxSize == 0) + nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); + BOOST_FOREACH(const CTxIn& txin, vin) + { + unsigned int offset = 41U + std::min(110U, (unsigned int)txin.scriptSig.size()); + if (nTxSize > offset) + nTxSize -= offset; + } + if (nTxSize == 0) return 0.0; + return dPriorityInputs / nTxSize; +} + std::string CTransaction::ToString() const { std::string str; diff --git a/src/core.h b/src/core.h index e31a7e6582..e61cad90ec 100644 --- a/src/core.h +++ b/src/core.h @@ -14,6 +14,10 @@ class CTransaction; +/** No amount larger than this (in satoshi) is valid */ +static const int64_t MAX_MONEY = 21000000 * COIN; +inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -50,11 +54,11 @@ public: class CInPoint { public: - CTransaction* ptx; + const CTransaction* ptx; unsigned int n; CInPoint() { SetNull(); } - CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } + CInPoint(const CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; } void SetNull() { ptx = NULL; n = (unsigned int) -1; } bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } }; @@ -217,6 +221,14 @@ public: uint256 GetHash() const; bool IsNewerThan(const CTransaction& old) const; + // Return sum of txouts. + int64_t GetValueOut() const; + // GetValueIn() is a method on CCoinsViewCache, because + // inputs must be known to compute value in. + + // Compute priority, given priority of inputs and (optionally) tx size + double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; + bool IsCoinBase() const { return (vin.size() == 1 && vin[0].prevout.IsNull()); diff --git a/src/crypter.cpp b/src/crypter.cpp index 10a34ae24a..4c43e3a798 100644 --- a/src/crypter.cpp +++ b/src/crypter.cpp @@ -4,9 +4,11 @@ #include "crypter.h" +#include "script.h" + #include <string> #include <vector> - +#include <boost/foreach.hpp> #include <openssl/aes.h> #include <openssl/evp.h> @@ -117,3 +119,156 @@ bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned return false; return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); } + +bool CCryptoKeyStore::SetCrypted() +{ + LOCK(cs_KeyStore); + if (fUseCrypto) + return true; + if (!mapKeys.empty()) + return false; + fUseCrypto = true; + return true; +} + +bool CCryptoKeyStore::Lock() +{ + if (!SetCrypted()) + return false; + + { + LOCK(cs_KeyStore); + vMasterKey.clear(); + } + + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + for (; mi != mapCryptedKeys.end(); ++mi) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; + CKeyingMaterial vchSecret; + if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + CKey key; + key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + if (key.GetPubKey() == vchPubKey) + break; + return false; + } + vMasterKey = vMasterKeyIn; + } + NotifyStatusChanged(this); + return true; +} + +bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::AddKeyPubKey(key, pubkey); + + if (IsLocked()) + return false; + + std::vector<unsigned char> vchCryptedSecret; + CKeyingMaterial vchSecret(key.begin(), key.end()); + if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) + return false; + + if (!AddCryptedKey(pubkey, vchCryptedSecret)) + return false; + } + return true; +} + + +bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) +{ + { + LOCK(cs_KeyStore); + if (!SetCrypted()) + return false; + + mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); + } + return true; +} + +bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::GetKey(address, keyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; + CKeyingMaterial vchSecret; + if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) + return false; + if (vchSecret.size() != 32) + return false; + keyOut.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); + return true; + } + } + return false; +} + +bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const +{ + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CKeyStore::GetPubKey(address, vchPubKeyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + vchPubKeyOut = (*mi).second.first; + return true; + } + } + return false; +} + +bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) +{ + { + LOCK(cs_KeyStore); + if (!mapCryptedKeys.empty() || IsCrypted()) + return false; + + fUseCrypto = true; + BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) + { + const CKey &key = mKey.second; + CPubKey vchPubKey = key.GetPubKey(); + CKeyingMaterial vchSecret(key.begin(), key.end()); + std::vector<unsigned char> vchCryptedSecret; + if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) + return false; + if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) + return false; + } + mapKeys.clear(); + } + return true; +} diff --git a/src/crypter.h b/src/crypter.h index 861c4f9441..4791428b48 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -7,6 +7,7 @@ #include "allocators.h" #include "serialize.h" +#include "keystore.h" class uint256; @@ -106,4 +107,86 @@ public: bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext); bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); +/** Keystore which keeps the private keys encrypted. + * It derives from the basic key store, which is used if no encryption is active. + */ +class CCryptoKeyStore : public CBasicKeyStore +{ +private: + CryptedKeyMap mapCryptedKeys; + + CKeyingMaterial vMasterKey; + + // if fUseCrypto is true, mapKeys must be empty + // if fUseCrypto is false, vMasterKey must be empty + bool fUseCrypto; + +protected: + bool SetCrypted(); + + // will encrypt previously unencrypted keys + bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); + + bool Unlock(const CKeyingMaterial& vMasterKeyIn); + +public: + CCryptoKeyStore() : fUseCrypto(false) + { + } + + bool IsCrypted() const + { + return fUseCrypto; + } + + bool IsLocked() const + { + if (!IsCrypted()) + return false; + bool result; + { + LOCK(cs_KeyStore); + result = vMasterKey.empty(); + } + return result; + } + + bool Lock(); + + virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); + bool HaveKey(const CKeyID &address) const + { + { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::HaveKey(address); + return mapCryptedKeys.count(address) > 0; + } + return false; + } + bool GetKey(const CKeyID &address, CKey& keyOut) const; + bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; + void GetKeys(std::set<CKeyID> &setAddress) const + { + if (!IsCrypted()) + { + CBasicKeyStore::GetKeys(setAddress); + return; + } + setAddress.clear(); + CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); + while (mi != mapCryptedKeys.end()) + { + setAddress.insert((*mi).first); + mi++; + } + } + + /* Wallet status (encrypted, locked) changed. + * Note: Called without locks held. + */ + boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged; +}; + #endif diff --git a/src/db.cpp b/src/db.cpp index a286d9f726..1f2ee1c554 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -479,113 +479,3 @@ void CDBEnv::Flush(bool fShutdown) } } - - - - - - - - - - -// -// CAddrDB -// - -CAddrDB::CAddrDB() -{ - pathAddr = GetDataDir() / "peers.dat"; -} - -bool CAddrDB::Write(const CAddrMan& addr) -{ - // Generate random temporary filename - unsigned short randv = 0; - RAND_bytes((unsigned char *)&randv, sizeof(randv)); - std::string tmpfn = strprintf("peers.dat.%04x", randv); - - // serialize addresses, checksum data up to that point, then append csum - CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(Params().MessageStart()); - ssPeers << addr; - uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); - ssPeers << hash; - - // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); - CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); - if (!fileout) - return error("CAddrman::Write() : open failed"); - - // Write and commit header, data - try { - fileout << ssPeers; - } - catch (std::exception &e) { - return error("CAddrman::Write() : I/O error"); - } - FileCommit(fileout); - fileout.fclose(); - - // replace existing peers.dat, if any, with new peers.dat.XXXX - if (!RenameOver(pathTmp, pathAddr)) - return error("CAddrman::Write() : Rename-into-place failed"); - - return true; -} - -bool CAddrDB::Read(CAddrMan& addr) -{ - // open input file, and associate with CAutoFile - FILE *file = fopen(pathAddr.string().c_str(), "rb"); - CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CAddrman::Read() : open failed"); - - // use file size to size memory buffer - int fileSize = GetFilesize(filein); - int dataSize = fileSize - sizeof(uint256); - //Don't try to resize to a negative number if file is small - if ( dataSize < 0 ) dataSize = 0; - vector<unsigned char> vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (std::exception &e) { - return error("CAddrman::Read() 2 : I/O error or stream data corrupted"); - } - filein.fclose(); - - CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); - if (hashIn != hashTmp) - return error("CAddrman::Read() : checksum mismatch; data corrupted"); - - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssPeers >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("CAddrman::Read() : invalid network magic number"); - - // de-serialize address data into one CAddrMan object - ssPeers >> addr; - } - catch (std::exception &e) { - return error("CAddrman::Read() : I/O error or stream data corrupted"); - } - - return true; -} - @@ -305,22 +305,4 @@ public: bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); }; - - - - - - - -/** Access to the (IP) address database (peers.dat) */ -class CAddrDB -{ -private: - boost::filesystem::path pathAddr; -public: - CAddrDB(); - bool Write(const CAddrMan& addr); - bool Read(CAddrMan& addr); -}; - #endif // BITCOIN_DB_H diff --git a/src/init.cpp b/src/init.cpp index da70ef9dba..72346be679 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -10,6 +10,7 @@ #include "init.h" #include "addrman.h" +#include "db.h" #include "rpcserver.h" #include "checkpoints.h" #include "miner.h" @@ -17,8 +18,10 @@ #include "txdb.h" #include "ui_interface.h" #include "util.h" +#ifdef ENABLE_WALLET #include "wallet.h" #include "walletdb.h" +#endif #include <inttypes.h> #include <stdint.h> @@ -35,9 +38,10 @@ using namespace std; using namespace boost; +#ifdef ENABLE_WALLET std::string strWalletFile; CWallet* pwalletMain; -CClientUIInterface uiInterface; +#endif #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for @@ -110,14 +114,19 @@ void Shutdown() mempool.AddTransactionsUpdated(1); StopRPCThreads(); ShutdownRPCMining(); +#ifdef ENABLE_WALLET if (pwalletMain) bitdb.Flush(false); GenerateBitcoins(false, NULL, 0); +#endif StopNode(); + UnregisterNodeSignals(GetNodeSignals()); { LOCK(cs_main); +#ifdef ENABLE_WALLET if (pwalletMain) pwalletMain->SetBestChain(chainActive.GetLocator()); +#endif if (pblocktree) pblocktree->Flush(); if (pcoinsTip) @@ -126,12 +135,16 @@ void Shutdown() delete pcoinsdbview; pcoinsdbview = NULL; delete pblocktree; pblocktree = NULL; } +#ifdef ENABLE_WALLET if (pwalletMain) bitdb.Flush(true); +#endif boost::filesystem::remove(GetPidFile()); UnregisterAllWallets(); +#ifdef ENABLE_WALLET if (pwalletMain) delete pwalletMain; +#endif LogPrintf("Shutdown : done\n"); } @@ -181,45 +194,43 @@ std::string HelpMessage(HelpMessageMode hmm) strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; strUsage += " -testnet " + _("Use the test network") + "\n"; - if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) - { - strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n"; - strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; - strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; - strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n"; - strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; - strUsage += " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n"; - strUsage += " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n"; - strUsage += " -onion=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"; - strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n"; - strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; - strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n"; - strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; - strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n"; - strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; - strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n"; - strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; - strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n"; - strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n"; - strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n"; - strUsage += " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n"; - strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n"; - strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n"; - strUsage += " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n"; - strUsage += " -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n"; - strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n"; + strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n"; + strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; + strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + "\n"; + strUsage += " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n"; + strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n"; + strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS proxy") + "\n"; + strUsage += " -socks=<n> " + _("Select SOCKS version for -proxy (4 or 5, default: 5)") + "\n"; + strUsage += " -onion=<ip:port> " + _("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)") + "\n"; + strUsage += " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n"; + strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n"; + strUsage += " -maxconnections=<n> " + _("Maintain at most <n> connections to peers (default: 125)") + "\n"; + strUsage += " -addnode=<ip> " + _("Add a node to connect to and attempt to keep the connection open") + "\n"; + strUsage += " -connect=<ip> " + _("Connect only to the specified node(s)") + "\n"; + strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n"; + strUsage += " -externalip=<ip> " + _("Specify your own public address") + "\n"; + strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n"; + strUsage += " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n"; + strUsage += " -checkpoints " + _("Only accept block chain matching built-in checkpoints (default: 1)") + "\n"; + strUsage += " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n"; + strUsage += " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n"; + strUsage += " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\n"; + strUsage += " -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n"; + strUsage += " -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n"; + strUsage += " -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n"; + strUsage += " -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n"; #ifdef USE_UPNP #if USE_UPNP - strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n"; + strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 1 when listening)") + "\n"; #else - strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; + strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; #endif #endif - strUsage += " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n"; - strUsage += " -debug=<category> " + _("Output debugging information (default: 0, supplying <category> is optional)") + "\n"; - strUsage += _("If <category> is not supplied, output all debugging information.") + "\n"; - strUsage += _("<category> can be:"); - strUsage += " addrman, alert, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below + strUsage += " -paytxfee=<amt> " + _("Fee per kB to add to transactions you send") + "\n"; + strUsage += " -debug=<category> " + _("Output debugging information (default: 0, supplying <category> is optional)") + "\n"; + strUsage += _("If <category> is not supplied, output all debugging information.") + "\n"; + strUsage += _("<category> can be:"); + strUsage += " addrman, alert, coindb, db, lock, rand, rpc, selectcoins, mempool, net"; // Don't translate these and qt below if (hmm == HMM_BITCOIN_QT) { strUsage += ", qt.\n"; @@ -228,15 +239,14 @@ std::string HelpMessage(HelpMessageMode hmm) { strUsage += ".\n"; } - strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n"; - strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; - strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; - strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " + strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n"; + strUsage += " -shrinkdebugfile " + _("Shrink debug.log file on client startup (default: 1 when no -debug)") + "\n"; + strUsage += " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n"; + strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " "solved instantly. This is intended for regression testing tools and app development.") + "\n"; #ifdef WIN32 - strUsage += " -printtodebugger " + _("Send trace/debug info to debugger") + "\n"; + strUsage += " -printtodebugger " + _("Send trace/debug info to debugger") + "\n"; #endif - } if (hmm == HMM_BITCOIN_QT) { @@ -250,53 +260,36 @@ std::string HelpMessage(HelpMessageMode hmm) #endif } - if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_CLI) - { - strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; - strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; - } - strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; - if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) - { - strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n"; - } else { - strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n"; - } - - if(hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) - { - strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n"; - strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; - strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; - strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; - strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; - strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; - strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; - strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; - strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n"; - strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n"; - strUsage += " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n"; - strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; - strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n"; - strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n"; - strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n"; - - strUsage += "\n" + _("Block creation options:") + "\n"; - strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; - strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n"; - strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n"; - } + strUsage += " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n"; + + strUsage += " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n"; + strUsage += " -rpcthreads=<n> " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; + strUsage += " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; + strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; + strUsage += " -alertnotify=<cmd> " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; + strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; + strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; + strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; + strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n"; + strUsage += " -checkblocks=<n> " + _("How many blocks to check at startup (default: 288, 0 = all)") + "\n"; + strUsage += " -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n"; + strUsage += " -txindex " + _("Maintain a full transaction index (default: 0)") + "\n"; + strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n"; + strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + "\n"; + strUsage += " -par=<n> " + _("Set the number of script verification threads (up to 16, 0 = auto, <0 = leave that many cores free, default: 0)") + "\n"; + + strUsage += "\n" + _("Block creation options:") + "\n"; + strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; + strUsage += " -blockmaxsize=<n> " + _("Set maximum block size in bytes (default: 250000)") + "\n"; + strUsage += " -blockprioritysize=<n> " + _("Set maximum size of high-priority/low-fee transactions in bytes (default: 27000)") + "\n"; strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; - if (hmm == HMM_BITCOIND || hmm == HMM_BITCOIN_QT) - { - strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n"; - strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n"; - strUsage += " -rpcsslciphers=<ciphers> " + _("Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)") + "\n"; - } + strUsage += " -rpcsslcertificatechainfile=<file.cert> " + _("Server certificate file (default: server.cert)") + "\n"; + strUsage += " -rpcsslprivatekeyfile=<file.pem> " + _("Server private key (default: server.pem)") + "\n"; + strUsage += " -rpcsslciphers=<ciphers> " + _("Acceptable ciphers (default: TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH)") + "\n"; return strUsage; } @@ -500,7 +493,9 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) fPrintToConsole = GetBoolArg("-printtoconsole", false); fPrintToDebugger = GetBoolArg("-printtodebugger", false); fLogTimestamps = GetBoolArg("-logtimestamps", true); +#ifdef ENABLE_WALLET bool fDisableWallet = GetBoolArg("-disablewallet", false); +#endif if (mapArgs.count("-timeout")) { @@ -546,16 +541,17 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) InitWarning(_("Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); } +#ifdef ENABLE_WALLET strWalletFile = GetArg("-wallet", "wallet.dat"); - +#endif // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log std::string strDataDir = GetDataDir().string(); - +#ifdef ENABLE_WALLET // Wallet file must be a plain filename without a directory if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile)) return InitError(strprintf(_("Wallet %s resides outside data directory %s"), strWalletFile.c_str(), strDataDir.c_str())); - +#endif // Make sure only a single Bitcoin process is using the data directory. boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. @@ -588,7 +584,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) int64_t nStart; // ********************************************************* Step 5: verify wallet database integrity - +#ifdef ENABLE_WALLET if (!fDisableWallet) { uiInterface.InitMessage(_("Verifying wallet...")); @@ -634,7 +630,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) return InitError(_("wallet.dat corrupt, salvage failed")); } } // (!fDisableWallet) - +#endif // ENABLE_WALLET // ********************************************************* Step 6: network initialization RegisterNodeSignals(GetNodeSignals()); @@ -901,7 +897,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) } // ********************************************************* Step 8: load wallet - +#ifdef ENABLE_WALLET if (fDisableWallet) { pwalletMain = NULL; LogPrintf("Wallet disabled!\n"); @@ -993,7 +989,9 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) nWalletDBUpdated++; } } // (!fDisableWallet) - +#else // ENABLE_WALLET + LogPrintf("No wallet compiled in!\n"); +#endif // !ENABLE_WALLET // ********************************************************* Step 9: import blocks // scan for better chains in the block chain database, that are not yet connected in the active best chain @@ -1037,25 +1035,29 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) //// debug print LogPrintf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); LogPrintf("nBestHeight = %d\n", chainActive.Height()); +#ifdef ENABLE_WALLET LogPrintf("setKeyPool.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); LogPrintf("mapWallet.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); LogPrintf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); +#endif StartNode(threadGroup); - // InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly. InitRPCMining(); if (fServer) StartRPCThreads(); +#ifdef ENABLE_WALLET // Generate coins in the background if (pwalletMain) GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", -1)); +#endif // ********************************************************* Step 12: finished uiInterface.InitMessage(_("Done loading")); +#ifdef ENABLE_WALLET if (pwalletMain) { // Add wallet transactions that aren't already in a block to mapTransactions pwalletMain->ReacceptWalletTransactions(); @@ -1063,6 +1065,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer) // Run a thread to flush wallet periodically threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); } +#endif return !fRequestShutdown; } diff --git a/src/init.h b/src/init.h index 864f2f4614..018d2d5ea3 100644 --- a/src/init.h +++ b/src/init.h @@ -26,8 +26,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer); enum HelpMessageMode { HMM_BITCOIND, - HMM_BITCOIN_QT, - HMM_BITCOIN_CLI + HMM_BITCOIN_QT }; std::string HelpMessage(HelpMessageMode mode); diff --git a/src/key.cpp b/src/key.cpp index 2fd68fa56b..b57b7c506c 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -148,10 +148,13 @@ public: } void SetSecretBytes(const unsigned char vch[32]) { + bool ret; BIGNUM bn; BN_init(&bn); - assert(BN_bin2bn(vch, 32, &bn)); - assert(EC_KEY_regenerate_key(pkey, &bn)); + ret = BN_bin2bn(vch, 32, &bn); + assert(ret); + ret = EC_KEY_regenerate_key(pkey, &bn); + assert(ret); BN_clear_free(&bn); } diff --git a/src/keystore.cpp b/src/keystore.cpp index 05427291e0..46402ea25b 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -56,155 +56,3 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) return false; } -bool CCryptoKeyStore::SetCrypted() -{ - LOCK(cs_KeyStore); - if (fUseCrypto) - return true; - if (!mapKeys.empty()) - return false; - fUseCrypto = true; - return true; -} - -bool CCryptoKeyStore::Lock() -{ - if (!SetCrypted()) - return false; - - { - LOCK(cs_KeyStore); - vMasterKey.clear(); - } - - NotifyStatusChanged(this); - return true; -} - -bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) -{ - { - LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - for (; mi != mapCryptedKeys.end(); ++mi) - { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CKeyingMaterial vchSecret; - if(!DecryptSecret(vMasterKeyIn, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - return false; - if (vchSecret.size() != 32) - return false; - CKey key; - key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); - if (key.GetPubKey() == vchPubKey) - break; - return false; - } - vMasterKey = vMasterKeyIn; - } - NotifyStatusChanged(this); - return true; -} - -bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::AddKeyPubKey(key, pubkey); - - if (IsLocked()) - return false; - - std::vector<unsigned char> vchCryptedSecret; - CKeyingMaterial vchSecret(key.begin(), key.end()); - if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) - return false; - - if (!AddCryptedKey(pubkey, vchCryptedSecret)) - return false; - } - return true; -} - - -bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) -{ - { - LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; - - mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); - } - return true; -} - -bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::GetKey(address, keyOut); - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - CKeyingMaterial vchSecret; - if (!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) - return false; - if (vchSecret.size() != 32) - return false; - keyOut.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); - return true; - } - } - return false; -} - -bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const -{ - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CKeyStore::GetPubKey(address, vchPubKeyOut); - - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - vchPubKeyOut = (*mi).second.first; - return true; - } - } - return false; -} - -bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) -{ - { - LOCK(cs_KeyStore); - if (!mapCryptedKeys.empty() || IsCrypted()) - return false; - - fUseCrypto = true; - BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys) - { - const CKey &key = mKey.second; - CPubKey vchPubKey = key.GetPubKey(); - CKeyingMaterial vchSecret(key.begin(), key.end()); - std::vector<unsigned char> vchCryptedSecret; - if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) - return false; - if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) - return false; - } - mapKeys.clear(); - } - return true; -} diff --git a/src/keystore.h b/src/keystore.h index 8d936bcab7..0d55e6c81e 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -93,87 +93,4 @@ public: typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > CryptedKeyMap; - -/** Keystore which keeps the private keys encrypted. - * It derives from the basic key store, which is used if no encryption is active. - */ -class CCryptoKeyStore : public CBasicKeyStore -{ -private: - CryptedKeyMap mapCryptedKeys; - - CKeyingMaterial vMasterKey; - - // if fUseCrypto is true, mapKeys must be empty - // if fUseCrypto is false, vMasterKey must be empty - bool fUseCrypto; - -protected: - bool SetCrypted(); - - // will encrypt previously unencrypted keys - bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); - - bool Unlock(const CKeyingMaterial& vMasterKeyIn); - -public: - CCryptoKeyStore() : fUseCrypto(false) - { - } - - bool IsCrypted() const - { - return fUseCrypto; - } - - bool IsLocked() const - { - if (!IsCrypted()) - return false; - bool result; - { - LOCK(cs_KeyStore); - result = vMasterKey.empty(); - } - return result; - } - - bool Lock(); - - virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey); - bool HaveKey(const CKeyID &address) const - { - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::HaveKey(address); - return mapCryptedKeys.count(address) > 0; - } - return false; - } - bool GetKey(const CKeyID &address, CKey& keyOut) const; - bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - void GetKeys(std::set<CKeyID> &setAddress) const - { - if (!IsCrypted()) - { - CBasicKeyStore::GetKeys(setAddress); - return; - } - setAddress.clear(); - CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - while (mi != mapCryptedKeys.end()) - { - setAddress.insert((*mi).first); - mi++; - } - } - - /* Wallet status (encrypted, locked) changed. - * Note: Called without locks held. - */ - boost::signals2::signal<void (CCryptoKeyStore* wallet)> NotifyStatusChanged; -}; - #endif diff --git a/src/m4/ax_boost_base.m4 b/src/m4/ax_boost_base.m4 index 57d14fe48d..c675f12d84 100644 --- a/src/m4/ax_boost_base.m4 +++ b/src/m4/ax_boost_base.m4 @@ -33,7 +33,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 21 +#serial 22 AC_DEFUN([AX_BOOST_BASE], [ @@ -97,6 +97,12 @@ if test "x$want_boost" = "xyes"; then ;; esac + dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give + dnl them priority over the other paths since, if libs are found there, they + dnl are almost assuredly the ones desired. + AC_REQUIRE([AC_CANONICAL_HOST]) + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + dnl first we check the system location for boost libraries dnl this location ist chosen if boost libraries are installed with the --layout=system option dnl or if you install boost with RPM diff --git a/src/main.cpp b/src/main.cpp index b6888f9750..d130e9705e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,10 @@ using namespace std; using namespace boost; +#if defined(NDEBUG) +# error "Bitcoin cannot be compiled without assertions." +#endif + // // Global state // @@ -149,17 +153,66 @@ void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock * // Registration of network node signals. // -int static GetHeight() +namespace { +// Maintain validation-specific state about nodes, protected by cs_main, instead +// by CNode's own locks. This simplifies asynchronous operation, where +// processing of incoming data is done after the ProcessMessage call returns, +// and we're no longer holding the node's locks. +struct CNodeState { + int nMisbehavior; + bool fShouldBan; + std::string name; + + CNodeState() { + nMisbehavior = 0; + fShouldBan = false; + } +}; + +map<NodeId, CNodeState> mapNodeState; + +// Requires cs_main. +CNodeState *State(NodeId pnode) { + map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); + if (it == mapNodeState.end()) + return NULL; + return &it->second; +} + +int GetHeight() { LOCK(cs_main); return chainActive.Height(); } +void InitializeNode(NodeId nodeid, const CNode *pnode) { + LOCK(cs_main); + CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; + state.name = pnode->addrName; +} + +void FinalizeNode(NodeId nodeid) { + LOCK(cs_main); + mapNodeState.erase(nodeid); +} +} + +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { + LOCK(cs_main); + CNodeState *state = State(nodeid); + if (state == NULL) + return false; + stats.nMisbehavior = state->nMisbehavior; + return true; +} + void RegisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); + nodeSignals.InitializeNode.connect(&InitializeNode); + nodeSignals.FinalizeNode.connect(&FinalizeNode); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) @@ -167,6 +220,8 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals) nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); + nodeSignals.InitializeNode.disconnect(&InitializeNode); + nodeSignals.FinalizeNode.disconnect(&FinalizeNode); } ////////////////////////////////////////////////////////////////////////////// @@ -379,21 +434,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; } -/** Amount of bitcoins spent by the transaction. - @return sum of all outputs (note: does not include fees) - */ -int64_t GetValueOut(const CTransaction& tx) -{ - int64_t nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("GetValueOut() : value out of range"); - } - return nValueOut; -} - // // Check transaction inputs, and make sure any // pay-to-script-hash transactions are evaluating IsStandard scripts @@ -660,7 +700,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; // Check for conflicts with in-memory transactions - CTransaction* ptxOld = NULL; { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -670,22 +709,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { // Disable replacement feature for now return false; - - // Allow replacing with a newer version of the same transaction - if (i != 0) - return false; - ptxOld = pool.mapNextTx[outpoint].ptx; - if (IsFinalTx(*ptxOld)) - return false; - if (!tx.IsNewerThan(*ptxOld)) - return false; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld) - return false; - } - break; } } } @@ -734,8 +757,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64_t nFees = view.GetValueIn(tx)-GetValueOut(tx); - unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + int64_t nValueIn = view.GetValueIn(tx); + int64_t nValueOut = tx.GetValueOut(); + int64_t nFees = nValueIn-nValueOut; + double dPriority = view.GetPriority(tx, chainActive.Height()); + + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); @@ -779,22 +807,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); } + // Store transaction in memory + pool.addUnchecked(hash, entry); } - // Store transaction in memory - { - if (ptxOld) - { - LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - pool.remove(*ptxOld); - } - pool.addUnchecked(hash, tx); - } - - ///// are we sure this is ok when loading transactions or restoring block txes - // If updated, erase old tx from wallet - if (ptxOld) - g_signals.EraseTransaction(ptxOld->GetHash()); g_signals.SyncTransaction(hash, tx, NULL); return true; @@ -1305,18 +1321,21 @@ void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) { + bool ret; // mark inputs spent if (!tx.IsCoinBase()) { BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoins &coins = inputs.GetCoins(txin.prevout.hash); CTxInUndo undo; - assert(coins.Spend(txin.prevout, undo)); + ret = coins.Spend(txin.prevout, undo); + assert(ret); txundo.vprevout.push_back(undo); } } // add outputs - assert(inputs.SetCoins(txhash, CCoins(tx, nHeight))); + ret = inputs.SetCoins(txhash, CCoins(tx, nHeight)); + assert(ret); } bool CScriptCheck::operator()() const { @@ -1370,12 +1389,12 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach } - if (nValueIn < GetValueOut(tx)) + if (nValueIn < tx.GetValueOut()) return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), REJECT_INVALID, "in < out"); // Tally transaction fees - int64_t nTxFee = nValueIn - GetValueOut(tx); + int64_t nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()), REJECT_INVALID, "fee < 0"); @@ -1628,7 +1647,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C REJECT_INVALID, "too many sigops"); } - nFees += view.GetValueIn(tx)-GetValueOut(tx); + nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector<CScriptCheck> vChecks; if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) @@ -1648,10 +1667,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (fBenchmark) LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) + if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", - GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)), + block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), REJECT_INVALID, "coinbase too large"); if (!control.Wait()) @@ -1690,7 +1709,9 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain - assert(view.SetBestBlock(pindex->GetBlockHash())); + bool ret; + ret = view.SetBestBlock(pindex->GetBlockHash()); + assert(ret); // Watch for transactions paying to me for (unsigned int i = 0; i < block.vtx.size(); i++) @@ -1785,7 +1806,9 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Flush changes to global coin state int64_t nStart = GetTimeMicros(); int nModified = view.GetCacheSize(); - assert(view.Flush()); + bool ret; + ret = view.Flush(); + assert(ret); int64_t nTime = GetTimeMicros() - nStart; if (fBenchmark) LogPrintf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); @@ -2215,6 +2238,8 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { + AssertLockHeld("cs_main"); + // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) @@ -2941,6 +2966,23 @@ bool static AlreadyHave(const CInv& inv) } +void Misbehaving(NodeId pnode, int howmuch) +{ + if (howmuch == 0) + return; + + CNodeState *state = State(pnode); + if (state == NULL) + return; + + state->nMisbehavior += howmuch; + if (state->nMisbehavior >= GetArg("-banscore", 100)) + { + LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name.c_str(), state->nMisbehavior-howmuch, state->nMisbehavior); + state->fShouldBan = true; + } else + LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name.c_str(), state->nMisbehavior-howmuch, state->nMisbehavior); +} void static ProcessGetData(CNode* pfrom) { @@ -3074,7 +3116,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pfrom->nVersion != 0) { pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); - pfrom->Misbehaving(1); + Misbehaving(pfrom->GetId(), 1); return false; } @@ -3179,7 +3221,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (pfrom->nVersion == 0) { // Must have a version message before anything else - pfrom->Misbehaving(1); + Misbehaving(pfrom->GetId(), 1); return false; } @@ -3200,7 +3242,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; if (vAddr.size() > 1000) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %"PRIszu"", vAddr.size()); } @@ -3263,7 +3305,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %"PRIszu"", vInv.size()); } @@ -3314,7 +3356,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %"PRIszu"", vInv.size()); } @@ -3487,7 +3529,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason(), inv.hash); if (nDoS > 0) - pfrom->Misbehaving(nDoS); + Misbehaving(pfrom->GetId(), nDoS); } } @@ -3514,7 +3556,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason(), inv.hash); if (nDoS > 0) - pfrom->Misbehaving(nDoS); + Misbehaving(pfrom->GetId(), nDoS); } } @@ -3657,7 +3699,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // This isn't a Misbehaving(100) (immediate ban) because the // peer might be an older or different implementation with // a different signature key, etc. - pfrom->Misbehaving(10); + Misbehaving(pfrom->GetId(), 10); } } } @@ -3670,7 +3712,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); else { LOCK(pfrom->cs_filter); @@ -3691,13 +3733,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); else - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); } } @@ -3962,6 +4004,16 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!lockMain) return true; + if (State(pto->GetId())->fShouldBan) { + if (pto->addr.IsLocal()) + LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString().c_str()); + else { + pto->fDisconnect = true; + CNode::Ban(pto->addr); + } + State(pto->GetId())->fShouldBan = false; + } + // Start block sync if (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; diff --git a/src/main.h b/src/main.h index cfbd0ea44a..fd5e352cb6 100644 --- a/src/main.h +++ b/src/main.h @@ -51,9 +51,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB -/** No amount larger than this (in satoshi) is valid */ -static const int64_t MAX_MONEY = 21000000 * COIN; -inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); } /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; /** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */ @@ -113,6 +110,7 @@ class CTxUndo; class CScriptCheck; class CValidationState; class CWalletInterface; +struct CNodeStateStats; struct CBlockTemplate; @@ -185,6 +183,8 @@ CBlockIndex * InsertBlockIndex(uint256 hash); bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); /** Abort with a message */ bool AbortNode(const std::string &msg); +/** Get statistics from node state */ +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, @@ -197,6 +197,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa +struct CNodeStateStats { + int nMisbehavior; +}; + struct CDiskBlockPos { int nFile; @@ -320,11 +324,6 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason); bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); -/** Amount of bitcoins spent by the transaction. - @return sum of all outputs (note: does not include fees) - */ -int64_t GetValueOut(const CTransaction& tx); - /** Undo information for a CBlock */ class CBlockUndo { diff --git a/src/miner.cpp b/src/miner.cpp index 03acadac55..edfbbf5736 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -10,9 +10,6 @@ #include "net.h" #include "wallet.h" -double dHashesPerSec = 0.0; -int64_t nHPSTimerStart = 0; - ////////////////////////////////////////////////////////////////////////////// // // BitcoinMiner @@ -54,51 +51,16 @@ void SHA256Transform(void* pstate, void* pinput, const void* pinit) ((uint32_t*)pstate)[i] = ctx.h[i]; } -// -// ScanHash scans nonces looking for a hash with at least some zero bits. -// It operates on big endian data. Caller does the byte reversing. -// All input buffers are 16-byte aligned. nNonce is usually preserved -// between calls, but periodically or if nNonce is 0xffff0000 or above, -// the block is rebuilt and nNonce starts over at zero. -// -unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) -{ - unsigned int& nNonce = *(unsigned int*)(pdata + 12); - for (;;) - { - // Crypto++ SHA256 - // Hash pdata using pmidstate as the starting state into - // pre-formatted buffer phash1, then hash phash1 into phash - nNonce++; - SHA256Transform(phash1, pdata, pmidstate); - SHA256Transform(phash, phash1, pSHA256InitState); - - // Return the nonce if the hash has at least some zero bits, - // caller will check if it has enough to reach the target - if (((unsigned short*)phash)[14] == 0) - return nNonce; - - // If nothing found after trying for a while, return -1 - if ((nNonce & 0xffff) == 0) - { - nHashesDone = 0xffff+1; - return (unsigned int) -1; - } - if ((nNonce & 0xfff) == 0) - boost::this_thread::interruption_point(); - } -} - // Some explaining would be appreciated class COrphan { public: - CTransaction* ptx; + const CTransaction* ptx; set<uint256> setDependsOn; double dPriority; double dFeePerKb; - COrphan(CTransaction* ptxIn) + COrphan(const CTransaction* ptxIn) { ptx = ptxIn; dPriority = dFeePerKb = 0; @@ -118,7 +80,7 @@ uint64_t nLastBlockTx = 0; uint64_t nLastBlockSize = 0; // We want to sort transactions by priority and fee, so: -typedef boost::tuple<double, double, CTransaction*> TxPriority; +typedef boost::tuple<double, double, const CTransaction*> TxPriority; class TxPriorityCompare { bool byFee; @@ -191,9 +153,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // This vector will be sorted into a priority queue: vector<TxPriority> vecPriority; vecPriority.reserve(mempool.mapTx.size()); - for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) + for (map<uint256, CTxMemPoolEntry>::iterator mi = mempool.mapTx.begin(); + mi != mempool.mapTx.end(); ++mi) { - CTransaction& tx = (*mi).second; + const CTransaction& tx = mi->second.GetTx(); if (tx.IsCoinBase() || !IsFinalTx(tx)) continue; @@ -228,7 +191,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) } mapDependers[txin.prevout.hash].push_back(porphan); porphan->setDependsOn.insert(txin.prevout.hash); - nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue; + nTotalIn += mempool.mapTx[txin.prevout.hash].GetTx().vout[txin.prevout.n].nValue; continue; } const CCoins &coins = view.GetCoins(txin.prevout.hash); @@ -244,24 +207,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Priority is sum(valuein * age) / modified_txsize unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - unsigned int nTxSizeMod = nTxSize; - // In order to avoid disincentivizing cleaning up the UTXO set we don't count - // the constant overhead for each txin and up to 110 bytes of scriptSig (which - // is enough to cover a compressed pubkey p2sh redemption) for priority. - // Providing any more cleanup incentive than making additional inputs free would - // risk encouraging people to create junk outputs to redeem later. - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); - if (nTxSizeMod > offset) - nTxSizeMod -= offset; - } - dPriority /= nTxSizeMod; + dPriority = tx.ComputePriority(dPriority, nTxSize); // This is a more accurate fee-per-kilobyte than is used by the client code, because the // client code rounds up the size to the nearest 1K. That's good, because it gives an // incentive to create smaller transactions. - double dFeePerKb = double(nTotalIn-GetValueOut(tx)) / (double(nTxSize)/1000.0); + double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); if (porphan) { @@ -269,7 +220,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) porphan->dFeePerKb = dFeePerKb; } else - vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second)); + vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &mi->second.GetTx())); } // Collect transactions into block @@ -286,7 +237,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Take highest priority transaction off the priority queue: double dPriority = vecPriority.front().get<0>(); double dFeePerKb = vecPriority.front().get<1>(); - CTransaction& tx = *(vecPriority.front().get<2>()); + const CTransaction& tx = *(vecPriority.front().get<2>()); std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); vecPriority.pop_back(); @@ -318,7 +269,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (!view.HaveInputs(tx)) continue; - int64_t nTxFees = view.GetValueIn(tx)-GetValueOut(tx); + int64_t nTxFees = view.GetValueIn(tx)-tx.GetValueOut(); nTxSigOps += GetP2SHSigOpCount(tx, view); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) @@ -392,16 +343,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return pblocktemplate.release(); } -CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) -{ - CPubKey pubkey; - if (!reservekey.GetReservedKey(pubkey)) - return NULL; - - CScript scriptPubKey = CScript() << pubkey << OP_CHECKSIG; - return CreateNewBlock(scriptPubKey); -} - void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) { // Update nExtraNonce @@ -465,6 +406,58 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash memcpy(phash1, &tmp.hash1, 64); } +#ifdef ENABLE_WALLET +////////////////////////////////////////////////////////////////////////////// +// +// Internal miner +// +double dHashesPerSec = 0.0; +int64_t nHPSTimerStart = 0; + +// +// ScanHash scans nonces looking for a hash with at least some zero bits. +// It operates on big endian data. Caller does the byte reversing. +// All input buffers are 16-byte aligned. nNonce is usually preserved +// between calls, but periodically or if nNonce is 0xffff0000 or above, +// the block is rebuilt and nNonce starts over at zero. +// +unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1, char* phash, unsigned int& nHashesDone) +{ + unsigned int& nNonce = *(unsigned int*)(pdata + 12); + for (;;) + { + // Crypto++ SHA256 + // Hash pdata using pmidstate as the starting state into + // pre-formatted buffer phash1, then hash phash1 into phash + nNonce++; + SHA256Transform(phash1, pdata, pmidstate); + SHA256Transform(phash, phash1, pSHA256InitState); + + // Return the nonce if the hash has at least some zero bits, + // caller will check if it has enough to reach the target + if (((unsigned short*)phash)[14] == 0) + return nNonce; + + // If nothing found after trying for a while, return -1 + if ((nNonce & 0xffff) == 0) + { + nHashesDone = 0xffff+1; + return (unsigned int) -1; + } + if ((nNonce & 0xfff) == 0) + boost::this_thread::interruption_point(); + } +} + +CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) +{ + CPubKey pubkey; + if (!reservekey.GetReservedKey(pubkey)) + return NULL; + + CScript scriptPubKey = CScript() << pubkey << OP_CHECKSIG; + return CreateNewBlock(scriptPubKey); +} bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { @@ -676,5 +669,5 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); } - +#endif diff --git a/src/net.cpp b/src/net.cpp index fcef9feea0..6ae749c657 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -80,6 +80,9 @@ CCriticalSection cs_setservAddNodeAddresses; vector<std::string> vAddedNodes; CCriticalSection cs_vAddedNodes; +NodeId nLastNodeId = 0; +CCriticalSection cs_nLastNodeId; + static CSemaphore *semOutbound = NULL; // Signals for message handling @@ -581,35 +584,21 @@ bool CNode::IsBanned(CNetAddr ip) return fResult; } -bool CNode::Misbehaving(int howmuch) -{ - if (addr.IsLocal()) +bool CNode::Ban(const CNetAddr &addr) { + int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban { - LogPrintf("Warning: Local node %s misbehaving (delta: %d)!\n", addrName.c_str(), howmuch); - return false; + LOCK(cs_setBanned); + if (setBanned[addr] < banTime) + setBanned[addr] = banTime; } - - nMisbehavior += howmuch; - if (nMisbehavior >= GetArg("-banscore", 100)) - { - int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban - LogPrintf("Misbehaving: %s (%d -> %d) DISCONNECTING\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); - { - LOCK(cs_setBanned); - if (setBanned[addr] < banTime) - setBanned[addr] = banTime; - } - CloseSocketDisconnect(); - return true; - } else - LogPrintf("Misbehaving: %s (%d -> %d)\n", addr.ToString().c_str(), nMisbehavior-howmuch, nMisbehavior); - return false; + return true; } #undef X #define X(name) stats.name = name void CNode::copyStats(CNodeStats &stats) { + stats.nodeid = this->GetId(); X(nServices); X(nLastSend); X(nLastRecv); @@ -619,7 +608,6 @@ void CNode::copyStats(CNodeStats &stats) X(cleanSubVer); X(fInbound); X(nStartingHeight); - X(nMisbehavior); X(nSendBytes); X(nRecvBytes); stats.fSyncNode = (this == pnodeSync); @@ -1690,7 +1678,7 @@ bool BindListenPort(const CService &addrBind, string& strError) return true; } -void static Discover() +void static Discover(boost::thread_group& threadGroup) { if (!fDiscover) return; @@ -1743,7 +1731,7 @@ void static Discover() // Don't use external IPv4 discovery, when -onlynet="IPv6" if (!IsLimited(NET_IPV4)) - boost::thread(boost::bind(&TraceThread<void (*)()>, "ext-ip", &ThreadGetMyExternalIP)); + threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "ext-ip", &ThreadGetMyExternalIP)); } void StartNode(boost::thread_group& threadGroup) @@ -1757,7 +1745,7 @@ void StartNode(boost::thread_group& threadGroup) if (pnodeLocalHost == NULL) pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); - Discover(); + Discover(threadGroup); // // Start threads @@ -1942,3 +1930,103 @@ void CNode::Fuzz(int nChance) // (more changes exponentially less likely): Fuzz(2); } + +// +// CAddrDB +// + +CAddrDB::CAddrDB() +{ + pathAddr = GetDataDir() / "peers.dat"; +} + +bool CAddrDB::Write(const CAddrMan& addr) +{ + // Generate random temporary filename + unsigned short randv = 0; + RAND_bytes((unsigned char *)&randv, sizeof(randv)); + std::string tmpfn = strprintf("peers.dat.%04x", randv); + + // serialize addresses, checksum data up to that point, then append csum + CDataStream ssPeers(SER_DISK, CLIENT_VERSION); + ssPeers << FLATDATA(Params().MessageStart()); + ssPeers << addr; + uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); + ssPeers << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("CAddrman::Write() : open failed"); + + // Write and commit header, data + try { + fileout << ssPeers; + } + catch (std::exception &e) { + return error("CAddrman::Write() : I/O error"); + } + FileCommit(fileout); + fileout.fclose(); + + // replace existing peers.dat, if any, with new peers.dat.XXXX + if (!RenameOver(pathTmp, pathAddr)) + return error("CAddrman::Write() : Rename-into-place failed"); + + return true; +} + +bool CAddrDB::Read(CAddrMan& addr) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathAddr.string().c_str(), "rb"); + CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CAddrman::Read() : open failed"); + + // use file size to size memory buffer + int fileSize = GetFilesize(filein); + int dataSize = fileSize - sizeof(uint256); + //Don't try to resize to a negative number if file is small + if ( dataSize < 0 ) dataSize = 0; + vector<unsigned char> vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (std::exception &e) { + return error("CAddrman::Read() 2 : I/O error or stream data corrupted"); + } + filein.fclose(); + + CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); + if (hashIn != hashTmp) + return error("CAddrman::Read() : checksum mismatch; data corrupted"); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssPeers >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("CAddrman::Read() : invalid network magic number"); + + // de-serialize address data into one CAddrMan object + ssPeers >> addr; + } + catch (std::exception &e) { + return error("CAddrman::Read() : I/O error or stream data corrupted"); + } + + return true; +} @@ -57,14 +57,19 @@ void StartNode(boost::thread_group& threadGroup); bool StopNode(); void SocketSendData(CNode *pnode); +typedef int NodeId; + // Signals for message handling struct CNodeSignals { boost::signals2::signal<int ()> GetHeight; boost::signals2::signal<bool (CNode*)> ProcessMessages; boost::signals2::signal<bool (CNode*, bool)> SendMessages; + boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode; + boost::signals2::signal<void (NodeId)> FinalizeNode; }; + CNodeSignals& GetNodeSignals(); @@ -109,12 +114,14 @@ extern limitedmap<CInv, int64_t> mapAlreadyAskedFor; extern std::vector<std::string> vAddedNodes; extern CCriticalSection cs_vAddedNodes; - +extern NodeId nLastNodeId; +extern CCriticalSection cs_nLastNodeId; class CNodeStats { public: + NodeId nodeid; uint64_t nServices; int64_t nLastSend; int64_t nLastRecv; @@ -124,7 +131,6 @@ public: std::string cleanSubVer; bool fInbound; int nStartingHeight; - int nMisbehavior; uint64_t nSendBytes; uint64_t nRecvBytes; bool fSyncNode; @@ -223,13 +229,13 @@ public: CCriticalSection cs_filter; CBloomFilter* pfilter; int nRefCount; + NodeId id; protected: // Denial-of-service detection/prevention // Key is IP address, value is banned-until-time static std::map<CNetAddr, int64_t> setBanned; static CCriticalSection cs_setBanned; - int nMisbehavior; // Basic fuzz-testing void Fuzz(int nChance); // modifies ssSend @@ -289,7 +295,6 @@ public: nStartingHeight = -1; fStartSync = false; fGetAddr = false; - nMisbehavior = 0; fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); pfilter = new CBloomFilter(); @@ -298,9 +303,16 @@ public: nPingUsecTime = 0; fPingQueued = false; + { + LOCK(cs_nLastNodeId); + id = nLastNodeId++; + } + // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) PushVersion(); + + GetNodeSignals().InitializeNode(GetId(), this); } ~CNode() @@ -312,6 +324,7 @@ public: } if (pfilter) delete pfilter; + GetNodeSignals().FinalizeNode(GetId()); } private: @@ -326,6 +339,9 @@ private: public: + NodeId GetId() const { + return id; + } int GetRefCount() { @@ -673,7 +689,7 @@ public: // new code. static void ClearBanned(); // needed for unit testing static bool IsBanned(CNetAddr ip); - bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot + static bool Ban(const CNetAddr &ip); void copyStats(CNodeStats &stats); // Network stats @@ -690,4 +706,15 @@ class CTransaction; void RelayTransaction(const CTransaction& tx, const uint256& hash); void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); +/** Access to the (IP) address database (peers.dat) */ +class CAddrDB +{ +private: + boost::filesystem::path pathAddr; +public: + CAddrDB(); + bool Write(const CAddrMan& addr); + bool Read(CAddrMan& addr); +}; + #endif diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 31c032ecbf..434373da29 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -96,6 +96,7 @@ QT_MOC_CPP = moc_aboutdialog.cpp moc_addressbookpage.cpp \ moc_optionsmodel.cpp moc_overviewpage.cpp moc_paymentserver.cpp \ moc_receiverequestdialog.cpp moc_qvalidatedlineedit.cpp moc_qvaluecombobox.cpp \ moc_receivecoinsdialog.cpp \ + moc_recentrequeststablemodel.cpp \ moc_rpcconsole.cpp moc_sendcoinsdialog.cpp moc_sendcoinsentry.cpp \ moc_signverifymessagedialog.cpp moc_splashscreen.cpp moc_trafficgraphwidget.cpp moc_transactiondesc.cpp \ moc_transactiondescdialog.cpp moc_transactionfilterproxy.cpp \ @@ -122,6 +123,7 @@ BITCOIN_QT_H = aboutdialog.h addressbookpage.h addresstablemodel.h \ optionsdialog.h \ optionsmodel.h overviewpage.h paymentrequestplus.h paymentserver.h \ receivecoinsdialog.h \ + recentrequeststablemodel.h \ receiverequestdialog.h qvalidatedlineedit.h qvaluecombobox.h rpcconsole.h \ sendcoinsdialog.h sendcoinsentry.h signverifymessagedialog.h splashscreen.h \ trafficgraphwidget.h transactiondescdialog.h transactiondesc.h transactionfilterproxy.h \ @@ -157,6 +159,7 @@ BITCOIN_QT_CPP = aboutdialog.cpp addressbookpage.cpp \ optionsdialog.cpp optionsmodel.cpp overviewpage.cpp paymentrequestplus.cpp \ paymentserver.cpp qvalidatedlineedit.cpp qvaluecombobox.cpp \ receivecoinsdialog.cpp receiverequestdialog.cpp \ + recentrequeststablemodel.cpp \ rpcconsole.cpp sendcoinsdialog.cpp sendcoinsentry.cpp \ signverifymessagedialog.cpp splashscreen.cpp trafficgraphwidget.cpp transactiondesc.cpp \ transactiondescdialog.cpp transactionfilterproxy.cpp transactionrecord.cpp \ @@ -197,7 +200,7 @@ endif bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ -I$(top_srcdir)/src/qt/forms bitcoin_qt_SOURCES = bitcoin.cpp -bitcoin_qt_LDADD = libbitcoinqt.a $(LIBBITCOIN) $(LIBLEVELDB) $(LIBMEMENV) \ +bitcoin_qt_LDADD = libbitcoinqt.a $(LIBBITCOIN_SERVER) $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) # forms/foo.h -> forms/ui_foo.h diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index d686cd4fd8..5e7d8e6178 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -297,7 +297,7 @@ QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, { if(orientation == Qt::Horizontal) { - if(role == Qt::DisplayRole) + if(role == Qt::DisplayRole && section < columns.size()) { return columns[section]; } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 57ce7da361..2b3bf3bfb5 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -53,7 +53,6 @@ static SplashScreen *splashref; static bool ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { - // Message from network thread if(guiref) { bool modal = (style & CClientUIInterface::MODAL); @@ -160,7 +159,7 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans /* qDebug() message handler --> debug.log */ #if QT_VERSION < 0x050000 -void DebugMessageHandler(QtMsgType type, const char * msg) +void DebugMessageHandler(QtMsgType type, const char *msg) { Q_UNUSED(type); LogPrint("qt", "Bitcoin-Qt: %s\n", msg); @@ -201,6 +200,13 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(bitcoin); QApplication app(argc, argv); +#if QT_VERSION > 0x050100 + // Generate high-dpi pixmaps + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif +#ifdef Q_OS_MAC + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); +#endif // Register meta types used for QMetaObject::invokeMethod qRegisterMetaType< bool* >(); @@ -313,11 +319,12 @@ int main(int argc, char *argv[]) splash.finish(&window); ClientModel clientModel(&optionsModel); + window.setClientModel(&clientModel); + WalletModel *walletModel = 0; if(pwalletMain) walletModel = new WalletModel(pwalletMain, &optionsModel); - window.setClientModel(&clientModel); if(walletModel) { window.addWallet("~Default", walletModel); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index a44627690f..b1daba5cba 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -69,28 +69,32 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : { GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); -#ifndef Q_OS_MAC if (!fIsTestnet) { setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet")); +#ifndef Q_OS_MAC QApplication::setWindowIcon(QIcon(":icons/bitcoin")); setWindowIcon(QIcon(":icons/bitcoin")); +#else + MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin")); +#endif } else { setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet") + " " + tr("[testnet]")); +#ifndef Q_OS_MAC QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet")); setWindowIcon(QIcon(":icons/bitcoin_testnet")); - } #else - setUnifiedTitleAndToolBarOnMac(true); - QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); - - if (!fIsTestnet) - MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin")); - else MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); #endif + } + +#if defined(Q_OS_MAC) && QT_VERSION < 0x050000 + // This property is not implemented in Qt 5. Setting it has no effect. + // A replacement API (QtMacUnifiedToolBar) is available in QtMacExtras. + setUnifiedTitleAndToolBarOnMac(true); +#endif // Create wallet frame and make it the central widget walletFrame = new WalletFrame(this); @@ -405,7 +409,7 @@ void BitcoinGUI::createTrayIcon(bool fIsTestnet) trayIcon->show(); #endif - notificator = new Notificator(QApplication::applicationName(), trayIcon); + notificator = new Notificator(QApplication::applicationName(), trayIcon, this); } void BitcoinGUI::createTrayIconMenu() diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index dcc528f4fd..05e8412528 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -84,7 +84,7 @@ signals: void alertsChanged(const QString &warnings); void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); - //! Asynchronous message notification + //! Fired when a message should be reported to the user void message(const QString &title, const QString &message, unsigned int style); public slots: diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 4ecc040bfb..14e5b07133 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -590,7 +590,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee l5->setText(((nBytes > 0) ? "~" : "") + QString::number(nBytes)); // Bytes l6->setText(sPriorityLabel); // Priority - l7->setText((fLowOutput ? (fDust ? tr("DUST") : tr("yes")) : tr("no"))); // Low Output / Dust + l7->setText((fLowOutput ? (fDust ? tr("Dust") : tr("yes")) : tr("no"))); // Low Output / Dust l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change // turn labels "red" @@ -600,10 +600,25 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l8->setStyleSheet((nChange > 0 && nChange < CENT) ? "color:red;" : ""); // Change < 0.01BTC // tool tips - l5->setToolTip(tr("This label turns red, if the transaction size is bigger than 1000 bytes.\n\n This means a fee of at least %1 per kb is required.\n\n Can vary +/- 1 Byte per input.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); - l6->setToolTip(tr("Transactions with higher priority get more likely into a block.\n\nThis label turns red, if the priority is smaller than \"medium\".\n\n This means a fee of at least %1 per kb is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); - l7->setToolTip(tr("This label turns red, if any recipient receives an amount smaller than %1.\n\n This means a fee of at least %2 is required. \n\n Amounts below 0.546 times the minimum relay fee are shown as DUST.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); - l8->setToolTip(tr("This label turns red, if the change is smaller than %1.\n\n This means a fee of at least %2 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)).arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee))); + QString toolTip1 = tr("This label turns red, if the transaction size is greater than 1000 bytes.") + "<br /><br />"; + toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)) + "<br /><br />"; + toolTip1 += tr("Can vary +/- 1 byte per input."); + + QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "<br /><br />"; + toolTip2 += tr("This label turns red, if the priority is smaller than \"medium\"") + "<br /><br />"; + toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)); + + QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)) + "<br /><br />"; + toolTip3 += tr("This means a fee of at least %1 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)) + "<br /><br />"; + toolTip3 += tr("Amounts below 0.546 times the minimum relay fee are shown as dust."); + + QString toolTip4 = tr("This label turns red, if the change is smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CENT)) + "<br /><br />"; + toolTip4 += tr("This means a fee of at least %1 is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::nMinTxFee)); + + l5->setToolTip(toolTip1); + l6->setToolTip(toolTip2); + l7->setToolTip(toolTip3); + l8->setToolTip(toolTip4); dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip()); dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index 055dd1f98d..e4faa312d4 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -38,8 +38,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlQuantityText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Quantity:</string> @@ -64,8 +67,11 @@ </item> <item row="1" column="0"> <widget class="QLabel" name="labelCoinControlBytesText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Bytes:</string> @@ -106,8 +112,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlAmountText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Amount:</string> @@ -132,8 +141,11 @@ </item> <item row="1" column="0"> <widget class="QLabel" name="labelCoinControlPriorityText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Priority:</string> @@ -148,6 +160,9 @@ <property name="contextMenuPolicy"> <enum>Qt::ActionsContextMenu</enum> </property> + <property name="text"> + <string notr="true">medium</string> + </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> @@ -171,8 +186,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlFeeText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Fee:</string> @@ -200,8 +218,11 @@ <property name="enabled"> <bool>false</bool> </property> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Low Output:</string> @@ -220,7 +241,7 @@ <enum>Qt::ActionsContextMenu</enum> </property> <property name="text"> - <string>no</string> + <string notr="true">no</string> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> @@ -245,8 +266,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlAfterFeeText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>After Fee:</string> @@ -274,8 +298,11 @@ <property name="enabled"> <bool>false</bool> </property> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Change:</string> @@ -377,7 +404,7 @@ <item> <widget class="QLabel" name="labelLocked"> <property name="text"> - <string>(1 locked)</string> + <string notr="true">(1 locked)</string> </property> </widget> </item> diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 6d1a72ecd2..e7138f5371 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -7,35 +7,60 @@ <x>0</x> <y>0</y> <width>776</width> - <height>343</height> + <height>364</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QGridLayout" name="gridLayout"> - <item row="3" column="0"> - <widget class="QLabel" name="label"> + <item row="7" column="2"> + <widget class="QCheckBox" name="reuseAddress"> + <property name="toolTip"> + <string>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</string> + </property> <property name="text"> - <string>&Amount:</string> + <string>R&euse an existing receiving address (not recommended)</string> + </property> + </widget> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>&Message:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="buddy"> - <cstring>reqAmount</cstring> + <cstring>reqMessage</cstring> </property> </widget> </item> - <item row="3" column="1"> - <widget class="BitcoinAmountField" name="reqAmount"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> + <item row="4" column="2"> + <widget class="QLineEdit" name="reqLabel"> + <property name="toolTip"> + <string>The label to associate with the new receiving address</string> </property> + </widget> + </item> + <item row="6" column="2"> + <widget class="QLineEdit" name="reqMessage"> <property name="toolTip"> - <string>The amount to request</string> + <string>The message to attach to payment request</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Use this form to request payments. All fields are optional.</string> </property> </widget> </item> @@ -52,73 +77,35 @@ </property> </widget> </item> - <item row="4" column="1"> - <widget class="QLineEdit" name="reqLabel"> - <property name="toolTip"> - <string>The label to associate with the receiving address</string> - </property> - </widget> - </item> <item row="5" column="0"> - <widget class="QLabel" name="label_3"> + <widget class="QLabel" name="label"> <property name="text"> - <string>&Message:</string> + <string>&Amount:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> <property name="buddy"> - <cstring>reqMessage</cstring> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QLineEdit" name="reqMessage"> - <property name="toolTip"> - <string>The message to attach to payment request</string> + <cstring>reqAmount</cstring> </property> </widget> </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string/> + <item row="5" column="2"> + <widget class="BitcoinAmountField" name="reqAmount"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="QCheckBox" name="reuseAddress"> <property name="toolTip"> - <string>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</string> - </property> - <property name="text"> - <string>R&euse an existing receiving address (not recommended)</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>Use this form to request payments. All fields are optional.</string> + <string>The amount to request</string> </property> </widget> </item> </layout> </item> <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPushButton" name="clearButton"> @@ -178,6 +165,98 @@ </item> </layout> </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QFrame" name="frame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_6"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Previously requested payments</string> + </property> + </widget> + </item> + <item> + <widget class="QTableView" name="recentRequestsView"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="showRequestButton"> + <property name="toolTip"> + <string>Show the selected request (does the same as double clicking an entry)</string> + </property> + <property name="text"> + <string>Show</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/edit</normaloff>:/icons/edit</iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeRequestButton"> + <property name="toolTip"> + <string>Remove the selected entries from the list</string> + </property> + <property name="text"> + <string>Remove</string> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> </layout> </widget> <customwidgets> @@ -187,6 +266,17 @@ <header>bitcoinamountfield.h</header> </customwidget> </customwidgets> + <tabstops> + <tabstop>reqLabel</tabstop> + <tabstop>reqAmount</tabstop> + <tabstop>reqMessage</tabstop> + <tabstop>reuseAddress</tabstop> + <tabstop>clearButton</tabstop> + <tabstop>receiveButton</tabstop> + <tabstop>recentRequestsView</tabstop> + <tabstop>showRequestButton</tabstop> + <tabstop>removeRequestButton</tabstop> + </tabstops> <resources> <include location="../bitcoin.qrc"/> </resources> diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui index c9cb3de69f..85928c9be5 100644 --- a/src/qt/forms/receiverequestdialog.ui +++ b/src/qt/forms/receiverequestdialog.ui @@ -84,13 +84,6 @@ </widget> </item> <item> - <widget class="QPushButton" name="btnCopyImage"> - <property name="text"> - <string>&Copy Image</string> - </property> - </widget> - </item> - <item> <widget class="QPushButton" name="btnSaveAs"> <property name="text"> <string>&Save Image...</string> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 790d5d6c39..db8271b016 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -39,7 +39,7 @@ </property> <layout class="QVBoxLayout" name="verticalLayoutCoinControl2"> <property name="spacing"> - <number>-1</number> + <number>0</number> </property> <property name="leftMargin"> <number>0</number> @@ -173,7 +173,16 @@ <string notr="true"/> </property> <layout class="QHBoxLayout" name="horizontalLayoutCoinControl5"> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -206,8 +215,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlQuantityText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Quantity:</string> @@ -219,12 +231,6 @@ </item> <item row="0" column="1"> <widget class="QLabel" name="labelCoinControlQuantity"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -244,8 +250,11 @@ </item> <item row="1" column="0"> <widget class="QLabel" name="labelCoinControlBytesText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Bytes:</string> @@ -254,12 +263,6 @@ </item> <item row="1" column="1"> <widget class="QLabel" name="labelCoinControlBytes"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -295,8 +298,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlAmountText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Amount:</string> @@ -308,12 +314,6 @@ </item> <item row="0" column="1"> <widget class="QLabel" name="labelCoinControlAmount"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -330,8 +330,11 @@ </item> <item row="1" column="0"> <widget class="QLabel" name="labelCoinControlPriorityText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Priority:</string> @@ -340,12 +343,6 @@ </item> <item row="1" column="1"> <widget class="QLabel" name="labelCoinControlPriority"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -353,7 +350,7 @@ <enum>Qt::ActionsContextMenu</enum> </property> <property name="text"> - <string>medium</string> + <string notr="true">medium</string> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> @@ -381,8 +378,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlFeeText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Fee:</string> @@ -394,12 +394,6 @@ </item> <item row="0" column="1"> <widget class="QLabel" name="labelCoinControlFee"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -416,8 +410,11 @@ </item> <item row="1" column="0"> <widget class="QLabel" name="labelCoinControlLowOutputText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Low Output:</string> @@ -426,12 +423,6 @@ </item> <item row="1" column="1"> <widget class="QLabel" name="labelCoinControlLowOutput"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -439,7 +430,7 @@ <enum>Qt::ActionsContextMenu</enum> </property> <property name="text"> - <string>no</string> + <string notr="true">no</string> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> @@ -467,8 +458,11 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="labelCoinControlAfterFeeText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>After Fee:</string> @@ -480,12 +474,6 @@ </item> <item row="0" column="1"> <widget class="QLabel" name="labelCoinControlAfterFee"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -502,8 +490,11 @@ </item> <item row="1" column="0"> <widget class="QLabel" name="labelCoinControlChangeText"> - <property name="styleSheet"> - <string notr="true">font-weight:bold;</string> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> </property> <property name="text"> <string>Change:</string> @@ -512,12 +503,6 @@ </item> <item row="1" column="1"> <widget class="QLabel" name="labelCoinControlChange"> - <property name="font"> - <font> - <family>Monospace</family> - <pointsize>10</pointsize> - </font> - </property> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> </property> @@ -555,13 +540,16 @@ </property> <item> <widget class="QCheckBox" name="checkBoxCoinControlChange"> + <property name="toolTip"> + <string>If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address.</string> + </property> <property name="text"> - <string>custom change address</string> + <string>Custom change address</string> </property> </widget> </item> <item> - <widget class="QLineEdit" name="lineEditCoinControlChange"> + <widget class="QValidatedLineEdit" name="lineEditCoinControlChange"> <property name="enabled"> <bool>false</bool> </property> @@ -626,7 +614,7 @@ <x>0</x> <y>0</y> <width>830</width> - <height>165</height> + <height>178</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1"> @@ -729,7 +717,7 @@ <cursorShape>IBeamCursor</cursorShape> </property> <property name="text"> - <string>123.456 BTC</string> + <string notr="true">123.456 BTC</string> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> @@ -778,6 +766,13 @@ </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>QValidatedLineEdit</class> + <extends>QLineEdit</extends> + <header>qvalidatedlineedit.h</header> + </customwidget> + </customwidgets> <resources> <include location="../bitcoin.qrc"/> </resources> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 051c3fb35b..b6cec5baf0 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -16,9 +16,6 @@ <property name="autoFillBackground"> <bool>false</bool> </property> - <property name="currentIndex"> - <number>0</number> - </property> <widget class="QFrame" name="SendCoins"> <property name="toolTip"> <string>This is a normal payment.</string> @@ -33,20 +30,7 @@ <property name="spacing"> <number>12</number> </property> - <item row="5" column="0"> - <widget class="QLabel" name="amountLabel"> - <property name="text"> - <string>A&mount:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>payAmount</cstring> - </property> - </widget> - </item> - <item row="3" column="0"> + <item row="0" column="0"> <widget class="QLabel" name="payToLabel"> <property name="text"> <string>Pay &To:</string> @@ -59,23 +43,7 @@ </property> </widget> </item> - <item row="5" column="1"> - <widget class="BitcoinAmountField" name="payAmount"/> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="labellLabel"> - <property name="text"> - <string>&Label:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="buddy"> - <cstring>addAsLabel</cstring> - </property> - </widget> - </item> - <item row="3" column="1"> + <item row="0" column="1"> <layout class="QHBoxLayout" name="payToLayout"> <property name="spacing"> <number>0</number> @@ -85,9 +53,6 @@ <property name="toolTip"> <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> </property> - <property name="maxLength"> - <number>34</number> - </property> </widget> </item> <item> @@ -127,7 +92,7 @@ <item> <widget class="QToolButton" name="deleteButton"> <property name="toolTip"> - <string>Remove this recipient</string> + <string>Remove this entry</string> </property> <property name="text"> <string/> @@ -140,13 +105,42 @@ </item> </layout> </item> - <item row="4" column="1"> + <item row="1" column="0"> + <widget class="QLabel" name="labellLabel"> + <property name="text"> + <string>&Label:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>addAsLabel</cstring> + </property> + </widget> + </item> + <item row="1" column="1"> <widget class="QLineEdit" name="addAsLabel"> <property name="toolTip"> <string>Enter a label for this address to add it to the list of used addresses</string> </property> </widget> </item> + <item row="2" column="0"> + <widget class="QLabel" name="amountLabel"> + <property name="text"> + <string>A&mount:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>payAmount</cstring> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="BitcoinAmountField" name="payAmount"/> + </item> </layout> </widget> <widget class="QFrame" name="SendCoins_InsecurePaymentRequest"> @@ -581,60 +575,77 @@ <property name="spacing"> <number>12</number> </property> - <item row="4" column="0"> - <widget class="QLabel" name="memoLabel_is"> + <item row="0" column="0"> + <widget class="QLabel" name="payToLabel_is"> <property name="text"> - <string>Memo:</string> + <string>Pay To:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> - <item row="5" column="0"> - <widget class="QLabel" name="amountLabel_is"> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="payToLayout_is"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="payTo_is"/> + </item> + <item> + <widget class="QToolButton" name="deleteButton_is"> + <property name="toolTip"> + <string>Remove this entry</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="memoLabel_is"> <property name="text"> - <string>Amount:</string> + <string>Memo:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> - <item row="3" column="0"> - <widget class="QLabel" name="payToLabel_is"> + <item row="1" column="1"> + <widget class="QLabel" name="memoTextLabel_is"> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="amountLabel_is"> <property name="text"> - <string>Pay To:</string> + <string>A&mount:</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> + <property name="buddy"> + <cstring>payAmount_is</cstring> + </property> </widget> </item> - <item row="5" column="2"> + <item row="2" column="1"> <widget class="BitcoinAmountField" name="payAmount_is"> <property name="acceptDrops"> <bool>false</bool> </property> </widget> </item> - <item row="3" column="2"> - <layout class="QHBoxLayout" name="payToLayout_is"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="payTo_is"/> - </item> - </layout> - </item> - <item row="4" column="2"> - <widget class="QLabel" name="memoTextLabel_is"> - <property name="textFormat"> - <enum>Qt::PlainText</enum> - </property> - </widget> - </item> </layout> </widget> <widget class="QFrame" name="SendCoins_SecurePaymentRequest"> @@ -1096,27 +1107,7 @@ <property name="spacing"> <number>12</number> </property> - <item row="4" column="0"> - <widget class="QLabel" name="memoLabel_s"> - <property name="text"> - <string>Memo:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="5" column="0"> - <widget class="QLabel" name="amountLabel_s"> - <property name="text"> - <string>Amount:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="3" column="0"> + <item row="0" column="0"> <widget class="QLabel" name="payToLabel_s"> <property name="text"> <string>Pay To:</string> @@ -1126,14 +1117,7 @@ </property> </widget> </item> - <item row="5" column="2"> - <widget class="BitcoinAmountField" name="payAmount_s"> - <property name="acceptDrops"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="3" column="2"> + <item row="0" column="1"> <layout class="QHBoxLayout" name="payToLayout_s"> <property name="spacing"> <number>0</number> @@ -1145,30 +1129,86 @@ </property> </widget> </item> + <item> + <widget class="QToolButton" name="deleteButton_s"> + <property name="toolTip"> + <string>Remove this entry</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../bitcoin.qrc"> + <normaloff>:/icons/remove</normaloff>:/icons/remove</iconset> + </property> + </widget> + </item> </layout> </item> - <item row="4" column="2"> + <item row="1" column="0"> + <widget class="QLabel" name="memoLabel_s"> + <property name="text"> + <string>Memo:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1"> <widget class="QLabel" name="memoTextLabel_s"> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> </widget> </item> + <item row="2" column="0"> + <widget class="QLabel" name="amountLabel_s"> + <property name="text"> + <string>A&mount:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy"> + <cstring>payAmount_s</cstring> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="BitcoinAmountField" name="payAmount_s"> + <property name="acceptDrops"> + <bool>false</bool> + </property> + </widget> + </item> </layout> </widget> </widget> <customwidgets> <customwidget> - <class>BitcoinAmountField</class> + <class>QValidatedLineEdit</class> <extends>QLineEdit</extends> - <header>bitcoinamountfield.h</header> + <header>qvalidatedlineedit.h</header> </customwidget> <customwidget> - <class>QValidatedLineEdit</class> + <class>BitcoinAmountField</class> <extends>QLineEdit</extends> - <header>qvalidatedlineedit.h</header> + <header>bitcoinamountfield.h</header> </customwidget> </customwidgets> + <tabstops> + <tabstop>payTo</tabstop> + <tabstop>addressBookButton</tabstop> + <tabstop>pasteButton</tabstop> + <tabstop>deleteButton</tabstop> + <tabstop>addAsLabel</tabstop> + <tabstop>payAmount</tabstop> + <tabstop>payAmount_is</tabstop> + <tabstop>deleteButton_is</tabstop> + <tabstop>payAmount_s</tabstop> + <tabstop>deleteButton_s</tabstop> + </tabstops> <resources> <include location="../bitcoin.qrc"/> </resources> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 2ce09b479e..8f8d67dc58 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -32,6 +32,7 @@ #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> + #include <QAbstractItemView> #include <QApplication> #include <QClipboard> diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index c9f6834ec2..1cef397d12 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -4,7 +4,6 @@ #include "notificator.h" - #include <QApplication> #include <QByteArray> #include <QIcon> @@ -32,7 +31,7 @@ // https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128 const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128; -Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent): +Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent) : QObject(parent), parent(parent), programName(programName), @@ -48,7 +47,7 @@ Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, } #ifdef USE_DBUS interface = new QDBusInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); + "/org/freedesktop/Notifications", "org.freedesktop.Notifications"); if(interface->isValid()) { mode = Freedesktop; diff --git a/src/qt/notificator.h b/src/qt/notificator.h index d6298c3c0d..abab986992 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -29,7 +29,7 @@ public: /** Create a new notificator. @note Ownership of trayIcon is not transferred to this object. */ - Notificator(const QString &programName=QString(), QSystemTrayIcon *trayIcon=0, QWidget *parent=0); + Notificator(const QString &programName, QSystemTrayIcon *trayIcon, QWidget *parent); ~Notificator(); // Message class diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 803a3c9ddb..06189aeaf3 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -48,5 +48,5 @@ void OpenURIDialog::on_selectFileButton_clicked() if(filename.isEmpty()) return; QUrl fileUri = QUrl::fromLocalFile(filename); - ui->uriEdit->setText("bitcoin:?request=" + QUrl::toPercentEncoding(fileUri.toString())); + ui->uriEdit->setText("bitcoin:?r=" + QUrl::toPercentEncoding(fileUri.toString())); } diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 34da0f90ec..ba5c06064f 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -232,7 +232,10 @@ bool PaymentServer::ipcSendCommandLine(int argc, char* argv[]) QLocalSocket* socket = new QLocalSocket(); socket->connectToServer(ipcServerName(), QIODevice::WriteOnly); if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) + { + delete socket; return false; + } QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); @@ -277,8 +280,11 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) : { uriServer = new QLocalServer(this); - if (!uriServer->listen(name)) - qDebug() << "PaymentServer::PaymentServer : Cannot start bitcoin: click-to-pay handler"; + if (!uriServer->listen(name)) { + // constructor is called early in init, so don't use "emit message()" here + QMessageBox::critical(0, tr("Payment request error"), + tr("Cannot start bitcoin: click-to-pay handler")); + } else { connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString))); @@ -295,12 +301,12 @@ PaymentServer::~PaymentServer() // OSX-specific way of handling bitcoin: URIs and // PaymentRequest mime types // -bool PaymentServer::eventFilter(QObject *, QEvent *event) +bool PaymentServer::eventFilter(QObject *object, QEvent *event) { - // clicking on bitcoin: URIs creates FileOpen events on the Mac: + // clicking on bitcoin: URIs creates FileOpen events on the Mac if (event->type() == QEvent::FileOpen) { - QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event); + QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event); if (!fileEvent->file().isEmpty()) handleURIOrFile(fileEvent->file()); else if (!fileEvent->url().isEmpty()) @@ -308,7 +314,8 @@ bool PaymentServer::eventFilter(QObject *, QEvent *event) return true; } - return false; + + return QObject::eventFilter(object, event); } void PaymentServer::initNetManager() @@ -359,17 +366,17 @@ void PaymentServer::handleURIOrFile(const QString& s) return; } - if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: + if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI { #if QT_VERSION < 0x050000 QUrl uri(s); #else QUrlQuery uri((QUrl(s))); #endif - if (uri.hasQueryItem("request")) + if (uri.hasQueryItem("r")) { QByteArray temp; - temp.append(uri.queryItemValue("request")); + temp.append(uri.queryItemValue("r")); QString decoded = QUrl::fromPercentEncoding(temp); QUrl fetchUrl(decoded, QUrl::StrictMode); diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 50f077f3bc..ab59388acc 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -77,10 +77,6 @@ public: // Return certificate store static X509_STORE* getCertStore() { return certStore; } - // Constructor registers this on the parent QApplication to - // receive QEvent::FileOpen events - bool eventFilter(QObject *object, QEvent *event); - // OptionsModel is used for getting proxy settings and display unit void setOptionsModel(OptionsModel *optionsModel); @@ -111,6 +107,11 @@ private slots: void reportSslErrors(QNetworkReply*, const QList<QSslError> &); void handlePaymentACK(const QString& paymentACKMsg); +protected: + // Constructor registers this on the parent QApplication to + // receive QEvent::FileOpen and QEvent:Drop events + bool eventFilter(QObject *object, QEvent *event); + private: static bool readPaymentRequest(const QString& filename, PaymentRequestPlus& request); bool processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient); diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 45b2325299..075a16dabf 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -12,6 +12,7 @@ #include "guiutil.h" #include "receiverequestdialog.h" #include "addresstablemodel.h" +#include "recentrequeststablemodel.h" #include <QMessageBox> #include <QTextDocument> @@ -27,6 +28,8 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget *parent) : #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac ui->clearButton->setIcon(QIcon()); ui->receiveButton->setIcon(QIcon()); + ui->showRequestButton->setIcon(QIcon()); + ui->removeRequestButton->setIcon(QIcon()); #endif connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); } @@ -39,6 +42,19 @@ void ReceiveCoinsDialog::setModel(WalletModel *model) { connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); updateDisplayUnit(); + + ui->recentRequestsView->setModel(model->getRecentRequestsTableModel()); + ui->recentRequestsView->setAlternatingRowColors(true); + ui->recentRequestsView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->recentRequestsView->setSelectionMode(QAbstractItemView::ContiguousSelection); + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Date, 130); + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Label, 120); +#if QT_VERSION < 0x050000 + ui->recentRequestsView->horizontalHeader()->setResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); +#else + ui->recentRequestsView->horizontalHeader()->setSectionResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch); +#endif + ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Amount, 100); } } @@ -76,7 +92,7 @@ void ReceiveCoinsDialog::updateDisplayUnit() void ReceiveCoinsDialog::on_receiveButton_clicked() { - if(!model || !model->getOptionsModel() || !model->getAddressTableModel()) + if(!model || !model->getOptionsModel() || !model->getAddressTableModel() || !model->getRecentRequestsTableModel()) return; QString address; @@ -101,11 +117,48 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, ""); } SendCoinsRecipient info(address, label, - ui->reqAmount->value(), ui->reqMessage->text()); + ui->reqAmount->value(), ui->reqMessage->text()); ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setModel(model->getOptionsModel()); dialog->setInfo(info); - dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); clear(); + + /* Store request for later reference */ + model->getRecentRequestsTableModel()->addNewRequest(info); +} + +void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex &index) +{ + const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel(); + ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); + dialog->setModel(model->getOptionsModel()); + dialog->setInfo(submodel->entry(index.row()).recipient); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); +} + +void ReceiveCoinsDialog::on_showRequestButton_clicked() +{ + if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) + return; + QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); + + foreach (QModelIndex index, selection) + { + on_recentRequestsView_doubleClicked(index); + } +} + +void ReceiveCoinsDialog::on_removeRequestButton_clicked() +{ + if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) + return; + QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); + if(selection.empty()) + return; + // correct for selection mode ContiguousSelection + QModelIndex firstIndex = selection.at(0); + model->getRecentRequestsTableModel()->removeRows(firstIndex.row(), selection.length(), firstIndex.parent()); } diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 9980edd1f5..4435bf6694 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -13,6 +13,9 @@ namespace Ui { } class WalletModel; class OptionsModel; +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE /** Dialog for requesting payment of bitcoins */ class ReceiveCoinsDialog : public QDialog @@ -36,6 +39,9 @@ private: private slots: void on_receiveButton_clicked(); + void on_showRequestButton_clicked(); + void on_removeRequestButton_clicked(); + void on_recentRequestsView_doubleClicked(const QModelIndex &index); void updateDisplayUnit(); }; diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 7e92715df8..b5e45341d9 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -85,12 +85,10 @@ ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : #ifndef USE_QRCODE ui->btnSaveAs->setVisible(false); - ui->btnCopyImage->setVisible(false); ui->lblQRCode->setVisible(false); #endif connect(ui->btnSaveAs, SIGNAL(clicked()), ui->lblQRCode, SLOT(saveImage())); - connect(ui->btnCopyImage, SIGNAL(clicked()), ui->lblQRCode, SLOT(copyImage())); } ReceiveRequestDialog::~ReceiveRequestDialog() diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp new file mode 100644 index 0000000000..06f64af146 --- /dev/null +++ b/src/qt/recentrequeststablemodel.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "recentrequeststablemodel.h" +#include "guiutil.h" +#include "bitcoinunits.h" +#include "optionsmodel.h" + +RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent): + walletModel(parent) +{ + /* These columns must match the indices in the ColumnIndex enumeration */ + columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount"); +} + +RecentRequestsTableModel::~RecentRequestsTableModel() +{ + /* Intentionally left empty */ +} + +int RecentRequestsTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return list.length(); +} + +int RecentRequestsTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length(); +} + +QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid() || index.row() >= list.length()) + return QVariant(); + + const RecentRequestEntry *rec = &list[index.row()]; + + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + switch(index.column()) + { + case Date: + return GUIUtil::dateTimeStr(rec->date); + case Label: + if(rec->recipient.label.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no label)"); + } + else + { + return rec->recipient.label; + } + case Message: + if(rec->recipient.message.isEmpty() && role == Qt::DisplayRole) + { + return tr("(no message)"); + } + else + { + return rec->recipient.message; + } + case Amount: + return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount); + } + } + return QVariant(); +} + +bool RecentRequestsTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return true; +} + +QVariant RecentRequestsTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole && section < columns.size()) + { + return columns[section]; + } + } + return QVariant(); +} + +QModelIndex RecentRequestsTableModel::index(int row, int column, const QModelIndex &parent) const +{ + return createIndex(row, column, 0); +} + +bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex &parent) +{ + Q_UNUSED(parent); + if(count > 0 && row >= 0 && (row+count) <= list.size()) + { + beginRemoveRows(parent, row, row + count - 1); + list.erase(list.begin() + row, list.begin() + row + count); + endRemoveRows(); + return true; + } else { + return false; + } +} + +Qt::ItemFlags RecentRequestsTableModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; +} + +void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient) +{ + RecentRequestEntry newEntry; + newEntry.date = QDateTime::currentDateTime(); + newEntry.recipient = recipient; + beginInsertRows(QModelIndex(), 0, 0); + list.prepend(newEntry); + endInsertRows(); +} diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h new file mode 100644 index 0000000000..d00a2a9055 --- /dev/null +++ b/src/qt/recentrequeststablemodel.h @@ -0,0 +1,61 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef RECENTREQUESTSTABLEMODEL_H +#define RECENTREQUESTSTABLEMODEL_H + +#include <QAbstractTableModel> +#include <QStringList> +#include <QDateTime> + +#include "walletmodel.h" + +class CWallet; + +struct RecentRequestEntry +{ + QDateTime date; + SendCoinsRecipient recipient; +}; + +/** Model for list of recently generated payment requests / bitcoin URIs. + * Part of wallet model. + */ +class RecentRequestsTableModel: public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit RecentRequestsTableModel(CWallet *wallet, WalletModel *parent = 0); + ~RecentRequestsTableModel(); + + enum ColumnIndex { + Date = 0, + Label = 1, + Message = 2, + Amount = 3 + }; + + /** @name Methods overridden from QAbstractTableModel + @{*/ + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + Qt::ItemFlags flags(const QModelIndex &index) const; + /*@}*/ + + const RecentRequestEntry &entry(int row) const { return list[row]; } + void addNewRequest(const SendCoinsRecipient &recipient); + +private: + WalletModel *walletModel; + QStringList columns; + QList<RecentRequestEntry> list; +}; + +#endif diff --git a/src/qt/res/icons/add.png b/src/qt/res/icons/add.png Binary files differindex f98e2a8ca7..ea53fc3493 100644 --- a/src/qt/res/icons/add.png +++ b/src/qt/res/icons/add.png diff --git a/src/qt/res/icons/address-book.png b/src/qt/res/icons/address-book.png Binary files differindex d41dbe6539..33a2d91754 100644 --- a/src/qt/res/icons/address-book.png +++ b/src/qt/res/icons/address-book.png diff --git a/src/qt/res/icons/bitcoin.png b/src/qt/res/icons/bitcoin.png Binary files differindex c90471bd07..ce5fbb0c2c 100644 --- a/src/qt/res/icons/bitcoin.png +++ b/src/qt/res/icons/bitcoin.png diff --git a/src/qt/res/icons/bitcoin_testnet.png b/src/qt/res/icons/bitcoin_testnet.png Binary files differindex d54125c0c6..1202021f53 100644 --- a/src/qt/res/icons/bitcoin_testnet.png +++ b/src/qt/res/icons/bitcoin_testnet.png diff --git a/src/qt/res/icons/clock1.png b/src/qt/res/icons/clock1.png Binary files differindex 448e47f947..9f0aa5db29 100644 --- a/src/qt/res/icons/clock1.png +++ b/src/qt/res/icons/clock1.png diff --git a/src/qt/res/icons/clock2.png b/src/qt/res/icons/clock2.png Binary files differindex c1a6e99f7f..bad00ccc63 100644 --- a/src/qt/res/icons/clock2.png +++ b/src/qt/res/icons/clock2.png diff --git a/src/qt/res/icons/clock3.png b/src/qt/res/icons/clock3.png Binary files differindex e429a402cf..7314d5302b 100644 --- a/src/qt/res/icons/clock3.png +++ b/src/qt/res/icons/clock3.png diff --git a/src/qt/res/icons/clock4.png b/src/qt/res/icons/clock4.png Binary files differindex ba036f47d3..07f5bfab5f 100644 --- a/src/qt/res/icons/clock4.png +++ b/src/qt/res/icons/clock4.png diff --git a/src/qt/res/icons/clock5.png b/src/qt/res/icons/clock5.png Binary files differindex 411d7a78a0..27e9630eb5 100644 --- a/src/qt/res/icons/clock5.png +++ b/src/qt/res/icons/clock5.png diff --git a/src/qt/res/icons/configure.png b/src/qt/res/icons/configure.png Binary files differindex 95bd319ce1..a1bd70aa4e 100644 --- a/src/qt/res/icons/configure.png +++ b/src/qt/res/icons/configure.png diff --git a/src/qt/res/icons/connect0_16.png b/src/qt/res/icons/connect0_16.png Binary files differindex 66f3ae4f86..a397e7e6a7 100644 --- a/src/qt/res/icons/connect0_16.png +++ b/src/qt/res/icons/connect0_16.png diff --git a/src/qt/res/icons/connect1_16.png b/src/qt/res/icons/connect1_16.png Binary files differindex 76000beee2..199fb44c53 100644 --- a/src/qt/res/icons/connect1_16.png +++ b/src/qt/res/icons/connect1_16.png diff --git a/src/qt/res/icons/connect2_16.png b/src/qt/res/icons/connect2_16.png Binary files differindex 6d9a37281a..2a85ba50d7 100644 --- a/src/qt/res/icons/connect2_16.png +++ b/src/qt/res/icons/connect2_16.png diff --git a/src/qt/res/icons/connect3_16.png b/src/qt/res/icons/connect3_16.png Binary files differindex a211700785..d4b35ecc5f 100644 --- a/src/qt/res/icons/connect3_16.png +++ b/src/qt/res/icons/connect3_16.png diff --git a/src/qt/res/icons/connect4_16.png b/src/qt/res/icons/connect4_16.png Binary files differindex c1232f5682..0376443b1c 100644 --- a/src/qt/res/icons/connect4_16.png +++ b/src/qt/res/icons/connect4_16.png diff --git a/src/qt/res/icons/debugwindow.png b/src/qt/res/icons/debugwindow.png Binary files differindex 1712adf0e7..767d0bb9f0 100644 --- a/src/qt/res/icons/debugwindow.png +++ b/src/qt/res/icons/debugwindow.png diff --git a/src/qt/res/icons/edit.png b/src/qt/res/icons/edit.png Binary files differindex 1d69145151..49e2ee3fc1 100644 --- a/src/qt/res/icons/edit.png +++ b/src/qt/res/icons/edit.png diff --git a/src/qt/res/icons/editcopy.png b/src/qt/res/icons/editcopy.png Binary files differindex f882aa2ad8..7807c59a88 100644 --- a/src/qt/res/icons/editcopy.png +++ b/src/qt/res/icons/editcopy.png diff --git a/src/qt/res/icons/editpaste.png b/src/qt/res/icons/editpaste.png Binary files differindex a192060bdd..e217e308ea 100644 --- a/src/qt/res/icons/editpaste.png +++ b/src/qt/res/icons/editpaste.png diff --git a/src/qt/res/icons/export.png b/src/qt/res/icons/export.png Binary files differindex 1df9c2398d..5c1f519054 100644 --- a/src/qt/res/icons/export.png +++ b/src/qt/res/icons/export.png diff --git a/src/qt/res/icons/filesave.png b/src/qt/res/icons/filesave.png Binary files differindex ae13a151d5..02e78b931a 100644 --- a/src/qt/res/icons/filesave.png +++ b/src/qt/res/icons/filesave.png diff --git a/src/qt/res/icons/history.png b/src/qt/res/icons/history.png Binary files differindex 10ac0e1592..ac955c7291 100644 --- a/src/qt/res/icons/history.png +++ b/src/qt/res/icons/history.png diff --git a/src/qt/res/icons/key.png b/src/qt/res/icons/key.png Binary files differindex ece0164f77..2638b4f231 100644 --- a/src/qt/res/icons/key.png +++ b/src/qt/res/icons/key.png diff --git a/src/qt/res/icons/lock_closed.png b/src/qt/res/icons/lock_closed.png Binary files differindex c566510c40..6de207db7d 100644 --- a/src/qt/res/icons/lock_closed.png +++ b/src/qt/res/icons/lock_closed.png diff --git a/src/qt/res/icons/lock_open.png b/src/qt/res/icons/lock_open.png Binary files differindex c98ca8663b..23ce3243aa 100644 --- a/src/qt/res/icons/lock_open.png +++ b/src/qt/res/icons/lock_open.png diff --git a/src/qt/res/icons/notsynced.png b/src/qt/res/icons/notsynced.png Binary files differindex c9e71184c5..87b1c3290b 100644 --- a/src/qt/res/icons/notsynced.png +++ b/src/qt/res/icons/notsynced.png diff --git a/src/qt/res/icons/overview.png b/src/qt/res/icons/overview.png Binary files differindex ee2511f01d..a274f0c488 100644 --- a/src/qt/res/icons/overview.png +++ b/src/qt/res/icons/overview.png diff --git a/src/qt/res/icons/qrcode.png b/src/qt/res/icons/qrcode.png Binary files differindex a8d97174b3..ee61aff2f0 100644 --- a/src/qt/res/icons/qrcode.png +++ b/src/qt/res/icons/qrcode.png diff --git a/src/qt/res/icons/quit.png b/src/qt/res/icons/quit.png Binary files differindex 0dde6f395c..bd73baee20 100644 --- a/src/qt/res/icons/quit.png +++ b/src/qt/res/icons/quit.png diff --git a/src/qt/res/icons/receive.png b/src/qt/res/icons/receive.png Binary files differindex 53ad1d1565..8ed337ca1a 100644 --- a/src/qt/res/icons/receive.png +++ b/src/qt/res/icons/receive.png diff --git a/src/qt/res/icons/remove.png b/src/qt/res/icons/remove.png Binary files differindex a44b6d130b..224d2c20c3 100644 --- a/src/qt/res/icons/remove.png +++ b/src/qt/res/icons/remove.png diff --git a/src/qt/res/icons/send.png b/src/qt/res/icons/send.png Binary files differindex ceb91ea66d..43c3d7922c 100644 --- a/src/qt/res/icons/send.png +++ b/src/qt/res/icons/send.png diff --git a/src/qt/res/icons/synced.png b/src/qt/res/icons/synced.png Binary files differindex 4d7e0e8821..9fad384768 100644 --- a/src/qt/res/icons/synced.png +++ b/src/qt/res/icons/synced.png diff --git a/src/qt/res/icons/toolbar.png b/src/qt/res/icons/toolbar.png Binary files differindex b694302871..c82d96519c 100644 --- a/src/qt/res/icons/toolbar.png +++ b/src/qt/res/icons/toolbar.png diff --git a/src/qt/res/icons/toolbar_testnet.png b/src/qt/res/icons/toolbar_testnet.png Binary files differindex dcac261c36..5995bc0667 100644 --- a/src/qt/res/icons/toolbar_testnet.png +++ b/src/qt/res/icons/toolbar_testnet.png diff --git a/src/qt/res/icons/transaction0.png b/src/qt/res/icons/transaction0.png Binary files differindex 4578666ee4..cfe1a1c8b8 100644 --- a/src/qt/res/icons/transaction0.png +++ b/src/qt/res/icons/transaction0.png diff --git a/src/qt/res/icons/transaction2.png b/src/qt/res/icons/transaction2.png Binary files differindex 01bb558a10..8a804b05ac 100644 --- a/src/qt/res/icons/transaction2.png +++ b/src/qt/res/icons/transaction2.png diff --git a/src/qt/res/icons/tx_inout.png b/src/qt/res/icons/tx_inout.png Binary files differindex 5f092f97aa..f1a7f7bbc3 100644 --- a/src/qt/res/icons/tx_inout.png +++ b/src/qt/res/icons/tx_inout.png diff --git a/src/qt/res/icons/tx_input.png b/src/qt/res/icons/tx_input.png Binary files differindex 0f5fea3a84..a2d324ee34 100644 --- a/src/qt/res/icons/tx_input.png +++ b/src/qt/res/icons/tx_input.png diff --git a/src/qt/res/icons/tx_mined.png b/src/qt/res/icons/tx_mined.png Binary files differindex 613f30fecc..a7acc6cf7b 100644 --- a/src/qt/res/icons/tx_mined.png +++ b/src/qt/res/icons/tx_mined.png diff --git a/src/qt/res/icons/tx_output.png b/src/qt/res/icons/tx_output.png Binary files differindex 9ae39fb329..a7c5ebf56b 100644 --- a/src/qt/res/icons/tx_output.png +++ b/src/qt/res/icons/tx_output.png diff --git a/src/qt/res/images/about.png b/src/qt/res/images/about.png Binary files differindex c9ab9511ef..fdede66172 100644 --- a/src/qt/res/images/about.png +++ b/src/qt/res/images/about.png diff --git a/src/qt/res/images/splash.png b/src/qt/res/images/splash.png Binary files differindex 376c274a8d..3f2b2fb2bf 100644 --- a/src/qt/res/images/splash.png +++ b/src/qt/res/images/splash.png diff --git a/src/qt/res/images/splash_testnet.png b/src/qt/res/images/splash_testnet.png Binary files differindex 2520e76e62..786dc9c3bb 100644 --- a/src/qt/res/images/splash_testnet.png +++ b/src/qt/res/images/splash_testnet.png diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index fa8da1c8dd..03cf7f51ea 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -227,8 +227,8 @@ void SendCoinsDialog::on_sendButton_clicked() alternativeUnits.append(BitcoinUnits::formatWithUnit(u, totalAmount)); } questionString.append(tr("Total Amount %1 (= %2)") - .arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)) - .arg(alternativeUnits.join(" "+tr("or")+" "))); + .arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)) + .arg(alternativeUnits.join(" " + tr("or") + " "))); QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), @@ -260,13 +260,11 @@ void SendCoinsDialog::clear() // Remove entries until only one left while(ui->entries->count()) { - delete ui->entries->takeAt(0)->widget(); + ui->entries->takeAt(0)->widget()->deleteLater(); } addEntry(); - updateRemoveEnabled(); - - ui->sendButton->setDefault(true); + updateTabsAndLabels(); } void SendCoinsDialog::reject() @@ -287,7 +285,7 @@ SendCoinsEntry *SendCoinsDialog::addEntry() connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels())); - updateRemoveEnabled(); + updateTabsAndLabels(); // Focus the field, so that entry can start immediately entry->clear(); @@ -300,27 +298,23 @@ SendCoinsEntry *SendCoinsDialog::addEntry() return entry; } -void SendCoinsDialog::updateRemoveEnabled() +void SendCoinsDialog::updateTabsAndLabels() { - // Remove buttons are enabled as soon as there is more than one send-entry - bool enabled = (ui->entries->count() > 1); - for(int i = 0; i < ui->entries->count(); ++i) - { - SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget()); - if(entry) - { - entry->setRemoveEnabled(enabled); - } - } setupTabChain(0); - coinControlUpdateLabels(); } void SendCoinsDialog::removeEntry(SendCoinsEntry* entry) { - delete entry; - updateRemoveEnabled(); + entry->hide(); + + // If the last entry is about to be removed add an empty one + if (ui->entries->count() == 1) + addEntry(); + + entry->deleteLater(); + + updateTabsAndLabels(); } QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) @@ -379,7 +373,7 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) } entry->setValue(rv); - coinControlUpdateLabels(); + updateTabsAndLabels(); } bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) @@ -537,20 +531,21 @@ void SendCoinsDialog::coinControlButtonClicked() // Coin Control: checkbox custom change address void SendCoinsDialog::coinControlChangeChecked(int state) { - if (model) + if (state == Qt::Unchecked) { - if (state == Qt::Checked) - CoinControlDialog::coinControl->destChange = CBitcoinAddress(ui->lineEditCoinControlChange->text().toStdString()).Get(); - else - CoinControlDialog::coinControl->destChange = CNoDestination(); + CoinControlDialog::coinControl->destChange = CNoDestination(); + ui->lineEditCoinControlChange->setValid(true); + ui->labelCoinControlChangeLabel->clear(); } + else + // use this to re-validate an already entered address + coinControlChangeEdited(ui->lineEditCoinControlChange->text()); ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked)); - ui->labelCoinControlChangeLabel->setVisible((state == Qt::Checked)); } // Coin Control: custom change address changed -void SendCoinsDialog::coinControlChangeEdited(const QString & text) +void SendCoinsDialog::coinControlChangeEdited(const QString& text) { if (model) { @@ -562,6 +557,10 @@ void SendCoinsDialog::coinControlChangeEdited(const QString & text) ui->labelCoinControlChangeLabel->setText(""); else if (!CBitcoinAddress(text.toStdString()).IsValid()) { + // invalid change address + CoinControlDialog::coinControl->destChange = CNoDestination(); + + ui->lineEditCoinControlChange->setValid(false); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address")); } @@ -579,6 +578,10 @@ void SendCoinsDialog::coinControlChangeEdited(const QString & text) ui->labelCoinControlChangeLabel->setText(tr("(no label)")); else { + // unknown change address + CoinControlDialog::coinControl->destChange = CNoDestination(); + + ui->lineEditCoinControlChange->setValid(false); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); } @@ -619,4 +622,3 @@ void SendCoinsDialog::coinControlUpdateLabels() ui->labelCoinControlInsuffFunds->hide(); } } - diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 4327e8e382..fcae26c720 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -46,7 +46,7 @@ public slots: void reject(); void accept(); SendCoinsEntry *addEntry(); - void updateRemoveEnabled(); + void updateTabsAndLabels(); void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); private: diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index 2641a66af4..4d28f68861 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -75,15 +75,13 @@ void SendCoinsEntry::setModel(WalletModel *model) connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); connect(ui->payAmount, SIGNAL(textChanged()), this, SIGNAL(payAmountChanged())); + connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); + connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked())); + connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked())); clear(); } -void SendCoinsEntry::setRemoveEnabled(bool enabled) -{ - ui->deleteButton->setEnabled(enabled); -} - void SendCoinsEntry::clear() { // clear UI elements for normal payment @@ -99,13 +97,11 @@ void SendCoinsEntry::clear() ui->memoTextLabel_s->clear(); ui->payAmount_s->clear(); - ui->payTo->setFocus(); - // update the display unit, to not use the default ("BTC") updateDisplayUnit(); } -void SendCoinsEntry::on_deleteButton_clicked() +void SendCoinsEntry::deleteClicked() { emit removeEntry(this); } diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 1c4ddaa8ef..2b696c77fb 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -46,7 +46,6 @@ public: void setFocus(); public slots: - void setRemoveEnabled(bool enabled); void clear(); signals: @@ -54,7 +53,7 @@ signals: void payAmountChanged(); private slots: - void on_deleteButton_clicked(); + void deleteClicked(); void on_payTo_textChanged(const QString &address); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); diff --git a/src/qt/test/Makefile.am b/src/qt/test/Makefile.am index 7891c3e975..cb6874700e 100644 --- a/src/qt/test/Makefile.am +++ b/src/qt/test/Makefile.am @@ -17,7 +17,7 @@ BUILT_SOURCES = $(TEST_QT_MOC_CPP) test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) $(QT_TEST_INCLUDES) test_bitcoin_qt_SOURCES = test_main.cpp uritests.cpp paymentservertests.cpp $(TEST_QT_H) nodist_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP) -test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN) $(LIBLEVELDB) \ +test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) \ $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index b8f2cc65cb..7dee7a9cda 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -7,12 +7,9 @@ #include <openssl/x509.h> #include <openssl/x509_vfy.h> -#include <QCoreApplication> -#include <QDebug> + #include <QFileOpenEvent> #include <QTemporaryFile> -#include <QVariant> - X509 *parse_b64der_cert(const char* cert_data) { @@ -41,9 +38,14 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig f.write((const char*)&data[0], data.size()); f.close(); - // Create a FileOpenEvent and send it directly to the server's event filter: + // Create a QObject, install event filter from PaymentServer + // and send a file open event to the object + QObject object; + object.installEventFilter(server); QFileOpenEvent event(f.fileName()); - server->eventFilter(NULL, &event); + // If sending the event fails, this will cause sigCatcher to be empty, + // which will lead to a test failure anyway. + QCoreApplication::sendEvent(&object, &event); QObject::disconnect(server, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), &sigCatcher, SLOT(getRecipient(SendCoinsRecipient))); diff --git a/src/qt/test/paymentservertests.h b/src/qt/test/paymentservertests.h index 0bff923ad4..884e535a60 100644 --- a/src/qt/test/paymentservertests.h +++ b/src/qt/test/paymentservertests.h @@ -20,8 +20,10 @@ private slots: class RecipientCatcher : public QObject { Q_OBJECT + public slots: void getRecipient(SendCoinsRecipient r); + public: SendCoinsRecipient recipient; }; diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 5c941c6387..ae584706f1 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,8 +1,7 @@ - - #include "paymentservertests.h" #include "uritests.h" +#include <QCoreApplication> #include <QObject> #include <QTest> @@ -11,6 +10,11 @@ int main(int argc, char *argv[]) { bool fInvalid = false; + // Don't remove this, it's needed to access + // QCoreApplication:: in the tests + QCoreApplication app(argc, argv); + app.setApplicationName("Bitcoin-Qt-test"); + URITests test1; if (QTest::qExec(&test1) != 0) fInvalid = true; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 8bd2bf564a..c0c4d53732 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -194,7 +194,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>"; } - int64_t nTxFee = nDebit - GetValueOut(wtx); + int64_t nTxFee = nDebit - wtx.GetValueOut(); if (nTxFee > 0) strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>"; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 675daa9c9c..6823557ebc 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -95,7 +95,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - int64_t nTxFee = nDebit - GetValueOut(wtx); + int64_t nTxFee = nDebit - wtx.GetValueOut(); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 2470af41a0..984a5a2e71 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -7,6 +7,7 @@ #include "addresstablemodel.h" #include "guiconstants.h" #include "transactiontablemodel.h" +#include "recentrequeststablemodel.h" #include "base58.h" #include "db.h" @@ -26,6 +27,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *parent) : QObject(parent), wallet(wallet), optionsModel(optionsModel), addressTableModel(0), transactionTableModel(0), + recentRequestsTableModel(0), cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), cachedNumTransactions(0), cachedEncryptionStatus(Unencrypted), @@ -33,6 +35,7 @@ WalletModel::WalletModel(CWallet *wallet, OptionsModel *optionsModel, QObject *p { addressTableModel = new AddressTableModel(wallet, this); transactionTableModel = new TransactionTableModel(wallet, this); + recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); // This timer will be fired repeatedly to update the balance pollTimer = new QTimer(this); @@ -325,6 +328,11 @@ TransactionTableModel *WalletModel::getTransactionTableModel() return transactionTableModel; } +RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() +{ + return recentRequestsTableModel; +} + WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { if(!wallet->IsCrypted()) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 32ddbbc6f6..44a1912ecc 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -18,6 +18,7 @@ class AddressTableModel; class OptionsModel; class TransactionTableModel; +class RecentRequestsTableModel; class WalletModelTransaction; class CCoinControl; @@ -88,6 +89,7 @@ public: OptionsModel *getOptionsModel(); AddressTableModel *getAddressTableModel(); TransactionTableModel *getTransactionTableModel(); + RecentRequestsTableModel *getRecentRequestsTableModel(); qint64 getBalance(const CCoinControl *coinControl = NULL) const; qint64 getUnconfirmedBalance() const; @@ -160,6 +162,7 @@ private: AddressTableModel *addressTableModel; TransactionTableModel *transactionTableModel; + RecentRequestsTableModel *recentRequestsTableModel; // Cache some values to be able to detect changes qint64 cachedBalance; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index ea71d58aa6..1a9c7866db 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -263,8 +263,8 @@ void WalletView::usedSendingAddresses() if(!walletModel) return; AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this); - dlg->setModel(walletModel->getAddressTableModel()); dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setModel(walletModel->getAddressTableModel()); dlg->show(); } @@ -273,7 +273,7 @@ void WalletView::usedReceivingAddresses() if(!walletModel) return; AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab, this); - dlg->setModel(walletModel->getAddressTableModel()); dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setModel(walletModel->getAddressTableModel()); dlg->show(); } diff --git a/src/qt/walletview.h b/src/qt/walletview.h index d951691621..ecfa06ac5a 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -96,6 +96,7 @@ public slots: /** Re-emit encryption status signal */ void updateEncryptionStatus(); + signals: /** Signal that we want to show the main window */ void showNormalIfMinimized(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 71663bbb34..34ae6e0543 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -153,28 +153,79 @@ Value settxfee(const Array& params, bool fHelp) Value getrawmempool(const Array& params, bool fHelp) { - if (fHelp || params.size() != 0) + if (fHelp || params.size() > 1) throw runtime_error( - "getrawmempool\n" + "getrawmempool ( verbose )\n" "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" - "\nResult:\n" - "[ (json array of string)\n" + "\nArguments:\n" + "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" + "\nResult: (for verbose = false):\n" + "[ (json array of string)\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" "]\n" + "\nResult: (for verbose = true):\n" + "{ (json object)\n" + " \"transactionid\" : { (json object)\n" + " \"size\" : n, (numeric) transaction size in bytes\n" + " \"fee\" : n, (numeric) transaction fee in bitcoins\n" + " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" + " \"height\" : n, (numeric) block height when transaction entered pool\n" + " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" + " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" + " \"transactionid\", (string) parent transaction id\n" + " ... ]\n" + " }, ...\n" + "]\n" "\nExamples\n" - + HelpExampleCli("getrawmempool", "") - + HelpExampleRpc("getrawmempool", "") + + HelpExampleCli("getrawmempool", "true") + + HelpExampleRpc("getrawmempool", "true") ); - vector<uint256> vtxid; - mempool.queryHashes(vtxid); + bool fVerbose = false; + if (params.size() > 0) + fVerbose = params[0].get_bool(); + + if (fVerbose) + { + LOCK(mempool.cs); + Object o; + BOOST_FOREACH(const PAIRTYPE(uint256, CTxMemPoolEntry)& entry, mempool.mapTx) + { + const uint256& hash = entry.first; + const CTxMemPoolEntry& e = entry.second; + Object info; + info.push_back(Pair("size", (int)e.GetTxSize())); + info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); + info.push_back(Pair("time", (boost::int64_t)e.GetTime())); + info.push_back(Pair("height", (int)e.GetHeight())); + info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); + info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); + const CTransaction& tx = e.GetTx(); + set<string> setDepends; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (mempool.exists(txin.prevout.hash)) + setDepends.insert(txin.prevout.hash.ToString()); + } + Array depends(setDepends.begin(), setDepends.end()); + info.push_back(Pair("depends", depends)); + o.push_back(Pair(hash.ToString(), info)); + } + return o; + } + else + { + vector<uint256> vtxid; + mempool.queryHashes(vtxid); - Array a; - BOOST_FOREACH(const uint256& hash, vtxid) - a.push_back(hash.ToString()); + Array a; + BOOST_FOREACH(const uint256& hash, vtxid) + a.push_back(hash.ToString()); - return a; + return a; + } } Value getblockhash(const Array& params, bool fHelp) diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 2667a5d5a5..c404ac274b 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -176,6 +176,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri if (strMethod == "verifychain" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]); return params; } @@ -244,3 +245,35 @@ int CommandLineRPC(int argc, char *argv[]) } return nRet; } + +std::string HelpMessageCli(bool mainProgram) +{ + string strUsage; + if(mainProgram) + { + strUsage += _("Options:") + "\n"; + strUsage += " -? " + _("This help message") + "\n"; + strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; + strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; + strUsage += " -testnet " + _("Use the test network") + "\n"; + strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " + "solved instantly. This is intended for regression testing tools and app development.") + "\n"; + } else { + strUsage += _("RPC client options:") + "\n"; + } + + strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; + strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n"; + strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; + strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; + strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; + + if(mainProgram) + { + strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; + strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; + } + + return strUsage; +} + diff --git a/src/rpcclient.h b/src/rpcclient.h index f3ea56c25b..e101d22ec5 100644 --- a/src/rpcclient.h +++ b/src/rpcclient.h @@ -14,4 +14,12 @@ int CommandLineRPC(int argc, char *argv[]); json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams); +/** Show help message for bitcoin-cli. + * The mainProgram argument is used to determine whether to show this message as main program + * (and include some common options) or as sub-header of another help message. + * + * @note the argument can be removed once bitcoin-cli functionality is removed from bitcoind + */ +std::string HelpMessageCli(bool mainProgram); + #endif diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 131a258c84..b81433120e 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -20,7 +20,8 @@ using namespace json_spirit; using namespace std; -// Key used by getwork/getblocktemplate miners. +#ifdef ENABLE_WALLET +// Key used by getwork miners. // Allocated in InitRPCMining, free'd in ShutdownRPCMining static CReserveKey* pMiningKey = NULL; @@ -40,6 +41,14 @@ void ShutdownRPCMining() delete pMiningKey; pMiningKey = NULL; } +#else +void InitRPCMining() +{ +} +void ShutdownRPCMining() +{ +} +#endif // Return average network hashes per second based on the last 'lookup' blocks, // or from the last difficulty change if 'lookup' is nonpositive. @@ -99,7 +108,7 @@ Value getnetworkhashps(const Array& params, bool fHelp) return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); } - +#ifdef ENABLE_WALLET Value getgenerate(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -197,7 +206,6 @@ Value setgenerate(const Array& params, bool fHelp) return Value::null; } - Value gethashespersec(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -216,6 +224,7 @@ Value gethashespersec(const Array& params, bool fHelp) return (boost::int64_t)0; return (boost::int64_t)dHashesPerSec; } +#endif Value getmininginfo(const Array& params, bool fHelp) @@ -248,16 +257,19 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("generate", getgenerate(params, false))); obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); - obj.push_back(Pair("hashespersec", gethashespersec(params, false))); obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("testnet", TestNet())); +#ifdef ENABLE_WALLET + obj.push_back(Pair("generate", getgenerate(params, false))); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); +#endif return obj; } +#ifdef ENABLE_WALLET Value getwork(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -381,7 +393,7 @@ Value getwork(const Array& params, bool fHelp) return CheckWork(pblock, *pwalletMain, *pMiningKey); } } - +#endif Value getblocktemplate(const Array& params, bool fHelp) { diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 99602bd1cf..fd72fe6386 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -3,11 +3,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "rpcserver.h" + +#include "main.h" #include "net.h" #include "netbase.h" #include "protocol.h" #include "sync.h" #include "util.h" +#ifdef ENABLE_WALLET +#include "init.h" // for getinfo +#include "wallet.h" // for getinfo +#endif #include <inttypes.h> @@ -110,7 +116,8 @@ Value getpeerinfo(const Array& params, bool fHelp) BOOST_FOREACH(const CNodeStats& stats, vstats) { Object obj; - + CNodeStateStats statestats; + bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); obj.push_back(Pair("addr", stats.addrName)); if (!(stats.addrLocal.empty())) obj.push_back(Pair("addrlocal", stats.addrLocal)); @@ -130,7 +137,9 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("subver", stats.cleanSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); obj.push_back(Pair("startingheight", stats.nStartingHeight)); - obj.push_back(Pair("banscore", stats.nMisbehavior)); + if (fStateStats) { + obj.push_back(Pair("banscore", statestats.nMisbehavior)); + } if (stats.fSyncNode) obj.push_back(Pair("syncnode", true)); @@ -315,7 +324,7 @@ Value getnettotals(const Array& params, bool fHelp) "\nResult:\n" "{\n" " \"totalbytesrecv\": n, (numeric) Total bytes received\n" - " \"totalbytessent\": n, (numeric) Total Bytes sent\n" + " \"totalbytessent\": n, (numeric) Total bytes sent\n" " \"timemillis\": t (numeric) Total cpu time\n" "}\n" "\nExamples:\n" @@ -329,3 +338,65 @@ Value getnettotals(const Array& params, bool fHelp) obj.push_back(Pair("timemillis", static_cast<boost::int64_t>(GetTimeMillis()))); return obj; } + +Value getinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info.\n" + "\nResult:\n" + "{\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" + " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"connections\": xxxxx, (numeric) the number of connections\n" + " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" + " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in btc\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"errors\": \"...\" (string) any error messages\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getinfo", "") + + HelpExampleRpc("getinfo", "") + ); + + proxyType proxy; + GetProxy(NET_IPV4, proxy); + + Object obj; + obj.push_back(Pair("version", (int)CLIENT_VERSION)); + obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + } +#endif + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("testnet", TestNet())); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + } +#endif + obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); +#ifdef ENABLE_WALLET + if (pwalletMain && pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); +#endif + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 939ca96f76..86025918ef 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -8,7 +8,12 @@ #include "init.h" #include "net.h" #include "uint256.h" +#include "core.h" +#include "main.h" +#include "keystore.h" +#ifdef ENABLE_WALLET #include "wallet.h" +#endif #include <stdint.h> @@ -78,7 +83,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) out.push_back(Pair("value", ValueFromAmount(txout.nValue))); out.push_back(Pair("n", (boost::int64_t)i)); Object o; - ScriptPubKeyToJSON(txout.scriptPubKey, o, false); + ScriptPubKeyToJSON(txout.scriptPubKey, o, true); out.push_back(Pair("scriptPubKey", o)); vout.push_back(out); } @@ -190,6 +195,7 @@ Value getrawtransaction(const Array& params, bool fHelp) return result; } +#ifdef ENABLE_WALLET Value listunspent(const Array& params, bool fHelp) { if (fHelp || params.size() > 3) @@ -303,6 +309,7 @@ Value listunspent(const Array& params, bool fHelp) return results; } +#endif Value createrawtransaction(const Array& params, bool fHelp) { @@ -508,7 +515,9 @@ Value signrawtransaction(const Array& params, bool fHelp) "this transaction depends on but may not yet be in the block chain.\n" "The third optional argument (may be null) is an array of base58-encoded private\n" "keys that, if given, will be the only keys used to sign the transaction.\n" +#ifdef ENABLE_WALLET + HelpRequiringPassphrase() + "\n" +#endif "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex string\n" @@ -605,8 +614,10 @@ Value signrawtransaction(const Array& params, bool fHelp) tempKeystore.AddKey(key); } } +#ifdef ENABLE_WALLET else EnsureWalletIsUnlocked(); +#endif // Add previous txouts given in the RPC call: if (params.size() > 1 && params[1].type() != null_type) @@ -662,7 +673,11 @@ Value signrawtransaction(const Array& params, bool fHelp) } } +#ifdef ENABLE_WALLET const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); +#else + const CKeyStore& keystore = tempKeystore; +#endif int nHashType = SIGHASH_ALL; if (params.size() > 3 && params[3].type() != null_type) diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index c746d8c8fb..c95f450c82 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -9,7 +9,10 @@ #include "init.h" #include "main.h" #include "util.h" +#include "ui_interface.h" +#ifdef ENABLE_WALLET #include "wallet.h" +#endif #include <boost/algorithm/string.hpp> #include <boost/asio.hpp> @@ -149,8 +152,10 @@ string CRPCTable::help(string strCommand) const continue; if (strCommand != "" && strMethod != strCommand) continue; +#ifdef ENABLE_WALLET if (pcmd->reqWallet && !pwalletMain) continue; +#endif try { @@ -228,12 +233,29 @@ static const CRPCCommand vRPCCommands[] = { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, { "getnettotals", &getnettotals, true, true, false }, { "getdifficulty", &getdifficulty, true, false, false }, - { "getnetworkhashps", &getnetworkhashps, true, false, false }, - { "getgenerate", &getgenerate, true, false, false }, - { "setgenerate", &setgenerate, true, true, false }, - { "gethashespersec", &gethashespersec, true, false, false }, { "getinfo", &getinfo, true, false, false }, + { "getrawmempool", &getrawmempool, true, false, false }, + { "getblock", &getblock, false, false, false }, + { "getblockhash", &getblockhash, false, false, false }, + { "settxfee", &settxfee, false, false, true }, + { "getrawtransaction", &getrawtransaction, false, false, false }, + { "createrawtransaction", &createrawtransaction, false, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false, false }, + { "decodescript", &decodescript, false, false, false }, + { "signrawtransaction", &signrawtransaction, false, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false, false }, + { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, + { "gettxout", &gettxout, true, false, false }, + { "verifychain", &verifychain, true, false, false }, + + /* Mining */ + { "getnetworkhashps", &getnetworkhashps, true, false, false }, { "getmininginfo", &getmininginfo, true, false, false }, + { "getblocktemplate", &getblocktemplate, true, false, false }, + { "submitblock", &submitblock, false, false, false }, + +#ifdef ENABLE_WALLET + /* Wallet */ { "getnewaddress", &getnewaddress, true, false, true }, { "getaccountaddress", &getaccountaddress, true, false, true }, { "getrawchangeaddress", &getrawchangeaddress, true, false, true }, @@ -258,36 +280,27 @@ static const CRPCCommand vRPCCommands[] = { "sendmany", &sendmany, false, false, true }, { "addmultisigaddress", &addmultisigaddress, false, false, true }, { "createmultisig", &createmultisig, true, true , false }, - { "getrawmempool", &getrawmempool, true, false, false }, - { "getblock", &getblock, false, false, false }, - { "getblockhash", &getblockhash, false, false, false }, { "gettransaction", &gettransaction, false, false, true }, { "listtransactions", &listtransactions, false, false, true }, { "listaddressgroupings", &listaddressgroupings, false, false, true }, { "signmessage", &signmessage, false, false, true }, { "verifymessage", &verifymessage, false, false, false }, - { "getwork", &getwork, true, false, true }, { "listaccounts", &listaccounts, false, false, true }, - { "settxfee", &settxfee, false, false, true }, - { "getblocktemplate", &getblocktemplate, true, false, false }, - { "submitblock", &submitblock, false, false, false }, { "listsinceblock", &listsinceblock, false, false, true }, { "dumpprivkey", &dumpprivkey, true, false, true }, { "dumpwallet", &dumpwallet, true, false, true }, { "importprivkey", &importprivkey, false, false, true }, { "importwallet", &importwallet, false, false, true }, { "listunspent", &listunspent, false, false, true }, - { "getrawtransaction", &getrawtransaction, false, false, false }, - { "createrawtransaction", &createrawtransaction, false, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false, false }, - { "decodescript", &decodescript, false, false, false }, - { "signrawtransaction", &signrawtransaction, false, false, false }, - { "sendrawtransaction", &sendrawtransaction, false, false, false }, - { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, - { "gettxout", &gettxout, true, false, false }, { "lockunspent", &lockunspent, false, false, true }, { "listlockunspent", &listlockunspent, false, false, true }, - { "verifychain", &verifychain, true, false, false }, + + /* Wallet-enabled mining */ + { "getgenerate", &getgenerate, true, false, false }, + { "setgenerate", &setgenerate, true, true, false }, + { "gethashespersec", &gethashespersec, true, false, false }, + { "getwork", &getwork, true, false, true }, +#endif // ENABLE_WALLET }; CRPCTable::CRPCTable() @@ -788,8 +801,10 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s const CRPCCommand *pcmd = tableRPC[strMethod]; if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); +#ifdef ENABLE_WALLET if (pcmd->reqWallet && !pwalletMain) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); +#endif // Observe safe mode string strWarning = GetWarnings("rpc"); @@ -804,6 +819,7 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s { if (pcmd->threadSafe) result = pcmd->actor(params, false); +#ifdef ENABLE_WALLET else if (!pwalletMain) { LOCK(cs_main); result = pcmd->actor(params, false); @@ -811,6 +827,12 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s LOCK2(cs_main, pwalletMain->cs_wallet); result = pcmd->actor(params, false); } +#else // ENABLE_WALLET + else { + LOCK(cs_main); + result = pcmd->actor(params, false); + } +#endif // !ENABLE_WALLET } return result; } @@ -820,4 +842,13 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s } } +std::string HelpExampleCli(string methodname, string args){ + return "> bitcoin-cli " + methodname + " " + args + "\n"; +} + +std::string HelpExampleRpc(string methodname, string args){ + return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " + "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; +} + const CRPCTable tableRPC; diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 1fd061547e..82fa9d88c5 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -33,15 +33,6 @@ std::string HelpRequiringPassphrase() : ""; } -std::string HelpExampleCli(string methodname, string args){ - return "> bitcoin-cli " + methodname + " " + args + "\n"; -} - -std::string HelpExampleRpc(string methodname, string args){ - return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " - "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; -} - void EnsureWalletIsUnlocked() { if (pwalletMain->IsLocked()) @@ -75,64 +66,6 @@ string AccountFromValue(const Value& value) return strAccount; } -Value getinfo(const Array& params, bool fHelp) -{ - if (fHelp || params.size() != 0) - throw runtime_error( - "getinfo\n" - "Returns an object containing various state info.\n" - "\nResult:\n" - "{\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in btc\n" - " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"errors\": \"...\" (string) any error messages\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getinfo", "") - + HelpExampleRpc("getinfo", "") - ); - - proxyType proxy; - GetProxy(NET_IPV4, proxy); - - Object obj; - obj.push_back(Pair("version", (int)CLIENT_VERSION)); - obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); - if (pwalletMain) { - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - } - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", TestNet())); - if (pwalletMain) { - obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); - } - obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee))); - if (pwalletMain && pwalletMain->IsCrypted()) - obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime)); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - - - Value getnewaddress(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -401,8 +334,7 @@ Value sendtoaddress(const Array& params, bool fHelp) if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) wtx.mapValue["to"] = params[3].get_str(); - if (pwalletMain->IsLocked()) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + EnsureWalletIsUnlocked(); string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); if (strError != "") @@ -1673,7 +1605,7 @@ Value gettransaction(const Array& params, bool fHelp) int64_t nCredit = wtx.GetCredit(); int64_t nDebit = wtx.GetDebit(); int64_t nNet = nCredit - nDebit; - int64_t nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0); + int64_t nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); if (wtx.IsFromMe()) @@ -1724,15 +1656,15 @@ Value keypoolrefill(const Array& params, bool fHelp) + HelpExampleRpc("keypoolrefill", "") ); - unsigned int kpSize = max(GetArg("-keypool", 100), (int64_t) 0); + // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool + unsigned int kpSize = 0; if (params.size() > 0) { if (params[0].get_int() < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size"); - kpSize = (unsigned int) params[0].get_int(); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); + kpSize = (unsigned int)params[0].get_int(); } EnsureWalletIsUnlocked(); - pwalletMain->TopUpKeyPool(kpSize); if (pwalletMain->GetKeyPoolSize() < kpSize) diff --git a/src/sync.cpp b/src/sync.cpp index 33e1219541..9a20c87f88 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -42,6 +42,8 @@ struct CLockLocation return mutexName+" "+sourceFile+":"+itostr(sourceLine); } + std::string MutexName() const { return mutexName; } + private: std::string mutexName; std::string sourceFile; @@ -126,4 +128,20 @@ void LeaveCritical() pop_lock(); } +std::string LocksHeld() +{ + std::string result; + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)&i, *lockstack) + result += i.second.ToString() + std::string("\n"); + return result; +} + +void AssertLockHeld(std::string strName) +{ + BOOST_FOREACH(const PAIRTYPE(void*, CLockLocation)&i, *lockstack) + if (i.second.MutexName() == strName) return; + LogPrintf("Lock %s not held; locks held:\n%s", strName.c_str(), LocksHeld().c_str()); + assert(0); +} + #endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h index 39f2cb5155..c50abf81b6 100644 --- a/src/sync.h +++ b/src/sync.h @@ -87,9 +87,12 @@ typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection; #ifdef DEBUG_LOCKORDER void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); void LeaveCritical(); +std::string LocksHeld(); +void AssertLockHeld(std::string strName); #else void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} +void static inline AssertLockHeld(std::string) {} #endif #ifdef DEBUG_LOCKCONTENTION diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index f0fb84bc54..fbca09b4dc 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -21,6 +21,7 @@ // Tests this internal-to-main.cpp method: extern bool AddOrphanTx(const CTransaction& tx); extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans); +extern void Misbehaving(NodeId nodeid, int howmuch); extern std::map<uint256, CTransaction> mapOrphanTransactions; extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev; @@ -38,16 +39,21 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CNode::ClearBanned(); CAddress addr1(ip(0xa0b0c001)); CNode dummyNode1(INVALID_SOCKET, addr1, "", true); - dummyNode1.Misbehaving(100); // Should get banned + dummyNode1.nVersion = 1; + Misbehaving(dummyNode1.GetId(), 100); // Should get banned + SendMessages(&dummyNode1, false); BOOST_CHECK(CNode::IsBanned(addr1)); BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned CAddress addr2(ip(0xa0b0c002)); CNode dummyNode2(INVALID_SOCKET, addr2, "", true); - dummyNode2.Misbehaving(50); + dummyNode2.nVersion = 1; + Misbehaving(dummyNode2.GetId(), 50); + SendMessages(&dummyNode2, false); BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be - dummyNode2.Misbehaving(50); + Misbehaving(dummyNode2.GetId(), 50); + SendMessages(&dummyNode2, false); BOOST_CHECK(CNode::IsBanned(addr2)); } @@ -57,11 +63,15 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) mapArgs["-banscore"] = "111"; // because 11 is my favorite number CAddress addr1(ip(0xa0b0c001)); CNode dummyNode1(INVALID_SOCKET, addr1, "", true); - dummyNode1.Misbehaving(100); + dummyNode1.nVersion = 1; + Misbehaving(dummyNode1.GetId(), 100); + SendMessages(&dummyNode1, false); BOOST_CHECK(!CNode::IsBanned(addr1)); - dummyNode1.Misbehaving(10); + Misbehaving(dummyNode1.GetId(), 10); + SendMessages(&dummyNode1, false); BOOST_CHECK(!CNode::IsBanned(addr1)); - dummyNode1.Misbehaving(1); + Misbehaving(dummyNode1.GetId(), 1); + SendMessages(&dummyNode1, false); BOOST_CHECK(CNode::IsBanned(addr1)); mapArgs.erase("-banscore"); } @@ -74,8 +84,10 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) CAddress addr(ip(0xa0b0c001)); CNode dummyNode(INVALID_SOCKET, addr, "", true); + dummyNode.nVersion = 1; - dummyNode.Misbehaving(100); + Misbehaving(dummyNode.GetId(), 100); + SendMessages(&dummyNode, false); BOOST_CHECK(CNode::IsBanned(addr)); SetMockTime(nStartTime+60*60); diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 180fd7effa..ccc8da1690 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -20,9 +20,14 @@ BUILT_SOURCES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) # test_bitcoin binary # test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(TESTDEFS) -test_bitcoin_LDADD = $(LIBBITCOIN) $(LIBLEVELDB) $(LIBMEMENV) \ - $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(BDB_LIBS) -test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ +test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ + $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) +if ENABLE_WALLET +test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) +endif +test_bitcoin_LDADD += $(BDB_LIBS) + +test_bitcoin_SOURCES = alert_tests.cpp \ allocator_tests.cpp base32_tests.cpp base58_tests.cpp base64_tests.cpp \ bignum_tests.cpp bloom_tests.cpp canonical_tests.cpp checkblock_tests.cpp \ Checkpoints_tests.cpp compress_tests.cpp DoS_tests.cpp getarg_tests.cpp \ @@ -30,7 +35,11 @@ test_bitcoin_SOURCES = accounting_tests.cpp alert_tests.cpp \ netbase_tests.cpp pmt_tests.cpp rpc_tests.cpp script_P2SH_tests.cpp \ script_tests.cpp serialize_tests.cpp sigopcount_tests.cpp test_bitcoin.cpp \ transaction_tests.cpp uint160_tests.cpp uint256_tests.cpp util_tests.cpp \ - wallet_tests.cpp sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) + sighash_tests.cpp $(JSON_TEST_FILES) $(RAW_TEST_FILES) + +if ENABLE_WALLET +test_bitcoin_SOURCES += accounting_tests.cpp wallet_tests.cpp rpc_wallet_tests.cpp +endif nodist_test_bitcoin_SOURCES = $(BUILT_SOURCES) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index dcb7f9abd4..ea6abb7e9a 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -8,7 +8,6 @@ #include <boost/test/unit_test.hpp> -extern CWallet* pwalletMain; extern void SHA256Transform(void* pstate, void* pinput, const void* pinit); BOOST_AUTO_TEST_SUITE(miner_tests) @@ -51,14 +50,16 @@ struct { // NOTE: These tests rely on CreateNewBlock doing its own self-validation! BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { - CReserveKey reservekey(pwalletMain); + CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; CBlockTemplate *pblocktemplate; CTransaction tx; CScript script; uint256 hash; + LOCK(cs_main); + // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) @@ -84,7 +85,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) delete pblocktemplate; // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; // block sigops > limit: 1000 CHECKMULTISIG + 1 @@ -99,10 +100,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -119,17 +120,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 10000000; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // orphan in mempool hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -138,7 +139,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[1]->GetHash(); tx.vout[0].nValue = 4900000000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; @@ -146,8 +147,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].prevout.n = 0; tx.vout[0].nValue = 5900000000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -157,8 +158,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -170,13 +171,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) script = CScript() << OP_0; tx.vout[0].scriptPubKey.SetDestination(script.GetID()); hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << (std::vector<unsigned char>)script; tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); - mempool.addUnchecked(hash,tx); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -186,21 +187,21 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 4900000000LL; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // subsidy changing int nHeight = chainActive.Height(); chainActive.Tip()->nHeight = 209999; - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = 210000; - BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); + BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = nHeight; diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 76580bae43..29195545da 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -9,9 +9,7 @@ using namespace std; using namespace json_spirit; -BOOST_AUTO_TEST_SUITE(rpc_tests) - -static Array +Array createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) { Array result; @@ -23,44 +21,7 @@ createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL) return result; } -BOOST_AUTO_TEST_CASE(rpc_addmultisig) -{ - rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; - - // old, 65-byte-long: - const char address1Hex[] = "0434e3e09f49ea168c5bbf53f877ff4206923858aab7c7e1df25bc263978107c95e35065a27ef6f1b27222db0ec97e0e895eaca603d3ee0d4c060ce3d8a00286c8"; - // new, compressed: - const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; - - Value v; - CBitcoinAddress address; - BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); - address.SetString(v.get_str()); - BOOST_CHECK(address.IsValid() && address.IsScript()); - - BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false)); - address.SetString(v.get_str()); - BOOST_CHECK(address.IsValid() && address.IsScript()); - - BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false)); - address.SetString(v.get_str()); - BOOST_CHECK(address.IsValid() && address.IsScript()); - - BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error); - BOOST_CHECK_THROW(addmultisig(createArgs(1), false), runtime_error); - BOOST_CHECK_THROW(addmultisig(createArgs(2, address1Hex), false), runtime_error); - - BOOST_CHECK_THROW(addmultisig(createArgs(1, ""), false), runtime_error); - BOOST_CHECK_THROW(addmultisig(createArgs(1, "NotAValidPubkey"), false), runtime_error); - - string short1(address1Hex, address1Hex+sizeof(address1Hex)-2); // last byte missing - BOOST_CHECK_THROW(addmultisig(createArgs(2, short1.c_str()), false), runtime_error); - - string short2(address1Hex+1, address1Hex+sizeof(address1Hex)); // first byte missing - BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); -} - -static Value CallRPC(string args) +Value CallRPC(string args) { vector<string> vArgs; boost::split(vArgs, args, boost::is_any_of(" \t")); @@ -79,34 +40,8 @@ static Value CallRPC(string args) } } -BOOST_AUTO_TEST_CASE(rpc_wallet) -{ - // Test RPC calls for various wallet statistics - Value r; - - BOOST_CHECK_NO_THROW(CallRPC("listunspent")); - BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error); - BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); - BOOST_CHECK(r.get_array().empty()); - - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); - - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); -} +BOOST_AUTO_TEST_SUITE(rpc_tests) BOOST_AUTO_TEST_CASE(rpc_rawparams) { diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp new file mode 100644 index 0000000000..2cf0fb350b --- /dev/null +++ b/src/test/rpc_wallet_tests.cpp @@ -0,0 +1,82 @@ +#include "rpcserver.h" +#include "rpcclient.h" + +#include "base58.h" + +#include <boost/algorithm/string.hpp> +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace json_spirit; + +extern Array createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL); +extern Value CallRPC(string args); + +BOOST_AUTO_TEST_SUITE(rpc_wallet_tests) + +BOOST_AUTO_TEST_CASE(rpc_addmultisig) +{ + rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; + + // old, 65-byte-long: + const char address1Hex[] = "0434e3e09f49ea168c5bbf53f877ff4206923858aab7c7e1df25bc263978107c95e35065a27ef6f1b27222db0ec97e0e895eaca603d3ee0d4c060ce3d8a00286c8"; + // new, compressed: + const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; + + Value v; + CBitcoinAddress address; + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false)); + address.SetString(v.get_str()); + BOOST_CHECK(address.IsValid() && address.IsScript()); + + BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(1), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(2, address1Hex), false), runtime_error); + + BOOST_CHECK_THROW(addmultisig(createArgs(1, ""), false), runtime_error); + BOOST_CHECK_THROW(addmultisig(createArgs(1, "NotAValidPubkey"), false), runtime_error); + + string short1(address1Hex, address1Hex+sizeof(address1Hex)-2); // last byte missing + BOOST_CHECK_THROW(addmultisig(createArgs(2, short1.c_str()), false), runtime_error); + + string short2(address1Hex+1, address1Hex+sizeof(address1Hex)); // first byte missing + BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); +} + +BOOST_AUTO_TEST_CASE(rpc_wallet) +{ + // Test RPC calls for various wallet statistics + Value r; + + BOOST_CHECK_NO_THROW(CallRPC("listunspent")); + BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error); + BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []")); + BOOST_CHECK(r.get_array().empty()); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); + BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); + BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f947167597..a4592fe803 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -7,14 +7,15 @@ #include "txdb.h" #include "ui_interface.h" #include "util.h" +#ifdef ENABLE_WALLET #include "wallet.h" +#endif #include <boost/filesystem.hpp> #include <boost/test/unit_test.hpp> CWallet* pwalletMain; -CClientUIInterface uiInterface; extern bool fPrintToConsole; extern void noui_connect(); @@ -27,7 +28,9 @@ struct TestingSetup { TestingSetup() { fPrintToDebugger = true; // don't want to write to debug.log file noui_connect(); +#ifdef ENABLE_WALLET bitdb.MakeMock(); +#endif pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); boost::filesystem::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); @@ -35,24 +38,32 @@ struct TestingSetup { pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(*pcoinsdbview); InitBlockIndex(); +#ifdef ENABLE_WALLET bool fFirstRun; pwalletMain = new CWallet("wallet.dat"); pwalletMain->LoadWallet(fFirstRun); RegisterWallet(pwalletMain); +#endif nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); + RegisterNodeSignals(GetNodeSignals()); } ~TestingSetup() { threadGroup.interrupt_all(); threadGroup.join_all(); + UnregisterNodeSignals(GetNodeSignals()); +#ifdef ENABLE_WALLET delete pwalletMain; pwalletMain = NULL; +#endif delete pcoinsTip; delete pcoinsdbview; delete pblocktree; +#ifdef ENABLE_WALLET bitdb.Flush(true); +#endif boost::filesystem::remove_all(pathTemp); } }; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d501b89ecf..be251d1d64 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -8,6 +8,33 @@ using namespace std; +CTxMemPoolEntry::CTxMemPoolEntry() +{ + nHeight = MEMPOOL_HEIGHT; +} + +CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, int64_t _nFee, + int64_t _nTime, double _dPriority, + unsigned int _nHeight): + tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight) +{ + nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); +} + +CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) +{ + *this = other; +} + +double +CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const +{ + int64_t nValueIn = tx.GetValueOut()+nFee; + double deltaPriority = ((double)(currentHeight-nHeight)*nValueIn)/nTxSize; + double dResult = dPriority + deltaPriority; + return dResult; +} + CTxMemPool::CTxMemPool() { // Sanity checks off by default for performance, because otherwise @@ -42,16 +69,17 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n) } -bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) +bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry) { // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); { - mapTx[hash] = tx; + mapTx[hash] = entry; + const CTransaction& tx = mapTx[hash].GetTx(); for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); + mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); nTransactionsUpdated++; } return true; @@ -113,13 +141,15 @@ void CTxMemPool::check(CCoinsViewCache *pcoins) const LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); LOCK(cs); - for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + for (std::map<uint256, CTxMemPoolEntry>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; - BOOST_FOREACH(const CTxIn &txin, it->second.vin) { + const CTransaction& tx = it->second.GetTx(); + BOOST_FOREACH(const CTxIn &txin, tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. - std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash); + std::map<uint256, CTxMemPoolEntry>::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) { - assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); + const CTransaction& tx2 = it2->second.GetTx(); + assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); } else { CCoins &coins = pcoins->GetCoins(txin.prevout.hash); assert(coins.IsAvailable(txin.prevout.n)); @@ -127,37 +157,38 @@ void CTxMemPool::check(CCoinsViewCache *pcoins) const // Check whether its inputs are marked in mapNextTx. std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout); assert(it3 != mapNextTx.end()); - assert(it3->second.ptx == &it->second); + assert(it3->second.ptx == &tx); assert(it3->second.n == i); i++; } } for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { uint256 hash = it->second.ptx->GetHash(); - std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash); + map<uint256, CTxMemPoolEntry>::const_iterator it2 = mapTx.find(hash); + const CTransaction& tx = it2->second.GetTx(); assert(it2 != mapTx.end()); - assert(&it2->second == it->second.ptx); - assert(it2->second.vin.size() > it->second.n); + assert(&tx == it->second.ptx); + assert(tx.vin.size() > it->second.n); assert(it->first == it->second.ptx->vin[it->second.n].prevout); } } -void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) +void CTxMemPool::queryHashes(vector<uint256>& vtxid) { vtxid.clear(); LOCK(cs); vtxid.reserve(mapTx.size()); - for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) + for (map<uint256, CTxMemPoolEntry>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) vtxid.push_back((*mi).first); } bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const { LOCK(cs); - std::map<uint256, CTransaction>::const_iterator i = mapTx.find(hash); + map<uint256, CTxMemPoolEntry>::const_iterator i = mapTx.find(hash); if (i == mapTx.end()) return false; - result = i->second; + result = i->second.GetTx(); return true; } diff --git a/src/txmempool.h b/src/txmempool.h index 57b92789fb..a652c424a4 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -13,6 +13,33 @@ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; /* + * CTxMemPool stores these: + */ +class CTxMemPoolEntry +{ +private: + CTransaction tx; + int64_t nFee; // Cached to avoid expensive parent-transaction lookups + size_t nTxSize; // ... and avoid recomputing tx size + int64_t nTime; // Local time when entering the mempool + double dPriority; // Priority when entering the mempool + unsigned int nHeight; // Chain height when entering the mempool + +public: + CTxMemPoolEntry(const CTransaction& _tx, int64_t _nFee, + int64_t _nTime, double _dPriority, unsigned int _nHeight); + CTxMemPoolEntry(); + CTxMemPoolEntry(const CTxMemPoolEntry& other); + + const CTransaction& GetTx() const { return this->tx; } + double GetPriority(unsigned int currentHeight) const; + int64_t GetFee() const { return nFee; } + size_t GetTxSize() const { return nTxSize; } + int64_t GetTime() const { return nTime; } + unsigned int GetHeight() const { return nHeight; } +}; + +/* * CTxMemPool stores valid-according-to-the-current-best-chain * transactions that may be included in the next block. * @@ -30,7 +57,7 @@ private: public: mutable CCriticalSection cs; - std::map<uint256, CTransaction> mapTx; + std::map<uint256, CTxMemPoolEntry> mapTx; std::map<COutPoint, CInPoint> mapNextTx; CTxMemPool(); @@ -44,7 +71,7 @@ public: void check(CCoinsViewCache *pcoins) const; void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; } - bool addUnchecked(const uint256& hash, const CTransaction &tx); + bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry); bool remove(const CTransaction &tx, bool fRecursive = false); bool removeConflicts(const CTransaction &tx); void clear(); diff --git a/src/util.cpp b/src/util.cpp index 65940758a1..a8c591cc7a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -95,6 +95,7 @@ string strMiscWarning; bool fNoListen = false; bool fLogTimestamps = false; volatile bool fReopenDebugLog = false; +CClientUIInterface uiInterface; // Init OpenSSL library multithreading support static CCriticalSection** ppmutexOpenSSL; @@ -1511,3 +1512,4 @@ void RenameThread(const char* name) (void)name; #endif } + diff --git a/src/wallet.cpp b/src/wallet.cpp index db957cbd05..241e937b1b 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -655,7 +655,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, int64_t nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - int64_t nValueOut = GetValueOut(*this); + int64_t nValueOut = GetValueOut(); nFee = nDebit - nValueOut; } @@ -765,6 +765,10 @@ void CWalletTx::AddSupportingTransactions() { tx = *mapWalletPrev[hash]; } + else + { + continue; + } int nDepth = tx.SetMerkleBranch(); vtxPrev.push_back(tx); @@ -895,7 +899,10 @@ void CWalletTx::RelayWalletTransaction() { BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { - if (!tx.IsCoinBase()) + // 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()); } @@ -1298,7 +1305,9 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, // Reserve a new key pair from key pool CPubKey vchPubKey; - assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + assert(ret); // should never fail, as we just unlocked scriptChange.SetDestination(vchPubKey.GetID()); } @@ -1342,15 +1351,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, strFailReason = _("Transaction too large"); return false; } - unsigned int nTxSizeMod = nBytes; - // See miner.c's dPriority logic for the matching network-node side code. - BOOST_FOREACH(const CTxIn& txin, (*(CTransaction*)&wtxNew).vin) - { - unsigned int offset = 41U + min(110U, (unsigned int)txin.scriptSig.size()); - if (nTxSizeMod > offset) - nTxSizeMod -= offset; - } - dPriority /= nTxSizeMod; + dPriority = wtxNew.ComputePriority(dPriority, nBytes); // Check that enough fee is included int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); |