aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xqa/pull-tester/build-tests.sh.in3
-rw-r--r--qa/rpc-tests/README.md6
-rw-r--r--qa/rpc-tests/util.sh84
-rwxr-xr-xqa/rpc-tests/wallet.sh97
-rw-r--r--src/Makefile.am11
-rw-r--r--src/bitcoin-cli.cpp8
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/coins.cpp16
-rw-r--r--src/coins.h4
-rw-r--r--src/compat.h4
-rw-r--r--src/core.cpp31
-rw-r--r--src/core.h16
-rw-r--r--src/init.cpp6
-rw-r--r--src/main.cpp89
-rw-r--r--src/main.h10
-rw-r--r--src/miner.cpp38
-rw-r--r--src/miner.h2
-rw-r--r--src/net.cpp2
-rw-r--r--src/net.h8
-rw-r--r--src/qt/macdockiconhandler.mm8
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/qt/transactiondesc.cpp2
-rw-r--r--src/qt/transactionrecord.cpp2
-rw-r--r--src/rpcblockchain.cpp79
-rw-r--r--src/rpcclient.cpp247
-rw-r--r--src/rpcclient.h17
-rw-r--r--src/rpcdump.cpp4
-rw-r--r--src/rpcmining.cpp47
-rw-r--r--src/rpcnet.cpp9
-rw-r--r--src/rpcprotocol.cpp262
-rw-r--r--src/rpcprotocol.h138
-rw-r--r--src/rpcrawtransaction.cpp4
-rw-r--r--src/rpcserver.cpp (renamed from src/bitcoinrpc.cpp)552
-rw-r--r--src/rpcserver.h (renamed from src/bitcoinrpc.h)62
-rw-r--r--src/rpcwallet.cpp6
-rw-r--r--src/test/miner_tests.cpp20
-rw-r--r--src/test/rpc_tests.cpp3
-rw-r--r--src/txmempool.cpp61
-rw-r--r--src/txmempool.h31
-rw-r--r--src/wallet.cpp12
40 files changed, 1214 insertions, 793 deletions
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/src/Makefile.am b/src/Makefile.am
index 7450507b34..561d2ca7ae 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,7 +12,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,7 +33,11 @@ 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 \
+libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp \
+ rpcclient.cpp \
+ rpcprotocol.cpp \
+ rpcserver.cpp \
+ bloom.cpp \
chainparams.cpp checkpoints.cpp core.cpp coins.cpp crypter.cpp db.cpp hash.cpp \
init.cpp key.cpp keystore.cpp leveldbwrapper.cpp main.cpp miner.cpp \
netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index aa6ce27c52..4e65947464 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"))
{
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 6dbab240bf..fdc66b8d65 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"
diff --git a/src/coins.cpp b/src/coins.cpp
index ed82c9df8b..86b2a6ef17 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -178,3 +178,19 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx)
}
return true;
}
+
+double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight)
+{
+ if (tx.IsCoinBase())
+ return 0.0;
+ double dResult = 0.0;
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ const CCoins &coins = GetCoins(txin.prevout.hash);
+ if (!coins.IsAvailable(txin.prevout.n)) continue;
+ if (coins.nHeight < nHeight) {
+ dResult += coins.vout[txin.prevout.n].nValue * (nHeight-coins.nHeight);
+ }
+ }
+ return tx.ComputePriority(dResult);
+}
diff --git a/src/coins.h b/src/coins.h
index 2c72ee88e1..0ad28524a1 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -340,13 +340,15 @@ public:
@param[in] tx transaction for which we are checking input total
@return Sum of value of all inputs (scriptSigs)
- @see CTransaction::FetchInputs
*/
int64_t GetValueIn(const CTransaction& tx);
// Check whether all prevouts of the transaction are present in the UTXO set represented by this view
bool HaveInputs(const CTransaction& tx);
+ // Return priority of tx at height nHeight
+ double GetPriority(const CTransaction &tx, int nHeight);
+
const CTxOut &GetOutputFor(const CTxIn& input);
private:
diff --git a/src/compat.h b/src/compat.h
index 1deef493c2..3924445b1a 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -11,7 +11,9 @@
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
+#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
+#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
@@ -26,6 +28,7 @@
#include <windows.h>
#include <ws2tcpip.h>
#else
+#include <sys/types.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <limits.h>
@@ -35,7 +38,6 @@
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <sys/socket.h>
-#include <sys/types.h>
#include <unistd.h>
#endif
diff --git a/src/core.cpp b/src/core.cpp
index 7a1c90e587..f41ea87fea 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -106,6 +106,37 @@ bool CTransaction::IsNewerThan(const CTransaction& old) const
return fNewer;
}
+int64_t CTransaction::GetValueOut() const
+{
+ int64_t nValueOut = 0;
+ BOOST_FOREACH(const CTxOut& txout, vout)
+ {
+ nValueOut += txout.nValue;
+ if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
+ throw std::runtime_error("CTransaction::GetValueOut() : value out of range");
+ }
+ return nValueOut;
+}
+
+double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const
+{
+ // In order to avoid disincentivizing cleaning up the UTXO set we don't count
+ // the constant overhead for each txin and up to 110 bytes of scriptSig (which
+ // is enough to cover a compressed pubkey p2sh redemption) for priority.
+ // Providing any more cleanup incentive than making additional inputs free would
+ // risk encouraging people to create junk outputs to redeem later.
+ if (nTxSize == 0)
+ nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION);
+ BOOST_FOREACH(const CTxIn& txin, vin)
+ {
+ unsigned int offset = 41U + std::min(110U, (unsigned int)txin.scriptSig.size());
+ if (nTxSize > offset)
+ nTxSize -= offset;
+ }
+ if (nTxSize == 0) return 0.0;
+ return dPriorityInputs / nTxSize;
+}
+
std::string CTransaction::ToString() const
{
std::string str;
diff --git a/src/core.h b/src/core.h
index e31a7e6582..e61cad90ec 100644
--- a/src/core.h
+++ b/src/core.h
@@ -14,6 +14,10 @@
class CTransaction;
+/** No amount larger than this (in satoshi) is valid */
+static const int64_t MAX_MONEY = 21000000 * COIN;
+inline bool MoneyRange(int64_t nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
+
/** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint
{
@@ -50,11 +54,11 @@ public:
class CInPoint
{
public:
- CTransaction* ptx;
+ const CTransaction* ptx;
unsigned int n;
CInPoint() { SetNull(); }
- CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; }
+ CInPoint(const CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; }
void SetNull() { ptx = NULL; n = (unsigned int) -1; }
bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); }
};
@@ -217,6 +221,14 @@ public:
uint256 GetHash() const;
bool IsNewerThan(const CTransaction& old) const;
+ // Return sum of txouts.
+ int64_t GetValueOut() const;
+ // GetValueIn() is a method on CCoinsViewCache, because
+ // inputs must be known to compute value in.
+
+ // Compute priority, given priority of inputs and (optionally) tx size
+ double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const;
+
bool IsCoinBase() const
{
return (vin.size() == 1 && vin[0].prevout.IsNull());
diff --git a/src/init.cpp b/src/init.cpp
index b2e7ddf335..da70ef9dba 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -10,7 +10,7 @@
#include "init.h"
#include "addrman.h"
-#include "bitcoinrpc.h"
+#include "rpcserver.h"
#include "checkpoints.h"
#include "miner.h"
#include "net.h"
@@ -112,7 +112,7 @@ void Shutdown()
ShutdownRPCMining();
if (pwalletMain)
bitdb.Flush(false);
- GenerateBitcoins(false, NULL);
+ GenerateBitcoins(false, NULL, 0);
StopNode();
{
LOCK(cs_main);
@@ -1050,7 +1050,7 @@ bool AppInit2(boost::thread_group& threadGroup, bool fForceServer)
// Generate coins in the background
if (pwalletMain)
- GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain);
+ GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", -1));
// ********************************************************* Step 12: finished
diff --git a/src/main.cpp b/src/main.cpp
index 705e94fabb..457fc941e7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -379,21 +379,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
}
-/** Amount of bitcoins spent by the transaction.
- @return sum of all outputs (note: does not include fees)
- */
-int64_t GetValueOut(const CTransaction& tx)
-{
- int64_t nValueOut = 0;
- BOOST_FOREACH(const CTxOut& txout, tx.vout)
- {
- nValueOut += txout.nValue;
- if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
- throw std::runtime_error("GetValueOut() : value out of range");
- }
- return nValueOut;
-}
-
//
// Check transaction inputs, and make sure any
// pay-to-script-hash transactions are evaluating IsStandard scripts
@@ -660,7 +645,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return false;
// Check for conflicts with in-memory transactions
- CTransaction* ptxOld = NULL;
{
LOCK(pool.cs); // protect pool.mapNextTx
for (unsigned int i = 0; i < tx.vin.size(); i++)
@@ -670,22 +654,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
{
// Disable replacement feature for now
return false;
-
- // Allow replacing with a newer version of the same transaction
- if (i != 0)
- return false;
- ptxOld = pool.mapNextTx[outpoint].ptx;
- if (IsFinalTx(*ptxOld))
- return false;
- if (!tx.IsNewerThan(*ptxOld))
- return false;
- for (unsigned int i = 0; i < tx.vin.size(); i++)
- {
- COutPoint outpoint = tx.vin[i].prevout;
- if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld)
- return false;
- }
- break;
}
}
}
@@ -734,8 +702,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
- int64_t nFees = view.GetValueIn(tx)-GetValueOut(tx);
- unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ int64_t nValueIn = view.GetValueIn(tx);
+ int64_t nValueOut = tx.GetValueOut();
+ int64_t nFees = nValueIn-nValueOut;
+ double dPriority = view.GetPriority(tx, chainActive.Height());
+
+ CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height());
+ unsigned int nSize = entry.GetTxSize();
// Don't accept it if it can't get into a block
int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY);
@@ -779,27 +752,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;
}
@@ -1373,12 +1331,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");
@@ -1631,7 +1589,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))
@@ -1651,10 +1609,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())
@@ -3100,8 +3058,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())
@@ -3168,7 +3128,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);
@@ -3427,6 +3387,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++)
{
@@ -3475,7 +3441,10 @@ 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)
@@ -3612,7 +3581,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,
diff --git a/src/main.h b/src/main.h
index cf803ae25e..c4e1839443 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. */
@@ -320,11 +317,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 +929,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 97f434851d..ecc40ac708 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -93,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;
@@ -118,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;
@@ -191,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;
@@ -228,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);
@@ -244,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)
{
@@ -269,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
@@ -286,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();
@@ -318,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)
@@ -650,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 c547cf3337..fcef9feea0 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -616,7 +616,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nTimeConnected);
X(addrName);
X(nVersion);
- X(strSubVer);
+ X(cleanSubVer);
X(fInbound);
X(nStartingHeight);
X(nMisbehavior);
diff --git a/src/net.h b/src/net.h
index 278462a0bc..effce35dc4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -121,7 +121,7 @@ public:
int64_t nTimeConnected;
std::string addrName;
int nVersion;
- std::string strSubVer;
+ std::string cleanSubVer;
bool fInbound;
int nStartingHeight;
int nMisbehavior;
@@ -203,7 +203,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;
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/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/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/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..f571ca52d6
--- /dev/null
+++ b/src/rpcclient.cpp
@@ -0,0 +1,247 @@
+// 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 &param, 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;
+}
diff --git a/src/rpcclient.h b/src/rpcclient.h
new file mode 100644
index 0000000000..f3ea56c25b
--- /dev/null
+++ b/src/rpcclient.h
@@ -0,0 +1,17 @@
+// 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);
+
+#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..99602bd1cf 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -2,9 +2,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 "net.h"
#include "netbase.h"
#include "protocol.h"
@@ -126,7 +124,10 @@ 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));
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..939ca96f76 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.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 "uint256.h"
diff --git a/src/bitcoinrpc.cpp b/src/rpcserver.cpp
index a1e7d14dcc..c746d8c8fb 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/rpcserver.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 "base58.h"
#include "init.h"
@@ -36,14 +36,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)
@@ -238,7 +230,7 @@ static const CRPCCommand vRPCCommands[] =
{ "getdifficulty", &getdifficulty, true, false, false },
{ "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 },
@@ -318,194 +310,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)
{
@@ -517,43 +321,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
@@ -588,59 +355,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:
@@ -720,6 +434,7 @@ static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketA
boost::asio::placeholders::error));
}
+
/**
* Accept and handle incoming connection.
*/
@@ -910,7 +625,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:
@@ -952,6 +666,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;
@@ -1105,263 +820,4 @@ 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);
-
- 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 &param, 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;
-}
-
-
-
-
-#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;
-}
-#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..b4e522de8f 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"
@@ -1675,7 +1673,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())
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index dcb7f9abd4..8001c4f65a 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -99,7 +99,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 +119,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 +128,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 +138,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 +146,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 +157,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 +170,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 +186,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..76580bae43 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"
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/wallet.cpp b/src/wallet.cpp
index db957cbd05..b9110d1271 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -655,7 +655,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
int64_t nDebit = GetDebit();
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
- int64_t nValueOut = GetValueOut(*this);
+ int64_t nValueOut = GetValueOut();
nFee = nDebit - nValueOut;
}
@@ -1342,15 +1342,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);