diff options
155 files changed, 2976 insertions, 1874 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 647be8f2e6..34509993a0 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)])], @@ -243,7 +250,7 @@ case $host in bdb_prefix=`$BREW --prefix berkeley-db4` export PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" CPPFLAGS="$CPPFLAGS -I$bdb_prefix/include" - LIBS="$LIBS -L/$bdb_prefix/lib" + LIBS="$LIBS -L$bdb_prefix/lib" fi fi @@ -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 @@ -593,6 +606,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 +732,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/README.md b/contrib/README.md index eb0d3ee2c9..167b5df4e1 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -13,8 +13,8 @@ This is a 'getwork' CPU mining client for Bitcoin. It is pure-python, and theref Use the raw transactions API to send coins received on a particular address (or addresses). -### [Wallet Tools](/contrib/wallettools) ### -Contains a wallet change password and unlock script. Used to prevent users from having to enter their password as a command-line argument. +### WalletTools +Removed. Please see [/contrib/bitrpc](/contrib/bitrpc). Repository Tools --------------------- @@ -52,3 +52,7 @@ tests each pull and when master is tested using jenkins. ### [Verify SF Binaries](/contrib/verifysfbinaries) ### This script attempts to download and verify the signature file SHA256SUMS.asc from SourceForge. +### [Developer tools](/control/devtools) ### +Specific tools for developers working on this repository. +Contains the script `github-merge.sh` for merging github pull requests securely and signing them using GPG. + diff --git a/contrib/bitrpc/README.md b/contrib/bitrpc/README.md index 2dde60a08e..f5ef2f0405 100644 --- a/contrib/bitrpc/README.md +++ b/contrib/bitrpc/README.md @@ -1,2 +1,8 @@ -### BitRPC ### -Allows for sending of all standard Bitcoin commands via RPC rather than as command line args.
\ No newline at end of file +### BitRPC +Allows for sending of all standard Bitcoin commands via RPC rather than as command line args. + +### Looking for Wallet Tools? +BitRPC.py is able to do the exact same thing as `walletchangepass.py` and `walletunlock.py`. Their respective commands in BitRPC.py are: + + bitrpc.py walletpassphrasechange + bitrpc.py walletpassphrase
\ No newline at end of file diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md new file mode 100644 index 0000000000..55d5d24cca --- /dev/null +++ b/contrib/devtools/README.md @@ -0,0 +1,38 @@ +Contents +=========== +This directory contains tools for developers working on this repository. + +github-merge.sh +---------------- + +A small script to automate merging pull-requests securely and sign them with GPG. + +For example: + + ./github-merge.sh bitcoin/bitcoin 3077 + +(in any git repository) will help you merge pull request #3077 for the +bitcoin/bitcoin repository. + +What it does: +* Fetch master and the pull request. +* Locally construct a merge commit. +* Show the diff that merge results in. +* Ask you to verify the resulting source tree (so you can do a make +check or whatever). +* Ask you whether to GPG sign the merge commit. +* Ask you whether to push the result upstream. + +This means that there are no potential race conditions (where a +pullreq gets updated while you're reviewing it, but before you click +merge), and when using GPG signatures, that even a compromised github +couldn't mess with the sources. + +Setup +--------- +Configuring the github-merge tool for the bitcoin repository is done in the following way: + + git config githubmerge.repository bitcoin/bitcoin + git config githubmerge.testcmd "make -j4 check" (adapt to whatever you want to use for testing) + git config --global user.signingkey mykeyid (if you want to GPG sign) + diff --git a/contrib/devtools/github-merge.sh b/contrib/devtools/github-merge.sh new file mode 100755 index 0000000000..e42b71a54a --- /dev/null +++ b/contrib/devtools/github-merge.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +# This script will locally construct a merge commit for a pull request on a +# github repository, inspect it, sign it and optionally push it. + +# The following temporary branches are created/overwritten and deleted: +# * pull/$PULL/base (the current master we're merging onto) +# * pull/$PULL/head (the current state of the remote pull request) +# * pull/$PULL/merge (github's merge) +# * pull/$PULL/local-merge (our merge) + +# In case of a clean merge that is accepted by the user, the local branch with +# name $BRANCH is overwritten with the merged result, and optionally pushed. + +REPO="$(git config --get githubmerge.repository)" +if [[ "d$REPO" == "d" ]]; then + echo "ERROR: No repository configured. Use this command to set:" >&2 + echo "git config githubmerge.repository <owner>/<repo>" >&2 + echo "In addition, you can set the following variables:" >&2 + echo "- githubmerge.host (default git@github.com)" >&2 + echo "- githubmerge.branch (default master)" >&2 + echo "- githubmerge.testcmd (default none)" >&2 + exit 1 +fi + +HOST="$(git config --get githubmerge.host)" +if [[ "d$HOST" == "d" ]]; then + HOST="git@github.com" +fi + +BRANCH="$(git config --get githubmerge.branch)" +if [[ "d$BRANCH" == "d" ]]; then + BRANCH="master" +fi + +TESTCMD="$(git config --get githubmerge.testcmd)" + +PULL="$1" + +if [[ "d$PULL" == "d" ]]; then + echo "Usage: $0 pullnumber [branch]" >&2 + exit 2 +fi + +if [[ "d$2" != "d" ]]; then + BRANCH="$2" +fi + +# Initialize source branches. +git checkout -q "$BRANCH" +if git fetch -q "$HOST":"$REPO" "+refs/pull/$PULL/*:refs/heads/pull/$PULL/*"; then + if ! git log -1q "refs/heads/pull/$PULL/head" >/dev/null 2>&1; then + echo "ERROR: Cannot find head of pull request #$PULL on $HOST:$REPO." >&2 + exit 3 + fi + if ! git log -1q "refs/heads/pull/$PULL/merge" >/dev/null 2>&1; then + echo "ERROR: Cannot find merge of pull request #$PULL on $HOST:$REPO." >&2 + exit 3 + fi +else + echo "ERROR: Cannot find pull request #$PULL on $HOST:$REPO." >&2 + exit 3 +fi +if git fetch -q "$HOST":"$REPO" +refs/heads/"$BRANCH":refs/heads/pull/"$PULL"/base; then + true +else + echo "ERROR: Cannot find branch $BRANCH on $HOST:$REPO." >&2 + exit 3 +fi +git checkout -q pull/"$PULL"/base +git branch -q -D pull/"$PULL"/local-merge 2>/dev/null +git checkout -q -b pull/"$PULL"/local-merge +TMPDIR="$(mktemp -d -t ghmXXXXX)" + +function cleanup() { + git checkout -q "$BRANCH" + git branch -q -D pull/"$PULL"/head 2>/dev/null + git branch -q -D pull/"$PULL"/base 2>/dev/null + git branch -q -D pull/"$PULL"/merge 2>/dev/null + git branch -q -D pull/"$PULL"/local-merge 2>/dev/null + rm -rf "$TMPDIR" +} + +# Create unsigned merge commit. +( + echo "Merge pull request #$PULL" + echo "" + git log --no-merges --topo-order --pretty='format:%h %s (%an)' pull/"$PULL"/base..pull/"$PULL"/head +)>"$TMPDIR/message" +if git merge -q --commit --no-edit --no-ff -m "$(<"$TMPDIR/message")" pull/"$PULL"/head; then + if [ "d$(git log --pretty='format:%s' -n 1)" != "dMerge pull request #$PULL" ]; then + echo "ERROR: Creating merge failed (already merged?)." >&2 + cleanup + exit 4 + fi +else + echo "ERROR: Cannot be merged cleanly." >&2 + git merge --abort + cleanup + exit 4 +fi + +# Run test command if configured. +if [[ "d$TESTCMD" != "d" ]]; then + # Go up to the repository's root. + while [ ! -d .git ]; do cd ..; done + if ! $TESTCMD; then + echo "ERROR: Running $TESTCMD failed." >&2 + cleanup + exit 5 + fi + # Show the created merge. + git diff pull/"$PULL"/merge..pull/"$PULL"/local-merge >"$TMPDIR"/diff + git diff pull/"$PULL"/base..pull/"$PULL"/local-merge + if [[ "$(<"$TMPDIR"/diff)" != "" ]]; then + echo "WARNING: merge differs from github!" >&2 + read -p "Type 'ignore' to continue. " -r >&2 + if [[ "d$REPLY" =~ ^d[iI][gG][nN][oO][rR][eE]$ ]]; then + echo "Difference with github ignored." >&2 + else + cleanup + exit 6 + fi + fi + read -p "Press 'd' to accept the diff. " -n 1 -r >&2 + echo + if [[ "d$REPLY" =~ ^d[dD]$ ]]; then + echo "Diff accepted." >&2 + else + echo "ERROR: Diff rejected." >&2 + cleanup + exit 6 + fi +else + # Verify the result. + echo "Dropping you on a shell so you can try building/testing the merged source." >&2 + echo "Run 'git diff HEAD~' to show the changes being merged." >&2 + echo "Type 'exit' when done." >&2 + bash -i + read -p "Press 'm' to accept the merge. " -n 1 -r >&2 + echo + if [[ "d$REPLY" =~ ^d[Mm]$ ]]; then + echo "Merge accepted." >&2 + else + echo "ERROR: Merge rejected." >&2 + cleanup + exit 7 + fi +fi + +# Sign the merge commit. +read -p "Press 's' to sign off on the merge. " -n 1 -r >&2 +echo +if [[ "d$REPLY" =~ ^d[Ss]$ ]]; then + if [[ "$(git config --get user.signingkey)" == "" ]]; then + echo "WARNING: No GPG signing key set, not signing. Set one using:" >&2 + echo "git config --global user.signingkey <key>" >&2 + git commit -q --signoff --amend --no-edit + else + git commit -q --gpg-sign --amend --no-edit + fi +fi + +# Clean up temporary branches, and put the result in $BRANCH. +git checkout -q "$BRANCH" +git reset -q --hard pull/"$PULL"/local-merge +cleanup + +# Push the result. +read -p "Type 'push' to push the result to $HOST:$REPO, branch $BRANCH. " -r >&2 +if [[ "d$REPLY" =~ ^d[Pp][Uu][Ss][Hh]$ ]]; then + git push "$HOST":"$REPO" refs/heads/"$BRANCH" +fi 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/contrib/wallettools/README.md b/contrib/wallettools/README.md deleted file mode 100644 index 3a71ba1436..0000000000 --- a/contrib/wallettools/README.md +++ /dev/null @@ -1,4 +0,0 @@ -### Wallet Tools ### -These are two simple python scripts which send the appropriate RPC commands to unlock a wallet and change a wallet password. **They are intended to prevent users from having to enter their password as a command-line argument which could then be stored in the console buffer/history in plaintext.** - -Both tools rely on bitcoin/bitcoind running with `server=1` and an `rpcuser` and `rpcpassword` set in `bitcoin.conf`. They can be easily modified for non-standard ports. [walletunlock.py](/contrib/wallettools/walletunlock.py) unlocks the wallet for 60 seconds by default, changeable in code, and both modules rely upon python-json-rpc. diff --git a/contrib/wallettools/walletchangepass.py b/contrib/wallettools/walletchangepass.py deleted file mode 100644 index 30f3f5b26a..0000000000 --- a/contrib/wallettools/walletchangepass.py +++ /dev/null @@ -1,5 +0,0 @@ -from jsonrpc import ServiceProxy -access = ServiceProxy("http://127.0.0.1:8332") -pwd = raw_input("Enter old wallet passphrase: ") -pwd2 = raw_input("Enter new wallet passphrase: ") -access.walletpassphrasechange(pwd, pwd2)
\ No newline at end of file diff --git a/contrib/wallettools/walletunlock.py b/contrib/wallettools/walletunlock.py deleted file mode 100644 index f847c6fe61..0000000000 --- a/contrib/wallettools/walletunlock.py +++ /dev/null @@ -1,4 +0,0 @@ -from jsonrpc import ServiceProxy -access = ServiceProxy("http://127.0.0.1:8332") -pwd = raw_input("Enter wallet passphrase: ") -access.walletpassphrase(pwd, 60)
\ No newline at end of file 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-unix.md b/doc/build-unix.md index 27228cdc1b..1d9d96a1ca 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -22,7 +22,7 @@ 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 @@ -178,3 +178,12 @@ 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. diff --git a/doc/release-process.md b/doc/release-process.md index feadb3c1b0..3d5c577f86 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: 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/genbuild.sh b/share/genbuild.sh index da50b0d109..edb847a242 100755 --- a/share/genbuild.sh +++ b/share/genbuild.sh @@ -13,6 +13,8 @@ else exit 1 fi +TIME=$(date '+%F %T %z') + if [ -e "$(which git)" -a -d ".git" ]; then # clean 'dirty' status of touched files that haven't been modified git diff >/dev/null 2>/dev/null 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 7450507b34..df4087c313 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 @@ -12,7 +15,10 @@ DIST_SUBDIRS = . qt test .PHONY: FORCE # bitcoin core # BITCOIN_CORE_H = addrman.h alert.h allocators.h base58.h bignum.h \ - bitcoinrpc.h bloom.h chainparams.h checkpoints.h checkqueue.h \ + rpcclient.h \ + rpcprotocol.h \ + rpcserver.h \ + bloom.h chainparams.h checkpoints.h checkqueue.h \ clientversion.h coincontrol.h compat.h core.h coins.h crypter.h db.h hash.h init.h \ key.h keystore.h leveldbwrapper.h limitedmap.h main.h miner.h mruset.h \ netbase.h net.h noui.h protocol.h script.h serialize.h sync.h threadsafety.h \ @@ -30,20 +36,67 @@ obj/build.h: FORCE $(abs_top_srcdir) version.o: obj/build.h -libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp bitcoinrpc.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) \ +libbitcoin_server_a_SOURCES = \ + addrman.cpp \ + alert.cpp \ + rpcserver.cpp \ + bloom.cpp \ + chainparams.cpp \ + checkpoints.cpp \ + coins.cpp \ + init.cpp \ + keystore.cpp \ + leveldbwrapper.cpp \ + main.cpp \ + net.cpp \ + noui.cpp \ + rpcblockchain.cpp \ + rpcnet.cpp \ + rpcrawtransaction.cpp \ + txdb.cpp \ + txmempool.cpp \ + $(JSON_H) \ + $(BITCOIN_CORE_H) + +libbitcoin_wallet_a_SOURCES = \ + db.cpp \ + crypter.cpp \ + miner.cpp \ + rpcdump.cpp \ + rpcmining.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) -nodist_libbitcoin_a_SOURCES = $(top_srcdir)/src/obj/build.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 # @@ -55,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 @@ -72,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 @@ -84,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 aa6ce27c52..04b75e7f1c 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -5,8 +5,9 @@ #include "util.h" #include "init.h" -#include "bitcoinrpc.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 6dbab240bf..e13d688158 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -3,7 +3,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bitcoinrpc.h" +#include "rpcserver.h" +#include "rpcclient.h" #include "init.h" #include "main.h" #include "noui.h" @@ -86,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 80cdcb0849..f41ea87fea 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -7,8 +7,6 @@ #include "util.h" -#include <stdint.h> - std::string COutPoint::ToString() const { return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n); @@ -108,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 d930f6f693..df3cedc202 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -10,15 +10,18 @@ #include "init.h" #include "addrman.h" -#include "bitcoinrpc.h" +#include "db.h" +#include "rpcserver.h" #include "checkpoints.h" #include "miner.h" #include "net.h" #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 @@ -109,15 +113,20 @@ void Shutdown() RenameThread("bitcoin-shutoff"); mempool.AddTransactionsUpdated(1); StopRPCThreads(); +#ifdef ENABLE_WALLET ShutdownRPCMining(); if (pwalletMain) bitdb.Flush(false); - GenerateBitcoins(false, NULL); + 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 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"; #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 " - "solved instantly. This is intended for regression testing tools and app development.") + "\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 " + "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,52 +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 += " -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; } @@ -499,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")) { @@ -545,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. @@ -587,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...")); @@ -633,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()); @@ -900,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"); @@ -992,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 @@ -1036,25 +1035,31 @@ 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); - +#ifdef ENABLE_WALLET // InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly. InitRPCMining(); +#endif if (fServer) StartRPCThreads(); +#ifdef ENABLE_WALLET // Generate coins in the background if (pwalletMain) - GenerateBitcoins(GetBoolArg("-gen", false), 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(); @@ -1062,6 +1067,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 54a2a1bee7..57d14fe48d 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 20 +#serial 21 AC_DEFUN([AX_BOOST_BASE], [ @@ -91,9 +91,11 @@ if test "x$want_boost" = "xyes"; then dnl are found, e.g. when only header-only libraries are installed! libsubdirs="lib" ax_arch=`uname -m` - if test $ax_arch = x86_64 -o $ax_arch = ppc64 -o $ax_arch = s390x -o $ax_arch = sparc64; then + case $ax_arch in + x86_64|ppc64|s390x|sparc64|aarch64) libsubdirs="lib64 lib lib64" - fi + ;; + esac 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 diff --git a/src/m4/ax_boost_filesystem.m4 b/src/m4/ax_boost_filesystem.m4 index 2a62da8d89..f162163cdc 100644 --- a/src/m4/ax_boost_filesystem.m4 +++ b/src/m4/ax_boost_filesystem.m4 @@ -31,7 +31,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 21 +#serial 26 AC_DEFUN([AX_BOOST_FILESYSTEM], [ @@ -81,14 +81,14 @@ AC_DEFUN([AX_BOOST_FILESYSTEM], AC_DEFINE(HAVE_BOOST_FILESYSTEM,,[define if the Boost::Filesystem library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` if test "x$ax_boost_user_filesystem_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_filesystem*.so* $BOOSTLIBDIR/libboost_filesystem*.dylib* $BOOSTLIBDIR/libboost_filesystem*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_filesystem.*\)\.so.*$;\1;' -e 's;^lib\(boost_filesystem.*\)\.a*$;\1;' -e 's;^lib\(boost_filesystem.*\)\.dylib$;\1;'` ; do + for libextension in `ls -r $BOOSTLIBDIR/libboost_filesystem* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], [link_filesystem="no"]) done - if test "x$link_program_options" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_filesystem*.{dll,a}* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_filesystem.*\)\.dll.*$;\1;' -e 's;^\(boost_filesystem.*\)\.a*$;\1;'` ; do + if test "x$link_filesystem" != "xyes"; then + for libextension in `ls -r $BOOSTLIBDIR/boost_filesystem* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_FILESYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_FILESYSTEM_LIB) link_filesystem="yes"; break], diff --git a/src/m4/ax_boost_program_options.m4 b/src/m4/ax_boost_program_options.m4 index d612f91da3..65a39c8c70 100644 --- a/src/m4/ax_boost_program_options.m4 +++ b/src/m4/ax_boost_program_options.m4 @@ -29,7 +29,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 20 +#serial 22 AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], [ @@ -74,14 +74,14 @@ AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], AC_DEFINE(HAVE_BOOST_PROGRAM_OPTIONS,,[define if the Boost::PROGRAM_OPTIONS library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` if test "x$ax_boost_user_program_options_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a*$;\1;'` ; do + for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], [link_program_options="no"]) done if test "x$link_program_options" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a*$;\1;'` ; do + for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], diff --git a/src/m4/ax_boost_system.m4 b/src/m4/ax_boost_system.m4 index 7fbf6d360d..c4c45559d8 100644 --- a/src/m4/ax_boost_system.m4 +++ b/src/m4/ax_boost_system.m4 @@ -31,7 +31,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 14 +#serial 17 AC_DEFUN([AX_BOOST_SYSTEM], [ @@ -83,14 +83,14 @@ AC_DEFUN([AX_BOOST_SYSTEM], LDFLAGS_SAVE=$LDFLAGS if test "x$ax_boost_user_system_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_system*.so* $BOOSTLIBDIR/libboost_system*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_system.*\)\.so.*$;\1;' -e 's;^lib\(boost_system.*\)\.a*$;\1;'` ; do + for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], [link_system="no"]) done if test "x$link_system" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_system*.{dll,a}* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_system.*\)\.dll.*$;\1;' -e 's;^\(boost_system.*\)\.a*$;\1;'` ; do + for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], diff --git a/src/m4/ax_boost_thread.m4 b/src/m4/ax_boost_thread.m4 index d9cd8a1d1d..79e12cdb4e 100644 --- a/src/m4/ax_boost_thread.m4 +++ b/src/m4/ax_boost_thread.m4 @@ -30,7 +30,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 22 +#serial 27 AC_DEFUN([AX_BOOST_THREAD], [ @@ -68,17 +68,13 @@ AC_DEFUN([AX_BOOST_THREAD], [AC_LANG_PUSH([C++]) CXXFLAGS_SAVE=$CXXFLAGS - # let us handle platform dependent issues in - # configure.ac - - # if test "x$build_os" = "xsolaris" ; then - # CXXFLAGS="-pthreads $CXXFLAGS" - # elif test "x$build_os" = "xming32" ; then - # CXXFLAGS="-mthreads $CXXFLAGS" - # else - # CXXFLAGS="-pthread $CXXFLAGS" - # fi - + if test "x$host_os" = "xsolaris" ; then + CXXFLAGS="-pthreads $CXXFLAGS" + elif test "x$host_os" = "xmingw32" ; then + CXXFLAGS="-mthreads $CXXFLAGS" + else + CXXFLAGS="-pthread $CXXFLAGS" + fi AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/thread/thread.hpp>]], [[boost::thread_group thrds; return 0;]])], @@ -87,13 +83,13 @@ AC_DEFUN([AX_BOOST_THREAD], AC_LANG_POP([C++]) ]) if test "x$ax_cv_boost_thread" = "xyes"; then - # if test "x$build_os" = "xsolaris" ; then - # BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" - # elif test "x$build_os" = "xming32" ; then - # BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" - # else - # BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" - # fi + if test "x$host_os" = "xsolaris" ; then + BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" + elif test "x$host_os" = "xmingw32" ; then + BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" + else + BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" + fi AC_SUBST(BOOST_CPPFLAGS) @@ -101,21 +97,21 @@ AC_DEFUN([AX_BOOST_THREAD], BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` LDFLAGS_SAVE=$LDFLAGS - # case "x$build_os" in - # *bsd* ) - # LDFLAGS="-pthread $LDFLAGS" - # break; - # ;; - # esac + case "x$host_os" in + *bsd* ) + LDFLAGS="-pthread $LDFLAGS" + break; + ;; + esac if test "x$ax_boost_user_thread_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_thread*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_thread*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.a*$;\1;'`; do + for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], [link_thread="no"]) done if test "x$link_thread" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_thread*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_thread.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_thread*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_thread.*\)\.a*$;\1;'` ; do + for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], @@ -134,17 +130,17 @@ AC_DEFUN([AX_BOOST_THREAD], if test "x$ax_lib" = "x"; then AC_MSG_ERROR(Could not find a version of the library!) fi - # if test "x$link_thread" = "xno"; then - # AC_MSG_ERROR(Could not link against $ax_lib !) - # else - # case "x$build_os" in - # *bsd* ) - # BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" - # break; - # ;; - # esac + if test "x$link_thread" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + else + case "x$host_os" in + *bsd* ) + BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" + break; + ;; + esac - # fi + fi fi CPPFLAGS="$CPPFLAGS_SAVED" diff --git a/src/m4/ax_pthread.m4 b/src/m4/ax_pthread.m4 index 6d400ed4e8..d383ad5c6d 100644 --- a/src/m4/ax_pthread.m4 +++ b/src/m4/ax_pthread.m4 @@ -82,7 +82,7 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 20 +#serial 21 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ @@ -103,8 +103,8 @@ if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) - AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) - AC_MSG_RESULT($ax_pthread_ok) + AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" @@ -164,6 +164,20 @@ case ${host_os} in ;; esac +# Clang doesn't consider unrecognized options an error unless we specify +# -Werror. We throw in some extra Clang-specific options to ensure that +# this doesn't happen for GCC, which also accepts -Werror. + +AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) +save_CFLAGS="$CFLAGS" +ax_pthread_extra_flags="-Werror" +CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], + [AC_MSG_RESULT([yes])], + [ax_pthread_extra_flags= + AC_MSG_RESULT([no])]) +CFLAGS="$save_CFLAGS" + if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do @@ -178,7 +192,7 @@ for flag in $ax_pthread_flags; do ;; pthread-config) - AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" @@ -193,7 +207,7 @@ for flag in $ax_pthread_flags; do save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we @@ -219,7 +233,7 @@ for flag in $ax_pthread_flags; do LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" - AC_MSG_RESULT($ax_pthread_ok) + AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = xyes; then break; fi @@ -245,9 +259,9 @@ if test "x$ax_pthread_ok" = xyes; then [attr_name=$attr; break], []) done - AC_MSG_RESULT($attr_name) + AC_MSG_RESULT([$attr_name]) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then - AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi @@ -261,24 +275,25 @@ if test "x$ax_pthread_ok" = xyes; then if test "$GCC" = "yes"; then flag="-D_REENTRANT" else + # TODO: What about Clang on Solaris? flag="-mt -D_REENTRANT" fi ;; esac - AC_MSG_RESULT(${flag}) + AC_MSG_RESULT([$flag]) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], - ax_cv_PTHREAD_PRIO_INHERIT, [ - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], + [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], - AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" @@ -301,13 +316,13 @@ fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" -AC_SUBST(PTHREAD_LIBS) -AC_SUBST(PTHREAD_CFLAGS) -AC_SUBST(PTHREAD_CC) +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no diff --git a/src/main.cpp b/src/main.cpp index 15fc8a24c3..d130e9705e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,6 @@ #include <inttypes.h> #include <sstream> -#include <stdint.h> #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> @@ -28,6 +27,10 @@ using namespace std; using namespace boost; +#if defined(NDEBUG) +# error "Bitcoin cannot be compiled without assertions." +#endif + // // Global state // @@ -150,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) @@ -168,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); } ////////////////////////////////////////////////////////////////////////////// @@ -380,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 @@ -553,7 +592,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) REJECT_INVALID, "vout empty"); // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed"), + return state.DoS(100, error("CheckTransaction() : size limits failed"), REJECT_INVALID, "oversize"); // Check for negative or overflow output values @@ -568,7 +607,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) REJECT_INVALID, "vout too large"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"), + return state.DoS(100, error("CheckTransaction() : txout total out of range"), REJECT_INVALID, "txout total too large"); } @@ -577,7 +616,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"), + return state.DoS(100, error("CheckTransaction() : duplicate inputs"), REJECT_INVALID, "duplicate inputs"); vInOutPoints.insert(txin.prevout); } @@ -661,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++) @@ -671,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; } } } @@ -735,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); @@ -780,27 +807,12 @@ 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); - LogPrint("mempool", "AcceptToMemoryPool: : accepted %s (poolsz %"PRIszu")\n", - hash.ToString().c_str(), - pool.mapTx.size()); return true; } @@ -1309,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 { @@ -1374,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"); @@ -1632,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)) @@ -1652,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()) @@ -1694,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++) @@ -1789,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); @@ -2219,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)) @@ -2945,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) { @@ -3078,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; } @@ -3101,8 +3139,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; - if (!vRecv.empty()) + if (!vRecv.empty()) { vRecv >> pfrom->strSubVer; + pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); + } if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; if (!vRecv.empty()) @@ -3169,7 +3209,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - LogPrintf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); + LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer.c_str(), pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); AddTimeData(pfrom->addr, nTime); @@ -3181,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; } @@ -3202,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()); } @@ -3265,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()); } @@ -3316,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()); } @@ -3428,6 +3468,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); + + LogPrint("mempool", "AcceptToMemoryPool: %s %s : accepted %s (poolsz %"PRIszu")\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + tx.GetHash().ToString().c_str(), + mempool.mapTx.size()); + // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { @@ -3476,11 +3522,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } int nDoS = 0; if (state.IsInvalid(nDoS)) - { + { + LogPrint("mempool", "%s from %s %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString().c_str(), + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + state.GetRejectReason().c_str()); pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason(), inv.hash); if (nDoS > 0) - pfrom->Misbehaving(nDoS); + Misbehaving(pfrom->GetId(), nDoS); } } @@ -3507,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); } } @@ -3613,7 +3662,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!(sProblem.empty())) { LogPrint("net", "pong %s %s: %s, %"PRIx64" expected, %"PRIx64" received, %"PRIszu" bytes\n", pfrom->addr.ToString().c_str(), - pfrom->strSubVer.c_str(), + pfrom->cleanSubVer.c_str(), sProblem.c_str(), pfrom->nPingNonceSent, nonce, @@ -3650,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); } } } @@ -3663,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); @@ -3684,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); } } @@ -3955,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 cf803ae25e..c52f37cc87 100644 --- a/src/main.h +++ b/src/main.h @@ -49,9 +49,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 { @@ -937,7 +936,7 @@ private: unsigned char chRejectCode; bool corruptionPossible; public: - CValidationState() : mode(MODE_VALID), nDoS(0) {} + CValidationState() : mode(MODE_VALID), nDoS(0), corruptionPossible(false) {} bool DoS(int level, bool ret = false, unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="", bool corruptionIn=false) { diff --git a/src/miner.cpp b/src/miner.cpp index 397c95c624..ecc40ac708 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -10,8 +10,6 @@ #include "net.h" #include "wallet.h" -#include <stdint.h> - double dHashesPerSec = 0.0; int64_t nHPSTimerStart = 0; @@ -95,12 +93,12 @@ unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1 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; @@ -120,7 +118,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; @@ -193,9 +191,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; @@ -230,7 +229,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); @@ -246,24 +245,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) { @@ -271,7 +258,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 @@ -288,7 +275,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(); @@ -320,7 +307,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) @@ -652,11 +639,10 @@ void static BitcoinMiner(CWallet *pwallet) } } -void GenerateBitcoins(bool fGenerate, CWallet* pwallet) +void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) { static boost::thread_group* minerThreads = NULL; - int nThreads = GetArg("-genproclimit", -1); if (nThreads < 0) { if (Params().NetworkID() == CChainParams::REGTEST) nThreads = 1; diff --git a/src/miner.h b/src/miner.h index 4879f55d51..26151f6cd5 100644 --- a/src/miner.h +++ b/src/miner.h @@ -16,7 +16,7 @@ class CScript; class CWallet; /** Run the miner threads */ -void GenerateBitcoins(bool fGenerate, CWallet* pwallet); +void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads); /** Generate a new block, without valid proof-of-work */ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey); diff --git a/src/net.cpp b/src/net.cpp index 59982b4066..6ae749c657 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -15,9 +15,6 @@ #include "db.h" #include "ui_interface.h" -#include <inttypes.h> -#include <stdint.h> - #ifdef WIN32 #include <string.h> #else @@ -83,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 @@ -584,45 +584,30 @@ 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); X(nTimeConnected); X(addrName); X(nVersion); - X(strSubVer); + X(cleanSubVer); X(fInbound); X(nStartingHeight); - X(nMisbehavior); X(nSendBytes); X(nRecvBytes); stats.fSyncNode = (this == pnodeSync); @@ -1693,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; @@ -1746,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) @@ -1760,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 @@ -1945,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,22 +114,23 @@ 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; int64_t nTimeConnected; std::string addrName; int nVersion; - std::string strSubVer; + std::string cleanSubVer; bool fInbound; int nStartingHeight; - int nMisbehavior; uint64_t nSendBytes; uint64_t nRecvBytes; bool fSyncNode; @@ -203,7 +209,11 @@ public: std::string addrName; CService addrLocal; int nVersion; - std::string strSubVer; + // strSubVer is whatever byte array we read from the wire. However, this field is intended + // to be printed out, displayed to humans in various forms and so on. So we sanitize it and + // store the sanitized version in cleanSubVer. The original should be used when dealing with + // the network or wire types and the cleaned string used when displayed or logged. + std::string strSubVer, cleanSubVer; bool fOneShot; bool fClient; bool fInbound; @@ -219,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 @@ -285,7 +295,6 @@ public: nStartingHeight = -1; fStartSync = false; fGetAddr = false; - nMisbehavior = 0; fRelayTxes = false; setInventoryKnown.max_size(SendBufferSize() / 1000); pfilter = new CBloomFilter(); @@ -294,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() @@ -308,6 +324,7 @@ public: } if (pfilter) delete pfilter; + GetNodeSignals().FinalizeNode(GetId()); } private: @@ -322,6 +339,9 @@ private: public: + NodeId GetId() const { + return id; + } int GetRefCount() { @@ -669,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 @@ -686,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/netbase.cpp b/src/netbase.cpp index 1392fa8233..37e1df9138 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -10,8 +10,6 @@ #include "uint256.h" #include "util.h" -#include <stdint.h> - #ifndef WIN32 #include <fcntl.h> #endif diff --git a/src/protocol.cpp b/src/protocol.cpp index 798227581e..be835507ab 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -7,8 +7,6 @@ #include "util.h" -#include <stdint.h> - #ifndef WIN32 # include <arpa/inet.h> #endif diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 91e4edc33b..434373da29 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -200,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/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/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 199a14598a..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"> - <widget class="QValidatedLineEdit" name="addAsLabel"> + <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/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm index 8f826941b2..86b8c834d4 100644 --- a/src/qt/macdockiconhandler.mm +++ b/src/qt/macdockiconhandler.mm @@ -8,6 +8,10 @@ #undef slots #include <Cocoa/Cocoa.h> +#if QT_VERSION < 0x050000 +extern void qt_mac_set_dock_menu(QMenu *); +#endif + @interface DockIconClickEventHandler : NSObject { MacDockIconHandler* dockIconHandler; @@ -52,7 +56,9 @@ MacDockIconHandler::MacDockIconHandler() : QObject() this->m_dummyWidget = new QWidget(); this->m_dockMenu = new QMenu(this->m_dummyWidget); this->setMainWindow(NULL); - +#if QT_VERSION < 0x050000 + qt_mac_set_dock_menu(this->m_dockMenu); +#endif [pool release]; } 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 a536fa2ad7..075a16dabf 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -117,11 +117,11 @@ 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(); 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/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 64a3a68ba7..d43cdc7e5f 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -8,7 +8,8 @@ #include "clientmodel.h" #include "guiutil.h" -#include "bitcoinrpc.h" +#include "rpcserver.h" +#include "rpcclient.h" #include "json/json_spirit_value.h" #include <openssl/crypto.h> diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 0adf24691e..0a4e80811f 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -216,12 +216,19 @@ void SendCoinsDialog::on_sendButton_clicked() questionString.append("</span> "); questionString.append(tr("added as transaction fee")); } - if(txFee > 0 || recipients.count() > 1) + + // add total amount in all subdivision units + questionString.append("<hr />"); + qint64 totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; + QStringList alternativeUnits; + foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits()) { - // add total amount string if there are more then one recipients or a fee is required - questionString.append("<hr />"); - questionString.append(tr("Total Amount %1").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTotalTransactionAmount()+txFee))); + if(u != model->getOptionsModel()->getDisplayUnit()) + 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") + " "))); QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), @@ -253,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() @@ -280,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(); @@ -293,27 +298,21 @@ 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->deleteLater(); + + // If the last entry was removed add an empty one + if (!ui->entries->count()) + addEntry(); + + updateTabsAndLabels(); } QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) @@ -372,7 +371,7 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) } entry->setValue(rv); - coinControlUpdateLabels(); + updateTabsAndLabels(); } bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) @@ -530,16 +529,17 @@ 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 @@ -555,6 +555,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")); } @@ -572,6 +576,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")); } @@ -612,4 +620,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/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 6f0b353e34..34ae6e0543 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -3,9 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - - -#include "bitcoinrpc.h" +#include "rpcserver.h" #include "main.h" #include "sync.h" @@ -155,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 new file mode 100644 index 0000000000..a3168917fc --- /dev/null +++ b/src/rpcclient.cpp @@ -0,0 +1,279 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-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 "rpcclient.h" + +#include "rpcprotocol.h" +#include "util.h" +#include "ui_interface.h" +#include "chainparams.h" // for Params().RPCPort() + +#include <stdint.h> + +#include <boost/algorithm/string.hpp> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> +#include <boost/bind.hpp> +#include <boost/filesystem.hpp> +#include <boost/foreach.hpp> +#include <boost/iostreams/concepts.hpp> +#include <boost/iostreams/stream.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> +#include "json/json_spirit_writer_template.h" + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword=<password> in the configuration file:\n%s\n" + "If the file does not exist, create it with owner-readable-only file permissions."), + GetConfigFile().string().c_str())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl", false); + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context); + SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL); + iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d); + + bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started + do { + bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort()))); + if (fConnected) break; + if (fWait) + MilliSleep(1000); + else + throw runtime_error("couldn't connect to server"); + } while (fWait); + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map<string, string> mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // Send request + string strRequest = JSONRPCRequest(strMethod, params, 1); + string strPost = HTTPPost(strRequest, mapRequestHeaders); + stream << strPost << std::flush; + + // Receive HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body + map<string, string> mapHeaders; + string strReply; + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + + if (nStatus == HTTP_UNAUTHORIZED) + throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) + throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + else if (strReply.empty()) + throw runtime_error("no response from server"); + + // Parse reply + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& reply = valReply.get_obj(); + if (reply.empty()) + throw runtime_error("expected reply to have result, error and id properties"); + + return reply; +} + +template<typename T> +void ConvertTo(Value& value, bool fAllowNull=false) +{ + if (fAllowNull && value.type() == null_type) + return; + if (value.type() == str_type) + { + // reinterpret string as unquoted json value + Value value2; + string strJSON = value.get_str(); + if (!read_string(strJSON, value2)) + throw runtime_error(string("Error parsing JSON:")+strJSON); + ConvertTo<T>(value2, fAllowNull); + value = value2; + } + else + { + value = value.get_value<T>(); + } +} + +// Convert strings to command-specific RPC representation +Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) +{ + Array params; + BOOST_FOREACH(const std::string ¶m, strParams) + params.push_back(param); + + int n = params.size(); + + // + // Special case non-string parameter types + // + if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]); + if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]); + if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]); + if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]); + if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]); + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]); + if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]); + if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]); + if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]); + if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]); + if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]); + if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]); + if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]); + if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]); + if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]); + if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]); + if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]); + if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]); + if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]); + if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]); + if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true); + if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true); + if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true); + if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]); + if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]); + if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]); + if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]); + if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]); + 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; +} + +int CommandLineRPC(int argc, char *argv[]) +{ + string strPrint; + int nRet = 0; + try + { + // Skip switches + while (argc > 1 && IsSwitchChar(argv[1][0])) + { + argc--; + argv++; + } + + // Method + if (argc < 2) + throw runtime_error("too few parameters"); + string strMethod = argv[1]; + + // Parameters default to strings + std::vector<std::string> strParams(&argv[2], &argv[argc]); + Array params = RPCConvertValues(strMethod, strParams); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { + strPrint = string("error: ") + e.what(); + nRet = 87; + } + catch (...) { + PrintException(NULL, "CommandLineRPC()"); + } + + if (strPrint != "") + { + fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); + } + 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 new file mode 100644 index 0000000000..e101d22ec5 --- /dev/null +++ b/src/rpcclient.h @@ -0,0 +1,25 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-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 _BITCOINRPC_CLIENT_H_ +#define _BITCOINRPC_CLIENT_H_ 1 + +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_utils.h" +#include "json/json_spirit_writer_template.h" + +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/rpcdump.cpp b/src/rpcdump.cpp index 53cec9a6fa..92f4c2c6dd 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -2,10 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - - #include "base58.h" -#include "bitcoinrpc.h" +#include "rpcserver.h" #include "init.h" #include "main.h" #include "sync.h" diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index e4fa7ed853..131a258c84 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -3,7 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bitcoinrpc.h" +#include "rpcserver.h" #include "chainparams.h" #include "db.h" #include "init.h" @@ -133,6 +133,7 @@ Value setgenerate(const Array& params, bool fHelp) "\nArguments:\n" "1. generate (boolean, required) Set to true to turn on generation, off to turn off.\n" "2. genproclimit (numeric, optional) Set the processor limit for when generation is on. Can be -1 for unlimited.\n" + " Note: in -regtest mode, genproclimit controls how many blocks are generated immediately.\n" "\nExamples:\n" "\nSet the generation on with a limit of one processor\n" + HelpExampleCli("setgenerate", "true 1") + @@ -144,21 +145,55 @@ Value setgenerate(const Array& params, bool fHelp) + HelpExampleRpc("setgenerate", "true, 1") ); + if (pwalletMain == NULL) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + bool fGenerate = true; if (params.size() > 0) fGenerate = params[0].get_bool(); + int nGenProcLimit = -1; if (params.size() > 1) { - int nGenProcLimit = params[1].get_int(); - mapArgs["-genproclimit"] = itostr(nGenProcLimit); + nGenProcLimit = params[1].get_int(); if (nGenProcLimit == 0) fGenerate = false; } - mapArgs["-gen"] = (fGenerate ? "1" : "0"); - assert(pwalletMain != NULL); - GenerateBitcoins(fGenerate, pwalletMain); + // -regtest mode: don't return until nGenProcLimit blocks are generated + if (fGenerate && Params().NetworkID() == CChainParams::REGTEST) + { + int nHeightStart = 0; + int nHeightEnd = 0; + int nHeight = 0; + int nGenerate = (nGenProcLimit > 0 ? nGenProcLimit : 1); + { // Don't keep cs_main locked + LOCK(cs_main); + nHeightStart = chainActive.Height(); + nHeight = nHeightStart; + nHeightEnd = nHeightStart+nGenerate; + } + int nHeightLast = -1; + while (nHeight < nHeightEnd) + { + if (nHeightLast != nHeight) + { + nHeightLast = nHeight; + GenerateBitcoins(fGenerate, pwalletMain, 1); + } + MilliSleep(1); + { // Don't keep cs_main locked + LOCK(cs_main); + nHeight = chainActive.Height(); + } + } + } + else // Not -regtest: start generate thread, return immediately + { + mapArgs["-gen"] = (fGenerate ? "1" : "0"); + GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit); + } + return Value::null; } diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 9f8dea80b0..93811e80ed 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -1,15 +1,20 @@ // Copyright (c) 2009-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 "rpcserver.h" - -#include "bitcoinrpc.h" +#include "main.h" #include "net.h" #include "netbase.h" #include "protocol.h" #include "sync.h" #include "util.h" +#ifdef ENABLE_WALLET +#include "wallet.h" // for getinfo +#include "init.h" // for getinfo +#endif +#include "main.h" // for getinfo #include <inttypes.h> @@ -112,7 +117,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)); @@ -126,10 +132,15 @@ Value getpeerinfo(const Array& params, bool fHelp) if (stats.dPingWait > 0.0) obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); - obj.push_back(Pair("subver", stats.strSubVer)); + // Use the sanitized form of subver here, to avoid tricksy remote peers from + // corrupting or modifiying the JSON output by putting special characters in + // their ver message. + 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)); @@ -314,7 +325,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" @@ -328,3 +339,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/rpcprotocol.cpp b/src/rpcprotocol.cpp new file mode 100644 index 0000000000..4a2241edaa --- /dev/null +++ b/src/rpcprotocol.cpp @@ -0,0 +1,262 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-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 "rpcprotocol.h" + +#include "util.h" + +#include <stdint.h> + +#include <boost/algorithm/string.hpp> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> +#include <boost/bind.hpp> +#include <boost/filesystem.hpp> +#include <boost/foreach.hpp> +#include <boost/iostreams/concepts.hpp> +#include <boost/iostreams/stream.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> +#include "json/json_spirit_writer_template.h" + +using namespace std; +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +// +// HTTP protocol +// +// This ain't Apache. We're just using HTTP header for the length field +// and to be compatible with other JSON-RPC implementations. +// + +string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders) +{ + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Connection: close\r\n" + << "Accept: application/json\r\n"; + BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" << strMsg; + + return s.str(); +} + +static string rfc1123Time() +{ + char buffer[64]; + time_t now; + time(&now); + struct tm* now_gmt = gmtime(&now); + string locale(setlocale(LC_TIME, NULL)); + setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); + setlocale(LC_TIME, locale.c_str()); + return string(buffer); +} + +string HTTPReply(int nStatus, const string& strMsg, bool keepalive) +{ + if (nStatus == HTTP_UNAUTHORIZED) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 296\r\n" + "\r\n" + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n" + "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n" + "<HTML>\r\n" + "<HEAD>\r\n" + "<TITLE>Error</TITLE>\r\n" + "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n" + "</HEAD>\r\n" + "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n" + "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); + const char *cStatus; + if (nStatus == HTTP_OK) cStatus = "OK"; + else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; + else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; + else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; + else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; + else cStatus = ""; + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: %s\r\n" + "Content-Length: %"PRIszu"\r\n" + "Content-Type: application/json\r\n" + "Server: bitcoin-json-rpc/%s\r\n" + "\r\n" + "%s", + nStatus, + cStatus, + rfc1123Time().c_str(), + keepalive ? "keep-alive" : "close", + strMsg.size(), + FormatFullVersion().c_str(), + strMsg.c_str()); +} + +bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, + string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector<string> vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char *ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + + return true; +} + +int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) +{ + string str; + getline(stream, str); + vector<string> vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return HTTP_INTERNAL_SERVER_ERROR; + proto = 0; + const char *ver = strstr(str.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) +{ + int nLen = 0; + while (true) + { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) + { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon+1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + + +int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, + string>& mapHeadersRet, string& strMessageRet, + int nProto) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read header + int nLen = ReadHTTPHeaders(stream, mapHeadersRet); + if (nLen < 0 || nLen > (int)MAX_SIZE) + return HTTP_INTERNAL_SERVER_ERROR; + + // Read message + if (nLen > 0) + { + vector<char> vch(nLen); + stream.read(&vch[0], nLen); + strMessageRet = string(vch.begin(), vch.end()); + } + + string sConHdr = mapHeadersRet["connection"]; + + if ((sConHdr != "close") && (sConHdr != "keep-alive")) + { + if (nProto >= 1) + mapHeadersRet["connection"] = "keep-alive"; + else + mapHeadersRet["connection"] = "close"; + } + + return HTTP_OK; +} + +// +// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, +// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were +// unspecified (HTTP errors and contents of 'error'). +// +// 1.0 spec: http://json-rpc.org/wiki/specification +// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http +// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx +// + +string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +{ + Object request; + request.push_back(Pair("method", strMethod)); + request.push_back(Pair("params", params)); + request.push_back(Pair("id", id)); + return write_string(Value(request), false) + "\n"; +} + +Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) +{ + Object reply; + if (error.type() != null_type) + reply.push_back(Pair("result", Value::null)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return reply; +} + +string JSONRPCReply(const Value& result, const Value& error, const Value& id) +{ + Object reply = JSONRPCReplyObj(result, error, id); + return write_string(Value(reply), false) + "\n"; +} + +Object JSONRPCError(int code, const string& message) +{ + Object error; + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} diff --git a/src/rpcprotocol.h b/src/rpcprotocol.h new file mode 100644 index 0000000000..6bf371e759 --- /dev/null +++ b/src/rpcprotocol.h @@ -0,0 +1,138 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-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 _BITCOINRPC_PROTOCOL_H_ +#define _BITCOINRPC_PROTOCOL_H_ 1 + +#include <list> +#include <map> +#include <stdint.h> +#include <string> +#include <boost/iostreams/concepts.hpp> +#include <boost/iostreams/stream.hpp> +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_utils.h" +#include "json/json_spirit_writer_template.h" + +// HTTP status codes +enum HTTPStatusCode +{ + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_INTERNAL_SERVER_ERROR = 500, +}; + +// Bitcoin RPC error codes +enum RPCErrorCode +{ + // Standard JSON-RPC 2.0 errors + RPC_INVALID_REQUEST = -32600, + RPC_METHOD_NOT_FOUND = -32601, + RPC_INVALID_PARAMS = -32602, + RPC_INTERNAL_ERROR = -32603, + RPC_PARSE_ERROR = -32700, + + // General application defined errors + RPC_MISC_ERROR = -1, // std::exception thrown in command handling + RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode + RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter + RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key + RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation + RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter + RPC_DATABASE_ERROR = -20, // Database error + RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format + RPC_SERVER_NOT_STARTED = -18, // RPC server was not started (StartRPCThreads() not called) + + // P2P client errors + RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected + RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks + RPC_CLIENT_NODE_ALREADY_ADDED = -23, // Node is already added + RPC_CLIENT_NODE_NOT_ADDED = -24, // Node has not been added before + + // Wallet errors + RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.) + RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account + RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name + RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first + RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first + RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect + RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) + RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet + RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked +}; + +// +// IOStream device that speaks SSL but can also speak non-SSL +// +template <typename Protocol> +class SSLIOStreamDevice : public boost::iostreams::device<boost::iostreams::bidirectional> { +public: + SSLIOStreamDevice(boost::asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(boost::asio::ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(boost::asio::ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(boost::asio::buffer(s, n)); + return stream.next_layer().read_some(boost::asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(boost::asio::ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return boost::asio::write(stream, boost::asio::buffer(s, n)); + return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + boost::asio::ip::tcp::resolver resolver(stream.get_io_service()); + boost::asio::ip::tcp::resolver::query query(server.c_str(), port.c_str()); + boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); + boost::asio::ip::tcp::resolver::iterator end; + boost::system::error_code error = boost::asio::error::host_not_found; + while (error && endpoint_iterator != end) + { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + boost::asio::ssl::stream<typename Protocol::socket>& stream; +}; + +std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders); +std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive); +bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, + std::string& http_method, std::string& http_uri); +int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto); +int ReadHTTPHeaders(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet); +int ReadHTTPMessage(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet, + std::string& strMessageRet, int nProto); +std::string JSONRPCRequest(const std::string& strMethod, const json_spirit::Array& params, const json_spirit::Value& id); +json_spirit::Object JSONRPCReplyObj(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id); +std::string JSONRPCReply(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id); +json_spirit::Object JSONRPCError(int code, const std::string& message); + +#endif diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 2225e216d3..86025918ef 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -3,14 +3,17 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - - #include "base58.h" -#include "bitcoinrpc.h" +#include "rpcserver.h" #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> @@ -80,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); } @@ -192,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) @@ -305,6 +309,7 @@ Value listunspent(const Array& params, bool fHelp) return results; } +#endif Value createrawtransaction(const Array& params, bool fHelp) { @@ -510,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" @@ -607,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) @@ -664,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/bitcoinrpc.cpp b/src/rpcserver.cpp index c715f13fd2..2dc7b34f88 100644 --- a/src/bitcoinrpc.cpp +++ b/src/rpcserver.cpp @@ -3,15 +3,16 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bitcoinrpc.h" +#include "rpcserver.h" #include "base58.h" #include "init.h" #include "main.h" #include "util.h" +#include "ui_interface.h" +#ifdef ENABLE_WALLET #include "wallet.h" - -#include <stdint.h> +#endif #include <boost/algorithm/string.hpp> #include <boost/asio.hpp> @@ -38,14 +39,6 @@ static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; static ssl::context* rpc_ssl_context = NULL; static boost::thread_group* rpc_worker_group = NULL; -Object JSONRPCError(int code, const string& message) -{ - Object error; - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); - return error; -} - void RPCTypeCheck(const Array& params, const list<Value_type>& typesExpected, bool fAllowNull) @@ -159,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 { @@ -238,11 +233,26 @@ static const CRPCCommand vRPCCommands[] = { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, { "getnettotals", &getnettotals, true, true, false }, { "getdifficulty", &getdifficulty, 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 }, + +#ifdef ENABLE_WALLET { "getnetworkhashps", &getnetworkhashps, true, false, false }, { "getgenerate", &getgenerate, true, false, false }, - { "setgenerate", &setgenerate, true, false, true }, + { "setgenerate", &setgenerate, true, true, false }, { "gethashespersec", &gethashespersec, true, false, false }, - { "getinfo", &getinfo, true, false, false }, { "getmininginfo", &getmininginfo, true, false, false }, { "getnewaddress", &getnewaddress, true, false, true }, { "getaccountaddress", &getaccountaddress, true, false, true }, @@ -268,9 +278,6 @@ 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 }, @@ -278,7 +285,6 @@ static const CRPCCommand vRPCCommands[] = { "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 }, @@ -287,17 +293,9 @@ static const CRPCCommand vRPCCommands[] = { "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 }, +#endif // ENABLE_WALLET }; CRPCTable::CRPCTable() @@ -320,194 +318,6 @@ const CRPCCommand *CRPCTable::operator[](string name) const return (*it).second; } -// -// HTTP protocol -// -// This ain't Apache. We're just using HTTP header for the length field -// and to be compatible with other JSON-RPC implementations. -// - -string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders) -{ - ostringstream s; - s << "POST / HTTP/1.1\r\n" - << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" - << "Host: 127.0.0.1\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size() << "\r\n" - << "Connection: close\r\n" - << "Accept: application/json\r\n"; - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - s << "\r\n" << strMsg; - - return s.str(); -} - -string rfc1123Time() -{ - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); -} - -static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) -{ - if (nStatus == HTTP_UNAUTHORIZED) - return strprintf("HTTP/1.0 401 Authorization Required\r\n" - "Date: %s\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" - "Content-Type: text/html\r\n" - "Content-Length: 296\r\n" - "\r\n" - "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n" - "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n" - "<HTML>\r\n" - "<HEAD>\r\n" - "<TITLE>Error</TITLE>\r\n" - "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n" - "</HEAD>\r\n" - "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n" - "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - const char *cStatus; - if (nStatus == HTTP_OK) cStatus = "OK"; - else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; - else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; - else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; - else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; - else cStatus = ""; - return strprintf( - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Connection: %s\r\n" - "Content-Length: %"PRIszu"\r\n" - "Content-Type: application/json\r\n" - "Server: bitcoin-json-rpc/%s\r\n" - "\r\n" - "%s", - nStatus, - cStatus, - rfc1123Time().c_str(), - keepalive ? "keep-alive" : "close", - strMsg.size(), - FormatFullVersion().c_str(), - strMsg.c_str()); -} - -bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, - string& http_method, string& http_uri) -{ - string str; - getline(stream, str); - - // HTTP request line is space-delimited - vector<string> vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return false; - - // HTTP methods permitted: GET, POST - http_method = vWords[0]; - if (http_method != "GET" && http_method != "POST") - return false; - - // HTTP URI must be an absolute path, relative to current host - http_uri = vWords[1]; - if (http_uri.size() == 0 || http_uri[0] != '/') - return false; - - // parse proto, if present - string strProto = ""; - if (vWords.size() > 2) - strProto = vWords[2]; - - proto = 0; - const char *ver = strstr(strProto.c_str(), "HTTP/1."); - if (ver != NULL) - proto = atoi(ver+7); - - return true; -} - -int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) -{ - string str; - getline(stream, str); - vector<string> vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return HTTP_INTERNAL_SERVER_ERROR; - proto = 0; - const char *ver = strstr(str.c_str(), "HTTP/1."); - if (ver != NULL) - proto = atoi(ver+7); - return atoi(vWords[1].c_str()); -} - -int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) -{ - int nLen = 0; - while (true) - { - string str; - std::getline(stream, str); - if (str.empty() || str == "\r") - break; - string::size_type nColon = str.find(":"); - if (nColon != string::npos) - { - string strHeader = str.substr(0, nColon); - boost::trim(strHeader); - boost::to_lower(strHeader); - string strValue = str.substr(nColon+1); - boost::trim(strValue); - mapHeadersRet[strHeader] = strValue; - if (strHeader == "content-length") - nLen = atoi(strValue.c_str()); - } - } - return nLen; -} - -int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, - string>& mapHeadersRet, string& strMessageRet, - int nProto) -{ - mapHeadersRet.clear(); - strMessageRet = ""; - - // Read header - int nLen = ReadHTTPHeaders(stream, mapHeadersRet); - if (nLen < 0 || nLen > (int)MAX_SIZE) - return HTTP_INTERNAL_SERVER_ERROR; - - // Read message - if (nLen > 0) - { - vector<char> vch(nLen); - stream.read(&vch[0], nLen); - strMessageRet = string(vch.begin(), vch.end()); - } - - string sConHdr = mapHeadersRet["connection"]; - - if ((sConHdr != "close") && (sConHdr != "keep-alive")) - { - if (nProto >= 1) - mapHeadersRet["connection"] = "keep-alive"; - else - mapHeadersRet["connection"] = "close"; - } - - return HTTP_OK; -} bool HTTPAuthorized(map<string, string>& mapHeaders) { @@ -519,43 +329,6 @@ bool HTTPAuthorized(map<string, string>& mapHeaders) return TimingResistantEqual(strUserPass, strRPCUserColonPass); } -// -// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, -// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were -// unspecified (HTTP errors and contents of 'error'). -// -// 1.0 spec: http://json-rpc.org/wiki/specification -// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http -// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx -// - -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) -{ - Object request; - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); - return write_string(Value(request), false) + "\n"; -} - -Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) -{ - Object reply; - if (error.type() != null_type) - reply.push_back(Pair("result", Value::null)); - else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); - return reply; -} - -string JSONRPCReply(const Value& result, const Value& error, const Value& id) -{ - Object reply = JSONRPCReplyObj(result, error, id); - return write_string(Value(reply), false) + "\n"; -} - void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) { // Send error reply from json-rpc error object @@ -590,59 +363,6 @@ bool ClientAllowed(const boost::asio::ip::address& address) return false; } -// -// IOStream device that speaks SSL but can also speak non-SSL -// -template <typename Protocol> -class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> { -public: - SSLIOStreamDevice(asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn) - { - fUseSSL = fUseSSLIn; - fNeedHandshake = fUseSSLIn; - } - - void handshake(ssl::stream_base::handshake_type role) - { - if (!fNeedHandshake) return; - fNeedHandshake = false; - stream.handshake(role); - } - std::streamsize read(char* s, std::streamsize n) - { - handshake(ssl::stream_base::server); // HTTPS servers read first - if (fUseSSL) return stream.read_some(asio::buffer(s, n)); - return stream.next_layer().read_some(asio::buffer(s, n)); - } - std::streamsize write(const char* s, std::streamsize n) - { - handshake(ssl::stream_base::client); // HTTPS clients write first - if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); - return asio::write(stream.next_layer(), asio::buffer(s, n)); - } - bool connect(const std::string& server, const std::string& port) - { - ip::tcp::resolver resolver(stream.get_io_service()); - ip::tcp::resolver::query query(server.c_str(), port.c_str()); - ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - ip::tcp::resolver::iterator end; - boost::system::error_code error = asio::error::host_not_found; - while (error && endpoint_iterator != end) - { - stream.lowest_layer().close(); - stream.lowest_layer().connect(*endpoint_iterator++, error); - } - if (error) - return false; - return true; - } - -private: - bool fNeedHandshake; - bool fUseSSL; - asio::ssl::stream<typename Protocol::socket>& stream; -}; - class AcceptedConnection { public: @@ -722,6 +442,7 @@ static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketA boost::asio::placeholders::error)); } + /** * Accept and handle incoming connection. */ @@ -912,7 +633,6 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6 deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); } - class JSONRequest { public: @@ -954,6 +674,7 @@ void JSONRequest::parse(const Value& valRequest) throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); } + static Object JSONRPCExecOne(const Value& req) { Object rpc_result; @@ -1075,8 +796,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"); @@ -1091,6 +814,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); @@ -1098,6 +822,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; } @@ -1107,255 +837,13 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s } } - -Object CallRPC(const string& strMethod, const Array& params) -{ - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword=<password> in the configuration file:\n%s\n" - "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().string().c_str())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl", false); - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context); - SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL); - iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort())))) - throw runtime_error("couldn't connect to server"); - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map<string, string> mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; - - // Receive HTTP reply status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - - // Receive HTTP reply message headers and body - map<string, string> mapHeaders; - string strReply; - ReadHTTPMessage(stream, mapHeaders, strReply, nProto); - - if (nStatus == HTTP_UNAUTHORIZED) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); - else if (strReply.empty()) - throw runtime_error("no response from server"); - - // Parse reply - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); - - return reply; -} - - - - -template<typename T> -void ConvertTo(Value& value, bool fAllowNull=false) -{ - if (fAllowNull && value.type() == null_type) - return; - if (value.type() == str_type) - { - // reinterpret string as unquoted json value - Value value2; - string strJSON = value.get_str(); - if (!read_string(strJSON, value2)) - throw runtime_error(string("Error parsing JSON:")+strJSON); - ConvertTo<T>(value2, fAllowNull); - value = value2; - } - else - { - value = value.get_value<T>(); - } -} - -// Convert strings to command-specific RPC representation -Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams) -{ - Array params; - BOOST_FOREACH(const std::string ¶m, strParams) - params.push_back(param); - - int n = params.size(); - - // - // Special case non-string parameter types - // - if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]); - if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]); - if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]); - if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]); - if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]); - if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]); - if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]); - if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]); - if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]); - if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]); - if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); - if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]); - if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]); - if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]); - if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]); - if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]); - if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]); - if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]); - if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true); - if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true); - if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true); - if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]); - if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]); - if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]); - if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]); - if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]); - 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]); - - return params; -} - -int CommandLineRPC(int argc, char *argv[]) -{ - string strPrint; - int nRet = 0; - try - { - // Skip switches - while (argc > 1 && IsSwitchChar(argv[1][0])) - { - argc--; - argv++; - } - - // Method - if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; - - // Parameters default to strings - std::vector<std::string> strParams(&argv[2], &argv[argc]); - Array params = RPCConvertValues(strMethod, strParams); - - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - - if (error.type() != null_type) - { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } - else - { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); - } - } - catch (boost::thread_interrupted) { - throw; - } - catch (std::exception& e) { - strPrint = string("error: ") + e.what(); - nRet = 87; - } - catch (...) { - PrintException(NULL, "CommandLineRPC()"); - } - - if (strPrint != "") - { - fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); - } - return nRet; +std::string HelpExampleCli(string methodname, string args){ + return "> bitcoin-cli " + methodname + " " + args + "\n"; } - - - -#ifdef TEST -int main(int argc, char *argv[]) -{ -#ifdef _MSC_VER - // Turn off Microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif - setbuf(stdin, NULL); - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - try - { - if (argc >= 2 && string(argv[1]) == "-server") - { - LogPrintf("server ready\n"); - ThreadRPCServer(NULL); - } - else - { - return CommandLineRPC(argc, argv); - } - } - catch (boost::thread_interrupted) { - throw; - } - catch (std::exception& e) { - PrintException(&e, "main()"); - } catch (...) { - PrintException(NULL, "main()"); - } - return 0; +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"; } -#endif const CRPCTable tableRPC; diff --git a/src/bitcoinrpc.h b/src/rpcserver.h index 9025ff9216..4d29e90c09 100644 --- a/src/bitcoinrpc.h +++ b/src/rpcserver.h @@ -3,10 +3,11 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef _BITCOINRPC_H_ -#define _BITCOINRPC_H_ 1 +#ifndef _BITCOINRPC_SERVER_H_ +#define _BITCOINRPC_SERVER_H_ 1 #include "uint256.h" +#include "rpcprotocol.h" #include <list> #include <map> @@ -18,66 +19,9 @@ #include "json/json_spirit_writer_template.h" class CBlockIndex; -class CReserveKey; - -// HTTP status codes -enum HTTPStatusCode -{ - HTTP_OK = 200, - HTTP_BAD_REQUEST = 400, - HTTP_UNAUTHORIZED = 401, - HTTP_FORBIDDEN = 403, - HTTP_NOT_FOUND = 404, - HTTP_INTERNAL_SERVER_ERROR = 500, -}; - -// Bitcoin RPC error codes -enum RPCErrorCode -{ - // Standard JSON-RPC 2.0 errors - RPC_INVALID_REQUEST = -32600, - RPC_METHOD_NOT_FOUND = -32601, - RPC_INVALID_PARAMS = -32602, - RPC_INTERNAL_ERROR = -32603, - RPC_PARSE_ERROR = -32700, - - // General application defined errors - RPC_MISC_ERROR = -1, // std::exception thrown in command handling - RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode - RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter - RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key - RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation - RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter - RPC_DATABASE_ERROR = -20, // Database error - RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format - RPC_SERVER_NOT_STARTED = -18, // RPC server was not started (StartRPCThreads() not called) - - // P2P client errors - RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected - RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks - RPC_CLIENT_NODE_ALREADY_ADDED = -23, // Node is already added - RPC_CLIENT_NODE_NOT_ADDED = -24, // Node has not been added before - - // Wallet errors - RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.) - RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account - RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name - RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first - RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first - RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect - RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) - RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet - RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked -}; - -json_spirit::Object JSONRPCError(int code, const std::string& message); void StartRPCThreads(); void StopRPCThreads(); -int CommandLineRPC(int argc, char *argv[]); - -/** Convert parameter values for RPC call from strings to command-specific JSON objects. */ -json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams); /* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 8b14c0aca9..82fa9d88c5 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -3,10 +3,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. - - #include "base58.h" -#include "bitcoinrpc.h" +#include "rpcserver.h" #include "init.h" #include "net.h" #include "netbase.h" @@ -35,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()) @@ -77,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) @@ -403,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 != "") @@ -1675,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()) @@ -1726,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/script.cpp b/src/script.cpp index 228d432ee3..2b66bc73d6 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -14,8 +14,6 @@ #include "uint256.h" #include "util.h" -#include <stdint.h> - #include <boost/foreach.hpp> #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_comparison.hpp> 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..dccd264e5a 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -20,17 +20,26 @@ 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 \ - key_tests.cpp miner_tests.cpp mruset_tests.cpp multisig_tests.cpp \ + key_tests.cpp mruset_tests.cpp multisig_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 miner_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..46c9ae021d 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -57,6 +57,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) CScript script; uint256 hash; + LOCK(cs_main); + // Simple block creation, nothing special yet: BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); @@ -99,7 +101,7 @@ 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)); @@ -119,7 +121,7 @@ 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)); @@ -128,7 +130,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // orphan in mempool hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -138,7 +140,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,7 +148,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].prevout.n = 0; tx.vout[0].nValue = 5900000000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, tx); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -157,7 +159,7 @@ 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); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -170,12 +172,12 @@ 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); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); @@ -186,10 +188,10 @@ 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); + mempool.addUnchecked(hash, CTxMemPoolEntry(tx, 11, GetTime(), 111.0, 11)); BOOST_CHECK(pblocktemplate = CreateNewBlockWithKey(reservekey)); delete pblocktemplate; mempool.clear(); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 4fb2aeb6ae..29195545da 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -1,4 +1,5 @@ -#include "bitcoinrpc.h" +#include "rpcserver.h" +#include "rpcclient.h" #include "base58.h" @@ -8,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; @@ -22,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")); @@ -78,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 44186b7325..a8c591cc7a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -12,9 +12,7 @@ #include "uint256.h" #include "version.h" -#include <inttypes.h> #include <stdarg.h> -#include <stdint.h> #ifndef WIN32 // for posix_fallocate @@ -97,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; @@ -1513,3 +1512,4 @@ void RenameThread(const char* name) (void)name; #endif } + diff --git a/src/wallet.cpp b/src/wallet.cpp index 31dfe18185..241e937b1b 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -10,7 +10,6 @@ #include "net.h" #include <inttypes.h> -#include <stdint.h> #include <boost/algorithm/string/replace.hpp> #include <openssl/rand.h> @@ -656,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; } @@ -766,6 +765,10 @@ void CWalletTx::AddSupportingTransactions() { tx = *mapWalletPrev[hash]; } + else + { + continue; + } int nDepth = tx.SetMerkleBranch(); vtxPrev.push_back(tx); @@ -896,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()); } @@ -1299,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()); } @@ -1343,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); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 6b3628b184..2dc6594e93 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -12,7 +12,6 @@ #include "wallet.h" #include <inttypes.h> -#include <stdint.h> #include <boost/filesystem.hpp> #include <boost/foreach.hpp> |