diff options
-rw-r--r-- | configure.ac | 25 | ||||
-rw-r--r-- | depends/packages/bdb.mk | 3 | ||||
-rw-r--r-- | doc/bips.md | 1 | ||||
-rwxr-xr-x | qa/rpc-tests/sendheaders.py | 18 | ||||
-rwxr-xr-x | qa/rpc-tests/test_framework/mininode.py | 15 | ||||
-rwxr-xr-x | qa/rpc-tests/wallet.py | 62 | ||||
-rw-r--r-- | src/httprpc.cpp | 4 | ||||
-rw-r--r-- | src/init.cpp | 26 | ||||
-rw-r--r-- | src/main.cpp | 18 | ||||
-rw-r--r-- | src/main.h | 2 | ||||
-rw-r--r-- | src/policy/policy.cpp | 3 | ||||
-rw-r--r-- | src/qt/intro.cpp | 20 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 6 | ||||
-rw-r--r-- | src/reverselock.h | 5 | ||||
-rw-r--r-- | src/rpcserver.cpp | 22 | ||||
-rw-r--r-- | src/rpcserver.h | 10 | ||||
-rw-r--r-- | src/test/reverselock_tests.cpp | 16 | ||||
-rw-r--r-- | src/wallet/test/wallet_tests.cpp | 25 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 7 | ||||
-rw-r--r-- | src/wallet/wallet.h | 164 | ||||
-rw-r--r-- | src/wallet/walletdb.cpp | 13 |
21 files changed, 287 insertions, 178 deletions
diff --git a/configure.ac b/configure.ac index 9161e2b2c0..07f9a4a6f3 100644 --- a/configure.ac +++ b/configure.ac @@ -619,6 +619,31 @@ if test x$use_boost = xyes; then BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" +TEMP_LIBS="$LIBS" +LIBS="$BOOST_LIBS $LIBS" +TEMP_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +AC_MSG_CHECKING([for mismatched boost c++11 scoped enums]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include "boost/config.hpp" + #include "boost/version.hpp" + #if !defined(BOOST_NO_SCOPED_ENUMS) && !defined(BOOST_NO_CXX11_SCOPED_ENUMS) && BOOST_VERSION < 105700 + #define BOOST_NO_SCOPED_ENUMS + #define BOOST_NO_CXX11_SCOPED_ENUMS + #define CHECK + #endif + #include "boost/filesystem.hpp" + ]],[[ + #if defined(CHECK) + boost::filesystem::copy_file("foo", "bar"); + #else + choke; + #endif + ]])], + [AC_MSG_RESULT(mismatched); AC_DEFINE(FORCE_BOOST_EMULATED_SCOPED_ENUMS, 1, [Define this symbol if boost scoped enums are emulated])], [AC_MSG_RESULT(ok)]) +LIBS="$TEMP_LIBS" +CPPFLAGS="$TEMP_CPPFLAGS" + dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if dnl a working version is available, else fall back to sleep. sleep was removed diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index 68841afdb8..e2f85ad4fc 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -12,7 +12,8 @@ $(package)_config_opts_linux=--with-pic endef define $(package)_preprocess_cmds - sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h + sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h && \ + sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c endef define $(package)_config_cmds diff --git a/doc/bips.md b/doc/bips.md index 962b216123..e73add0130 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -18,4 +18,5 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.12.0**): * [`BIP 66`](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki): The strict DER rules and associated version 3 blocks have been implemented since **v0.10.0** ([PR #5713](https://github.com/bitcoin/bitcoin/pull/5713)). * [`BIP 70`](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki) [`71`](https://github.com/bitcoin/bips/blob/master/bip-0071.mediawiki) [`72`](https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki): Payment Protocol support has been available in Bitcoin Core GUI since **v0.9.0** ([PR #5216](https://github.com/bitcoin/bitcoin/pull/5216)). * [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, but only enforced for peer versions `>=70011` as of **v0.12.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579)). +* [`BIP 125`](https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki): Opt-in full replace-by-fee signaling honoured in mempool and mining as of **v0.12.0** ([PR 6871](https://github.com/bitcoin/bitcoin/pull/6871)). * [`BIP 130`](https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki): direct headers announcement is negotiated with peer versions `>=70012` as of **v0.12.0** ([PR 6494](https://github.com/bitcoin/bitcoin/pull/6494)). diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py index e6e26dbce3..7572bc2776 100755 --- a/qa/rpc-tests/sendheaders.py +++ b/qa/rpc-tests/sendheaders.py @@ -220,18 +220,20 @@ class SendHeadersTest(BitcoinTestFramework): # mine count blocks and return the new tip def mine_blocks(self, count): + # Clear out last block announcement from each p2p listener + [ x.clear_last_announcement() for x in self.p2p_connections ] self.nodes[0].generate(count) return int(self.nodes[0].getbestblockhash(), 16) # mine a reorg that invalidates length blocks (replacing them with # length+1 blocks). - # peers is the p2p nodes we're using; we clear their state after the + # Note: we clear the state of our p2p connections after the # to-be-reorged-out blocks are mined, so that we don't break later tests. # return the list of block hashes newly mined - def mine_reorg(self, length, peers): + def mine_reorg(self, length): self.nodes[0].generate(length) # make sure all invalidated blocks are node0's sync_blocks(self.nodes, wait=0.1) - [x.clear_last_announcement() for x in peers] + [x.clear_last_announcement() for x in self.p2p_connections] tip_height = self.nodes[1].getblockcount() hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1)) @@ -245,6 +247,8 @@ class SendHeadersTest(BitcoinTestFramework): inv_node = InvNode() test_node = TestNode() + self.p2p_connections = [inv_node, test_node] + connections = [] connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node)) # Set nServices to 0 for test_node, so no block download will occur outside of @@ -303,7 +307,6 @@ class SendHeadersTest(BitcoinTestFramework): prev_tip = int(self.nodes[0].getbestblockhash(), 16) test_node.get_headers(locator=[prev_tip], hashstop=0L) test_node.sync_with_ping() - test_node.clear_last_announcement() # Clear out empty headers response # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) @@ -352,8 +355,6 @@ class SendHeadersTest(BitcoinTestFramework): # broadcast it) assert_equal(inv_node.last_inv, None) assert_equal(inv_node.last_headers, None) - inv_node.clear_last_announcement() - test_node.clear_last_announcement() tip = self.mine_blocks(1) assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=[tip]), True) @@ -368,7 +369,7 @@ class SendHeadersTest(BitcoinTestFramework): # getheaders or inv from peer. for j in xrange(2): # First try mining a reorg that can propagate with header announcement - new_block_hashes = self.mine_reorg(length=7, peers=[test_node, inv_node]) + new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True) @@ -376,7 +377,7 @@ class SendHeadersTest(BitcoinTestFramework): block_time += 8 # Mine a too-large reorg, which should be announced with a single inv - new_block_hashes = self.mine_reorg(length=8, peers=[test_node, inv_node]) + new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] assert_equal(inv_node.check_last_announcement(inv=[tip]), True) assert_equal(test_node.check_last_announcement(inv=[tip]), True) @@ -407,7 +408,6 @@ class SendHeadersTest(BitcoinTestFramework): test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) test_node.get_data([tip]) test_node.wait_for_block(tip) - test_node.clear_last_announcement() elif i == 2: test_node.get_data([tip]) test_node.wait_for_block(tip) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 8e49b56565..ca65fb6e79 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -1004,6 +1004,18 @@ class msg_reject(object): class NodeConnCB(object): def __init__(self): self.verack_received = False + # deliver_sleep_time is helpful for debugging race conditions in p2p + # tests; it causes message delivery to sleep for the specified time + # before acquiring the global lock and delivering the next message. + self.deliver_sleep_time = None + + def set_deliver_sleep_time(self, value): + with mininode_lock: + self.deliver_sleep_time = value + + def get_deliver_sleep_time(self): + with mininode_lock: + return self.deliver_sleep_time # Spin until verack message is received from the node. # Tests may want to use this as a signal that the test can begin. @@ -1017,6 +1029,9 @@ class NodeConnCB(object): time.sleep(0.05) def deliver(self, conn, message): + deliver_sleep = self.get_deliver_sleep_time() + if deliver_sleep is not None: + time.sleep(deliver_sleep) with mininode_lock: try: getattr(self, 'on_' + message.command)(conn, message) diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index 6045b8268c..43ec621a40 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -3,21 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -# Exercise the wallet. Ported from wallet.sh. -# Does the following: -# a) creates 3 nodes, with an empty chain (no blocks). -# b) node0 mines a block -# c) node1 mines 101 blocks, so now nodes 0 and 1 have 50btc, node2 has none. -# d) node0 sends 21 btc to node2, in two transactions (11 btc, then 10 btc). -# e) node0 mines a block, collects the fee on the second transaction -# f) node1 mines 100 blocks, to mature node0's just-mined block -# g) check that node0 has 100-21, node2 has 21 -# h) node0 should now have 2 unspent outputs; send these to node2 via raw tx broadcast by node1 -# i) have node1 mine a block -# j) check balances - node0 should have 0, node2 should have 100 -# k) test ResendWalletTransactions - create transactions, startup fourth node, make sure it syncs -# from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -190,7 +175,7 @@ class WalletTest (BitcoinTestFramework): for uTx in unspentTxs: if uTx['txid'] == zeroValueTxid: found = True - assert_equal(uTx['amount'], Decimal('0.00000000')); + assert_equal(uTx['amount'], Decimal('0')) assert(found) #do some -walletbroadcast tests @@ -202,21 +187,22 @@ class WalletTest (BitcoinTestFramework): connect_nodes_bi(self.nodes,0,2) self.sync_all() - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2); + txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) self.nodes[1].generate(1) #mine a block, tx should not be in there self.sync_all() - assert_equal(self.nodes[2].getbalance(), node_2_bal); #should not be changed because tx was not broadcasted + assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted #now broadcast from another node, mine a block, sync, and check the balance self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) self.nodes[1].generate(1) self.sync_all() + node_2_bal += 2 txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) - assert_equal(self.nodes[2].getbalance(), node_2_bal + Decimal('2')); #should not be + assert_equal(self.nodes[2].getbalance(), node_2_bal) #create another tx - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2); + txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) #restart the nodes with -walletbroadcast=1 stop_nodes(self.nodes) @@ -229,23 +215,24 @@ class WalletTest (BitcoinTestFramework): self.nodes[0].generate(1) sync_blocks(self.nodes) + node_2_bal += 2 #tx should be added to balance because after restarting the nodes tx should be broadcastet - assert_equal(self.nodes[2].getbalance(), node_2_bal + Decimal('4')); #should not be + assert_equal(self.nodes[2].getbalance(), node_2_bal) #send a tx with value in a string (PR#6380 +) txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2") txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-2.00000000')) + assert_equal(txObj['amount'], Decimal('-2')) txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001") txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.00010000')) + assert_equal(txObj['amount'], Decimal('-0.0001')) #check if JSON parser can handle scientific notation in strings txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4") txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.00010000')) + assert_equal(txObj['amount'], Decimal('-0.0001')) #this should fail errorString = "" @@ -254,7 +241,7 @@ class WalletTest (BitcoinTestFramework): except JSONRPCException,e: errorString = e.error['message'] - assert_equal("Invalid amount" in errorString, True); + assert_equal("Invalid amount" in errorString, True) errorString = "" try: @@ -262,7 +249,30 @@ class WalletTest (BitcoinTestFramework): except JSONRPCException,e: errorString = e.error['message'] - assert_equal("not an integer" in errorString, True); + assert_equal("not an integer" in errorString, True) + + #check if wallet or blochchain maintenance changes the balance + self.sync_all() + self.nodes[0].generate(1) + self.sync_all() + balance_nodes = [self.nodes[i].getbalance() for i in range(3)] + + maintenance = [ + '-rescan', + '-reindex', + '-zapwallettxes=1', + '-zapwallettxes=2', + '-salvagewallet', + ] + for m in maintenance: + stop_nodes(self.nodes) + wait_bitcoinds() + self.nodes = start_nodes(3, self.options.tmpdir, [[m]] * 3) + connect_nodes_bi(self.nodes,0,1) + connect_nodes_bi(self.nodes,1,2) + connect_nodes_bi(self.nodes,0,2) + self.sync_all() + assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)]) if __name__ == '__main__': diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 2920aa26f7..1466dc0cbf 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -226,7 +226,7 @@ bool StartHTTPRPC() assert(EventBase()); httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase()); - RPCRegisterTimerInterface(httpRPCTimerInterface); + RPCSetTimerInterface(httpRPCTimerInterface); return true; } @@ -240,7 +240,7 @@ void StopHTTPRPC() LogPrint("rpc", "Stopping HTTP RPC server\n"); UnregisterHTTPHandler("/", true); if (httpRPCTimerInterface) { - RPCUnregisterTimerInterface(httpRPCTimerInterface); + RPCUnsetTimerInterface(httpRPCTimerInterface); delete httpRPCTimerInterface; httpRPCTimerInterface = 0; } diff --git a/src/init.cpp b/src/init.cpp index ba85a7972e..374e756abd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -476,6 +476,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Node relay options:")); if (showDebug) strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard())); + strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Minimum bytes per sigop in transactions we relay and mine (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER)); strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); @@ -750,6 +751,16 @@ void InitParameterInteraction() } } +static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) +{ + return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); +} + +static std::string AmountErrMsg(const char * const optname, const std::string& strValue) +{ + return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue); +} + void InitLogging() { fPrintToConsole = GetBoolArg("-printtoconsole", false); @@ -933,12 +944,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) ::minRelayTxFee = CFeeRate(n); else - return InitError(strprintf(_("Invalid amount for -minrelaytxfee=<amount>: '%s'"), mapArgs["-minrelaytxfee"])); + return InitError(AmountErrMsg("minrelaytxfee", mapArgs["-minrelaytxfee"])); } fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !Params().RequireStandard()); if (Params().RequireStandard() && !fRequireStandard) return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); + nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp); #ifdef ENABLE_WALLET if (mapArgs.count("-mintxfee")) @@ -947,7 +959,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (ParseMoney(mapArgs["-mintxfee"], n) && n > 0) CWallet::minTxFee = CFeeRate(n); else - return InitError(strprintf(_("Invalid amount for -mintxfee=<amount>: '%s'"), mapArgs["-mintxfee"])); + return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"])); } if (mapArgs.count("-fallbackfee")) { @@ -962,7 +974,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) { CAmount nFeePerK = 0; if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK)) - return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s'"), mapArgs["-paytxfee"])); + return InitError(AmountErrMsg("paytxfee", mapArgs["-paytxfee"])); if (nFeePerK > nHighTransactionFeeWarning) InitWarning(_("-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); payTxFee = CFeeRate(nFeePerK, 1000); @@ -976,7 +988,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) { CAmount nMaxFee = 0; if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee)) - return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s'"), mapArgs["-maptxfee"])); + return InitError(AmountErrMsg("maxtxfee", mapArgs["-maptxfee"])); if (nMaxFee > nHighTransactionMaxFeeWarning) InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); maxTxFee = nMaxFee; @@ -1188,13 +1200,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) - return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind)); + return InitError(ResolveErrMsg("bind", strBind)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-whitebind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, 0, false)) - return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind)); + return InitError(ResolveErrMsg("whitebind", strBind)); if (addrBind.GetPort() == 0) return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST)); @@ -1214,7 +1226,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) - return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr)); + return InitError(ResolveErrMsg("externalip", strAddr)); AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); } } diff --git a/src/main.cpp b/src/main.cpp index c058843ae3..06374cc1b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,6 +69,7 @@ bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; bool fRequireStandard = true; +unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; @@ -929,16 +930,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C if (fRequireStandard && !AreInputsStandard(tx, view)) return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); - // Check that the transaction doesn't have an excessive number of - // sigops, making it impossible to mine. Since the coinbase transaction - // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than - // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than - // merely non-standard transaction. unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, - strprintf("%d > %d", nSigOps, MAX_STANDARD_TX_SIGOPS)); CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; @@ -964,6 +957,15 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps); unsigned int nSize = entry.GetTxSize(); + // Check that the transaction doesn't have an excessive number of + // sigops, making it impossible to mine. Since the coinbase transaction + // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than + // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than + // merely non-standard transaction. + if ((nSigOps > MAX_STANDARD_TX_SIGOPS) || (nBytesPerSigOp && nSigOps > nSize / nBytesPerSigOp)) + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, + strprintf("%d", nSigOps)); + CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); diff --git a/src/main.h b/src/main.h index cadd281c89..cefaedabf5 100644 --- a/src/main.h +++ b/src/main.h @@ -100,6 +100,7 @@ static const bool DEFAULT_RELAYPRIORITY = true; /** Default for -permitbaremultisig */ static const bool DEFAULT_PERMIT_BAREMULTISIG = true; +static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20; static const bool DEFAULT_CHECKPOINTS_ENABLED = true; static const bool DEFAULT_TXINDEX = false; static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100; @@ -130,6 +131,7 @@ extern int nScriptCheckThreads; extern bool fTxIndex; extern bool fIsBareMultisigStd; extern bool fRequireStandard; +extern unsigned int nBytesPerSigOp; extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; extern size_t nCoinCacheUsage; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 273a482fa1..019df72279 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -23,9 +23,6 @@ * 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 diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index e0b84ba13f..f324c6dc5a 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -15,9 +15,15 @@ #include <QSettings> #include <QMessageBox> -/* Minimum free space (in bytes) needed for data directory */ +#include <cmath> + static const uint64_t GB_BYTES = 1000000000LL; -static const uint64_t BLOCK_CHAIN_SIZE = 20LL * GB_BYTES; +/* Minimum free space (in GB) needed for data directory */ +static const uint64_t BLOCK_CHAIN_SIZE = 80; +/* Minimum free space (in GB) needed for data directory when pruned; Does not include prune target */ +static const uint64_t CHAIN_STATE_SIZE = 2; +/* Total required space (in GB) depending on user choice (prune, not prune) */ +static uint64_t requiredSpace; /* Check free space asynchronously to prevent hanging the UI thread. @@ -112,7 +118,11 @@ Intro::Intro(QWidget *parent) : signalled(false) { ui->setupUi(this); - ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES)); + uint64_t pruneTarget = std::max<int64_t>(0, GetArg("-prune", 0)); + requiredSpace = BLOCK_CHAIN_SIZE; + if (pruneTarget) + requiredSpace = CHAIN_STATE_SIZE + std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES); + ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(requiredSpace)); startThread(); } @@ -216,9 +226,9 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable ui->freeSpace->setText(""); } else { QString freeString = tr("%n GB of free space available", "", bytesAvailable/GB_BYTES); - if(bytesAvailable < BLOCK_CHAIN_SIZE) + if(bytesAvailable < requiredSpace * GB_BYTES) { - freeString += " " + tr("(of %n GB needed)", "", BLOCK_CHAIN_SIZE/GB_BYTES); + freeString += " " + tr("(of %n GB needed)", "", requiredSpace); ui->freeSpace->setStyleSheet("QLabel { color: #800000 }"); } else { ui->freeSpace->setStyleSheet(""); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 4c869b9ac5..7178bc00e6 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -278,7 +278,9 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) : #endif // Register RPC timer interface rpcTimerInterface = new QtRPCTimerInterface(); - RPCRegisterTimerInterface(rpcTimerInterface); + // avoid accidentally overwriting an existing, non QTThread + // based timer interface + RPCSetTimerInterfaceIfUnset(rpcTimerInterface); startExecutor(); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); @@ -293,7 +295,7 @@ RPCConsole::~RPCConsole() { GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); Q_EMIT stopExecutor(); - RPCUnregisterTimerInterface(rpcTimerInterface); + RPCUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; } diff --git a/src/reverselock.h b/src/reverselock.h index 567636e16a..fac1ccb793 100644 --- a/src/reverselock.h +++ b/src/reverselock.h @@ -15,10 +15,12 @@ public: explicit reverse_lock(Lock& lock) : lock(lock) { lock.unlock(); + lock.swap(templock); } ~reverse_lock() { - lock.lock(); + templock.lock(); + templock.swap(lock); } private: @@ -26,6 +28,7 @@ private: reverse_lock& operator=(reverse_lock const&); Lock& lock; + Lock templock; }; #endif // BITCOIN_REVERSELOCK_H diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index bc419d14d9..78d6898bc3 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -33,7 +33,7 @@ static bool fRPCInWarmup = true; static std::string rpcWarmupStatus("RPC server started"); static CCriticalSection cs_rpcWarmup; /* Timer-creating functions */ -static std::vector<RPCTimerInterface*> timerInterfaces; +static RPCTimerInterface* timerInterface = NULL; /* Map of name to timer. * @note Can be changed to std::unique_ptr when C++11 */ static std::map<std::string, boost::shared_ptr<RPCTimerBase> > deadlineTimers; @@ -546,24 +546,28 @@ std::string HelpExampleRpc(const std::string& methodname, const std::string& arg "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; } -void RPCRegisterTimerInterface(RPCTimerInterface *iface) +void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) { - timerInterfaces.push_back(iface); + if (!timerInterface) + timerInterface = iface; } -void RPCUnregisterTimerInterface(RPCTimerInterface *iface) +void RPCSetTimerInterface(RPCTimerInterface *iface) { - std::vector<RPCTimerInterface*>::iterator i = std::find(timerInterfaces.begin(), timerInterfaces.end(), iface); - assert(i != timerInterfaces.end()); - timerInterfaces.erase(i); + timerInterface = iface; +} + +void RPCUnsetTimerInterface(RPCTimerInterface *iface) +{ + if (timerInterface == iface) + timerInterface = NULL; } void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds) { - if (timerInterfaces.empty()) + if (!timerInterface) throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); deadlineTimers.erase(name); - RPCTimerInterface* timerInterface = timerInterfaces[0]; LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)))); } diff --git a/src/rpcserver.h b/src/rpcserver.h index f85ab42f02..9dce318872 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -100,10 +100,12 @@ public: virtual RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis) = 0; }; -/** Register factory function for timers */ -void RPCRegisterTimerInterface(RPCTimerInterface *iface); -/** Unregister factory function for timers */ -void RPCUnregisterTimerInterface(RPCTimerInterface *iface); +/** Set the factory function for timers */ +void RPCSetTimerInterface(RPCTimerInterface *iface); +/** Set the factory function for timer, but only, if unset */ +void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface); +/** Unset factory function for timers */ +void RPCUnsetTimerInterface(RPCTimerInterface *iface); /** * Run func nSeconds from now. diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp index e7e627ae0f..8bdff97000 100644 --- a/src/test/reverselock_tests.cpp +++ b/src/test/reverselock_tests.cpp @@ -42,22 +42,18 @@ BOOST_AUTO_TEST_CASE(reverselock_errors) BOOST_CHECK(failed); BOOST_CHECK(!lock.owns_lock()); - // Make sure trying to lock a lock after it has been reverse locked fails - failed = false; - bool locked = false; + // Locking the original lock after it has been taken by a reverse lock + // makes no sense. Ensure that the original lock no longer owns the lock + // after giving it to a reverse one. lock.lock(); BOOST_CHECK(lock.owns_lock()); - - try { + { reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); - lock.lock(); - locked = true; - } catch(...) { - failed = true; + BOOST_CHECK(!lock.owns_lock()); } - BOOST_CHECK(locked && failed); + BOOST_CHECK(failed); BOOST_CHECK(lock.owns_lock()); } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index f03cb4896c..e84d588026 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) empty_wallet(); } -BOOST_AUTO_TEST_CASE(pruning_in_ApproximateBestSet) +BOOST_AUTO_TEST_CASE(ApproximateBestSubset) { CoinSet setCoinsRet; CAmount nValueRet; @@ -336,15 +336,28 @@ BOOST_AUTO_TEST_CASE(pruning_in_ApproximateBestSet) LOCK(wallet.cs_wallet); empty_wallet(); + + // Test vValue sort order + for (int i = 0; i < 1000; i++) + add_coin(1000 * COIN); + add_coin(3 * COIN); + + BOOST_CHECK(wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); + + empty_wallet(); + + // Test trimming for (int i = 0; i < 100; i++) - add_coin(10 * CENT); + add_coin(10 * COIN); for (int i = 0; i < 100; i++) - add_coin(1000 * CENT); + add_coin(1000 * COIN); - BOOST_CHECK(wallet.SelectCoinsMinConf(100001 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(wallet.SelectCoinsMinConf(100001 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet)); // We need all 100 larger coins and exactly one small coin. - // Superfluous small coins must be pruned: - BOOST_CHECK_EQUAL(nValueRet, 100010 * CENT); + // Superfluous small coins must be trimmed from the set: + BOOST_CHECK_EQUAL(nValueRet, 100010 * COIN); BOOST_CHECK_EQUAL(setCoinsRet.size(), 101); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 581c688fc7..6b2ebebba7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -817,6 +817,13 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } iter++; } + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be recomputed + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } } } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index ce57f4dae1..9bbd52f14c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -61,7 +61,6 @@ static const CAmount nHighTransactionMaxFeeWarning = 100 * nHighTransactionFeeWa static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; static const bool DEFAULT_WALLETBROADCAST = true; -class CAccountingEntry; class CBlockIndex; class CCoinControl; class COutput; @@ -447,6 +446,86 @@ public: } }; +/** + * Internal transfers. + * Database key is acentry<account><counter>. + */ +class CAccountingEntry +{ +public: + std::string strAccount; + CAmount nCreditDebit; + int64_t nTime; + std::string strOtherAccount; + std::string strComment; + mapValue_t mapValue; + int64_t nOrderPos; //! position in ordered transaction list + uint64_t nEntryNo; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + nOrderPos = -1; + nEntryNo = 0; + } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + //! Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(LIMITED_STRING(strOtherAccount, 65536)); + + if (!ser_action.ForRead()) + { + WriteOrderPos(nOrderPos, mapValue); + + if (!(mapValue.empty() && _ssExtra.empty())) + { + CDataStream ss(nType, nVersion); + ss.insert(ss.begin(), '\0'); + ss << mapValue; + ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); + strComment.append(ss.str()); + } + } + + READWRITE(LIMITED_STRING(strComment, 65536)); + + size_t nSepPos = strComment.find("\0", 0, 1); + if (ser_action.ForRead()) + { + mapValue.clear(); + if (std::string::npos != nSepPos) + { + CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion); + ss >> mapValue; + _ssExtra = std::vector<char>(ss.begin(), ss.end()); + } + ReadOrderPos(nOrderPos, mapValue); + } + if (std::string::npos != nSepPos) + strComment.erase(nSepPos); + + mapValue.erase("n"); + } + +private: + std::vector<char> _ssExtra; +}; /** @@ -843,87 +922,4 @@ public: } }; - - -/** - * Internal transfers. - * Database key is acentry<account><counter>. - */ -class CAccountingEntry -{ -public: - std::string strAccount; - CAmount nCreditDebit; - int64_t nTime; - std::string strOtherAccount; - std::string strComment; - mapValue_t mapValue; - int64_t nOrderPos; //! position in ordered transaction list - uint64_t nEntryNo; - - CAccountingEntry() - { - SetNull(); - } - - void SetNull() - { - nCreditDebit = 0; - nTime = 0; - strAccount.clear(); - strOtherAccount.clear(); - strComment.clear(); - nOrderPos = -1; - nEntryNo = 0; - } - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - //! Note: strAccount is serialized as part of the key, not here. - READWRITE(nCreditDebit); - READWRITE(nTime); - READWRITE(LIMITED_STRING(strOtherAccount, 65536)); - - if (!ser_action.ForRead()) - { - WriteOrderPos(nOrderPos, mapValue); - - if (!(mapValue.empty() && _ssExtra.empty())) - { - CDataStream ss(nType, nVersion); - ss.insert(ss.begin(), '\0'); - ss << mapValue; - ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); - strComment.append(ss.str()); - } - } - - READWRITE(LIMITED_STRING(strComment, 65536)); - - size_t nSepPos = strComment.find("\0", 0, 1); - if (ser_action.ForRead()) - { - mapValue.clear(); - if (std::string::npos != nSepPos) - { - CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion); - ss >> mapValue; - _ssExtra = std::vector<char>(ss.begin(), ss.end()); - } - ReadOrderPos(nOrderPos, mapValue); - } - if (std::string::npos != nSepPos) - strComment.erase(nSepPos); - - mapValue.erase("n"); - } - -private: - std::vector<char> _ssExtra; -}; - #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 88dc3102da..5266946ca0 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -15,6 +15,12 @@ #include "utiltime.h" #include "wallet/wallet.h" +#if defined(FORCE_BOOST_EMULATED_SCOPED_ENUMS) +#define BOOST_NO_SCOPED_ENUMS +#define BOOST_NO_CXX11_SCOPED_ENUMS +#endif + +#include <boost/version.hpp> #include <boost/filesystem.hpp> #include <boost/foreach.hpp> #include <boost/scoped_ptr.hpp> @@ -960,8 +966,13 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); string strType, strErr; - bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, + bool fReadOK; + { + // Required in LoadKeyMetadata(): + LOCK(dummyWallet.cs_wallet); + fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, wss, strType, strErr); + } if (!IsKeyType(strType)) continue; if (!fReadOK) |