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/rpc-tests/rest.py39
-rw-r--r--src/Makefile.am2
-rw-r--r--src/bitcoin-cli.cpp9
-rw-r--r--src/bitcoin-tx.cpp1
-rw-r--r--src/init.cpp55
-rw-r--r--src/main.cpp140
-rw-r--r--src/main.h36
-rw-r--r--src/miner.cpp1
-rw-r--r--src/net.cpp14
-rw-r--r--src/net.h13
-rw-r--r--src/policy/policy.cpp178
-rw-r--r--src/policy/policy.h58
-rw-r--r--src/primitives/transaction.cpp9
-rw-r--r--src/primitives/transaction.h3
-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.cpp2
-rw-r--r--src/rpcrawtransaction.cpp1
-rw-r--r--src/rpcserver.cpp2
-rw-r--r--src/script/sign.cpp3
-rw-r--r--src/script/standard.cpp20
-rw-r--r--src/script/standard.h18
-rw-r--r--src/test/data/bitcoin-util-test.json2
-rw-r--r--src/test/data/tx_invalid.json8
-rw-r--r--src/test/data/txcreatesign.hex2
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/rpc_tests.cpp21
-rw-r--r--src/test/script_P2SH_tests.cpp1
-rw-r--r--src/test/transaction_tests.cpp1
-rw-r--r--src/test/util_tests.cpp66
-rw-r--r--src/utilstrencodings.cpp120
-rw-r--r--src/utilstrencodings.h7
-rw-r--r--src/wallet/wallet.cpp10
-rw-r--r--src/wallet/wallet.h3
41 files changed, 644 insertions, 269 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/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/src/Makefile.am b/src/Makefile.am
index 59618c4940..b82c6dc37a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -111,6 +111,7 @@ BITCOIN_CORE_H = \
netbase.h \
noui.h \
policy/fees.h \
+ policy/policy.h \
pow.h \
primitives/block.h \
primitives/transaction.h \
@@ -176,6 +177,7 @@ libbitcoin_server_a_SOURCES = \
net.cpp \
noui.cpp \
policy/fees.cpp \
+ policy/policy.cpp \
pow.cpp \
rest.cpp \
rpcblockchain.cpp \
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index d451720141..903777ba51 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -190,6 +190,15 @@ int CommandLineRPC(int argc, char *argv[])
throw CConnectionFailed("server in warmup");
strPrint = "error: " + error.write();
nRet = abs(code);
+ if (error.isObject())
+ {
+ UniValue errCode = find_value(error, "code");
+ UniValue errMsg = find_value(error, "message");
+ strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
+
+ if (errMsg.isStr())
+ strPrint += "error message:\n"+errMsg.get_str();
+ }
} else {
// Result
if (result.isNull())
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 45990f6bd8..9ad57d5c6f 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -8,6 +8,7 @@
#include "consensus/consensus.h"
#include "core_io.h"
#include "keystore.h"
+#include "policy/policy.h"
#include "primitives/transaction.h"
#include "script/script.h"
#include "script/sign.h"
diff --git a/src/init.cpp b/src/init.cpp
index d30a9a3f88..4addc663c8 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -18,6 +18,7 @@
#include "main.h"
#include "miner.h"
#include "net.h"
+#include "policy/policy.h"
#include "rpcserver.h"
#include "script/standard.h"
#include "scheduler.h"
@@ -329,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:"));
@@ -724,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))
@@ -744,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
@@ -920,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/main.cpp b/src/main.cpp
index 624003e99f..03c09f0a27 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -11,10 +11,12 @@
#include "chainparams.h"
#include "checkpoints.h"
#include "checkqueue.h"
+#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "init.h"
#include "merkleblock.h"
#include "net.h"
+#include "policy/policy.h"
#include "pow.h"
#include "txdb.h"
#include "txmempool.h"
@@ -605,76 +607,6 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
return nEvicted;
}
-
-
-
-
-
-
-bool IsStandardTx(const CTransaction& tx, string& reason)
-{
- if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) {
- reason = "version";
- return false;
- }
-
- // Extremely large transactions with lots of inputs can cost the network
- // almost as much to process as they cost the sender in fees, because
- // computing signature hashes is O(ninputs*txsize). Limiting transactions
- // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks.
- unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
- if (sz >= MAX_STANDARD_TX_SIZE) {
- reason = "tx-size";
- return false;
- }
-
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- {
- // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
- // keys. (remember the 520 byte limit on redeemScript size) That works
- // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
- // bytes of scriptSig, which we round off to 1650 bytes for some minor
- // future-proofing. That's also enough to spend a 20-of-20
- // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
- // considered standard)
- if (txin.scriptSig.size() > 1650) {
- reason = "scriptsig-size";
- return false;
- }
- if (!txin.scriptSig.IsPushOnly()) {
- reason = "scriptsig-not-pushonly";
- return false;
- }
- }
-
- unsigned int nDataOut = 0;
- txnouttype whichType;
- BOOST_FOREACH(const CTxOut& txout, tx.vout) {
- if (!::IsStandard(txout.scriptPubKey, whichType)) {
- reason = "scriptpubkey";
- return false;
- }
-
- if (whichType == TX_NULL_DATA)
- nDataOut++;
- else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
- reason = "bare-multisig";
- return false;
- } else if (txout.IsDust(::minRelayTxFee)) {
- reason = "dust";
- return false;
- }
- }
-
- // only one OP_RETURN txout is permitted
- if (nDataOut > 1) {
- reason = "multi-op-return";
- return false;
- }
-
- return true;
-}
-
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
if (tx.nLockTime == 0)
@@ -693,74 +625,6 @@ bool CheckFinalTx(const CTransaction &tx)
return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime());
}
-/**
- * Check transaction inputs to mitigate two
- * potential denial-of-service attacks:
- *
- * 1. scriptSigs with extra data stuffed into them,
- * not consumed by scriptPubKey (or P2SH script)
- * 2. P2SH scripts with a crazy number of expensive
- * CHECKSIG/CHECKMULTISIG operations
- */
-bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
-{
- if (tx.IsCoinBase())
- return true; // Coinbases don't use vin normally
-
- for (unsigned int i = 0; i < tx.vin.size(); i++)
- {
- const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
-
- vector<vector<unsigned char> > vSolutions;
- txnouttype whichType;
- // get the scriptPubKey corresponding to this input:
- const CScript& prevScript = prev.scriptPubKey;
- if (!Solver(prevScript, whichType, vSolutions))
- return false;
- int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
- if (nArgsExpected < 0)
- return false;
-
- // Transactions with extra stuff in their scriptSigs are
- // non-standard. Note that this EvalScript() call will
- // be quick, because if there are any operations
- // beside "push data" in the scriptSig
- // IsStandardTx() will have already returned false
- // and this method isn't called.
- vector<vector<unsigned char> > stack;
- if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker()))
- return false;
-
- if (whichType == TX_SCRIPTHASH)
- {
- if (stack.empty())
- return false;
- CScript subscript(stack.back().begin(), stack.back().end());
- vector<vector<unsigned char> > vSolutions2;
- txnouttype whichType2;
- if (Solver(subscript, whichType2, vSolutions2))
- {
- int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2);
- if (tmpExpected < 0)
- return false;
- nArgsExpected += tmpExpected;
- }
- else
- {
- // Any other Script with less than 15 sigops OK:
- unsigned int sigops = subscript.GetSigOpCount(true);
- // ... extra data left on the stack after execution is OK, too:
- return (sigops <= MAX_P2SH_SIGOPS);
- }
- }
-
- if (stack.size() != (unsigned int)nArgsExpected)
- return false;
- }
-
- return true;
-}
-
unsigned int GetLegacySigOpCount(const CTransaction& tx)
{
unsigned int nSigOps = 0;
diff --git a/src/main.h b/src/main.h
index a4c712bea3..ce18bd709f 100644
--- a/src/main.h
+++ b/src/main.h
@@ -14,7 +14,6 @@
#include "chain.h"
#include "chainparams.h"
#include "coins.h"
-#include "consensus/consensus.h"
#include "net.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
@@ -47,19 +46,8 @@ class CValidationState;
struct CNodeStateStats;
-/** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/
-static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
-static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0;
-/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/
-static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000;
/** Default for accepting alerts from the P2P network. */
static const bool DEFAULT_ALERTS = true;
-/** The maximum size for transactions we're willing to relay/mine */
-static const unsigned int MAX_STANDARD_TX_SIZE = 100000;
-/** Maximum number of signature check operations in an IsStandard() P2SH script */
-static const unsigned int MAX_P2SH_SIGOPS = 15;
-/** The maximum number of sigops we're willing to relay/mine in a single tx */
-static const unsigned int MAX_STANDARD_TX_SIGOPS = MAX_BLOCK_SIGOPS/5;
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
/** The maximum size of a blk?????.dat file (since 0.8) */
@@ -274,25 +262,6 @@ struct CDiskTxPos : public CDiskBlockPos
CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
-/**
- * Check transaction inputs, and make sure any
- * pay-to-script-hash transactions are evaluating IsStandard scripts
- *
- * Why bother? To avoid denial-of-service attacks; an attacker
- * can submit a standard HASH... OP_EQUAL transaction,
- * which will get accepted into blocks. The redemption
- * script can be anything; an attacker could use a very
- * expensive-to-check-upon-redemption script like:
- * DUP CHECKSIG DROP ... repeated 100 times... OP_1
- */
-
-/**
- * Check for standard transaction types
- * @param[in] mapInputs Map of previous transactions that have outputs we're spending
- * @return True if all inputs (scriptSigs) use only standard transaction forms
- */
-bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
-
/**
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
* @return number of sigops this transaction's outputs will produce when spent
@@ -324,11 +293,6 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
/** Context-independent validity checks */
bool CheckTransaction(const CTransaction& tx, CValidationState& state);
-/** Check for standard transaction types
- * @return True if all outputs (scriptPubKeys) use only standard transaction forms
- */
-bool IsStandardTx(const CTransaction& tx, std::string& reason);
-
/**
* Check if transaction is final and can be included in a block with the
* specified height and time. Consensus critical.
diff --git a/src/miner.cpp b/src/miner.cpp
index d3f5094fe9..5e575f45f1 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -12,6 +12,7 @@
#include "hash.h"
#include "main.h"
#include "net.h"
+#include "policy/policy.h"
#include "pow.h"
#include "primitives/transaction.h"
#include "timedata.h"
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/policy/policy.cpp b/src/policy/policy.cpp
new file mode 100644
index 0000000000..169fef4af4
--- /dev/null
+++ b/src/policy/policy.cpp
@@ -0,0 +1,178 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+// NOTE: This file is intended to be customised by the end user, and includes only local node policy logic
+
+#include "policy/policy.h"
+
+#include "main.h"
+#include "tinyformat.h"
+#include "util.h"
+#include "utilstrencodings.h"
+
+#include <boost/foreach.hpp>
+
+ /**
+ * Check transaction inputs to mitigate two
+ * potential denial-of-service attacks:
+ *
+ * 1. scriptSigs with extra data stuffed into them,
+ * not consumed by scriptPubKey (or P2SH script)
+ * 2. P2SH scripts with a crazy number of expensive
+ * CHECKSIG/CHECKMULTISIG operations
+ *
+ * Check transaction inputs, and make sure any
+ * pay-to-script-hash transactions are evaluating IsStandard scripts
+ *
+ * Why bother? To avoid denial-of-service attacks; an attacker
+ * can submit a standard HASH... OP_EQUAL transaction,
+ * which will get accepted into blocks. The redemption
+ * script can be anything; an attacker could use a very
+ * expensive-to-check-upon-redemption script like:
+ * DUP CHECKSIG DROP ... repeated 100 times... OP_1
+ */
+
+bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
+{
+ std::vector<std::vector<unsigned char> > vSolutions;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
+ return false;
+
+ if (whichType == TX_MULTISIG)
+ {
+ unsigned char m = vSolutions.front()[0];
+ unsigned char n = vSolutions.back()[0];
+ // Support up to x-of-3 multisig txns as standard
+ if (n < 1 || n > 3)
+ return false;
+ if (m < 1 || m > n)
+ return false;
+ }
+
+ return whichType != TX_NONSTANDARD;
+}
+
+bool IsStandardTx(const CTransaction& tx, std::string& reason)
+{
+ if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) {
+ reason = "version";
+ return false;
+ }
+
+ // Extremely large transactions with lots of inputs can cost the network
+ // almost as much to process as they cost the sender in fees, because
+ // computing signature hashes is O(ninputs*txsize). Limiting transactions
+ // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks.
+ unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
+ if (sz >= MAX_STANDARD_TX_SIZE) {
+ reason = "tx-size";
+ return false;
+ }
+
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
+ // keys. (remember the 520 byte limit on redeemScript size) That works
+ // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
+ // bytes of scriptSig, which we round off to 1650 bytes for some minor
+ // future-proofing. That's also enough to spend a 20-of-20
+ // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not
+ // considered standard)
+ if (txin.scriptSig.size() > 1650) {
+ reason = "scriptsig-size";
+ return false;
+ }
+ if (!txin.scriptSig.IsPushOnly()) {
+ reason = "scriptsig-not-pushonly";
+ return false;
+ }
+ }
+
+ unsigned int nDataOut = 0;
+ txnouttype whichType;
+ BOOST_FOREACH(const CTxOut& txout, tx.vout) {
+ if (!::IsStandard(txout.scriptPubKey, whichType)) {
+ reason = "scriptpubkey";
+ return false;
+ }
+
+ if (whichType == TX_NULL_DATA)
+ nDataOut++;
+ else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
+ reason = "bare-multisig";
+ return false;
+ } else if (txout.IsDust(::minRelayTxFee)) {
+ reason = "dust";
+ return false;
+ }
+ }
+
+ // only one OP_RETURN txout is permitted
+ if (nDataOut > 1) {
+ reason = "multi-op-return";
+ return false;
+ }
+
+ return true;
+}
+
+bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
+{
+ if (tx.IsCoinBase())
+ return true; // Coinbases don't use vin normally
+
+ for (unsigned int i = 0; i < tx.vin.size(); i++)
+ {
+ const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
+
+ std::vector<std::vector<unsigned char> > vSolutions;
+ txnouttype whichType;
+ // get the scriptPubKey corresponding to this input:
+ const CScript& prevScript = prev.scriptPubKey;
+ if (!Solver(prevScript, whichType, vSolutions))
+ return false;
+ int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
+ if (nArgsExpected < 0)
+ return false;
+
+ // Transactions with extra stuff in their scriptSigs are
+ // non-standard. Note that this EvalScript() call will
+ // be quick, because if there are any operations
+ // beside "push data" in the scriptSig
+ // IsStandardTx() will have already returned false
+ // and this method isn't called.
+ std::vector<std::vector<unsigned char> > stack;
+ if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker()))
+ return false;
+
+ if (whichType == TX_SCRIPTHASH)
+ {
+ if (stack.empty())
+ return false;
+ CScript subscript(stack.back().begin(), stack.back().end());
+ std::vector<std::vector<unsigned char> > vSolutions2;
+ txnouttype whichType2;
+ if (Solver(subscript, whichType2, vSolutions2))
+ {
+ int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2);
+ if (tmpExpected < 0)
+ return false;
+ nArgsExpected += tmpExpected;
+ }
+ else
+ {
+ // Any other Script with less than 15 sigops OK:
+ unsigned int sigops = subscript.GetSigOpCount(true);
+ // ... extra data left on the stack after execution is OK, too:
+ return (sigops <= MAX_P2SH_SIGOPS);
+ }
+ }
+
+ if (stack.size() != (unsigned int)nArgsExpected)
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/policy/policy.h b/src/policy/policy.h
new file mode 100644
index 0000000000..1551aecde8
--- /dev/null
+++ b/src/policy/policy.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_POLICY_H
+#define BITCOIN_POLICY_H
+
+#include "consensus/consensus.h"
+#include "script/interpreter.h"
+#include "script/standard.h"
+
+#include <string>
+
+class CCoinsViewCache;
+
+/** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/
+static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
+static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0;
+/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/
+static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000;
+/** The maximum size for transactions we're willing to relay/mine */
+static const unsigned int MAX_STANDARD_TX_SIZE = 100000;
+/** Maximum number of signature check operations in an IsStandard() P2SH script */
+static const unsigned int MAX_P2SH_SIGOPS = 15;
+/** The maximum number of sigops we're willing to relay/mine in a single tx */
+static const unsigned int MAX_STANDARD_TX_SIGOPS = MAX_BLOCK_SIGOPS/5;
+/**
+ * Standard script verification flags that standard transactions will comply
+ * with. However scripts violating these flags may still be present in valid
+ * blocks and we must accept those blocks.
+ */
+static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
+ SCRIPT_VERIFY_DERSIG |
+ SCRIPT_VERIFY_STRICTENC |
+ SCRIPT_VERIFY_MINIMALDATA |
+ SCRIPT_VERIFY_NULLDUMMY |
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
+ SCRIPT_VERIFY_CLEANSTACK |
+ SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+
+/** For convenience, standard but not mandatory verify flags. */
+static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
+
+bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
+ /**
+ * Check for standard transaction types
+ * @return True if all outputs (scriptPubKeys) use only standard transaction forms
+ */
+bool IsStandardTx(const CTransaction& tx, std::string& reason);
+ /**
+ * Check for standard transaction types
+ * @param[in] mapInputs Map of previous transactions that have outputs we're spending
+ * @return True if all inputs (scriptSigs) use only standard transaction forms
+ */
+bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
+
+#endif // BITCOIN_POLICY_H
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index d864a9b6d3..606dbea798 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -87,15 +87,6 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
return *this;
}
-bool CTransaction::IsEquivalentTo(const CTransaction& tx) const
-{
- CMutableTransaction tx1 = *this;
- CMutableTransaction tx2 = tx;
- for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript();
- for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript();
- return CTransaction(tx1) == CTransaction(tx2);
-}
-
CAmount CTransaction::GetValueOut() const
{
CAmount nValueOut = 0;
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 0c9ebb7b88..6cfd93a9a1 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -222,9 +222,6 @@ public:
return hash;
}
- // True if only scriptSigs are different
- bool IsEquivalentTo(const CTransaction& tx) const;
-
// Return sum of txouts.
CAmount GetValueOut() const;
// GetValueIn() is a method on CCoinsViewCache, because
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..9cdd0770e3 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)
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 20394fc2c1..7d1db0b60e 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -11,6 +11,7 @@
#include "main.h"
#include "merkleblock.h"
#include "net.h"
+#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpcserver.h"
#include "script/script.h"
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 287cfb2f13..aeb51fed56 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/sign.cpp b/src/script/sign.cpp
index 4543ca303f..8b43183b6d 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -5,9 +5,10 @@
#include "script/sign.h"
-#include "primitives/transaction.h"
#include "key.h"
#include "keystore.h"
+#include "policy/policy.h"
+#include "primitives/transaction.h"
#include "script/standard.h"
#include "uint256.h"
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index ce50e3aad8..66657127ab 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -180,26 +180,6 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c
return -1;
}
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
-{
- vector<valtype> vSolutions;
- if (!Solver(scriptPubKey, whichType, vSolutions))
- return false;
-
- if (whichType == TX_MULTISIG)
- {
- unsigned char m = vSolutions.front()[0];
- unsigned char n = vSolutions.back()[0];
- // Support up to x-of-3 multisig txns as standard
- if (n < 1 || n > 3)
- return false;
- if (m < 1 || m > n)
- return false;
- }
-
- return whichType != TX_NONSTANDARD;
-}
-
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
vector<valtype> vSolutions;
diff --git a/src/script/standard.h b/src/script/standard.h
index 3b401dfe4f..46ae5f9f10 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -39,23 +39,6 @@ extern unsigned nMaxDatacarrierBytes;
*/
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;
-/**
- * Standard script verification flags that standard transactions will comply
- * with. However scripts violating these flags may still be present in valid
- * blocks and we must accept those blocks.
- */
-static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS |
- SCRIPT_VERIFY_DERSIG |
- SCRIPT_VERIFY_STRICTENC |
- SCRIPT_VERIFY_MINIMALDATA |
- SCRIPT_VERIFY_NULLDUMMY |
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
- SCRIPT_VERIFY_CLEANSTACK |
- SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
-
-/** For convenience, standard but not mandatory verify flags. */
-static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
-
enum txnouttype
{
TX_NONSTANDARD,
@@ -86,7 +69,6 @@ const char* GetTxnOutputType(txnouttype t);
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json
index 6090421cb6..afd35af503 100644
--- a/src/test/data/bitcoin-util-test.json
+++ b/src/test/data/bitcoin-util-test.json
@@ -52,7 +52,7 @@
["-create",
"in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
"set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
- "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\"}]",
+ "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
"sign=ALL",
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
"output_cmp": "txcreatesign.hex"
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/data/txcreatesign.hex b/src/test/data/txcreatesign.hex
index 56ce28a865..a46fcc88cb 100644
--- a/src/test/data/txcreatesign.hex
+++ b/src/test/data/txcreatesign.hex
@@ -1 +1 @@
-01000000018594c5bdcaec8f06b78b596f31cd292a294fd031e24eec716f43dac91ea7494d0000000000ffffffff01a0860100000000001976a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac00000000
+01000000018594c5bdcaec8f06b78b596f31cd292a294fd031e24eec716f43dac91ea7494d000000008b48304502210096a75056c9e2cc62b7214777b3d2a592cfda7092520126d4ebfcd6d590c99bd8022051bb746359cf98c0603f3004477eac68701132380db8facba19c89dc5ab5c5e201410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ffffffff01a0860100000000001976a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac00000000
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 6b189a6b55..b65c299adc 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -4,7 +4,7 @@
#include "key.h"
#include "keystore.h"
-#include "main.h"
+#include "policy/policy.h"
#include "script/script.h"
#include "script/script_error.h"
#include "script/interpreter.h"
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/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index c8cfe28729..16c9a4a868 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -5,6 +5,7 @@
#include "key.h"
#include "keystore.h"
#include "main.h"
+#include "policy/policy.h"
#include "script/script.h"
#include "script/script_error.h"
#include "script/sign.h"
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 9ce7aae966..260524f7cc 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -12,6 +12,7 @@
#include "key.h"
#include "keystore.h"
#include "main.h"
+#include "policy/policy.h"
#include "script/script.h"
#include "script/script_error.h"
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/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
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 44e28de0cd..7b3cd9803b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -12,6 +12,7 @@
#include "consensus/validation.h"
#include "main.h"
#include "net.h"
+#include "policy/policy.h"
#include "script/script.h"
#include "script/sign.h"
#include "timedata.h"
@@ -1331,6 +1332,15 @@ bool CWalletTx::IsTrusted() const
return true;
}
+bool CWalletTx::IsEquivalentTo(const CWalletTx& tx) const
+{
+ CMutableTransaction tx1 = *this;
+ CMutableTransaction tx2 = tx;
+ for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript();
+ for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript();
+ return CTransaction(tx1) == CTransaction(tx2);
+}
+
std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime)
{
std::vector<uint256> result;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index f36c98e9fc..003266ba19 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -378,6 +378,9 @@ public:
return (GetDebit(filter) > 0);
}
+ // True if only scriptSigs are different
+ bool IsEquivalentTo(const CWalletTx& tx) const;
+
bool IsTrusted() const;
bool WriteToDisk(CWalletDB *pwalletdb);