aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/init/bitcoind.openrc6
-rw-r--r--contrib/init/bitcoind.openrcconf6
-rw-r--r--doc/REST-interface.md4
-rwxr-xr-xqa/pull-tester/rpc-tests.sh5
-rwxr-xr-xqa/rpc-tests/fundrawtransaction.py37
-rwxr-xr-xqa/rpc-tests/rawtransactions.py1
-rwxr-xr-xqa/rpc-tests/rest.py39
-rwxr-xr-xqa/rpc-tests/txn_clone.py4
-rw-r--r--src/chainparams.cpp3
-rw-r--r--src/chainparams.h3
-rw-r--r--src/init.cpp54
-rw-r--r--src/memusage.h67
-rw-r--r--src/miner.cpp9
-rw-r--r--src/net.cpp14
-rw-r--r--src/net.h13
-rw-r--r--src/primitives/transaction.cpp5
-rw-r--r--src/primitives/transaction.h9
-rw-r--r--src/qt/addressbookpage.cpp3
-rw-r--r--src/qt/bitcoin.cpp8
-rw-r--r--src/qt/clientmodel.cpp6
-rw-r--r--src/qt/coincontroldialog.cpp11
-rw-r--r--src/qt/receivecoinsdialog.cpp3
-rw-r--r--src/rest.cpp19
-rw-r--r--src/rpcblockchain.cpp4
-rw-r--r--src/rpcserver.cpp2
-rw-r--r--src/script/script.cpp5
-rw-r--r--src/script/script.h3
-rw-r--r--src/test/data/tx_invalid.json8
-rw-r--r--src/test/rpc_tests.cpp21
-rw-r--r--src/test/util_tests.cpp66
-rw-r--r--src/txmempool.cpp14
-rw-r--r--src/txmempool.h7
-rw-r--r--src/utilstrencodings.cpp120
-rw-r--r--src/utilstrencodings.h7
34 files changed, 494 insertions, 92 deletions
diff --git a/contrib/init/bitcoind.openrc b/contrib/init/bitcoind.openrc
index a94f03680d..eda1a96fb4 100644
--- a/contrib/init/bitcoind.openrc
+++ b/contrib/init/bitcoind.openrc
@@ -32,7 +32,11 @@ required_files="${BITCOIND_CONFIGFILE}"
start_stop_daemon_args="-u ${BITCOIND_USER} \
-N ${BITCOIND_NICE} -w 2000"
pidfile="${BITCOIND_PIDFILE}"
-retry=60
+
+# The retry schedule to use when stopping the daemon. Could be either
+# a timeout in seconds or multiple signal/timeout pairs (like
+# "SIGKILL/180 SIGTERM/300")
+retry="${BITCOIND_SIGTERM_TIMEOUT}"
depend() {
need localmount net
diff --git a/contrib/init/bitcoind.openrcconf b/contrib/init/bitcoind.openrcconf
index d8d7f58337..0cbff6d30d 100644
--- a/contrib/init/bitcoind.openrcconf
+++ b/contrib/init/bitcoind.openrcconf
@@ -25,3 +25,9 @@
# Additional options (avoid -conf and -datadir, use flags above)
BITCOIND_OPTS="-disablewallet"
+# The timeout in seconds OpenRC will wait for bitcoind to terminate
+# after a SIGTERM has been raised.
+# Note that this will be mapped as argument to start-stop-daemon's
+# '--retry' option, which means you can specify a retry schedule
+# here. For more information see man 8 start-stop-daemon.
+BITCOIND_SIGTERM_TIMEOUT=60
diff --git a/doc/REST-interface.md b/doc/REST-interface.md
index 2219ceb65f..1ba01362db 100644
--- a/doc/REST-interface.md
+++ b/doc/REST-interface.md
@@ -26,13 +26,11 @@ The HTTP request and response are both handled entirely in-memory, thus making m
With the /notxdetails/ option JSON response will only contain the transaction hash instead of the complete transaction details. The option only affects the JSON response.
####Blockheaders
-`GET /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex>`
+`GET /rest/headers/<COUNT>/<BLOCK-HASH>.<bin|hex|json>`
Given a block hash,
Returns <COUNT> amount of blockheaders in upward direction.
-JSON is not supported.
-
####Chaininfos
`GET /rest/chaininfo.json`
diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh
index b282082395..72a282bc00 100755
--- a/qa/pull-tester/rpc-tests.sh
+++ b/qa/pull-tester/rpc-tests.sh
@@ -19,10 +19,8 @@ testScripts=(
'wallet.py'
'listtransactions.py'
'mempool_resurrect_test.py'
- 'txn_doublespend.py'
'txn_doublespend.py --mineblock'
'txn_clone.py'
- 'txn_clone.py --mineblock'
'getchaintips.py'
'rawtransactions.py'
'rest.py'
@@ -44,6 +42,8 @@ testScriptsExt=(
'bipdersig.py'
'getblocktemplate_longpoll.py'
'getblocktemplate_proposals.py'
+ 'txn_doublespend.py'
+ 'txn_clone.py --mineblock'
'pruning.py'
'forknotify.py'
'invalidateblock.py'
@@ -54,7 +54,6 @@ testScriptsExt=(
'smartfees.py'
'maxblocksinflight.py'
'invalidblockrequest.py'
- 'rawtransactions.py'
# 'forknotify.py'
'p2p-acceptblock.py'
);
diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py
index e859b26433..80f1d1e128 100755
--- a/qa/rpc-tests/fundrawtransaction.py
+++ b/qa/rpc-tests/fundrawtransaction.py
@@ -30,6 +30,7 @@ class RawTransactionsTest(BitcoinTestFramework):
feeTolerance = Decimal(0.00000002) #if the fee's positive delta is higher than this value tests will fail, neg. delta always fail the tests
self.nodes[2].generate(1)
+ self.sync_all()
self.nodes[0].generate(101)
self.sync_all()
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5);
@@ -46,17 +47,10 @@ class RawTransactionsTest(BitcoinTestFramework):
outputs = { self.nodes[0].getnewaddress() : 1.0 }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- totalOut = 0
- for out in dec_tx['vout']:
- totalOut += out['value']
-
- assert_equal(len(dec_tx['vin']), 1) #one vin coin
- assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
- assert_equal(fee + totalOut, 1.5) #the 1.5BTC coin must be taken
+ assert_equal(len(dec_tx['vin']) > 0, True) #test if we have enought inputs
##############################
# simple test with two coins #
@@ -69,14 +63,7 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- totalOut = 0
- for out in dec_tx['vout']:
- totalOut += out['value']
-
- assert_equal(len(dec_tx['vin']), 2) #one vin coin
- assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
- assert_equal(dec_tx['vin'][1]['scriptSig']['hex'], '')
- assert_equal(fee + totalOut, 2.5) #the 1.5BTC+1.0BTC coins must have be taken
+ assert_equal(len(dec_tx['vin']) > 0, True) #test if we have enough inputs
##############################
# simple test with two coins #
@@ -89,13 +76,8 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
fee = rawtxfund['fee']
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- totalOut = 0
- for out in dec_tx['vout']:
- totalOut += out['value']
-
- assert_equal(len(dec_tx['vin']), 1) #one vin coin
+ assert_equal(len(dec_tx['vin']) > 0, True)
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
- assert_equal(fee + totalOut, 5.0) #the 5.0BTC coin must have be taken
################################
@@ -113,11 +95,8 @@ class RawTransactionsTest(BitcoinTestFramework):
for out in dec_tx['vout']:
totalOut += out['value']
- assert_equal(len(dec_tx['vin']), 2) #one vin coin
+ assert_equal(len(dec_tx['vin']) > 0, True)
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
- assert_equal(dec_tx['vin'][1]['scriptSig']['hex'], '')
- assert_equal(fee + totalOut, 6.0) #the 5.0BTC + 1.0BTC coins must have be taken
-
#########################################################################
@@ -220,8 +199,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 1)
assert_equal(len(dec_tx['vout']), 2)
- assert_equal(fee + totalOut, 2.5) #this tx must use the 1.0BTC and the 1.5BTC coin
-
###########################################
# test a fundrawtransaction with two VINs #
@@ -264,8 +241,6 @@ class RawTransactionsTest(BitcoinTestFramework):
matchingIns+=1
assert_equal(matchingIns, 2) #we now must see two vins identical to vins given as params
- assert_equal(fee + totalOut, 7.5) #this tx must use the 1.0BTC and the 1.5BTC coin
-
#########################################################
# test a fundrawtransaction with two VINs and two vOUTs #
@@ -300,8 +275,6 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(matchingOuts, 2)
assert_equal(len(dec_tx['vout']), 3)
- assert_equal(fee + totalOut, 7.5) #this tx must use the 1.0BTC and the 1.5BTC coin
-
##############################################
# test a fundrawtransaction with invalid vin #
diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py
index 1378514c84..173faf736e 100755
--- a/qa/rpc-tests/rawtransactions.py
+++ b/qa/rpc-tests/rawtransactions.py
@@ -40,6 +40,7 @@ class RawTransactionsTest(BitcoinTestFramework):
#prepare some coins for multiple *rawtransaction commands
self.nodes[2].generate(1)
+ self.sync_all()
self.nodes[0].generate(101)
self.sync_all()
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5);
diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py
index 6c51b2fcd9..1a2d326cc3 100755
--- a/qa/rpc-tests/rest.py
+++ b/qa/rpc-tests/rest.py
@@ -235,12 +235,43 @@ class RESTTest (BitcoinTestFramework):
assert_equal(response_header_str.encode("hex")[0:160], response_header_hex_str[0:160])
# check json format
- json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
- json_obj = json.loads(json_string)
- assert_equal(json_obj['hash'], bb_hash)
+ block_json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
+ block_json_obj = json.loads(block_json_string)
+ assert_equal(block_json_obj['hash'], bb_hash)
+
+ # compare with json block header
+ response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", "", True)
+ assert_equal(response_header_json.status, 200)
+ response_header_json_str = response_header_json.read()
+ json_obj = json.loads(response_header_json_str)
+ assert_equal(len(json_obj), 1) #ensure that there is one header in the json response
+ assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same
+
+ #compare with normal RPC block response
+ rpc_block_json = self.nodes[0].getblock(bb_hash)
+ assert_equal(json_obj[0]['hash'], rpc_block_json['hash'])
+ assert_equal(json_obj[0]['confirmations'], rpc_block_json['confirmations'])
+ assert_equal(json_obj[0]['height'], rpc_block_json['height'])
+ assert_equal(json_obj[0]['version'], rpc_block_json['version'])
+ assert_equal(json_obj[0]['merkleroot'], rpc_block_json['merkleroot'])
+ assert_equal(json_obj[0]['time'], rpc_block_json['time'])
+ assert_equal(json_obj[0]['nonce'], rpc_block_json['nonce'])
+ assert_equal(json_obj[0]['bits'], rpc_block_json['bits'])
+ assert_equal(json_obj[0]['difficulty'], rpc_block_json['difficulty'])
+ assert_equal(json_obj[0]['chainwork'], rpc_block_json['chainwork'])
+ assert_equal(json_obj[0]['previousblockhash'], rpc_block_json['previousblockhash'])
+
+ #see if we can get 5 headers in one response
+ self.nodes[1].generate(5)
+ self.sync_all()
+ response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/5/'+bb_hash+self.FORMAT_SEPARATOR+"json", "", True)
+ assert_equal(response_header_json.status, 200)
+ response_header_json_str = response_header_json.read()
+ json_obj = json.loads(response_header_json_str)
+ assert_equal(len(json_obj), 5) #now we should have 5 header objects
# do tx test
- tx_hash = json_obj['tx'][0]['txid'];
+ tx_hash = block_json_obj['tx'][0]['txid'];
json_string = http_get_call(url.hostname, url.port, '/rest/tx/'+tx_hash+self.FORMAT_SEPARATOR+"json")
json_obj = json.loads(json_string)
assert_equal(json_obj['txid'], tx_hash)
diff --git a/qa/rpc-tests/txn_clone.py b/qa/rpc-tests/txn_clone.py
index 0d276ecc91..e8ced0e5bb 100755
--- a/qa/rpc-tests/txn_clone.py
+++ b/qa/rpc-tests/txn_clone.py
@@ -125,6 +125,8 @@ class TxnMallTest(BitcoinTestFramework):
# Reconnect the split network, and sync chain:
connect_nodes(self.nodes[1], 2)
+ self.nodes[2].sendrawtransaction(fund_bar_tx["hex"])
+ self.nodes[2].sendrawtransaction(tx2["hex"])
self.nodes[2].generate(1) # Mine another block to make sure we sync
sync_blocks(self.nodes)
@@ -136,7 +138,7 @@ class TxnMallTest(BitcoinTestFramework):
# Verify expected confirmations
assert_equal(tx1["confirmations"], -1)
assert_equal(tx1_clone["confirmations"], 2)
- assert_equal(tx2["confirmations"], 0)
+ assert_equal(tx2["confirmations"], 1)
# Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured,
# less possible orphaned matured subsidy
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 3d172765cb..d693fba05d 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -50,7 +50,6 @@ public:
pchMessageStart[3] = 0xd9;
vAlertPubKey = ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284");
nDefaultPort = 8333;
- nMinerThreads = 0;
nPruneAfterHeight = 100000;
/**
@@ -146,7 +145,6 @@ public:
pchMessageStart[3] = 0x07;
vAlertPubKey = ParseHex("04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a");
nDefaultPort = 18333;
- nMinerThreads = 0;
nPruneAfterHeight = 1000;
//! Modify the testnet genesis block so the timestamp is valid for a later start.
@@ -204,7 +202,6 @@ public:
pchMessageStart[1] = 0xbf;
pchMessageStart[2] = 0xb5;
pchMessageStart[3] = 0xda;
- nMinerThreads = 1;
genesis.nTime = 1296688602;
genesis.nBits = 0x207fffff;
genesis.nNonce = 2;
diff --git a/src/chainparams.h b/src/chainparams.h
index 8f13c2814e..66d865b620 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -50,8 +50,6 @@ public:
const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; }
int GetDefaultPort() const { return nDefaultPort; }
- /** Used if GenerateBitcoins is called with a negative number of threads */
- int DefaultMinerThreads() const { return nMinerThreads; }
const CBlock& GenesisBlock() const { return genesis; }
/** Make miner wait to have peers to avoid wasting work */
bool MiningRequiresPeers() const { return fMiningRequiresPeers; }
@@ -78,7 +76,6 @@ protected:
//! Raw pub key bytes for the broadcast alert signing key.
std::vector<unsigned char> vAlertPubKey;
int nDefaultPort;
- int nMinerThreads;
uint64_t nPruneAfterHeight;
std::vector<CDNSSeedData> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
diff --git a/src/init.cpp b/src/init.cpp
index abaf9b81d2..4addc663c8 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -330,6 +330,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt("-whitelist=<netmask>", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") +
" " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway"));
+ strUsage += HelpMessageOpt("-whiteconnections=<n>", strprintf(_("Reserve this many inbound connections for whitelisted peers (default: %d)"), 0));
#ifdef ENABLE_WALLET
strUsage += HelpMessageGroup(_("Wallet options:"));
@@ -725,16 +726,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
}
- // Make sure enough file descriptors are available
- int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
- nMaxConnections = GetArg("-maxconnections", 125);
- nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
- int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
- if (nFD < MIN_CORE_FILEDESCRIPTORS)
- return InitError(_("Not enough file descriptors available."));
- if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections)
- nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS;
-
// if using block pruning, then disable txindex
if (GetArg("-prune", 0)) {
if (GetBoolArg("-txindex", false))
@@ -745,6 +736,47 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
#endif
}
+
+ // Make sure enough file descriptors are available
+ int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
+ int nUserMaxConnections = GetArg("-maxconnections", 125);
+ nMaxConnections = std::max(nUserMaxConnections, 0);
+ int nUserWhiteConnections = GetArg("-whiteconnections", 0);
+ nWhiteConnections = std::max(nUserWhiteConnections, 0);
+
+ if ((mapArgs.count("-whitelist")) || (mapArgs.count("-whitebind"))) {
+ if (!(mapArgs.count("-maxconnections"))) {
+ // User is using whitelist feature,
+ // but did not specify -maxconnections parameter.
+ // Silently increase the default to compensate,
+ // so that the whitelist connection reservation feature
+ // does not inadvertently reduce the default
+ // inbound connection capacity of the network.
+ nMaxConnections += nWhiteConnections;
+ }
+ } else {
+ // User not using whitelist feature.
+ // Silently disable connection reservation,
+ // for the same reason as above.
+ nWhiteConnections = 0;
+ }
+
+ // Trim requested connection counts, to fit into system limitations
+ nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
+ int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
+ if (nFD < MIN_CORE_FILEDESCRIPTORS)
+ return InitError(_("Not enough file descriptors available."));
+ nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections);
+
+ if (nMaxConnections < nUserMaxConnections)
+ InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
+
+ // Connection capacity is prioritized in this order:
+ // outbound connections (hardcoded to 8),
+ // then whitelisted connections,
+ // then non-whitelisted connections get whatever's left (if any).
+ if ((nWhiteConnections > 0) && (nWhiteConnections >= (nMaxConnections - 8)))
+ InitWarning(strprintf(_("All non-whitelisted incoming connections will be dropped, because -whiteconnections is %d and -maxconnections is only %d."), nWhiteConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
@@ -921,6 +953,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Using data directory %s\n", strDataDir);
LogPrintf("Using config file %s\n", GetConfigFile().string());
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
+ if (nWhiteConnections > 0)
+ LogPrintf("Reserving %i of these connections for whitelisted inbound peers\n", nWhiteConnections);
std::ostringstream strErrors;
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
diff --git a/src/memusage.h b/src/memusage.h
index 9f7de9e2e1..7a831e6d33 100644
--- a/src/memusage.h
+++ b/src/memusage.h
@@ -11,6 +11,7 @@
#include <set>
#include <vector>
+#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
@@ -20,12 +21,28 @@ namespace memusage
/** Compute the total memory used by allocating alloc bytes. */
static size_t MallocUsage(size_t alloc);
+/** Dynamic memory usage for built-in types is zero. */
+static inline size_t DynamicUsage(const int8_t& v) { return 0; }
+static inline size_t DynamicUsage(const uint8_t& v) { return 0; }
+static inline size_t DynamicUsage(const int16_t& v) { return 0; }
+static inline size_t DynamicUsage(const uint16_t& v) { return 0; }
+static inline size_t DynamicUsage(const int32_t& v) { return 0; }
+static inline size_t DynamicUsage(const uint32_t& v) { return 0; }
+static inline size_t DynamicUsage(const int64_t& v) { return 0; }
+static inline size_t DynamicUsage(const uint64_t& v) { return 0; }
+static inline size_t DynamicUsage(const float& v) { return 0; }
+static inline size_t DynamicUsage(const double& v) { return 0; }
+template<typename X> static inline size_t DynamicUsage(X * const &v) { return 0; }
+template<typename X> static inline size_t DynamicUsage(const X * const &v) { return 0; }
+template<typename X, typename Y> static inline size_t DynamicUsage(std::pair<X, Y> &p) { return 0; }
+
/** Compute the memory used for dynamically allocated but owned data structures.
* For generic data types, this is *not* recursive. DynamicUsage(vector<vector<int> >)
* will compute the memory used for the vector<int>'s, but not for the ints inside.
* This is for efficiency reasons, as these functions are intended to be fast. If
* application data structures require more accurate inner accounting, they should
- * do the recursion themselves, or use more efficient caching + updating on modification.
+ * use RecursiveDynamicUsage, iterate themselves, or use more efficient caching +
+ * updating on modification.
*/
template<typename X> static size_t DynamicUsage(const std::vector<X>& v);
template<typename X> static size_t DynamicUsage(const std::set<X>& s);
@@ -34,6 +51,12 @@ template<typename X, typename Y> static size_t DynamicUsage(const boost::unorder
template<typename X, typename Y, typename Z> static size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& s);
template<typename X> static size_t DynamicUsage(const X& x);
+template<typename X> static size_t RecursiveDynamicUsage(const std::vector<X>& v);
+template<typename X> static size_t RecursiveDynamicUsage(const std::set<X>& v);
+template<typename X, typename Y> static size_t RecursiveDynamicUsage(const std::map<X, Y>& v);
+template<typename X, typename Y> static size_t RecursiveDynamicUsage(const std::pair<X, Y>& v);
+template<typename X> static size_t RecursiveDynamicUsage(const X& v);
+
static inline size_t MallocUsage(size_t alloc)
{
// Measured on libc6 2.19 on Linux.
@@ -66,17 +89,53 @@ static inline size_t DynamicUsage(const std::vector<X>& v)
}
template<typename X>
+static inline size_t RecursiveDynamicUsage(const std::vector<X>& v)
+{
+ size_t usage = DynamicUsage(v);
+ BOOST_FOREACH(const X& x, v) {
+ usage += RecursiveDynamicUsage(x);
+ }
+ return usage;
+}
+
+template<typename X>
static inline size_t DynamicUsage(const std::set<X>& s)
{
return MallocUsage(sizeof(stl_tree_node<X>)) * s.size();
}
+template<typename X>
+static inline size_t RecursiveDynamicUsage(const std::set<X>& v)
+{
+ size_t usage = DynamicUsage(v);
+ BOOST_FOREACH(const X& x, v) {
+ usage += RecursiveDynamicUsage(x);
+ }
+ return usage;
+}
+
template<typename X, typename Y>
static inline size_t DynamicUsage(const std::map<X, Y>& m)
{
return MallocUsage(sizeof(stl_tree_node<std::pair<const X, Y> >)) * m.size();
}
+template<typename X, typename Y>
+static inline size_t RecursiveDynamicUsage(const std::map<X, Y>& v)
+{
+ size_t usage = DynamicUsage(v);
+ for (typename std::map<X, Y>::const_iterator it = v.begin(); it != v.end(); it++) {
+ usage += RecursiveDynamicUsage(*it);
+ }
+ return usage;
+}
+
+template<typename X, typename Y>
+static inline size_t RecursiveDynamicUsage(const std::pair<X, Y>& v)
+{
+ return RecursiveDynamicUsage(v.first) + RecursiveDynamicUsage(v.second);
+}
+
// Boost data structures
template<typename X>
@@ -106,6 +165,12 @@ static inline size_t DynamicUsage(const X& x)
return x.DynamicMemoryUsage();
}
+template<typename X>
+static inline size_t RecursiveDynamicUsage(const X& x)
+{
+ return DynamicUsage(x);
+}
+
}
#endif
diff --git a/src/miner.cpp b/src/miner.cpp
index bdb85c8c79..5e575f45f1 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -542,13 +542,8 @@ void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainpar
{
static boost::thread_group* minerThreads = NULL;
- if (nThreads < 0) {
- // In regtest threads defaults to 1
- if (Params().DefaultMinerThreads())
- nThreads = Params().DefaultMinerThreads();
- else
- nThreads = GetNumCores();
- }
+ if (nThreads < 0)
+ nThreads = GetNumCores();
if (minerThreads != NULL)
{
diff --git a/src/net.cpp b/src/net.cpp
index 950311ee3a..2c7ba0ca79 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -79,6 +79,7 @@ uint64_t nLocalHostNonce = 0;
static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman;
int nMaxConnections = 125;
+int nWhiteConnections = 0;
bool fAddressesInitialized = false;
vector<CNode*> vNodes;
@@ -928,6 +929,7 @@ void ThreadSocketHandler()
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
+ int nMaxInbound = nMaxConnections - MAX_OUTBOUND_CONNECTIONS;
if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
@@ -947,8 +949,14 @@ void ThreadSocketHandler()
if (nErr != WSAEWOULDBLOCK)
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
}
- else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS)
+ else if (nInbound >= nMaxInbound)
{
+ LogPrint("net", "connection from %s dropped (full)\n", addr.ToString());
+ CloseSocket(hSocket);
+ }
+ else if (!whitelisted && (nInbound >= (nMaxInbound - nWhiteConnections)))
+ {
+ LogPrint("net", "connection from %s dropped (non-whitelisted)\n", addr.ToString());
CloseSocket(hSocket);
}
else if (CNode::IsBanned(addr) && !whitelisted)
@@ -962,6 +970,8 @@ void ThreadSocketHandler()
pnode->AddRef();
pnode->fWhitelisted = whitelisted;
+ LogPrint("net", "connection from %s accepted\n", addr.ToString());
+
{
LOCK(cs_vNodes);
vNodes.push_back(pnode);
@@ -2295,4 +2305,4 @@ void DumpBanlist()
LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
-} \ No newline at end of file
+}
diff --git a/src/net.h b/src/net.h
index f15b85474f..86d74e2174 100644
--- a/src/net.h
+++ b/src/net.h
@@ -141,7 +141,20 @@ extern bool fListen;
extern uint64_t nLocalServices;
extern uint64_t nLocalHostNonce;
extern CAddrMan addrman;
+
+// The allocation of connections against the maximum allowed (nMaxConnections)
+// is prioritized as follows:
+// 1st: Outbound connections (MAX_OUTBOUND_CONNECTIONS)
+// 2nd: Inbound connections from whitelisted peers (nWhiteConnections)
+// 3rd: Inbound connections from non-whitelisted peers
+// Thus, the number of connection slots for the general public to use is:
+// nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + nWhiteConnections)
+// Any additional inbound connections beyond limits will be immediately closed
+
+/** Maximum number of connections to simultaneously allow (aka connection slots) */
extern int nMaxConnections;
+/** Number of connection slots to reserve for inbound from whitelisted peers */
+extern int nWhiteConnections;
extern std::vector<CNode*> vNodes;
extern CCriticalSection cs_vNodes;
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 606dbea798..7ed2d45973 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -72,6 +72,11 @@ void CTransaction::UpdateHash() const
*const_cast<uint256*>(&hash) = SerializeHash(*this);
}
+size_t CTransaction::DynamicMemoryUsage() const
+{
+ return memusage::RecursiveDynamicUsage(vin) + memusage::RecursiveDynamicUsage(vout);
+}
+
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 6cfd93a9a1..77326c64b0 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -7,6 +7,7 @@
#define BITCOIN_PRIMITIVES_TRANSACTION_H
#include "amount.h"
+#include "memusage.h"
#include "script/script.h"
#include "serialize.h"
#include "uint256.h"
@@ -48,6 +49,8 @@ public:
}
std::string ToString() const;
+
+ size_t DynamicMemoryUsage() const { return 0; }
};
/** An input of a transaction. It contains the location of the previous
@@ -96,6 +99,8 @@ public:
}
std::string ToString() const;
+
+ size_t DynamicMemoryUsage() const { return scriptSig.DynamicMemoryUsage(); }
};
/** An output of a transaction. It contains the public key that the next input
@@ -166,6 +171,8 @@ public:
}
std::string ToString() const;
+
+ size_t DynamicMemoryUsage() const { return scriptPubKey.DynamicMemoryUsage(); }
};
struct CMutableTransaction;
@@ -249,6 +256,8 @@ public:
}
std::string ToString() const;
+
+ size_t DynamicMemoryUsage() const;
};
/** A mutable version of CTransaction. */
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 5485d89f3e..54635f1d54 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -254,8 +254,7 @@ void AddressBookPage::done(int retval)
// Figure out which address was selected, and return it
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
- foreach (QModelIndex index, indexes)
- {
+ foreach (const QModelIndex& index, indexes) {
QVariant address = table->model()->data(index);
returnValue = address.toString();
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 8740b98b70..07250e72c2 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -48,6 +48,7 @@
#include <QThread>
#include <QTimer>
#include <QTranslator>
+#include <QSslConfiguration>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@@ -515,6 +516,13 @@ int main(int argc, char *argv[])
#ifdef Q_OS_MAC
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
+#if QT_VERSION >= 0x050500
+ // Because of the POODLE attack it is recommended to disable SSLv3 (https://disablessl3.com/),
+ // so set SSL protocols to TLS1.0+.
+ QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration();
+ sslconf.setProtocol(QSsl::TlsV1_0OrLater);
+ QSslConfiguration::setDefaultConfiguration(sslconf);
+#endif
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 8e29cdeb06..5ac11324d9 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -53,9 +53,9 @@ int ClientModel::getNumConnections(unsigned int flags) const
return vNodes.size();
int nNum = 0;
- BOOST_FOREACH(CNode* pnode, vNodes)
- if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
- nNum++;
+ BOOST_FOREACH(const CNode* pnode, vNodes)
+ if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
+ nNum++;
return nNum;
}
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 7531fbddcb..eea4503533 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -118,7 +118,7 @@ CoinControlDialog::CoinControlDialog(QWidget *parent) :
// (un)select all
connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));
- // change coin control first column label due Qt4 bug.
+ // change coin control first column label due Qt4 bug.
// see https://github.com/bitcoin/bitcoin/issues/5716
ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString());
@@ -492,8 +492,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
coinControl->ListSelected(vCoinControl);
model->getOutputs(vCoinControl, vOutputs);
- BOOST_FOREACH(const COutput& out, vOutputs)
- {
+ BOOST_FOREACH(const COutput& out, vOutputs) {
// unselect already spent, very unlikely scenario, this could happen
// when selected are spent elsewhere, like rpc or another computer
uint256 txhash = out.tx->GetHash();
@@ -691,8 +690,7 @@ void CoinControlDialog::updateView()
map<QString, vector<COutput> > mapCoins;
model->listCoins(mapCoins);
- BOOST_FOREACH(PAIRTYPE(QString, vector<COutput>) coins, mapCoins)
- {
+ BOOST_FOREACH(const PAIRTYPE(QString, vector<COutput>)& coins, mapCoins) {
QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem();
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
QString sWalletAddress = coins.first;
@@ -719,8 +717,7 @@ void CoinControlDialog::updateView()
double dPrioritySum = 0;
int nChildren = 0;
int nInputSum = 0;
- BOOST_FOREACH(const COutput& out, coins.second)
- {
+ BOOST_FOREACH(const COutput& out, coins.second) {
int nInputSize = 0;
nSum += out.tx->vout[out.i].nValue;
nChildren++;
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 28cbd3abed..fd225f51a6 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -185,8 +185,7 @@ void ReceiveCoinsDialog::on_showRequestButton_clicked()
return;
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
- foreach (QModelIndex index, selection)
- {
+ foreach (const QModelIndex& index, selection) {
on_recentRequestsView_doubleClicked(index);
}
}
diff --git a/src/rest.cpp b/src/rest.cpp
index a1bd893bec..dfe01495f7 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -65,6 +65,7 @@ public:
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
+extern UniValue blockheaderToJSON(const CBlockIndex* blockindex);
static RestErr RESTERR(enum HTTPStatusCode status, string message)
{
@@ -134,14 +135,14 @@ static bool rest_headers(AcceptedConnection* conn,
if (!ParseHashStr(hashStr, hash))
throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
- std::vector<CBlockHeader> headers;
+ std::vector<const CBlockIndex *> headers;
headers.reserve(count);
{
LOCK(cs_main);
BlockMap::const_iterator it = mapBlockIndex.find(hash);
const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
while (pindex != NULL && chainActive.Contains(pindex)) {
- headers.push_back(pindex->GetBlockHeader());
+ headers.push_back(pindex);
if (headers.size() == (unsigned long)count)
break;
pindex = chainActive.Next(pindex);
@@ -149,8 +150,8 @@ static bool rest_headers(AcceptedConnection* conn,
}
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
- BOOST_FOREACH(const CBlockHeader &header, headers) {
- ssHeader << header;
+ BOOST_FOREACH(const CBlockIndex *pindex, headers) {
+ ssHeader << pindex->GetBlockHeader();
}
switch (rf) {
@@ -166,6 +167,16 @@ static bool rest_headers(AcceptedConnection* conn,
return true;
}
+ case RF_JSON: {
+ UniValue jsonHeaders(UniValue::VARR);
+ BOOST_FOREACH(const CBlockIndex *pindex, headers) {
+ jsonHeaders.push_back(blockheaderToJSON(pindex));
+ }
+ string strJSON = jsonHeaders.write() + "\n";
+ conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
+ return true;
+ }
+
default: {
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
}
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index b7c3eb1724..c2de6cb244 100644
--- a/src/rpcblockchain.cpp
+++ b/src/rpcblockchain.cpp
@@ -77,7 +77,6 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
return result;
}
-
UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false)
{
UniValue result(UniValue::VOBJ);
@@ -118,7 +117,6 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
return result;
}
-
UniValue getblockcount(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -760,6 +758,7 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp)
"{\n"
" \"size\": xxxxx (numeric) Current tx count\n"
" \"bytes\": xxxxx (numeric) Sum of all tx sizes\n"
+ " \"usage\": xxxxx (numeric) Total memory usage for the mempool\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getmempoolinfo", "")
@@ -769,6 +768,7 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp)
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("size", (int64_t) mempool.size()));
ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize()));
+ ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage()));
return ret;
}
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index daf10736bf..8f5ff10a80 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -124,7 +124,7 @@ CAmount AmountFromValue(const UniValue& value)
if (!value.isReal() && !value.isNum())
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number");
CAmount amount;
- if (!ParseMoney(value.getValStr(), amount))
+ if (!ParseFixedPoint(value.getValStr(), 8, &amount))
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
if (!MoneyRange(amount))
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
diff --git a/src/script/script.cpp b/src/script/script.cpp
index fd33924732..b1d2ceeb9f 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -260,3 +260,8 @@ std::string CScript::ToString() const
}
return str;
}
+
+size_t CScript::DynamicMemoryUsage() const
+{
+ return memusage::DynamicUsage(*(static_cast<const std::vector<unsigned char>*>(this)));
+}
diff --git a/src/script/script.h b/src/script/script.h
index e39ca57f4f..aea34d05f4 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_SCRIPT_SCRIPT_H
#define BITCOIN_SCRIPT_SCRIPT_H
+#include "memusage.h"
#include "crypto/common.h"
#include <assert.h>
@@ -607,6 +608,8 @@ public:
// The default std::vector::clear() does not release memory.
std::vector<unsigned char>().swap(*this);
}
+
+ size_t DynamicMemoryUsage() const;
};
class CReserveScript
diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json
index 9def4042da..20bdbd08a5 100644
--- a/src/test/data/tx_invalid.json
+++ b/src/test/data/tx_invalid.json
@@ -137,6 +137,8 @@
["Argument missing"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "NOP2 1"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
["Argument negative with by-blockheight nLockTime=0"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]],
@@ -145,10 +147,14 @@
["Argument negative with by-blocktime nLockTime=500,000,000"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP2 1"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
+"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"],
["Input locked"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]],
"010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"],
["Another input being unlocked isn't sufficient; the CHECKLOCKTIMEVERIFY-using input must be unlocked"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"] ,
@@ -158,6 +164,8 @@
["Argument/tx height/time mismatch, both versions"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP2 1"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
+[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]],
+"01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 NOP2 1"]],
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 NOP2 1"]],
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 9e99ff6286..25599beafc 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -142,6 +142,24 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values)
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1.00000000")), 100000000LL);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("20999999.9999999")), 2099999999999990LL);
BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("20999999.99999999")), 2099999999999999LL);
+
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1e-8")), COIN/100000000);
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.1e-7")), COIN/100000000);
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.01e-6")), COIN/100000000);
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.0000000000000000000000000000000000000000000000000000000000000000000000000001e+68")), COIN/100000000);
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("10000000000000000000000000000000000000000000000000000000000000000e-64")), COIN);
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000e64")), COIN);
+
+ BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e-9")), UniValue); //should fail
+ BOOST_CHECK_THROW(AmountFromValue(ValueFromString("0.000000019")), UniValue); //should fail
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000001000000")), 1LL); //should pass, cut trailing 0
+ BOOST_CHECK_THROW(AmountFromValue(ValueFromString("19e-9")), UniValue); //should fail
+ BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.19e-6")), 19); //should pass, leading 0 is present
+
+ BOOST_CHECK_THROW(AmountFromValue(ValueFromString("92233720368.54775808")), UniValue); //overflow error
+ BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e+11")), UniValue); //overflow error
+ BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e11")), UniValue); //overflow error signless
+ BOOST_CHECK_THROW(AmountFromValue(ValueFromString("93e+9")), UniValue); //overflow error
}
BOOST_AUTO_TEST_CASE(json_parse_errors)
@@ -151,6 +169,9 @@ BOOST_AUTO_TEST_CASE(json_parse_errors)
// Valid, with leading or trailing whitespace
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").get_real(), 1.0);
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").get_real(), 1.0);
+
+ BOOST_CHECK_THROW(AmountFromValue(ParseNonRFCJSONValue(".19e-6")), std::runtime_error); //should fail, missing leading 0, therefore invalid JSON
+ BOOST_CHECK_EQUAL(AmountFromValue(ParseNonRFCJSONValue("0.00000000000000000000000000000000000001e+30 ")), 1);
// Invalid, initial garbage
BOOST_CHECK_THROW(ParseNonRFCJSONValue("[1.0"), std::runtime_error);
BOOST_CHECK_THROW(ParseNonRFCJSONValue("a1.0"), std::runtime_error);
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 5cb5894251..e956cc5b90 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -418,4 +418,70 @@ BOOST_AUTO_TEST_CASE(test_FormatSubVersion)
BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments),std::string("/Test:0.9.99(comment1)/"));
BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments2),std::string("/Test:0.9.99(comment1; comment2)/"));
}
+
+BOOST_AUTO_TEST_CASE(test_ParseFixedPoint)
+{
+ int64_t amount = 0;
+ BOOST_CHECK(ParseFixedPoint("0", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 0LL);
+ BOOST_CHECK(ParseFixedPoint("1", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 100000000LL);
+ BOOST_CHECK(ParseFixedPoint("0.0", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 0LL);
+ BOOST_CHECK(ParseFixedPoint("-0.1", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, -10000000LL);
+ BOOST_CHECK(ParseFixedPoint("1.1", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 110000000LL);
+ BOOST_CHECK(ParseFixedPoint("1.10000000000000000", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 110000000LL);
+ BOOST_CHECK(ParseFixedPoint("1.1e1", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 1100000000LL);
+ BOOST_CHECK(ParseFixedPoint("1.1e-1", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 11000000LL);
+ BOOST_CHECK(ParseFixedPoint("1000", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 100000000000LL);
+ BOOST_CHECK(ParseFixedPoint("-1000", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, -100000000000LL);
+ BOOST_CHECK(ParseFixedPoint("0.00000001", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 1LL);
+ BOOST_CHECK(ParseFixedPoint("0.0000000100000000", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 1LL);
+ BOOST_CHECK(ParseFixedPoint("-0.00000001", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, -1LL);
+ BOOST_CHECK(ParseFixedPoint("1000000000.00000001", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 100000000000000001LL);
+ BOOST_CHECK(ParseFixedPoint("9999999999.99999999", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, 999999999999999999LL);
+ BOOST_CHECK(ParseFixedPoint("-9999999999.99999999", 8, &amount));
+ BOOST_CHECK_EQUAL(amount, -999999999999999999LL);
+
+ BOOST_CHECK(!ParseFixedPoint("", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("a-1000", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-a1000", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-1000a", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-01000", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("00.1", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint(".1", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("--0.1", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("0.000000001", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-0.000000001", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("0.00000001000000001", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-10000000000.00000000", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("10000000000.00000000", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-10000000000.00000001", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("10000000000.00000001", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-10000000000.00000009", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("10000000000.00000009", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-99999999999.99999999", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("99999909999.09999999", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("92233720368.54775807", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("92233720368.54775808", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-92233720368.54775808", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("-92233720368.54775809", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("1.1e", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("1.1e-", 8, &amount));
+ BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 2292191be4..4caa5fc821 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -18,7 +18,7 @@
using namespace std;
CTxMemPoolEntry::CTxMemPoolEntry():
- nFee(0), nTxSize(0), nModSize(0), nTime(0), dPriority(0.0), hadNoDependencies(false)
+ nFee(0), nTxSize(0), nModSize(0), nUsageSize(0), nTime(0), dPriority(0.0), hadNoDependencies(false)
{
nHeight = MEMPOOL_HEIGHT;
}
@@ -31,6 +31,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
{
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
nModSize = tx.CalculateModifiedSize(nTxSize);
+ nUsageSize = tx.DynamicMemoryUsage();
}
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
@@ -101,6 +102,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
nTransactionsUpdated++;
totalTxSize += entry.GetTxSize();
+ cachedInnerUsage += entry.DynamicMemoryUsage();
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
return true;
@@ -146,6 +148,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
removed.push_back(tx);
totalTxSize -= mapTx[hash].GetTxSize();
+ cachedInnerUsage -= mapTx[hash].DynamicMemoryUsage();
mapTx.erase(hash);
nTransactionsUpdated++;
minerPolicyEstimator->removeTx(hash);
@@ -226,6 +229,7 @@ void CTxMemPool::clear()
mapTx.clear();
mapNextTx.clear();
totalTxSize = 0;
+ cachedInnerUsage = 0;
++nTransactionsUpdated;
}
@@ -237,6 +241,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
uint64_t checkTotal = 0;
+ uint64_t innerUsage = 0;
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
@@ -245,6 +250,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
for (std::map<uint256, CTxMemPoolEntry>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
unsigned int i = 0;
checkTotal += it->second.GetTxSize();
+ innerUsage += it->second.DynamicMemoryUsage();
const CTransaction& tx = it->second.GetTx();
bool fDependsWait = false;
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
@@ -299,6 +305,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
}
assert(totalTxSize == checkTotal);
+ assert(innerUsage == cachedInnerUsage);
}
void CTxMemPool::queryHashes(vector<uint256>& vtxid)
@@ -419,3 +426,8 @@ bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
return mempool.exists(txid) || base->HaveCoins(txid);
}
+
+size_t CTxMemPool::DynamicMemoryUsage() const {
+ LOCK(cs);
+ return memusage::DynamicUsage(mapTx) + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + cachedInnerUsage;
+}
diff --git a/src/txmempool.h b/src/txmempool.h
index 7271a5f603..ea36ce1ad5 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -40,6 +40,7 @@ private:
CAmount nFee; //! Cached to avoid expensive parent-transaction lookups
size_t nTxSize; //! ... and avoid recomputing tx size
size_t nModSize; //! ... and modified size for priority
+ size_t nUsageSize; //! ... and total memory usage
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
@@ -58,6 +59,7 @@ public:
int64_t GetTime() const { return nTime; }
unsigned int GetHeight() const { return nHeight; }
bool WasClearAtEntry() const { return hadNoDependencies; }
+ size_t DynamicMemoryUsage() const { return nUsageSize; }
};
class CBlockPolicyEstimator;
@@ -73,6 +75,7 @@ public:
CInPoint(const CTransaction* ptxIn, uint32_t nIn) { ptx = ptxIn; n = nIn; }
void SetNull() { ptx = NULL; n = (uint32_t) -1; }
bool IsNull() const { return (ptx == NULL && n == (uint32_t) -1); }
+ size_t DynamicMemoryUsage() const { return 0; }
};
/**
@@ -93,6 +96,7 @@ private:
CBlockPolicyEstimator* minerPolicyEstimator;
uint64_t totalTxSize; //! sum of all mempool tx' byte sizes
+ uint64_t cachedInnerUsage; //! sum of dynamic memory usage of all the map elements (NOT the maps themselves)
public:
mutable CCriticalSection cs;
@@ -139,6 +143,7 @@ public:
LOCK(cs);
return mapTx.size();
}
+
uint64_t GetTotalTxSize()
{
LOCK(cs);
@@ -162,6 +167,8 @@ public:
/** Write/Read estimates to disk */
bool WriteFeeEstimates(CAutoFile& fileout) const;
bool ReadFeeEstimates(CAutoFile& filein);
+
+ size_t DynamicMemoryUsage() const;
};
/**
diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp
index 7d1de7d6a8..7208ca9474 100644
--- a/src/utilstrencodings.cpp
+++ b/src/utilstrencodings.cpp
@@ -538,3 +538,123 @@ int atoi(const std::string& str)
{
return atoi(str.c_str());
}
+
+/** Upper bound for mantissa.
+ * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
+ * Larger integers cannot consist of arbitrary combinations of 0-9:
+ *
+ * 999999999999999999 1^18-1
+ * 9223372036854775807 (1<<63)-1 (max int64_t)
+ * 9999999999999999999 1^19-1 (would overflow)
+ */
+static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
+
+/** Helper function for ParseFixedPoint */
+static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
+{
+ if(ch == '0')
+ ++mantissa_tzeros;
+ else {
+ for (int i=0; i<=mantissa_tzeros; ++i) {
+ if (mantissa > (UPPER_BOUND / 10LL))
+ return false; /* overflow */
+ mantissa *= 10;
+ }
+ mantissa += ch - '0';
+ mantissa_tzeros = 0;
+ }
+ return true;
+}
+
+bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
+{
+ int64_t mantissa = 0;
+ int64_t exponent = 0;
+ int mantissa_tzeros = 0;
+ bool mantissa_sign = false;
+ bool exponent_sign = false;
+ int ptr = 0;
+ int end = val.size();
+ int point_ofs = 0;
+
+ if (ptr < end && val[ptr] == '-') {
+ mantissa_sign = true;
+ ++ptr;
+ }
+ if (ptr < end)
+ {
+ if (val[ptr] == '0') {
+ /* pass single 0 */
+ ++ptr;
+ } else if (val[ptr] >= '1' && val[ptr] <= '9') {
+ while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
+ return false; /* overflow */
+ ++ptr;
+ }
+ } else return false; /* missing expected digit */
+ } else return false; /* empty string or loose '-' */
+ if (ptr < end && val[ptr] == '.')
+ {
+ ++ptr;
+ if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9')
+ {
+ while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
+ return false; /* overflow */
+ ++ptr;
+ ++point_ofs;
+ }
+ } else return false; /* missing expected digit */
+ }
+ if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
+ {
+ ++ptr;
+ if (ptr < end && val[ptr] == '+')
+ ++ptr;
+ else if (ptr < end && val[ptr] == '-') {
+ exponent_sign = true;
+ ++ptr;
+ }
+ if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') {
+ if (exponent > (UPPER_BOUND / 10LL))
+ return false; /* overflow */
+ exponent = exponent * 10 + val[ptr] - '0';
+ ++ptr;
+ }
+ } else return false; /* missing expected digit */
+ }
+ if (ptr != end)
+ return false; /* trailing garbage */
+
+ /* finalize exponent */
+ if (exponent_sign)
+ exponent = -exponent;
+ exponent = exponent - point_ofs + mantissa_tzeros;
+
+ /* finalize mantissa */
+ if (mantissa_sign)
+ mantissa = -mantissa;
+
+ /* convert to one 64-bit fixed-point value */
+ exponent += decimals;
+ if (exponent < 0)
+ return false; /* cannot represent values smaller than 10^-decimals */
+ if (exponent >= 18)
+ return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
+
+ for (int i=0; i < exponent; ++i) {
+ if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
+ return false; /* overflow */
+ mantissa *= 10;
+ }
+ if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
+ return false; /* overflow */
+
+ if (amount_out)
+ *amount_out = mantissa;
+
+ return true;
+}
+
diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h
index 58329b51bb..dcd56751f2 100644
--- a/src/utilstrencodings.h
+++ b/src/utilstrencodings.h
@@ -109,4 +109,11 @@ bool TimingResistantEqual(const T& a, const T& b)
return accumulator == 0;
}
+/** Parse number as fixed point according to JSON number syntax.
+ * See http://json.org/number.gif
+ * @returns true on success, false on error.
+ * @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger.
+ */
+bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
+
#endif // BITCOIN_UTILSTRENCODINGS_H