aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--doc/bips.md1
-rw-r--r--doc/developer-notes.md169
-rw-r--r--doc/release-notes.md18
-rwxr-xr-xqa/pull-tester/rpc-tests.py2
-rw-r--r--qa/replace-by-fee/.gitignore1
-rw-r--r--qa/replace-by-fee/README.md13
-rwxr-xr-xqa/replace-by-fee/rbf-tests.py360
-rwxr-xr-xqa/rpc-tests/keypool.py163
-rwxr-xr-xqa/rpc-tests/replace-by-fee.py512
-rwxr-xr-xqa/rpc-tests/sendheaders.py519
-rwxr-xr-xqa/rpc-tests/smartfees.py52
-rwxr-xr-xqa/rpc-tests/test_framework/mininode.py32
-rw-r--r--qa/rpc-tests/test_framework/util.py3
-rwxr-xr-xqa/rpc-tests/wallet.py39
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.test.include3
-rw-r--r--src/bitcoin-cli.cpp7
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/chain.cpp3
-rw-r--r--src/chainparams.cpp3
-rw-r--r--src/chainparams.h2
-rw-r--r--src/coins.cpp25
-rw-r--r--src/coins.h18
-rw-r--r--src/consensus/merkle.cpp172
-rw-r--r--src/consensus/merkle.h32
-rw-r--r--src/init.cpp323
-rw-r--r--src/init.h4
-rw-r--r--src/main.cpp572
-rw-r--r--src/main.h31
-rw-r--r--src/miner.cpp5
-rw-r--r--src/miner.h3
-rw-r--r--src/net.cpp9
-rw-r--r--src/net.h16
-rw-r--r--src/netbase.cpp2
-rw-r--r--src/netbase.h4
-rw-r--r--src/policy/fees.cpp52
-rw-r--r--src/policy/fees.h17
-rw-r--r--src/policy/policy.cpp2
-rw-r--r--src/policy/policy.h2
-rw-r--r--src/primitives/block.cpp63
-rw-r--r--src/primitives/block.h6
-rw-r--r--src/qt/bitcoin.cpp30
-rw-r--r--src/qt/bitcoingui.cpp18
-rw-r--r--src/qt/bitcoingui.h3
-rw-r--r--src/qt/clientmodel.cpp65
-rw-r--r--src/qt/clientmodel.h10
-rw-r--r--src/qt/coincontroldialog.cpp15
-rw-r--r--src/qt/guiconstants.h2
-rw-r--r--src/qt/intro.cpp2
-rw-r--r--src/qt/intro.h2
-rw-r--r--src/qt/optionsdialog.cpp75
-rw-r--r--src/qt/optionsdialog.h23
-rw-r--r--src/qt/optionsmodel.cpp9
-rw-r--r--src/qt/optionsmodel.h4
-rw-r--r--src/qt/paymentrequestplus.cpp2
-rw-r--r--src/qt/paymentrequestplus.h2
-rw-r--r--src/qt/qvalidatedlineedit.cpp16
-rw-r--r--src/qt/qvalidatedlineedit.h4
-rw-r--r--src/qt/rpcconsole.cpp12
-rw-r--r--src/qt/rpcconsole.h2
-rw-r--r--src/qt/sendcoinsdialog.cpp7
-rw-r--r--src/qt/utilitydialog.cpp20
-rw-r--r--src/rpcblockchain.cpp7
-rw-r--r--src/rpcclient.cpp2
-rw-r--r--src/rpcmining.cpp76
-rw-r--r--src/rpcserver.cpp2
-rw-r--r--src/rpcserver.h2
-rw-r--r--src/script/standard.cpp1
-rw-r--r--src/script/standard.h3
-rw-r--r--src/secp256k1/Makefile.am2
-rw-r--r--src/test/bignum.h180
-rw-r--r--src/test/coins_tests.cpp16
-rw-r--r--src/test/main_tests.cpp1
-rw-r--r--src/test/merkle_tests.cpp136
-rw-r--r--src/test/miner_tests.cpp3
-rw-r--r--src/test/pmt_tests.cpp3
-rw-r--r--src/test/policyestimator_tests.cpp57
-rw-r--r--src/test/scriptnum10.h183
-rw-r--r--src/test/scriptnum_tests.cpp41
-rw-r--r--src/test/test_bitcoin.cpp12
-rw-r--r--src/torcontrol.cpp13
-rw-r--r--src/txmempool.cpp27
-rw-r--r--src/txmempool.h35
-rw-r--r--src/ui_interface.h3
-rw-r--r--src/uint256.h6
-rw-r--r--src/util.cpp12
-rw-r--r--src/util.h5
-rw-r--r--src/version.h5
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/db.h1
-rw-r--r--src/wallet/wallet.cpp34
-rw-r--r--src/wallet/wallet.h6
-rw-r--r--src/wallet/walletdb.cpp2
-rw-r--r--src/wallet/walletdb.h2
-rw-r--r--src/zmq/zmqnotificationinterface.cpp6
-rw-r--r--src/zmq/zmqpublishnotifier.cpp10
97 files changed, 3564 insertions, 922 deletions
diff --git a/Makefile.am b/Makefile.am
index 303ad3b06f..b2b7811729 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -111,7 +111,7 @@ $(APP_DIST_DIR)/Applications:
$(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/Bitcoin-Qt
$(OSX_DMG): $(APP_DIST_EXTRAS)
- $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "Bitcoin-Core" -no-pad -r -apple -o $@ dist
+ $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "Bitcoin-Core" -no-pad -r -dir-mode 0755 -apple -o $@ dist
$(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE): contrib/macdeploy/$(OSX_BACKGROUND_IMAGE)
$(MKDIR_P) $(@D)
diff --git a/doc/bips.md b/doc/bips.md
index c780e2dde0..962b216123 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -18,3 +18,4 @@ 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 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/doc/developer-notes.md b/doc/developer-notes.md
index 7fe292f1f8..01eea931ad 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -204,3 +204,172 @@ If a set of tools is used by the build system or scripts the repository (for
example, lcov) it is perfectly acceptable to add its files to `.gitignore`
and commit them.
+Development guidelines
+============================
+
+A few non-style-related recommendations for developers, as well as points to
+pay attention to for reviewers of Bitcoin Core code.
+
+General Bitcoin Core
+----------------------
+
+- New features should be exposed on RPC first, then can be made available in the GUI
+
+ - *Rationale*: RPC allows for better automatic testing. The test suite for
+ the GUI is very limited
+
+- Make sure pulls pass Travis CI before merging
+
+ - *Rationale*: Makes sure that they pass thorough testing, and that the tester will keep passing
+ on the master branch. Otherwise all new pull requests will start failing the tests, resulting in
+ confusion and mayhem
+
+ - *Explanation*: If the test suite is to be updated for a change, this has to
+ be done first
+
+Wallet
+-------
+
+- Make sure that that no crashes happen with run-time option `-disablewallet`.
+
+ - *Rationale*: In RPC code that conditionally use the wallet (such as
+ `validateaddress`) it is easy to forget that global pointer `pwalletMain`
+ can be NULL. See `qa/rpc-tests/disablewallet.py` for functional tests
+ exercising the API with `-disablewallet`
+
+- Include `db_cxx.h` (BerkeleyDB header) only when `ENABLE_WALLET` is set
+
+ - *Rationale*: Otherwise compilation of the disable-wallet build will fail in environments without BerkeleyDB
+
+General C++
+-------------
+
+- Assertions should not have side-effects
+
+ - *Rationale*: Even though the source code is set to to refuse to compile
+ with assertions disabled, having side-effects in assertions is unexpected and
+ makes the code harder to understand
+
+- If you use the .h, you must link the .cpp
+
+ - *Rationale*: Include files are the interface for the implementation file. Including one but
+ not linking the other is confusing. Please avoid that. Moving functions from
+ the `.h` to the `.cpp` should not result in build errors
+
+- Use the RAII (Resource Acquisition Is Initialization) paradigm where possible. For example by using
+ `scoped_pointer` for allocations in a function.
+
+ - *Rationale*: This avoids memory and resource leaks, and ensures exception safety
+
+C++ data structures
+--------------------
+
+- Never use the std::map [] syntax when reading from a map, but instead use .find()
+
+ - *Rationale*: [] does an insert (of the default element) if the item doesn't
+ exist in the map yet. This has resulted in memory leaks in the past, as well as
+ race conditions (expecting read-read behavior). Using [] is fine for *writing* to a map
+
+- Do not compare an iterator from one data structure with an iterator of
+ another data structure (even if of the same type)
+
+ - *Rationale*: Behavior is undefined. In C++ parlor this means "may reformat
+ the universe", in practice this has resulted in at least one hard-to-debug crash bug
+
+- Watch out for vector out-of-bounds exceptions. `&vch[0]` is illegal for an
+ empty vector, `&vch[vch.size()]` is always illegal. Use `begin_ptr(vch)` and
+ `end_ptr(vch)` to get the begin and end pointer instead (defined in
+ `serialize.h`)
+
+- Vector bounds checking is only enabled in debug mode. Do not rely on it
+
+- Make sure that constructors initialize all fields. If this is skipped for a
+ good reason (i.e., optimization on the critical path), add an explicit
+ comment about this
+
+ - *Rationale*: Ensure determinism by avoiding accidental use of uninitialized
+ values. Also, static analyzers balk about this.
+
+- Use explicitly signed or unsigned `char`s, or even better `uint8_t` and
+ `int8_t`. Do not use bare `char` unless it is to pass to a third-party API.
+ This type can be signed or unsigned depending on the architecture, which can
+ lead to interoperability problems or dangerous conditions such as
+ out-of-bounds array accesses
+
+- Prefer explicit constructions over implicit ones that rely on 'magical' C++ behavior
+
+ - *Rationale*: Easier to understand what is happening, thus easier to spot mistakes, even for those
+ that are not language lawyers
+
+Strings and formatting
+------------------------
+
+- Be careful of LogPrint versus LogPrintf. LogPrint takes a 'category' argument, LogPrintf does not.
+
+ - *Rationale*: Confusion of these can result in runtime exceptions due to
+ formatting mismatch, and it is easy to get wrong because of subtly similar naming
+
+- Use std::string, avoid C string manipulation functions
+
+ - *Rationale*: C++ string handling is marginally safer, less scope for
+ buffer overflows and surprises with \0 characters. Also some C string manipulations
+ tend to act differently depending on platform, or even the user locale
+
+- Use ParseInt32, ParseInt64, ParseDouble from `utilstrencodings.h` for number parsing
+
+ - *Rationale*: These functions do overflow checking, and avoid pesky locale issues
+
+- For `strprintf`, `LogPrint`, `LogPrintf` formatting characters don't need size specifiers
+
+ - *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out to avoid confusion
+
+Threads and synchronization
+----------------------------
+
+- Build and run tests with `-DDEBUG_LOCKORDER` to verify that no potential
+ deadlocks are introduced. As of 0.12, this is defined by default when
+ configuring with `--enable-debug`
+
+- When using `LOCK`/`TRY_LOCK` be aware that the lock exists in the context of
+ the current scope, so surround the statement and the code that needs the lock
+ with braces
+
+ OK:
+
+```c++
+{
+ TRY_LOCK(cs_vNodes, lockNodes);
+ ...
+}
+```
+
+ Wrong:
+
+```c++
+TRY_LOCK(cs_vNodes, lockNodes);
+{
+ ...
+}
+```
+
+Source code organization
+--------------------------
+
+- Implementation code should go into the `.cpp` file and not the `.h`, unless necessary due to template usage or
+ when performance due to inlining is critical
+
+ - *Rationale*: Shorter and simpler header files are easier to read, and reduce compile time
+
+- Don't import anything into the global namespace (`using namespace ...`). Use
+ fully specified types such as `std::string`.
+
+ - *Rationale*: Avoids symbol conflicts
+
+GUI
+-----
+
+- Do not display or manipulate dialogs in model code (classes `*Model`)
+
+ - *Rationale*: Model classes pass through events and data from the core, they
+ should not interact with the user. That's where View classes come in. The converse also
+ holds: try to not directly access core data structures from Views.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 7db27f9fac..f7958381b6 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -151,8 +151,8 @@ mining with the getblocktemplate protocol to a pool: this will affect you at
the pool operator's discretion, which must be no later than BIP65 achieving its
951/1001 status.
-Automatically listen on Tor
-----------------------------
+Automatically use Tor hidden services
+-------------------------------------
Starting with Tor version 0.2.7.1 it is possible, through Tor's control socket
API, to create and destroy 'ephemeral' hidden services programmatically.
@@ -160,8 +160,9 @@ Bitcoin Core has been updated to make use of this.
This means that if Tor is running (and proper authorization is available),
Bitcoin Core automatically creates a hidden service to listen on, without
-manual configuration. This will positively affect the number of available
-.onion nodes.
+manual configuration. Bitcoin Core will also use Tor automatically to connect
+to other .onion nodes if the control socket can be successfully opened. This
+will positively affect the number of available .onion nodes and their usage.
This new feature is enabled by default if Bitcoin Core is listening, and
a connection to Tor can be made. It can be configured with the `-listenonion`,
@@ -205,6 +206,15 @@ Libsecp256k1 has undergone very extensive testing and validation.
A side effect of this change is that libconsensus no longer depends on OpenSSL.
+Direct headers announcement (BIP 130)
+-------------------------------------
+
+Between compatible peers, BIP 130 direct headers announcement is used. This
+means that blocks are advertized by announcing their headers directly, instead
+of just announcing the hash. In a reorganization, all new headers are sent,
+instead of just the new tip. This can often prevent an extra roundtrip before
+the actual block is downloaded.
+
0.12.0 Change log
=================
diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py
index 7a30db68dd..5004b09c18 100755
--- a/qa/pull-tester/rpc-tests.py
+++ b/qa/pull-tester/rpc-tests.py
@@ -91,6 +91,7 @@ testScripts = [
'p2p-fullblocktest.py',
'blockchain.py',
'disablewallet.py',
+ 'sendheaders.py',
]
testScriptsExt = [
'bip65-cltv.py',
@@ -112,6 +113,7 @@ testScriptsExt = [
'p2p-acceptblock.py',
'mempool_packages.py',
'maxuploadtarget.py',
+ 'replace-by-fee.py',
]
#Enable ZMQ tests
diff --git a/qa/replace-by-fee/.gitignore b/qa/replace-by-fee/.gitignore
new file mode 100644
index 0000000000..b2c4f4657a
--- /dev/null
+++ b/qa/replace-by-fee/.gitignore
@@ -0,0 +1 @@
+python-bitcoinlib
diff --git a/qa/replace-by-fee/README.md b/qa/replace-by-fee/README.md
new file mode 100644
index 0000000000..baad86de9a
--- /dev/null
+++ b/qa/replace-by-fee/README.md
@@ -0,0 +1,13 @@
+Replace-by-fee regression tests
+===============================
+
+First get version v0.5.0 of the python-bitcoinlib library. In this directory
+run:
+
+ git clone -n https://github.com/petertodd/python-bitcoinlib
+ (cd python-bitcoinlib && git checkout 8270bfd9c6ac37907d75db3d8b9152d61c7255cd)
+
+Then run the tests themselves with a bitcoind available running in regtest
+mode:
+
+ ./rbf-tests.py
diff --git a/qa/replace-by-fee/rbf-tests.py b/qa/replace-by-fee/rbf-tests.py
new file mode 100755
index 0000000000..1ee6c83875
--- /dev/null
+++ b/qa/replace-by-fee/rbf-tests.py
@@ -0,0 +1,360 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#
+# Test replace-by-fee
+#
+
+import os
+import sys
+
+# Add python-bitcoinlib to module search path, prior to any system-wide
+# python-bitcoinlib.
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinlib"))
+
+import unittest
+
+import bitcoin
+bitcoin.SelectParams('regtest')
+
+import bitcoin.rpc
+
+from bitcoin.core import *
+from bitcoin.core.script import *
+from bitcoin.wallet import *
+
+MAX_REPLACEMENT_LIMIT = 100
+
+class Test_ReplaceByFee(unittest.TestCase):
+ proxy = None
+
+ @classmethod
+ def setUpClass(cls):
+ if cls.proxy is None:
+ cls.proxy = bitcoin.rpc.Proxy()
+
+ @classmethod
+ def mine_mempool(cls):
+ """Mine until mempool is empty"""
+ mempool_size = 1
+ while mempool_size:
+ cls.proxy.call('generate', 1)
+ new_mempool_size = len(cls.proxy.getrawmempool())
+
+ # It's possible to get stuck in a loop here if the mempool has
+ # transactions that can't be mined.
+ assert(new_mempool_size != mempool_size)
+ mempool_size = new_mempool_size
+
+ @classmethod
+ def tearDownClass(cls):
+ # Make sure mining works
+ cls.mine_mempool()
+
+ def make_txout(self, amount, confirmed=True, scriptPubKey=CScript([1])):
+ """Create a txout with a given amount and scriptPubKey
+
+ Mines coins as needed.
+
+ confirmed - txouts created will be confirmed in the blockchain;
+ unconfirmed otherwise.
+ """
+ fee = 1*COIN
+ while self.proxy.getbalance() < amount + fee:
+ self.proxy.call('generate', 100)
+
+ addr = P2SHBitcoinAddress.from_redeemScript(CScript([]))
+ txid = self.proxy.sendtoaddress(addr, amount + fee)
+
+ tx1 = self.proxy.getrawtransaction(txid)
+
+ i = None
+ for i, txout in enumerate(tx1.vout):
+ if txout.scriptPubKey == addr.to_scriptPubKey():
+ break
+ assert i is not None
+
+ tx2 = CTransaction([CTxIn(COutPoint(txid, i), CScript([1, CScript([])]), nSequence=0)],
+ [CTxOut(amount, scriptPubKey)])
+
+ tx2_txid = self.proxy.sendrawtransaction(tx2, True)
+
+ # If requested, ensure txouts are confirmed.
+ if confirmed:
+ self.mine_mempool()
+
+ return COutPoint(tx2_txid, 0)
+
+ def test_simple_doublespend(self):
+ """Simple doublespend"""
+ tx0_outpoint = self.make_txout(1.1*COIN)
+
+ tx1a = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(1*COIN, CScript([b'a']))])
+ tx1a_txid = self.proxy.sendrawtransaction(tx1a, True)
+
+ # Should fail because we haven't changed the fee
+ tx1b = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(1*COIN, CScript([b'b']))])
+
+ try:
+ tx1b_txid = self.proxy.sendrawtransaction(tx1b, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26) # insufficient fee
+ else:
+ self.fail()
+
+ # Extra 0.1 BTC fee
+ tx1b = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(0.9*COIN, CScript([b'b']))])
+ tx1b_txid = self.proxy.sendrawtransaction(tx1b, True)
+
+ # tx1a is in fact replaced
+ with self.assertRaises(IndexError):
+ self.proxy.getrawtransaction(tx1a_txid)
+
+ self.assertEqual(tx1b, self.proxy.getrawtransaction(tx1b_txid))
+
+ def test_doublespend_chain(self):
+ """Doublespend of a long chain"""
+
+ initial_nValue = 50*COIN
+ tx0_outpoint = self.make_txout(initial_nValue)
+
+ prevout = tx0_outpoint
+ remaining_value = initial_nValue
+ chain_txids = []
+ while remaining_value > 10*COIN:
+ remaining_value -= 1*COIN
+ tx = CTransaction([CTxIn(prevout, nSequence=0)],
+ [CTxOut(remaining_value, CScript([1]))])
+ txid = self.proxy.sendrawtransaction(tx, True)
+ chain_txids.append(txid)
+ prevout = COutPoint(txid, 0)
+
+ # Whether the double-spend is allowed is evaluated by including all
+ # child fees - 40 BTC - so this attempt is rejected.
+ dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(initial_nValue - 30*COIN, CScript([1]))])
+
+ try:
+ self.proxy.sendrawtransaction(dbl_tx, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26) # insufficient fee
+ else:
+ self.fail()
+
+ # Accepted with sufficient fee
+ dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(1*COIN, CScript([1]))])
+ self.proxy.sendrawtransaction(dbl_tx, True)
+
+ for doublespent_txid in chain_txids:
+ with self.assertRaises(IndexError):
+ self.proxy.getrawtransaction(doublespent_txid)
+
+ def test_doublespend_tree(self):
+ """Doublespend of a big tree of transactions"""
+
+ initial_nValue = 50*COIN
+ tx0_outpoint = self.make_txout(initial_nValue)
+
+ def branch(prevout, initial_value, max_txs, *, tree_width=5, fee=0.0001*COIN, _total_txs=None):
+ if _total_txs is None:
+ _total_txs = [0]
+ if _total_txs[0] >= max_txs:
+ return
+
+ txout_value = (initial_value - fee) // tree_width
+ if txout_value < fee:
+ return
+
+ vout = [CTxOut(txout_value, CScript([i+1]))
+ for i in range(tree_width)]
+ tx = CTransaction([CTxIn(prevout, nSequence=0)],
+ vout)
+
+ self.assertTrue(len(tx.serialize()) < 100000)
+ txid = self.proxy.sendrawtransaction(tx, True)
+ yield tx
+ _total_txs[0] += 1
+
+ for i, txout in enumerate(tx.vout):
+ yield from branch(COutPoint(txid, i), txout_value,
+ max_txs,
+ tree_width=tree_width, fee=fee,
+ _total_txs=_total_txs)
+
+ fee = 0.0001*COIN
+ n = MAX_REPLACEMENT_LIMIT
+ tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
+ self.assertEqual(len(tree_txs), n)
+
+ # Attempt double-spend, will fail because too little fee paid
+ dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(initial_nValue - fee*n, CScript([1]))])
+ try:
+ self.proxy.sendrawtransaction(dbl_tx, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26) # insufficient fee
+ else:
+ self.fail()
+
+ # 1 BTC fee is enough
+ dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))])
+ self.proxy.sendrawtransaction(dbl_tx, True)
+
+ for tx in tree_txs:
+ with self.assertRaises(IndexError):
+ self.proxy.getrawtransaction(tx.GetHash())
+
+ # Try again, but with more total transactions than the "max txs
+ # double-spent at once" anti-DoS limit.
+ for n in (MAX_REPLACEMENT_LIMIT, MAX_REPLACEMENT_LIMIT*2):
+ fee = 0.0001*COIN
+ tx0_outpoint = self.make_txout(initial_nValue)
+ tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
+ self.assertEqual(len(tree_txs), n)
+
+ dbl_tx = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(initial_nValue - fee*n, CScript([1]))])
+ try:
+ self.proxy.sendrawtransaction(dbl_tx, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26)
+ else:
+ self.fail()
+
+ for tx in tree_txs:
+ self.proxy.getrawtransaction(tx.GetHash())
+
+ def test_replacement_feeperkb(self):
+ """Replacement requires fee-per-KB to be higher"""
+ tx0_outpoint = self.make_txout(1.1*COIN)
+
+ tx1a = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(1*COIN, CScript([b'a']))])
+ tx1a_txid = self.proxy.sendrawtransaction(tx1a, True)
+
+ # Higher fee, but the fee per KB is much lower, so the replacement is
+ # rejected.
+ tx1b = CTransaction([CTxIn(tx0_outpoint, nSequence=0)],
+ [CTxOut(0.001*COIN,
+ CScript([b'a'*999000]))])
+
+ try:
+ tx1b_txid = self.proxy.sendrawtransaction(tx1b, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26) # insufficient fee
+ else:
+ self.fail()
+
+ def test_spends_of_conflicting_outputs(self):
+ """Replacements that spend conflicting tx outputs are rejected"""
+ utxo1 = self.make_txout(1.2*COIN)
+ utxo2 = self.make_txout(3.0*COIN)
+
+ tx1a = CTransaction([CTxIn(utxo1, nSequence=0)],
+ [CTxOut(1.1*COIN, CScript([b'a']))])
+ tx1a_txid = self.proxy.sendrawtransaction(tx1a, True)
+
+ # Direct spend an output of the transaction we're replacing.
+ tx2 = CTransaction([CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0),
+ CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)],
+ tx1a.vout)
+
+ try:
+ tx2_txid = self.proxy.sendrawtransaction(tx2, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26)
+ else:
+ self.fail()
+
+ # Spend tx1a's output to test the indirect case.
+ tx1b = CTransaction([CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)],
+ [CTxOut(1.0*COIN, CScript([b'a']))])
+ tx1b_txid = self.proxy.sendrawtransaction(tx1b, True)
+
+ tx2 = CTransaction([CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0),
+ CTxIn(COutPoint(tx1b_txid, 0))],
+ tx1a.vout)
+
+ try:
+ tx2_txid = self.proxy.sendrawtransaction(tx2, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26)
+ else:
+ self.fail()
+
+ def test_new_unconfirmed_inputs(self):
+ """Replacements that add new unconfirmed inputs are rejected"""
+ confirmed_utxo = self.make_txout(1.1*COIN)
+ unconfirmed_utxo = self.make_txout(0.1*COIN, False)
+
+ tx1 = CTransaction([CTxIn(confirmed_utxo)],
+ [CTxOut(1.0*COIN, CScript([b'a']))])
+ tx1_txid = self.proxy.sendrawtransaction(tx1, True)
+
+ tx2 = CTransaction([CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)],
+ tx1.vout)
+
+ try:
+ tx2_txid = self.proxy.sendrawtransaction(tx2, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26)
+ else:
+ self.fail()
+
+ def test_too_many_replacements(self):
+ """Replacements that evict too many transactions are rejected"""
+ # Try directly replacing more than MAX_REPLACEMENT_LIMIT
+ # transactions
+
+ # Start by creating a single transaction with many outputs
+ initial_nValue = 10*COIN
+ utxo = self.make_txout(initial_nValue)
+ fee = 0.0001*COIN
+ split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1))
+ actual_fee = initial_nValue - split_value*(MAX_REPLACEMENT_LIMIT+1)
+
+ outputs = []
+ for i in range(MAX_REPLACEMENT_LIMIT+1):
+ outputs.append(CTxOut(split_value, CScript([1])))
+
+ splitting_tx = CTransaction([CTxIn(utxo, nSequence=0)], outputs)
+ txid = self.proxy.sendrawtransaction(splitting_tx, True)
+
+ # Now spend each of those outputs individually
+ for i in range(MAX_REPLACEMENT_LIMIT+1):
+ tx_i = CTransaction([CTxIn(COutPoint(txid, i), nSequence=0)],
+ [CTxOut(split_value-fee, CScript([b'a']))])
+ self.proxy.sendrawtransaction(tx_i, True)
+
+ # Now create doublespend of the whole lot, should fail
+ # Need a big enough fee to cover all spending transactions and have
+ # a higher fee rate
+ double_spend_value = (split_value-100*fee)*(MAX_REPLACEMENT_LIMIT+1)
+ inputs = []
+ for i in range(MAX_REPLACEMENT_LIMIT+1):
+ inputs.append(CTxIn(COutPoint(txid, i), nSequence=0))
+ double_tx = CTransaction(inputs, [CTxOut(double_spend_value, CScript([b'a']))])
+
+ try:
+ self.proxy.sendrawtransaction(double_tx, True)
+ except bitcoin.rpc.JSONRPCException as exp:
+ self.assertEqual(exp.error['code'], -26)
+ self.assertEqual("too many potential replacements" in exp.error['message'], True)
+ else:
+ self.fail()
+
+ # If we remove an input, it should pass
+ double_tx = CTransaction(inputs[0:-1],
+ [CTxOut(double_spend_value, CScript([b'a']))])
+
+ self.proxy.sendrawtransaction(double_tx, True)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py
index 5a67220021..92d91e029a 100755
--- a/qa/rpc-tests/keypool.py
+++ b/qa/rpc-tests/keypool.py
@@ -6,15 +6,8 @@
# Exercise the wallet keypool, and interaction with wallet encryption/locking
# Add python-bitcoinrpc to module search path:
-import os
-import sys
-
-import json
-import shutil
-import subprocess
-import tempfile
-import traceback
+from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
@@ -39,107 +32,65 @@ def check_array_result(object_array, to_match, expected):
if num_matched == 0:
raise AssertionError("No objects matched %s"%(str(to_match)))
-def run_test(nodes, tmpdir):
- # Encrypt wallet and wait to terminate
- nodes[0].encryptwallet('test')
- bitcoind_processes[0].wait()
- # Restart node 0
- nodes[0] = start_node(0, tmpdir)
- # Keep creating keys
- addr = nodes[0].getnewaddress()
- try:
- addr = nodes[0].getnewaddress()
- raise AssertionError('Keypool should be exhausted after one address')
- except JSONRPCException,e:
- assert(e.error['code']==-12)
-
- # put three new keys in the keypool
- nodes[0].walletpassphrase('test', 12000)
- nodes[0].keypoolrefill(3)
- nodes[0].walletlock()
-
- # drain the keys
- addr = set()
- addr.add(nodes[0].getrawchangeaddress())
- addr.add(nodes[0].getrawchangeaddress())
- addr.add(nodes[0].getrawchangeaddress())
- addr.add(nodes[0].getrawchangeaddress())
- # assert that four unique addresses were returned
- assert(len(addr) == 4)
- # the next one should fail
- try:
- addr = nodes[0].getrawchangeaddress()
- raise AssertionError('Keypool should be exhausted after three addresses')
- except JSONRPCException,e:
- assert(e.error['code']==-12)
-
- # refill keypool with three new addresses
- nodes[0].walletpassphrase('test', 12000)
- nodes[0].keypoolrefill(3)
- nodes[0].walletlock()
+class KeyPoolTest(BitcoinTestFramework):
- # drain them by mining
- nodes[0].generate(1)
- nodes[0].generate(1)
- nodes[0].generate(1)
- nodes[0].generate(1)
- try:
+ def run_test(self):
+ nodes = self.nodes
+ # Encrypt wallet and wait to terminate
+ nodes[0].encryptwallet('test')
+ bitcoind_processes[0].wait()
+ # Restart node 0
+ nodes[0] = start_node(0, self.options.tmpdir)
+ # Keep creating keys
+ addr = nodes[0].getnewaddress()
+ try:
+ addr = nodes[0].getnewaddress()
+ raise AssertionError('Keypool should be exhausted after one address')
+ except JSONRPCException,e:
+ assert(e.error['code']==-12)
+
+ # put three new keys in the keypool
+ nodes[0].walletpassphrase('test', 12000)
+ nodes[0].keypoolrefill(3)
+ nodes[0].walletlock()
+
+ # drain the keys
+ addr = set()
+ addr.add(nodes[0].getrawchangeaddress())
+ addr.add(nodes[0].getrawchangeaddress())
+ addr.add(nodes[0].getrawchangeaddress())
+ addr.add(nodes[0].getrawchangeaddress())
+ # assert that four unique addresses were returned
+ assert(len(addr) == 4)
+ # the next one should fail
+ try:
+ addr = nodes[0].getrawchangeaddress()
+ raise AssertionError('Keypool should be exhausted after three addresses')
+ except JSONRPCException,e:
+ assert(e.error['code']==-12)
+
+ # refill keypool with three new addresses
+ nodes[0].walletpassphrase('test', 12000)
+ nodes[0].keypoolrefill(3)
+ nodes[0].walletlock()
+
+ # drain them by mining
nodes[0].generate(1)
- raise AssertionError('Keypool should be exhausted after three addesses')
- except JSONRPCException,e:
- assert(e.error['code']==-12)
-
-def main():
- import optparse
-
- parser = optparse.OptionParser(usage="%prog [options]")
- parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
- help="Leave bitcoinds and test.* datadir on exit or error")
- parser.add_option("--srcdir", dest="srcdir", default="../../src",
- help="Source directory containing bitcoind/bitcoin-cli (default: %default%)")
- parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
- help="Root directory for datadirs")
- (options, args) = parser.parse_args()
-
- os.environ['PATH'] = options.srcdir+":"+os.environ['PATH']
-
- check_json_precision()
-
- success = False
- nodes = []
- try:
- print("Initializing test directory "+options.tmpdir)
- if not os.path.isdir(options.tmpdir):
- os.makedirs(options.tmpdir)
- initialize_chain(options.tmpdir)
-
- nodes = start_nodes(1, options.tmpdir)
-
- run_test(nodes, options.tmpdir)
-
- success = True
-
- except AssertionError as e:
- print("Assertion failed: "+e.message)
- except JSONRPCException as e:
- print("JSONRPC error: "+e.error['message'])
- traceback.print_tb(sys.exc_info()[2])
- except Exception as e:
- print("Unexpected exception caught during testing: "+str(sys.exc_info()[0]))
- traceback.print_tb(sys.exc_info()[2])
+ nodes[0].generate(1)
+ nodes[0].generate(1)
+ nodes[0].generate(1)
+ try:
+ nodes[0].generate(1)
+ raise AssertionError('Keypool should be exhausted after three addesses')
+ except JSONRPCException,e:
+ assert(e.error['code']==-12)
- if not options.nocleanup:
- print("Cleaning up")
- stop_nodes(nodes)
- wait_bitcoinds()
- shutil.rmtree(options.tmpdir)
+ def setup_chain(self):
+ print("Initializing test directory "+self.options.tmpdir)
+ initialize_chain(self.options.tmpdir)
- if success:
- print("Tests successful")
- sys.exit(0)
- else:
- print("Failed")
- sys.exit(1)
+ def setup_network(self):
+ self.nodes = start_nodes(1, self.options.tmpdir)
if __name__ == '__main__':
- main()
+ KeyPoolTest().main()
diff --git a/qa/rpc-tests/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py
new file mode 100755
index 0000000000..537a1ed8d9
--- /dev/null
+++ b/qa/rpc-tests/replace-by-fee.py
@@ -0,0 +1,512 @@
+#!/usr/bin/env python2
+# Copyright (c) 2014-2015 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#
+# Test replace by fee code
+#
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+from test_framework.script import *
+from test_framework.mininode import *
+import binascii
+
+COIN = 100000000
+MAX_REPLACEMENT_LIMIT = 100
+
+def satoshi_round(amount):
+ return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
+
+def txToHex(tx):
+ return binascii.hexlify(tx.serialize()).decode('utf-8')
+
+def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
+ """Create a txout with a given amount and scriptPubKey
+
+ Mines coins as needed.
+
+ confirmed - txouts created will be confirmed in the blockchain;
+ unconfirmed otherwise.
+ """
+ fee = 1*COIN
+ while node.getbalance() < satoshi_round((amount + fee)/COIN):
+ node.generate(100)
+ #print (node.getbalance(), amount, fee)
+
+ new_addr = node.getnewaddress()
+ #print new_addr
+ txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN))
+ tx1 = node.getrawtransaction(txid, 1)
+ txid = int(txid, 16)
+ i = None
+
+ for i, txout in enumerate(tx1['vout']):
+ #print i, txout['scriptPubKey']['addresses']
+ if txout['scriptPubKey']['addresses'] == [new_addr]:
+ #print i
+ break
+ assert i is not None
+
+ tx2 = CTransaction()
+ tx2.vin = [CTxIn(COutPoint(txid, i))]
+ tx2.vout = [CTxOut(amount, scriptPubKey)]
+ tx2.rehash()
+
+ tx2_hex = binascii.hexlify(tx2.serialize()).decode('utf-8')
+ #print tx2_hex
+
+ signed_tx = node.signrawtransaction(binascii.hexlify(tx2.serialize()).decode('utf-8'))
+
+ txid = node.sendrawtransaction(signed_tx['hex'], True)
+
+ # If requested, ensure txouts are confirmed.
+ if confirmed:
+ while len(node.getrawmempool()):
+ node.generate(1)
+
+ return COutPoint(int(txid, 16), 0)
+
+class ReplaceByFeeTest(BitcoinTestFramework):
+
+ def setup_network(self):
+ self.nodes = []
+ self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000",
+ "-relaypriority=0", "-whitelist=127.0.0.1"]))
+ self.is_network_split = False
+
+ def run_test(self):
+ make_utxo(self.nodes[0], 1*COIN)
+
+ print "Running test simple doublespend..."
+ self.test_simple_doublespend()
+
+ print "Running test doublespend chain..."
+ self.test_doublespend_chain()
+
+ print "Running test doublespend tree..."
+ self.test_doublespend_tree()
+
+ print "Running test replacement feeperkb..."
+ self.test_replacement_feeperkb()
+
+ print "Running test spends of conflicting outputs..."
+ self.test_spends_of_conflicting_outputs()
+
+ print "Running test new unconfirmed inputs..."
+ self.test_new_unconfirmed_inputs()
+
+ print "Running test too many replacements..."
+ self.test_too_many_replacements()
+
+ print "Running test opt-in..."
+ self.test_opt_in()
+
+ print "Passed\n"
+
+ def test_simple_doublespend(self):
+ """Simple doublespend"""
+ tx0_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
+
+ tx1a = CTransaction()
+ tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a_hex = txToHex(tx1a)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+
+ # Should fail because we haven't changed the fee
+ tx1b = CTransaction()
+ tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1b.vout = [CTxOut(1*COIN, CScript([b'b']))]
+ tx1b_hex = txToHex(tx1b)
+
+ try:
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26) # insufficient fee
+ else:
+ assert(False)
+
+ # Extra 0.1 BTC fee
+ tx1b = CTransaction()
+ tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1b.vout = [CTxOut(0.9*COIN, CScript([b'b']))]
+ tx1b_hex = txToHex(tx1b)
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+
+ mempool = self.nodes[0].getrawmempool()
+
+ assert (tx1a_txid not in mempool)
+ assert (tx1b_txid in mempool)
+
+ assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid))
+
+ def test_doublespend_chain(self):
+ """Doublespend of a long chain"""
+
+ initial_nValue = 50*COIN
+ tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
+
+ prevout = tx0_outpoint
+ remaining_value = initial_nValue
+ chain_txids = []
+ while remaining_value > 10*COIN:
+ remaining_value -= 1*COIN
+ tx = CTransaction()
+ tx.vin = [CTxIn(prevout, nSequence=0)]
+ tx.vout = [CTxOut(remaining_value, CScript([1]))]
+ tx_hex = txToHex(tx)
+ txid = self.nodes[0].sendrawtransaction(tx_hex, True)
+ chain_txids.append(txid)
+ prevout = COutPoint(int(txid, 16), 0)
+
+ # Whether the double-spend is allowed is evaluated by including all
+ # child fees - 40 BTC - so this attempt is rejected.
+ dbl_tx = CTransaction()
+ dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1]))]
+ dbl_tx_hex = txToHex(dbl_tx)
+
+ try:
+ self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26) # insufficient fee
+ else:
+ assert(False) # transaction mistakenly accepted!
+
+ # Accepted with sufficient fee
+ dbl_tx = CTransaction()
+ dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ dbl_tx.vout = [CTxOut(1*COIN, CScript([1]))]
+ dbl_tx_hex = txToHex(dbl_tx)
+ self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
+
+ mempool = self.nodes[0].getrawmempool()
+ for doublespent_txid in chain_txids:
+ assert(doublespent_txid not in mempool)
+
+ def test_doublespend_tree(self):
+ """Doublespend of a big tree of transactions"""
+
+ initial_nValue = 50*COIN
+ tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
+
+ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _total_txs=None):
+ if _total_txs is None:
+ _total_txs = [0]
+ if _total_txs[0] >= max_txs:
+ return
+
+ txout_value = (initial_value - fee) // tree_width
+ if txout_value < fee:
+ return
+
+ vout = [CTxOut(txout_value, CScript([i+1]))
+ for i in range(tree_width)]
+ tx = CTransaction()
+ tx.vin = [CTxIn(prevout, nSequence=0)]
+ tx.vout = vout
+ tx_hex = txToHex(tx)
+
+ assert(len(tx.serialize()) < 100000)
+ txid = self.nodes[0].sendrawtransaction(tx_hex, True)
+ yield tx
+ _total_txs[0] += 1
+
+ txid = int(txid, 16)
+
+ for i, txout in enumerate(tx.vout):
+ for x in branch(COutPoint(txid, i), txout_value,
+ max_txs,
+ tree_width=tree_width, fee=fee,
+ _total_txs=_total_txs):
+ yield x
+
+ fee = 0.0001*COIN
+ n = MAX_REPLACEMENT_LIMIT
+ tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
+ assert_equal(len(tree_txs), n)
+
+ # Attempt double-spend, will fail because too little fee paid
+ dbl_tx = CTransaction()
+ dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))]
+ dbl_tx_hex = txToHex(dbl_tx)
+ try:
+ self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26) # insufficient fee
+ else:
+ assert(False)
+
+ # 1 BTC fee is enough
+ dbl_tx = CTransaction()
+ dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))]
+ dbl_tx_hex = txToHex(dbl_tx)
+ self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
+
+ mempool = self.nodes[0].getrawmempool()
+
+ for tx in tree_txs:
+ tx.rehash()
+ assert (tx.hash not in mempool)
+
+ # Try again, but with more total transactions than the "max txs
+ # double-spent at once" anti-DoS limit.
+ for n in (MAX_REPLACEMENT_LIMIT+1, MAX_REPLACEMENT_LIMIT*2):
+ fee = 0.0001*COIN
+ tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
+ tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
+ assert_equal(len(tree_txs), n)
+
+ dbl_tx = CTransaction()
+ dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))]
+ dbl_tx_hex = txToHex(dbl_tx)
+ try:
+ self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ assert_equal("too many potential replacements" in exp.error['message'], True)
+ else:
+ assert(False)
+
+ for tx in tree_txs:
+ tx.rehash()
+ self.nodes[0].getrawtransaction(tx.hash)
+
+ def test_replacement_feeperkb(self):
+ """Replacement requires fee-per-KB to be higher"""
+ tx0_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
+
+ tx1a = CTransaction()
+ tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a_hex = txToHex(tx1a)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+
+ # Higher fee, but the fee per KB is much lower, so the replacement is
+ # rejected.
+ tx1b = CTransaction()
+ tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1b.vout = [CTxOut(0.001*COIN, CScript([b'a'*999000]))]
+ tx1b_hex = txToHex(tx1b)
+
+ try:
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26) # insufficient fee
+ else:
+ assert(False)
+
+ def test_spends_of_conflicting_outputs(self):
+ """Replacements that spend conflicting tx outputs are rejected"""
+ utxo1 = make_utxo(self.nodes[0], 1.2*COIN)
+ utxo2 = make_utxo(self.nodes[0], 3.0*COIN)
+
+ tx1a = CTransaction()
+ tx1a.vin = [CTxIn(utxo1, nSequence=0)]
+ tx1a.vout = [CTxOut(1.1*COIN, CScript([b'a']))]
+ tx1a_hex = txToHex(tx1a)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+
+ tx1a_txid = int(tx1a_txid, 16)
+
+ # Direct spend an output of the transaction we're replacing.
+ tx2 = CTransaction()
+ tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0)]
+ tx2.vin.append(CTxIn(COutPoint(tx1a_txid, 0), nSequence=0))
+ tx2.vout = tx1a.vout
+ tx2_hex = txToHex(tx2)
+
+ try:
+ tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ else:
+ assert(False)
+
+ # Spend tx1a's output to test the indirect case.
+ tx1b = CTransaction()
+ tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
+ tx1b.vout = [CTxOut(1.0*COIN, CScript([b'a']))]
+ tx1b_hex = txToHex(tx1b)
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+ tx1b_txid = int(tx1b_txid, 16)
+
+ tx2 = CTransaction()
+ tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0),
+ CTxIn(COutPoint(tx1b_txid, 0))]
+ tx2.vout = tx1a.vout
+ tx2_hex = txToHex(tx2)
+
+ try:
+ tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ else:
+ assert(False)
+
+ def test_new_unconfirmed_inputs(self):
+ """Replacements that add new unconfirmed inputs are rejected"""
+ confirmed_utxo = make_utxo(self.nodes[0], 1.1*COIN)
+ unconfirmed_utxo = make_utxo(self.nodes[0], 0.1*COIN, False)
+
+ tx1 = CTransaction()
+ tx1.vin = [CTxIn(confirmed_utxo)]
+ tx1.vout = [CTxOut(1.0*COIN, CScript([b'a']))]
+ tx1_hex = txToHex(tx1)
+ tx1_txid = self.nodes[0].sendrawtransaction(tx1_hex, True)
+
+ tx2 = CTransaction()
+ tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)]
+ tx2.vout = tx1.vout
+ tx2_hex = txToHex(tx2)
+
+ try:
+ tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ else:
+ assert(False)
+
+ def test_too_many_replacements(self):
+ """Replacements that evict too many transactions are rejected"""
+ # Try directly replacing more than MAX_REPLACEMENT_LIMIT
+ # transactions
+
+ # Start by creating a single transaction with many outputs
+ initial_nValue = 10*COIN
+ utxo = make_utxo(self.nodes[0], initial_nValue)
+ fee = 0.0001*COIN
+ split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1))
+ actual_fee = initial_nValue - split_value*(MAX_REPLACEMENT_LIMIT+1)
+
+ outputs = []
+ for i in range(MAX_REPLACEMENT_LIMIT+1):
+ outputs.append(CTxOut(split_value, CScript([1])))
+
+ splitting_tx = CTransaction()
+ splitting_tx.vin = [CTxIn(utxo, nSequence=0)]
+ splitting_tx.vout = outputs
+ splitting_tx_hex = txToHex(splitting_tx)
+
+ txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, True)
+ txid = int(txid, 16)
+
+ # Now spend each of those outputs individually
+ for i in range(MAX_REPLACEMENT_LIMIT+1):
+ tx_i = CTransaction()
+ tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)]
+ tx_i.vout = [CTxOut(split_value-fee, CScript([b'a']))]
+ tx_i_hex = txToHex(tx_i)
+ self.nodes[0].sendrawtransaction(tx_i_hex, True)
+
+ # Now create doublespend of the whole lot; should fail.
+ # Need a big enough fee to cover all spending transactions and have
+ # a higher fee rate
+ double_spend_value = (split_value-100*fee)*(MAX_REPLACEMENT_LIMIT+1)
+ inputs = []
+ for i in range(MAX_REPLACEMENT_LIMIT+1):
+ inputs.append(CTxIn(COutPoint(txid, i), nSequence=0))
+ double_tx = CTransaction()
+ double_tx.vin = inputs
+ double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))]
+ double_tx_hex = txToHex(double_tx)
+
+ try:
+ self.nodes[0].sendrawtransaction(double_tx_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ assert_equal("too many potential replacements" in exp.error['message'], True)
+ else:
+ assert(False)
+
+ # If we remove an input, it should pass
+ double_tx = CTransaction()
+ double_tx.vin = inputs[0:-1]
+ double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))]
+ double_tx_hex = txToHex(double_tx)
+ self.nodes[0].sendrawtransaction(double_tx_hex, True)
+
+ def test_opt_in(self):
+ """ Replacing should only work if orig tx opted in """
+ tx0_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
+
+ # Create a non-opting in transaction
+ tx1a = CTransaction()
+ tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)]
+ tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx1a_hex = txToHex(tx1a)
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
+
+ # Shouldn't be able to double-spend
+ tx1b = CTransaction()
+ tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
+ tx1b.vout = [CTxOut(0.9*COIN, CScript([b'b']))]
+ tx1b_hex = txToHex(tx1b)
+
+ try:
+ tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ else:
+ print tx1b_txid
+ assert(False)
+
+ tx1_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
+
+ # Create a different non-opting in transaction
+ tx2a = CTransaction()
+ tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)]
+ tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))]
+ tx2a_hex = txToHex(tx2a)
+ tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True)
+
+ # Still shouldn't be able to double-spend
+ tx2b = CTransaction()
+ tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
+ tx2b.vout = [CTxOut(0.9*COIN, CScript([b'b']))]
+ tx2b_hex = txToHex(tx2b)
+
+ try:
+ tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
+ except JSONRPCException as exp:
+ assert_equal(exp.error['code'], -26)
+ else:
+ assert(False)
+
+ # Now create a new transaction that spends from tx1a and tx2a
+ # opt-in on one of the inputs
+ # Transaction should be replaceable on either input
+
+ tx1a_txid = int(tx1a_txid, 16)
+ tx2a_txid = int(tx2a_txid, 16)
+
+ tx3a = CTransaction()
+ tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff),
+ CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd)]
+ tx3a.vout = [CTxOut(0.9*COIN, CScript([b'c'])), CTxOut(0.9*COIN, CScript([b'd']))]
+ tx3a_hex = txToHex(tx3a)
+
+ self.nodes[0].sendrawtransaction(tx3a_hex, True)
+
+ tx3b = CTransaction()
+ tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
+ tx3b.vout = [CTxOut(0.5*COIN, CScript([b'e']))]
+ tx3b_hex = txToHex(tx3b)
+
+ tx3c = CTransaction()
+ tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)]
+ tx3c.vout = [CTxOut(0.5*COIN, CScript([b'f']))]
+ tx3c_hex = txToHex(tx3c)
+
+ self.nodes[0].sendrawtransaction(tx3b_hex, True)
+ # If tx3b was accepted, tx3c won't look like a replacement,
+ # but make sure it is accepted anyway
+ self.nodes[0].sendrawtransaction(tx3c_hex, True)
+
+if __name__ == '__main__':
+ ReplaceByFeeTest().main()
diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py
new file mode 100755
index 0000000000..d7f4292090
--- /dev/null
+++ b/qa/rpc-tests/sendheaders.py
@@ -0,0 +1,519 @@
+#!/usr/bin/env python2
+#
+# Distributed under the MIT/X11 software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+
+from test_framework.mininode import *
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+import time
+from test_framework.blocktools import create_block, create_coinbase
+
+'''
+SendHeadersTest -- test behavior of headers messages to announce blocks.
+
+Setup:
+
+- Two nodes, two p2p connections to node0. One p2p connection should only ever
+ receive inv's (omitted from testing description below, this is our control).
+ Second node is used for creating reorgs.
+
+Part 1: No headers announcements before "sendheaders"
+a. node mines a block [expect: inv]
+ send getdata for the block [expect: block]
+b. node mines another block [expect: inv]
+ send getheaders and getdata [expect: headers, then block]
+c. node mines another block [expect: inv]
+ peer mines a block, announces with header [expect: getdata]
+d. node mines another block [expect: inv]
+
+Part 2: After "sendheaders", headers announcements should generally work.
+a. peer sends sendheaders [expect: no response]
+ peer sends getheaders with current tip [expect: no response]
+b. node mines a block [expect: tip header]
+c. for N in 1, ..., 10:
+ * for announce-type in {inv, header}
+ - peer mines N blocks, announces with announce-type
+ [ expect: getheaders/getdata or getdata, deliver block(s) ]
+ - node mines a block [ expect: 1 header ]
+
+Part 3: Headers announcements stop after large reorg and resume after getheaders or inv from peer.
+- For response-type in {inv, getheaders}
+ * node mines a 7 block reorg [ expect: headers announcement of 8 blocks ]
+ * node mines an 8-block reorg [ expect: inv at tip ]
+ * peer responds with getblocks/getdata [expect: inv, blocks ]
+ * node mines another block [ expect: inv at tip, peer sends getdata, expect: block ]
+ * node mines another block at tip [ expect: inv ]
+ * peer responds with getheaders with an old hashstop more than 8 blocks back [expect: headers]
+ * peer requests block [ expect: block ]
+ * node mines another block at tip [ expect: inv, peer sends getdata, expect: block ]
+ * peer sends response-type [expect headers if getheaders, getheaders/getdata if mining new block]
+ * node mines 1 block [expect: 1 header, peer responds with getdata]
+
+Part 4: Test direct fetch behavior
+a. Announce 2 old block headers.
+ Expect: no getdata requests.
+b. Announce 3 new blocks via 1 headers message.
+ Expect: one getdata request for all 3 blocks.
+ (Send blocks.)
+c. Announce 1 header that forks off the last two blocks.
+ Expect: no response.
+d. Announce 1 more header that builds on that fork.
+ Expect: one getdata request for two blocks.
+e. Announce 16 more headers that build on that fork.
+ Expect: getdata request for 14 more blocks.
+f. Announce 1 more header that builds on that fork.
+ Expect: no response.
+'''
+
+class BaseNode(NodeConnCB):
+ def __init__(self):
+ NodeConnCB.__init__(self)
+ self.create_callback_map()
+ self.connection = None
+ self.last_inv = None
+ self.last_headers = None
+ self.last_block = None
+ self.ping_counter = 1
+ self.last_pong = msg_pong(0)
+ self.last_getdata = None
+ self.sleep_time = 0.05
+ self.block_announced = False
+
+ def clear_last_announcement(self):
+ with mininode_lock:
+ self.block_announced = False
+ self.last_inv = None
+ self.last_headers = None
+
+ def add_connection(self, conn):
+ self.connection = conn
+
+ # Request data for a list of block hashes
+ def get_data(self, block_hashes):
+ msg = msg_getdata()
+ for x in block_hashes:
+ msg.inv.append(CInv(2, x))
+ self.connection.send_message(msg)
+
+ def get_headers(self, locator, hashstop):
+ msg = msg_getheaders()
+ msg.locator.vHave = locator
+ msg.hashstop = hashstop
+ self.connection.send_message(msg)
+
+ def send_block_inv(self, blockhash):
+ msg = msg_inv()
+ msg.inv = [CInv(2, blockhash)]
+ self.connection.send_message(msg)
+
+ # Wrapper for the NodeConn's send_message function
+ def send_message(self, message):
+ self.connection.send_message(message)
+
+ def on_inv(self, conn, message):
+ self.last_inv = message
+ self.block_announced = True
+
+ def on_headers(self, conn, message):
+ self.last_headers = message
+ self.block_announced = True
+
+ def on_block(self, conn, message):
+ self.last_block = message.block
+ self.last_block.calc_sha256()
+
+ def on_getdata(self, conn, message):
+ self.last_getdata = message
+
+ def on_pong(self, conn, message):
+ self.last_pong = message
+
+ # Test whether the last announcement we received had the
+ # right header or the right inv
+ # inv and headers should be lists of block hashes
+ def check_last_announcement(self, headers=None, inv=None):
+ expect_headers = headers if headers != None else []
+ expect_inv = inv if inv != None else []
+ test_function = lambda: self.block_announced
+ self.sync(test_function)
+ with mininode_lock:
+ self.block_announced = False
+
+ success = True
+ compare_inv = []
+ if self.last_inv != None:
+ compare_inv = [x.hash for x in self.last_inv.inv]
+ if compare_inv != expect_inv:
+ success = False
+
+ hash_headers = []
+ if self.last_headers != None:
+ # treat headers as a list of block hashes
+ hash_headers = [ x.sha256 for x in self.last_headers.headers ]
+ if hash_headers != expect_headers:
+ success = False
+
+ self.last_inv = None
+ self.last_headers = None
+ return success
+
+ # Syncing helpers
+ def sync(self, test_function, timeout=60):
+ while timeout > 0:
+ with mininode_lock:
+ if test_function():
+ return
+ time.sleep(self.sleep_time)
+ timeout -= self.sleep_time
+ raise AssertionError("Sync failed to complete")
+
+ def sync_with_ping(self, timeout=60):
+ self.send_message(msg_ping(nonce=self.ping_counter))
+ test_function = lambda: self.last_pong.nonce == self.ping_counter
+ self.sync(test_function, timeout)
+ self.ping_counter += 1
+ return
+
+ def wait_for_block(self, blockhash, timeout=60):
+ test_function = lambda: self.last_block != None and self.last_block.sha256 == blockhash
+ self.sync(test_function, timeout)
+ return
+
+ def wait_for_getdata(self, hash_list, timeout=60):
+ if hash_list == []:
+ return
+
+ test_function = lambda: self.last_getdata != None and [x.hash for x in self.last_getdata.inv] == hash_list
+ self.sync(test_function, timeout)
+ return
+
+ def send_header_for_blocks(self, new_blocks):
+ headers_message = msg_headers()
+ headers_message.headers = [ CBlockHeader(b) for b in new_blocks ]
+ self.send_message(headers_message)
+
+ def send_getblocks(self, locator):
+ getblocks_message = msg_getblocks()
+ getblocks_message.locator.vHave = locator
+ self.send_message(getblocks_message)
+
+# InvNode: This peer should only ever receive inv's, because it doesn't ever send a
+# "sendheaders" message.
+class InvNode(BaseNode):
+ def __init__(self):
+ BaseNode.__init__(self)
+
+# TestNode: This peer is the one we use for most of the testing.
+class TestNode(BaseNode):
+ def __init__(self):
+ BaseNode.__init__(self)
+
+class SendHeadersTest(BitcoinTestFramework):
+ def setup_chain(self):
+ initialize_chain_clean(self.options.tmpdir, 2)
+
+ def setup_network(self):
+ self.nodes = []
+ self.nodes = start_nodes(2, self.options.tmpdir, [["-debug", "-logtimemicros=1"]]*2)
+ connect_nodes(self.nodes[0], 1)
+
+ # mine count blocks and return the new tip
+ def mine_blocks(self, count):
+ 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
+ # 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):
+ 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]
+
+ tip_height = self.nodes[1].getblockcount()
+ hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1))
+ self.nodes[1].invalidateblock(hash_to_invalidate)
+ all_hashes = self.nodes[1].generate(length+1) # Must be longer than the orig chain
+ sync_blocks(self.nodes, wait=0.1)
+ return [int(x, 16) for x in all_hashes]
+
+ def run_test(self):
+ # Setup the p2p connections and start up the network thread.
+ inv_node = InvNode()
+ test_node = TestNode()
+
+ 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
+ # direct fetching
+ connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0))
+ inv_node.add_connection(connections[0])
+ test_node.add_connection(connections[1])
+
+ NetworkThread().start() # Start up network handling in another thread
+
+ # Test logic begins here
+ inv_node.wait_for_verack()
+ test_node.wait_for_verack()
+
+ tip = int(self.nodes[0].getbestblockhash(), 16)
+
+ # PART 1
+ # 1. Mine a block; expect inv announcements each time
+ print "Part 1: headers don't start before sendheaders message..."
+ for i in xrange(4):
+ old_tip = tip
+ tip = self.mine_blocks(1)
+ assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
+ assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ # Try a few different responses; none should affect next announcement
+ if i == 0:
+ # first request the block
+ test_node.get_data([tip])
+ test_node.wait_for_block(tip, timeout=5)
+ elif i == 1:
+ # next try requesting header and block
+ test_node.get_headers(locator=[old_tip], hashstop=tip)
+ test_node.get_data([tip])
+ test_node.wait_for_block(tip)
+ test_node.clear_last_announcement() # since we requested headers...
+ elif i == 2:
+ # this time announce own block via headers
+ height = self.nodes[0].getblockcount()
+ last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time']
+ block_time = last_time + 1
+ new_block = create_block(tip, create_coinbase(height+1), block_time)
+ new_block.solve()
+ test_node.send_header_for_blocks([new_block])
+ test_node.wait_for_getdata([new_block.sha256], timeout=5)
+ test_node.send_message(msg_block(new_block))
+ test_node.sync_with_ping() # make sure this block is processed
+ inv_node.clear_last_announcement()
+ test_node.clear_last_announcement()
+
+ print "Part 1: success!"
+ print "Part 2: announce blocks with headers after sendheaders message..."
+ # PART 2
+ # 2. Send a sendheaders message and test that headers announcements
+ # commence and keep working.
+ test_node.send_message(msg_sendheaders())
+ 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)
+ assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
+ assert_equal(test_node.check_last_announcement(headers=[tip]), True)
+
+ height = self.nodes[0].getblockcount()+1
+ block_time += 10 # Advance far enough ahead
+ for i in xrange(10):
+ # Mine i blocks, and alternate announcing either via
+ # inv (of tip) or via headers. After each, new blocks
+ # mined by the node should successfully be announced
+ # with block header, even though the blocks are never requested
+ for j in xrange(2):
+ blocks = []
+ for b in xrange(i+1):
+ blocks.append(create_block(tip, create_coinbase(height), block_time))
+ blocks[-1].solve()
+ tip = blocks[-1].sha256
+ block_time += 1
+ height += 1
+ if j == 0:
+ # Announce via inv
+ test_node.send_block_inv(tip)
+ test_node.wait_for_getdata([tip], timeout=5)
+ # Test that duplicate inv's won't result in duplicate
+ # getdata requests, or duplicate headers announcements
+ inv_node.send_block_inv(tip)
+ # Should have received a getheaders as well!
+ test_node.send_header_for_blocks(blocks)
+ test_node.wait_for_getdata([x.sha256 for x in blocks[0:-1]], timeout=5)
+ [ inv_node.send_block_inv(x.sha256) for x in blocks[0:-1] ]
+ inv_node.sync_with_ping()
+ else:
+ # Announce via headers
+ test_node.send_header_for_blocks(blocks)
+ test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5)
+ # Test that duplicate headers won't result in duplicate
+ # getdata requests (the check is further down)
+ inv_node.send_header_for_blocks(blocks)
+ inv_node.sync_with_ping()
+ [ test_node.send_message(msg_block(x)) for x in blocks ]
+ test_node.sync_with_ping()
+ inv_node.sync_with_ping()
+ # This block should not be announced to the inv node (since it also
+ # 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)
+ height += 1
+ block_time += 1
+
+ print "Part 2: success!"
+
+ print "Part 3: headers announcements can stop after large reorg, and resume after headers/inv from peer..."
+
+ # PART 3. Headers announcements can stop after large reorg, and resume after
+ # 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])
+ 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)
+
+ 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])
+ 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)
+
+ block_time += 9
+
+ fork_point = self.nodes[0].getblock("%02x" % new_block_hashes[0])["previousblockhash"]
+ fork_point = int(fork_point, 16)
+
+ # Use getblocks/getdata
+ test_node.send_getblocks(locator = [fork_point])
+ assert_equal(test_node.check_last_announcement(inv=new_block_hashes[0:-1]), True)
+ test_node.get_data(new_block_hashes)
+ test_node.wait_for_block(new_block_hashes[-1])
+
+ for i in xrange(3):
+ # Mine another block, still should get only an inv
+ tip = self.mine_blocks(1)
+ assert_equal(inv_node.check_last_announcement(inv=[tip]), True)
+ assert_equal(test_node.check_last_announcement(inv=[tip]), True)
+ if i == 0:
+ # Just get the data -- shouldn't cause headers announcements to resume
+ test_node.get_data([tip])
+ test_node.wait_for_block(tip)
+ elif i == 1:
+ # Send a getheaders message that shouldn't trigger headers announcements
+ # to resume (best header sent will be too old)
+ 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)
+ # This time, try sending either a getheaders to trigger resumption
+ # of headers announcements, or mine a new block and inv it, also
+ # triggering resumption of headers announcements.
+ if j == 0:
+ test_node.get_headers(locator=[tip], hashstop=0L)
+ test_node.sync_with_ping()
+ else:
+ test_node.send_block_inv(tip)
+ test_node.sync_with_ping()
+ # New blocks should now be announced with header
+ 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)
+
+ print "Part 3: success!"
+
+ print "Part 4: Testing direct fetch behavior..."
+ tip = self.mine_blocks(1)
+ height = self.nodes[0].getblockcount() + 1
+ last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time']
+ block_time = last_time + 1
+
+ # Create 2 blocks. Send the blocks, then send the headers.
+ blocks = []
+ for b in xrange(2):
+ blocks.append(create_block(tip, create_coinbase(height), block_time))
+ blocks[-1].solve()
+ tip = blocks[-1].sha256
+ block_time += 1
+ height += 1
+ inv_node.send_message(msg_block(blocks[-1]))
+
+ inv_node.sync_with_ping() # Make sure blocks are processed
+ test_node.last_getdata = None
+ test_node.send_header_for_blocks(blocks);
+ test_node.sync_with_ping()
+ # should not have received any getdata messages
+ with mininode_lock:
+ assert_equal(test_node.last_getdata, None)
+
+ # This time, direct fetch should work
+ blocks = []
+ for b in xrange(3):
+ blocks.append(create_block(tip, create_coinbase(height), block_time))
+ blocks[-1].solve()
+ tip = blocks[-1].sha256
+ block_time += 1
+ height += 1
+
+ test_node.send_header_for_blocks(blocks)
+ test_node.sync_with_ping()
+ test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=test_node.sleep_time)
+
+ [ test_node.send_message(msg_block(x)) for x in blocks ]
+
+ test_node.sync_with_ping()
+
+ # Now announce a header that forks the last two blocks
+ tip = blocks[0].sha256
+ height -= 1
+ blocks = []
+
+ # Create extra blocks for later
+ for b in xrange(20):
+ blocks.append(create_block(tip, create_coinbase(height), block_time))
+ blocks[-1].solve()
+ tip = blocks[-1].sha256
+ block_time += 1
+ height += 1
+
+ # Announcing one block on fork should not trigger direct fetch
+ # (less work than tip)
+ test_node.last_getdata = None
+ test_node.send_header_for_blocks(blocks[0:1])
+ test_node.sync_with_ping()
+ with mininode_lock:
+ assert_equal(test_node.last_getdata, None)
+
+ # Announcing one more block on fork should trigger direct fetch for
+ # both blocks (same work as tip)
+ test_node.send_header_for_blocks(blocks[1:2])
+ test_node.sync_with_ping()
+ test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=test_node.sleep_time)
+
+ # Announcing 16 more headers should trigger direct fetch for 14 more
+ # blocks
+ test_node.send_header_for_blocks(blocks[2:18])
+ test_node.sync_with_ping()
+ test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=test_node.sleep_time)
+
+ # Announcing 1 more header should not trigger any response
+ test_node.last_getdata = None
+ test_node.send_header_for_blocks(blocks[18:19])
+ test_node.sync_with_ping()
+ with mininode_lock:
+ assert_equal(test_node.last_getdata, None)
+
+ print "Part 4: success!"
+
+ # Finally, check that the inv node never received a getdata request,
+ # throughout the test
+ assert_equal(inv_node.last_getdata, None)
+
+if __name__ == '__main__':
+ SendHeadersTest().main()
diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py
index c15c5fda09..ecfffc1b45 100755
--- a/qa/rpc-tests/smartfees.py
+++ b/qa/rpc-tests/smartfees.py
@@ -120,15 +120,26 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True):
last_e = e
valid_estimate = False
invalid_estimates = 0
- for e in all_estimates:
+ for i,e in enumerate(all_estimates): # estimate is for i+1
if e >= 0:
valid_estimate = True
+ # estimatesmartfee should return the same result
+ assert_equal(node.estimatesmartfee(i+1)["feerate"], e)
+
else:
invalid_estimates += 1
- # Once we're at a high enough confirmation count that we can give an estimate
- # We should have estimates for all higher confirmation counts
- if valid_estimate and e < 0:
- raise AssertionError("Invalid estimate appears at higher confirm count than valid estimate")
+
+ # estimatesmartfee should still be valid
+ approx_estimate = node.estimatesmartfee(i+1)["feerate"]
+ answer_found = node.estimatesmartfee(i+1)["blocks"]
+ assert(approx_estimate > 0)
+ assert(answer_found > i+1)
+
+ # Once we're at a high enough confirmation count that we can give an estimate
+ # We should have estimates for all higher confirmation counts
+ if valid_estimate:
+ raise AssertionError("Invalid estimate appears at higher confirm count than valid estimate")
+
# Check on the expected number of different confirmation counts
# that we might not have valid estimates for
if invalid_estimates > max_invalid:
@@ -184,13 +195,13 @@ class EstimateFeeTest(BitcoinTestFramework):
# NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes,
# (17k is room enough for 110 or so transactions)
self.nodes.append(start_node(1, self.options.tmpdir,
- ["-blockprioritysize=1500", "-blockmaxsize=18000",
+ ["-blockprioritysize=1500", "-blockmaxsize=17000",
"-maxorphantx=1000", "-relaypriority=0", "-debug=estimatefee"]))
connect_nodes(self.nodes[1], 0)
# Node2 is a stingy miner, that
- # produces too small blocks (room for only 70 or so transactions)
- node2args = ["-blockprioritysize=0", "-blockmaxsize=12000", "-maxorphantx=1000", "-relaypriority=0"]
+ # produces too small blocks (room for only 55 or so transactions)
+ node2args = ["-blockprioritysize=0", "-blockmaxsize=8000", "-maxorphantx=1000", "-relaypriority=0"]
self.nodes.append(start_node(2, self.options.tmpdir, node2args))
connect_nodes(self.nodes[0], 2)
@@ -229,22 +240,19 @@ class EstimateFeeTest(BitcoinTestFramework):
self.fees_per_kb = []
self.memutxo = []
self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting
- print("Checking estimates for 1/2/3/6/15/25 blocks")
- print("Creating transactions and mining them with a huge block size")
- # Create transactions and mine 20 big blocks with node 0 such that the mempool is always emptied
- self.transact_and_mine(30, self.nodes[0])
- check_estimates(self.nodes[1], self.fees_per_kb, 1)
+ print("Will output estimates for 1/2/3/6/15/25 blocks")
- print("Creating transactions and mining them with a block size that can't keep up")
- # Create transactions and mine 30 small blocks with node 2, but create txs faster than we can mine
- self.transact_and_mine(20, self.nodes[2])
- check_estimates(self.nodes[1], self.fees_per_kb, 3)
+ for i in xrange(2):
+ print("Creating transactions and mining them with a block size that can't keep up")
+ # Create transactions and mine 10 small blocks with node 2, but create txs faster than we can mine
+ self.transact_and_mine(10, self.nodes[2])
+ check_estimates(self.nodes[1], self.fees_per_kb, 14)
- print("Creating transactions and mining them at a block size that is just big enough")
- # Generate transactions while mining 40 more blocks, this time with node1
- # which mines blocks with capacity just above the rate that transactions are being created
- self.transact_and_mine(40, self.nodes[1])
- check_estimates(self.nodes[1], self.fees_per_kb, 2)
+ print("Creating transactions and mining them at a block size that is just big enough")
+ # Generate transactions while mining 10 more blocks, this time with node1
+ # which mines blocks with capacity just above the rate that transactions are being created
+ self.transact_and_mine(10, self.nodes[1])
+ check_estimates(self.nodes[1], self.fees_per_kb, 2)
# Finish by mining a normal-sized block:
while len(self.nodes[1].getrawmempool()) > 0:
diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py
index b7d78e74fa..64985d58e2 100755
--- a/qa/rpc-tests/test_framework/mininode.py
+++ b/qa/rpc-tests/test_framework/mininode.py
@@ -751,8 +751,8 @@ class msg_inv(object):
class msg_getdata(object):
command = "getdata"
- def __init__(self):
- self.inv = []
+ def __init__(self, inv=None):
+ self.inv = inv if inv != None else []
def deserialize(self, f):
self.inv = deser_vector(f, CInv)
@@ -905,6 +905,20 @@ class msg_mempool(object):
def __repr__(self):
return "msg_mempool()"
+class msg_sendheaders(object):
+ command = "sendheaders"
+
+ def __init__(self):
+ pass
+
+ def deserialize(self, f):
+ pass
+
+ def serialize(self):
+ return ""
+
+ def __repr__(self):
+ return "msg_sendheaders()"
# getheaders message has
# number of entries
@@ -990,6 +1004,17 @@ class NodeConnCB(object):
def __init__(self):
self.verack_received = False
+ # Spin until verack message is received from the node.
+ # Tests may want to use this as a signal that the test can begin.
+ # This can be called from the testing thread, so it needs to acquire the
+ # global lock.
+ def wait_for_verack(self):
+ while True:
+ with mininode_lock:
+ if self.verack_received:
+ return
+ time.sleep(0.05)
+
# Derived classes should call this function once to set the message map
# which associates the derived classes' functions to incoming messages
def create_callback_map(self):
@@ -1084,7 +1109,7 @@ class NodeConn(asyncore.dispatcher):
"regtest": "\xfa\xbf\xb5\xda" # regtest
}
- def __init__(self, dstaddr, dstport, rpc, callback, net="regtest"):
+ def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=1):
asyncore.dispatcher.__init__(self, map=mininode_socket_map)
self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport))
self.dstaddr = dstaddr
@@ -1102,6 +1127,7 @@ class NodeConn(asyncore.dispatcher):
# stuff version msg into sendbuf
vt = msg_version()
+ vt.nServices = services
vt.addrTo.ip = self.dstaddr
vt.addrTo.port = self.dstport
vt.addrFrom.ip = "0.0.0.0"
diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py
index 30dd5de585..d9d5129f21 100644
--- a/qa/rpc-tests/test_framework/util.py
+++ b/qa/rpc-tests/test_framework/util.py
@@ -67,6 +67,9 @@ def check_json_precision():
if satoshis != 2000000000000003:
raise RuntimeError("JSON encode/decode loses precision")
+def count_bytes(hex_string):
+ return len(bytearray.fromhex(hex_string))
+
def sync_blocks(rpc_connections, wait=1):
"""
Wait until everybody has the same block count
diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py
index f9ec6f429b..6f6bc31895 100755
--- a/qa/rpc-tests/wallet.py
+++ b/qa/rpc-tests/wallet.py
@@ -24,6 +24,17 @@ from test_framework.util import *
class WalletTest (BitcoinTestFramework):
+ def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size):
+ """Return curr_balance after asserting the fee was in range"""
+ fee = balance_with_fee - curr_balance
+ target_fee = fee_per_byte * tx_size
+ if fee < target_fee:
+ raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)"%(str(fee), str(target_fee)))
+ # allow the node's estimation to be at most 2 bytes off
+ if fee > fee_per_byte * (tx_size + 2):
+ raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)"%(str(fee), str(target_fee)))
+ return curr_balance
+
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
@@ -104,33 +115,37 @@ class WalletTest (BitcoinTestFramework):
# Send 10 BTC normal
address = self.nodes[0].getnewaddress("test")
- self.nodes[2].settxfee(Decimal('0.001'))
+ fee_per_byte = Decimal('0.001') / 1000
+ self.nodes[2].settxfee(fee_per_byte * 1000)
txid = self.nodes[2].sendtoaddress(address, 10, "", "", False)
self.nodes[2].generate(1)
self.sync_all()
- assert_equal(self.nodes[2].getbalance(), Decimal('89.99900000'))
- assert_equal(self.nodes[0].getbalance(), Decimal('10.00000000'))
+ node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('90'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ assert_equal(self.nodes[0].getbalance(), Decimal('10'))
# Send 10 BTC with subtract fee from amount
txid = self.nodes[2].sendtoaddress(address, 10, "", "", True)
self.nodes[2].generate(1)
self.sync_all()
- assert_equal(self.nodes[2].getbalance(), Decimal('79.99900000'))
- assert_equal(self.nodes[0].getbalance(), Decimal('19.99900000'))
+ node_2_bal -= Decimal('10')
+ assert_equal(self.nodes[2].getbalance(), node_2_bal)
+ node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
# Sendmany 10 BTC
txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [])
self.nodes[2].generate(1)
self.sync_all()
- assert_equal(self.nodes[2].getbalance(), Decimal('69.99800000'))
- assert_equal(self.nodes[0].getbalance(), Decimal('29.99900000'))
+ node_0_bal += Decimal('10')
+ node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
+ assert_equal(self.nodes[0].getbalance(), node_0_bal)
# Sendmany 10 BTC with subtract fee from amount
txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address])
self.nodes[2].generate(1)
self.sync_all()
- assert_equal(self.nodes[2].getbalance(), Decimal('59.99800000'))
- assert_equal(self.nodes[0].getbalance(), Decimal('39.99800000'))
+ node_2_bal -= Decimal('10')
+ assert_equal(self.nodes[2].getbalance(), node_2_bal)
+ node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid)))
# Test ResendWalletTransactions:
# Create a couple of transactions, then start up a fourth
@@ -191,14 +206,14 @@ class WalletTest (BitcoinTestFramework):
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(), Decimal('59.99800000')); #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()
txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted)
- assert_equal(self.nodes[2].getbalance(), Decimal('61.99800000')); #should not be
+ assert_equal(self.nodes[2].getbalance(), node_2_bal + Decimal('2')); #should not be
#create another tx
txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2);
@@ -216,7 +231,7 @@ class WalletTest (BitcoinTestFramework):
sync_blocks(self.nodes)
#tx should be added to balance because after restarting the nodes tx should be broadcastet
- assert_equal(self.nodes[2].getbalance(), Decimal('63.99800000')); #should not be
+ assert_equal(self.nodes[2].getbalance(), node_2_bal + Decimal('4')); #should not be
#send a tx with value in a string (PR#6380 +)
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")
diff --git a/src/Makefile.am b/src/Makefile.am
index f1e98dabde..40f2e19af0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -100,6 +100,7 @@ BITCOIN_CORE_H = \
compat/sanity.h \
compressor.h \
consensus/consensus.h \
+ consensus/merkle.h \
consensus/params.h \
consensus/validation.h \
core_io.h \
@@ -268,6 +269,7 @@ libbitcoin_common_a_SOURCES = \
chainparams.cpp \
coins.cpp \
compressor.cpp \
+ consensus/merkle.cpp \
core_read.cpp \
core_write.cpp \
hash.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 2328d0b4cc..c377183ad5 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -35,7 +35,7 @@ GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.r
BITCOIN_TESTS =\
test/arith_uint256_tests.cpp \
- test/bignum.h \
+ test/scriptnum10.h \
test/addrman_tests.cpp \
test/alert_tests.cpp \
test/allocator_tests.cpp \
@@ -57,6 +57,7 @@ BITCOIN_TESTS =\
test/dbwrapper_tests.cpp \
test/main_tests.cpp \
test/mempool_tests.cpp \
+ test/merkle_tests.cpp \
test/miner_tests.cpp \
test/mruset_tests.cpp \
test/multisig_tests.cpp \
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 9564573657..58ced1ade9 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -22,6 +22,7 @@
using namespace std;
+static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
std::string HelpMessageCli()
@@ -29,10 +30,10 @@ std::string HelpMessageCli()
string strUsage;
strUsage += HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("This help message"));
- strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf"));
+ strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
AppendParamsHelpMessages(strUsage);
- strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
+ strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 8332, 18332));
strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
@@ -141,7 +142,7 @@ static void http_request_done(struct evhttp_request *req, void *ctx)
UniValue CallRPC(const string& strMethod, const UniValue& params)
{
- std::string host = GetArg("-rpcconnect", "127.0.0.1");
+ std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
int port = GetArg("-rpcport", BaseParams().RPCPort());
// Create event base
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index addf0e6a26..4cee2d3cf0 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -151,6 +151,9 @@ bool AppInit(int argc, char* argv[])
#endif
SoftSetBoolArg("-server", true);
+ // Set this early so that parameter interactions go to console
+ InitLogging();
+ InitParameterInteraction();
fRet = AppInit2(threadGroup, scheduler);
}
catch (const std::exception& e) {
diff --git a/src/chain.cpp b/src/chain.cpp
index 5b8ce076c4..3450ed6c3f 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -51,6 +51,9 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
}
const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
+ if (pindex == NULL) {
+ return NULL;
+ }
if (pindex->nHeight > Height())
pindex = pindex->GetAncestor(Height());
while (pindex && !Contains(pindex))
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 5d6d1ef9d8..a46866a2be 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
+#include "consensus/merkle.h"
#include "tinyformat.h"
#include "util.h"
@@ -32,7 +33,7 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi
genesis.nVersion = nVersion;
genesis.vtx.push_back(txNew);
genesis.hashPrevBlock.SetNull();
- genesis.hashMerkleRoot = genesis.ComputeMerkleRoot();
+ genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
return genesis;
}
diff --git a/src/chainparams.h b/src/chainparams.h
index cb061d596e..8aa0c71d61 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -65,7 +65,7 @@ public:
/** Policy: Filter transactions that do not match well-defined patterns */
bool RequireStandard() const { return fRequireStandard; }
int64_t MaxTipAge() const { return nMaxTipAge; }
- int64_t PruneAfterHeight() const { return nPruneAfterHeight; }
+ uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
/** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */
bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; }
/** In the future use NetworkIDString() for RPC fields */
diff --git a/src/coins.cpp b/src/coins.cpp
index 96b336ce77..723e114708 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -160,18 +160,23 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
if (itUs == cacheCoins.end()) {
- if (!it->second.coins.IsPruned()) {
- // The parent cache does not have an entry, while the child
- // cache does have (a non-pruned) one. Move the data up, and
- // mark it as fresh (if the grandparent did have it, we
- // would have pulled it in at first GetCoins).
- assert(it->second.flags & CCoinsCacheEntry::FRESH);
+ // The parent cache does not have an entry, while the child does
+ // We can ignore it if it's both FRESH and pruned in the child
+ if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coins.IsPruned())) {
+ // Otherwise we will need to create it in the parent
+ // and move the data up and mark it as dirty
CCoinsCacheEntry& entry = cacheCoins[it->first];
entry.coins.swap(it->second.coins);
cachedCoinsUsage += entry.coins.DynamicMemoryUsage();
- entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
+ entry.flags = CCoinsCacheEntry::DIRTY;
+ // We can mark it FRESH in the parent if it was FRESH in the child
+ // Otherwise it might have just been flushed from the parent's cache
+ // and already exist in the grandparent
+ if (it->second.flags & CCoinsCacheEntry::FRESH)
+ entry.flags |= CCoinsCacheEntry::FRESH;
}
} else {
+ // Found the entry in the parent cache
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
// The grandparent does not have an entry, and the child is
// modified and being pruned. This means we can just delete
@@ -238,8 +243,9 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
return true;
}
-double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
+double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const
{
+ inChainInputValue = 0;
if (tx.IsCoinBase())
return 0.0;
double dResult = 0.0;
@@ -248,8 +254,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
const CCoins* coins = AccessCoins(txin.prevout.hash);
assert(coins);
if (!coins->IsAvailable(txin.prevout.n)) continue;
- if (coins->nHeight < nHeight) {
+ if (coins->nHeight <= nHeight) {
dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight);
+ inChainInputValue += coins->vout[txin.prevout.n].nValue;
}
}
return tx.ComputePriority(dResult);
diff --git a/src/coins.h b/src/coins.h
index 3b45cb0a34..d174422100 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -29,11 +29,11 @@
* - VARINT(nHeight)
*
* The nCode value consists of:
- * - bit 1: IsCoinBase()
- * - bit 2: vout[0] is not spent
- * - bit 4: vout[1] is not spent
+ * - bit 0: IsCoinBase()
+ * - bit 1: vout[0] is not spent
+ * - bit 2: vout[1] is not spent
* - The higher bits encode N, the number of non-zero bytes in the following bitvector.
- * - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at
+ * - In case both bit 1 and bit 2 are unset, they encode N-1, as there must be at
* least one non-spent output).
*
* Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e
@@ -58,7 +58,7 @@
*
* - version = 1
* - code = 9 (coinbase, neither vout[0] or vout[1] are unspent,
- * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow)
+ * 2 (1, +1 because both bit 1 and bit 2 are unset) non-zero bitvector bytes follow)
* - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent
* - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee
* * 86ef97d579: compact amount representation for 234925952 (2.35 BTC)
@@ -456,8 +456,12 @@ public:
//! Check whether all prevouts of the transaction are present in the UTXO set represented by this view
bool HaveInputs(const CTransaction& tx) const;
- //! Return priority of tx at height nHeight
- double GetPriority(const CTransaction &tx, int nHeight) const;
+ /**
+ * Return priority of tx at height nHeight. Also calculate the sum of the values of the inputs
+ * that are already in the chain. These are the inputs that will age and increase priority as
+ * new blocks are added to the chain.
+ */
+ double GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const;
const CTxOut &GetOutputFor(const CTxIn& input) const;
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
new file mode 100644
index 0000000000..9a8afa8a33
--- /dev/null
+++ b/src/consensus/merkle.cpp
@@ -0,0 +1,172 @@
+#include "merkle.h"
+#include "hash.h"
+#include "utilstrencodings.h"
+
+/* WARNING! If you're reading this because you're learning about crypto
+ and/or designing a new system that will use merkle trees, keep in mind
+ that the following merkle tree algorithm has a serious flaw related to
+ duplicate txids, resulting in a vulnerability (CVE-2012-2459).
+
+ The reason is that if the number of hashes in the list at a given time
+ is odd, the last one is duplicated before computing the next level (which
+ is unusual in Merkle trees). This results in certain sequences of
+ transactions leading to the same merkle root. For example, these two
+ trees:
+
+ A A
+ / \ / \
+ B C B C
+ / \ | / \ / \
+ D E F D E F F
+ / \ / \ / \ / \ / \ / \ / \
+ 1 2 3 4 5 6 1 2 3 4 5 6 5 6
+
+ for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and
+ 6 are repeated) result in the same root hash A (because the hash of both
+ of (F) and (F,F) is C).
+
+ The vulnerability results from being able to send a block with such a
+ transaction list, with the same merkle root, and the same block hash as
+ the original without duplication, resulting in failed validation. If the
+ receiving node proceeds to mark that block as permanently invalid
+ however, it will fail to accept further unmodified (and thus potentially
+ valid) versions of the same block. We defend against this by detecting
+ the case where we would hash two identical hashes at the end of the list
+ together, and treating that identically to the block having an invalid
+ merkle root. Assuming no double-SHA256 collisions, this will detect all
+ known ways of changing the transactions without affecting the merkle
+ root.
+*/
+
+/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */
+static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector<uint256>* pbranch) {
+ if (pbranch) pbranch->clear();
+ if (leaves.size() == 0) {
+ if (pmutated) *pmutated = false;
+ if (proot) *proot = uint256();
+ return;
+ }
+ bool mutated = false;
+ // count is the number of leaves processed so far.
+ uint32_t count = 0;
+ // inner is an array of eagerly computed subtree hashes, indexed by tree
+ // level (0 being the leaves).
+ // For example, when count is 25 (11001 in binary), inner[4] is the hash of
+ // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
+ // the last leaf. The other inner entries are undefined.
+ uint256 inner[32];
+ // Which position in inner is a hash that depends on the matching leaf.
+ int matchlevel = -1;
+ // First process all leaves into 'inner' values.
+ while (count < leaves.size()) {
+ uint256 h = leaves[count];
+ bool matchh = count == branchpos;
+ count++;
+ int level;
+ // For each of the lower bits in count that are 0, do 1 step. Each
+ // corresponds to an inner value that existed before processing the
+ // current leaf, and each needs a hash to combine it.
+ for (level = 0; !(count & (((uint32_t)1) << level)); level++) {
+ if (pbranch) {
+ if (matchh) {
+ pbranch->push_back(inner[level]);
+ } else if (matchlevel == level) {
+ pbranch->push_back(h);
+ matchh = true;
+ }
+ }
+ mutated |= (inner[level] == h);
+ CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
+ }
+ // Store the resulting hash at inner position level.
+ inner[level] = h;
+ if (matchh) {
+ matchlevel = level;
+ }
+ }
+ // Do a final 'sweep' over the rightmost branch of the tree to process
+ // odd levels, and reduce everything to a single top value.
+ // Level is the level (counted from the bottom) up to which we've sweeped.
+ int level = 0;
+ // As long as bit number level in count is zero, skip it. It means there
+ // is nothing left at this level.
+ while (!(count & (((uint32_t)1) << level))) {
+ level++;
+ }
+ uint256 h = inner[level];
+ bool matchh = matchlevel == level;
+ while (count != (((uint32_t)1) << level)) {
+ // If we reach this point, h is an inner value that is not the top.
+ // We combine it with itself (Bitcoin's special rule for odd levels in
+ // the tree) to produce a higher level one.
+ if (pbranch && matchh) {
+ pbranch->push_back(h);
+ }
+ CHash256().Write(h.begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
+ // Increment count to the value it would have if two entries at this
+ // level had existed.
+ count += (((uint32_t)1) << level);
+ level++;
+ // And propagate the result upwards accordingly.
+ while (!(count & (((uint32_t)1) << level))) {
+ if (pbranch) {
+ if (matchh) {
+ pbranch->push_back(inner[level]);
+ } else if (matchlevel == level) {
+ pbranch->push_back(h);
+ matchh = true;
+ }
+ }
+ CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
+ level++;
+ }
+ }
+ // Return result.
+ if (pmutated) *pmutated = mutated;
+ if (proot) *proot = h;
+}
+
+uint256 ComputeMerkleRoot(const std::vector<uint256>& leaves, bool* mutated) {
+ uint256 hash;
+ MerkleComputation(leaves, &hash, mutated, -1, NULL);
+ return hash;
+}
+
+std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position) {
+ std::vector<uint256> ret;
+ MerkleComputation(leaves, NULL, NULL, position, &ret);
+ return ret;
+}
+
+uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint256>& vMerkleBranch, uint32_t nIndex) {
+ uint256 hash = leaf;
+ for (std::vector<uint256>::const_iterator it = vMerkleBranch.begin(); it != vMerkleBranch.end(); ++it) {
+ if (nIndex & 1) {
+ hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash));
+ } else {
+ hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it));
+ }
+ nIndex >>= 1;
+ }
+ return hash;
+}
+
+uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
+{
+ std::vector<uint256> leaves;
+ leaves.resize(block.vtx.size());
+ for (size_t s = 0; s < block.vtx.size(); s++) {
+ leaves[s] = block.vtx[s].GetHash();
+ }
+ return ComputeMerkleRoot(leaves, mutated);
+}
+
+std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
+{
+ std::vector<uint256> leaves;
+ leaves.resize(block.vtx.size());
+ for (size_t s = 0; s < block.vtx.size(); s++) {
+ leaves[s] = block.vtx[s].GetHash();
+ }
+ return ComputeMerkleBranch(leaves, position);
+}
diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h
new file mode 100644
index 0000000000..6ef59745ac
--- /dev/null
+++ b/src/consensus/merkle.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_MERKLE
+#define BITCOIN_MERKLE
+
+#include <stdint.h>
+#include <vector>
+
+#include "primitives/transaction.h"
+#include "primitives/block.h"
+#include "uint256.h"
+
+uint256 ComputeMerkleRoot(const std::vector<uint256>& leaves, bool* mutated = NULL);
+std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position);
+uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint256>& branch, uint32_t position);
+
+/*
+ * Compute the Merkle root of the transactions in a block.
+ * *mutated is set to true if a duplicated subtree was found.
+ */
+uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = NULL);
+
+/*
+ * Compute the Merkle branch for the tree of transactions in a block, for a
+ * given position.
+ * This can be verified using ComputeMerkleRootFromBranch.
+ */
+std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position);
+
+#endif
diff --git a/src/init.cpp b/src/init.cpp
index cd84e7747a..01c7189675 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -66,6 +66,10 @@ using namespace std;
CWallet* pwalletMain = NULL;
#endif
bool fFeeEstimatesInitialized = false;
+static const bool DEFAULT_PROXYRANDOMIZE = true;
+static const bool DEFAULT_REST_ENABLE = false;
+static const bool DEFAULT_DISABLE_SAFEMODE = false;
+static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
#if ENABLE_ZMQ
static CZMQNotificationInterface* pzmqNotificationInterface = NULL;
@@ -296,7 +300,7 @@ void OnRPCPreCommand(const CRPCCommand& cmd)
{
// Observe safe mode
string strWarning = GetWarnings("rpc");
- if (strWarning != "" && !GetBoolArg("-disablesafemode", false) &&
+ if (strWarning != "" && !GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) &&
!cmd.okSafeMode)
throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
}
@@ -316,7 +320,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));
strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS));
strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL));
- strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf"));
+ strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
if (mode == HMM_BITCOIND)
{
#ifndef WIN32
@@ -332,7 +336,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
#ifndef WIN32
- strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), "bitcoind.pid"));
+ strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME));
#endif
strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
@@ -341,30 +345,33 @@ std::string HelpMessage(HelpMessageMode mode)
#ifndef WIN32
strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"));
#endif
- strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), 0));
+ strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
strUsage += HelpMessageGroup(_("Connection options:"));
strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open"));
- strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), 100));
- strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), 86400));
+ strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD));
+ strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME));
strUsage += HelpMessageOpt("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s)"));
strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)"));
- strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + _("(default: 1)"));
+ strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP));
strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect)"));
strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address"));
- strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), 0));
+ strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED));
strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"));
strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION));
strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS));
- strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), 5000));
- strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), 1000));
+ strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER));
+ strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER));
strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy"));
strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"));
- strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), 1));
- strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), 8333, 18333));
+ strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG));
+ strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), 1));
+ if (showDebug)
+ strUsage += HelpMessageOpt("-enforcenodebloom", strprintf("Enforce minimum protocol version to limit use of bloom filters (default: %u)", 0));
+ strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), Params(CBaseChainParams::MAIN).GetDefaultPort(), Params(CBaseChainParams::TESTNET).GetDefaultPort()));
strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
- strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), 1));
+ strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE));
strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
@@ -385,7 +392,7 @@ std::string HelpMessage(HelpMessageMode mode)
#ifdef ENABLE_WALLET
strUsage += HelpMessageGroup(_("Wallet options:"));
strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));
- strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), 100));
+ strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
if (showDebug)
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
@@ -393,14 +400,14 @@ std::string HelpMessage(HelpMessageMode mode)
CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK())));
strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup"));
strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet.dat on startup"));
- strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), 0));
- strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), 1));
+ strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS));
+ strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));
strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET));
strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)));
strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));
strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), "wallet.dat"));
- strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), true));
+ strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST));
strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"));
strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +
" " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"));
@@ -409,46 +416,46 @@ std::string HelpMessage(HelpMessageMode mode)
#if ENABLE_ZMQ
strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
strUsage += HelpMessageOpt("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>"));
- strUsage += HelpMessageOpt("-zmqpubhashtransaction=<address>", _("Enable publish hash transaction in <address>"));
+ strUsage += HelpMessageOpt("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"));
- strUsage += HelpMessageOpt("-zmqpubrawtransaction=<address>", _("Enable publish raw transaction in <address>"));
+ strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"));
#endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
if (showDebug)
{
- strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", 1));
+ strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED));
#ifdef ENABLE_WALLET
strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE));
#endif
- strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", 0));
- strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", 0));
+ strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE));
+ strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE));
strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages");
strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages");
#ifdef ENABLE_WALLET
- strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1));
+ strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET));
#endif
- strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
+ strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT));
strUsage += HelpMessageOpt("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT));
strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT));
strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
}
- string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, mempoolrej, net, proxy, prune, http, libevent"; // Don't translate these and qt below
+ string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, mempoolrej, net, proxy, prune, http, libevent, tor, zmq"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT)
debugCategories += ", qt";
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + _("<category> can be:") + " " + debugCategories + ".");
- strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), 0));
+ strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), DEFAULT_GENERATE));
strUsage += HelpMessageOpt("-genproclimit=<n>", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), DEFAULT_GENERATE_THREADS));
strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)"));
- strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), 0));
- strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), 1));
+ strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS));
+ strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS));
if (showDebug)
{
strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS));
- strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)", 15));
- strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", 1));
+ strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)", DEFAULT_LIMITFREERELAY));
+ strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", DEFAULT_RELAYPRIORITY));
strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE));
}
strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"),
@@ -456,8 +463,10 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file"));
if (showDebug)
{
- strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction priority and fee per kB when mining blocks (default: %u)", 0));
- strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", 1));
+ strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction priority and fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY));
+#ifdef ENABLE_WALLET
+ strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB));
+#endif
}
strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)"));
@@ -466,11 +475,11 @@ 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("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), 1));
+ 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));
strUsage += HelpMessageGroup(_("Block creation options:"));
- strUsage += HelpMessageOpt("-blockminsize=<n>", strprintf(_("Set minimum block size in bytes (default: %u)"), 0));
+ strUsage += HelpMessageOpt("-blockminsize=<n>", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE));
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE));
if (showDebug)
@@ -478,7 +487,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("RPC server options:"));
strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands"));
- strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), 0));
+ strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE));
strUsage += HelpMessageOpt("-rpcbind=<addr>", _("Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)"));
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
@@ -490,22 +499,6 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT));
}
- if (mode == HMM_BITCOIN_QT)
- {
- strUsage += HelpMessageGroup(_("UI Options:"));
- if (showDebug) {
- strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", "Allow self signed root certificates (default: 0)");
- }
- strUsage += HelpMessageOpt("-choosedatadir", _("Choose data directory on startup (default: 0)"));
- strUsage += HelpMessageOpt("-lang=<lang>", _("Set language, for example \"de_DE\" (default: system locale)"));
- strUsage += HelpMessageOpt("-min", _("Start minimized"));
- strUsage += HelpMessageOpt("-rootcertificates=<file>", _("Set SSL root certificates for payment request (default: -system-)"));
- strUsage += HelpMessageOpt("-splash", _("Show splash screen on startup (default: 1)"));
- if (showDebug) {
- strUsage += HelpMessageOpt("-uiplatform", "Select platform to customize UI for (one of windows, macosx, other; default: platform compiled on)");
- }
- }
-
return strUsage;
}
@@ -522,11 +515,14 @@ std::string LicenseInfo()
"\n";
}
-static void BlockNotifyCallback(const uint256& hashNewTip)
+static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex)
{
+ if (initialSync || !pBlockIndex)
+ return;
+
std::string strCmd = GetArg("-blocknotify", "");
- boost::replace_all(strCmd, "%s", hashNewTip.GetHex());
+ boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
@@ -588,6 +584,7 @@ void CleanupBlockRevFiles()
void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
{
+ const CChainParams& chainparams = Params();
RenameThread("bitcoin-loadblk");
// -reindex
if (fReindex) {
@@ -601,14 +598,14 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
if (!file)
break; // This error is logged in OpenBlockFile
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
- LoadExternalBlockFile(file, &pos);
+ LoadExternalBlockFile(chainparams, file, &pos);
nFile++;
}
pblocktree->WriteReindexing(false);
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
- InitBlockIndex();
+ InitBlockIndex(chainparams);
}
// hardcoded $DATADIR/bootstrap.dat
@@ -619,7 +616,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
CImportingNow imp;
boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
LogPrintf("Importing bootstrap.dat...\n");
- LoadExternalBlockFile(file);
+ LoadExternalBlockFile(chainparams, file);
RenameOver(pathBootstrap, pathBootstrapOld);
} else {
LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string());
@@ -632,13 +629,13 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
if (file) {
CImportingNow imp;
LogPrintf("Importing blocks file %s...\n", path.string());
- LoadExternalBlockFile(file);
+ LoadExternalBlockFile(chainparams, file);
} else {
LogPrintf("Warning: Could not open blocks file %s\n", path.string());
}
}
- if (GetBoolArg("-stopafterblockimport", false)) {
+ if (GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
}
@@ -670,13 +667,98 @@ bool AppInitServers(boost::thread_group& threadGroup)
return false;
if (!StartHTTPRPC())
return false;
- if (GetBoolArg("-rest", false) && !StartREST())
+ if (GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST())
return false;
if (!StartHTTPServer())
return false;
return true;
}
+// Parameter interaction based on rules
+void InitParameterInteraction()
+{
+ // when specifying an explicit binding address, you want to listen on it
+ // even when -connect or -proxy is specified
+ if (mapArgs.count("-bind")) {
+ if (SoftSetBoolArg("-listen", true))
+ LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__);
+ }
+ if (mapArgs.count("-whitebind")) {
+ if (SoftSetBoolArg("-listen", true))
+ LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);
+ }
+
+ if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
+ // when only connecting to trusted nodes, do not seed via DNS, or listen by default
+ if (SoftSetBoolArg("-dnsseed", false))
+ LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);
+ if (SoftSetBoolArg("-listen", false))
+ LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);
+ }
+
+ if (mapArgs.count("-proxy")) {
+ // to protect privacy, do not listen by default if a default proxy server is specified
+ if (SoftSetBoolArg("-listen", false))
+ LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
+ // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1
+ // to listen locally, so don't rely on this happening through -listen below.
+ if (SoftSetBoolArg("-upnp", false))
+ LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
+ // to protect privacy, do not discover addresses by default
+ if (SoftSetBoolArg("-discover", false))
+ LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
+ }
+
+ if (!GetBoolArg("-listen", DEFAULT_LISTEN)) {
+ // do not map ports or try to retrieve public IP when not listening (pointless)
+ if (SoftSetBoolArg("-upnp", false))
+ LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
+ if (SoftSetBoolArg("-discover", false))
+ LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
+ if (SoftSetBoolArg("-listenonion", false))
+ LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
+ }
+
+ if (mapArgs.count("-externalip")) {
+ // if an explicit public IP is specified, do not try to find others
+ if (SoftSetBoolArg("-discover", false))
+ LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
+ }
+
+ if (GetBoolArg("-salvagewallet", false)) {
+ // Rewrite just private keys: rescan to find transactions
+ if (SoftSetBoolArg("-rescan", true))
+ LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
+ }
+
+ // -zapwallettx implies a rescan
+ if (GetBoolArg("-zapwallettxes", false)) {
+ if (SoftSetBoolArg("-rescan", true))
+ LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
+ }
+
+ // disable walletbroadcast and whitelistalwaysrelay in blocksonly mode
+ if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
+ if (SoftSetBoolArg("-whitelistalwaysrelay", false))
+ LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistalwaysrelay=0\n", __func__);
+#ifdef ENABLE_WALLET
+ if (SoftSetBoolArg("-walletbroadcast", false))
+ LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
+#endif
+ }
+}
+
+void InitLogging()
+{
+ fPrintToConsole = GetBoolArg("-printtoconsole", false);
+ fLogTimestamps = GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
+ fLogTimeMicros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+ fLogIPs = GetBoolArg("-logips", DEFAULT_LOGIPS);
+
+ LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+ LogPrintf("Bitcoin version %s (%s)\n", FormatFullVersion(), CLIENT_DATE);
+}
+
/** Initialize bitcoin.
* @pre Parameters should be parsed and config file should be read.
*/
@@ -741,78 +823,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 2: parameter interactions
const CChainParams& chainparams = Params();
- // Set this early so that parameter interactions go to console
- fPrintToConsole = GetBoolArg("-printtoconsole", false);
- fLogTimestamps = GetBoolArg("-logtimestamps", true);
- fLogTimeMicros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
- fLogIPs = GetBoolArg("-logips", false);
-
- LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
- LogPrintf("Bitcoin version %s (%s)\n", FormatFullVersion(), CLIENT_DATE);
-
- // when specifying an explicit binding address, you want to listen on it
- // even when -connect or -proxy is specified
- if (mapArgs.count("-bind")) {
- if (SoftSetBoolArg("-listen", true))
- LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__);
- }
- if (mapArgs.count("-whitebind")) {
- if (SoftSetBoolArg("-listen", true))
- LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);
- }
-
- if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
- // when only connecting to trusted nodes, do not seed via DNS, or listen by default
- if (SoftSetBoolArg("-dnsseed", false))
- LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);
- if (SoftSetBoolArg("-listen", false))
- LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);
- }
-
- if (mapArgs.count("-proxy")) {
- // to protect privacy, do not listen by default if a default proxy server is specified
- if (SoftSetBoolArg("-listen", false))
- LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
- // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1
- // to listen locally, so don't rely on this happening through -listen below.
- if (SoftSetBoolArg("-upnp", false))
- LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
- // to protect privacy, do not discover addresses by default
- if (SoftSetBoolArg("-discover", false))
- LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
- }
-
- if (!GetBoolArg("-listen", DEFAULT_LISTEN)) {
- // do not map ports or try to retrieve public IP when not listening (pointless)
- if (SoftSetBoolArg("-upnp", false))
- LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
- if (SoftSetBoolArg("-discover", false))
- LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
- if (SoftSetBoolArg("-listenonion", false))
- LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
- }
-
- if (mapArgs.count("-externalip")) {
- // if an explicit public IP is specified, do not try to find others
- if (SoftSetBoolArg("-discover", false))
- LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
- }
-
- if (GetBoolArg("-salvagewallet", false)) {
- // Rewrite just private keys: rescan to find transactions
- if (SoftSetBoolArg("-rescan", true))
- LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
- }
-
- // -zapwallettx implies a rescan
- if (GetBoolArg("-zapwallettxes", false)) {
- if (SoftSetBoolArg("-rescan", true))
- LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
- }
+ // also see: InitParameterInteraction()
// if using block pruning, then disable txindex
if (GetArg("-prune", 0)) {
- if (GetBoolArg("-txindex", false))
+ if (GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
#ifdef ENABLE_WALLET
if (GetBoolArg("-rescan", false)) {
@@ -821,16 +836,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#endif
}
- // disable walletbroadcast and whitelistalwaysrelay in blocksonly mode
- if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
- if (SoftSetBoolArg("-whitelistalwaysrelay", false))
- LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistalwaysrelay=0\n", __func__);
-#ifdef ENABLE_WALLET
- if (SoftSetBoolArg("-walletbroadcast", false))
- LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
-#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", DEFAULT_MAX_PEER_CONNECTIONS);
@@ -873,13 +878,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
mempool.setSanityCheck(1.0 / ratio);
}
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
- fCheckpointsEnabled = GetBoolArg("-checkpoints", true);
+ fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
- // -mempoollimit limits
- int64_t nMempoolSizeLimit = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t nMempoolDescendantSizeLimit = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
- if (nMempoolSizeLimit < 0 || nMempoolSizeLimit < nMempoolDescendantSizeLimit * 40)
- return InitError(strprintf(_("-maxmempool must be at least %d MB"), GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) / 25));
+ // mempool limits
+ int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMin = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
+ if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
+ return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
@@ -971,13 +976,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
- bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", true);
- fSendFreeTransactions = GetBoolArg("-sendfreetransactions", false);
+ bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
+ fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);
std::string strWalletFile = GetArg("-wallet", "wallet.dat");
#endif // ENABLE_WALLET
- fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", true);
+ fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
+ fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes);
fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS);
@@ -1026,7 +1032,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (fPrintToDebugLog)
OpenDebugLog();
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION));
+#else
+ LogPrintf("Using OpenSSL version %s\n", OpenSSL_version(OPENSSL_VERSION));
+#endif
+
#ifdef ENABLE_WALLET
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
#endif
@@ -1123,7 +1134,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
- bool proxyRandomize = GetBoolArg("-proxyrandomize", true);
+ bool proxyRandomize = GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
std::string proxyArg = GetArg("-proxy", "");
@@ -1158,7 +1169,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// see Step 2: parameter interactions for more information about these
fListen = GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = GetBoolArg("-discover", true);
- fNameLookup = GetBoolArg("-dns", true);
+ fNameLookup = GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
bool fBound = false;
if (fListen) {
@@ -1247,7 +1258,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greated than nMaxDbcache
int64_t nBlockTreeDBCache = nTotalCache / 8;
- if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
+ if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", DEFAULT_TXINDEX))
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
nTotalCache -= nBlockTreeDBCache;
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
@@ -1297,13 +1308,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
// Initialize the block index (no-op if non-empty database was already loaded)
- if (!InitBlockIndex()) {
+ if (!InitBlockIndex(chainparams)) {
strLoadError = _("Error initializing block database");
break;
}
// Check for changed -txindex state
- if (fTxIndex != GetBoolArg("-txindex", false)) {
+ if (fTxIndex != GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
strLoadError = _("You need to rebuild the database using -reindex to change -txindex");
break;
}
@@ -1332,7 +1343,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
- if (!CVerifyDB().VerifyDB(pcoinsdbview, GetArg("-checklevel", DEFAULT_CHECKLEVEL),
+ if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, GetArg("-checklevel", DEFAULT_CHECKLEVEL),
GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
break;
@@ -1529,7 +1540,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
}
- pwalletMain->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", true));
+ pwalletMain->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
} // (!fDisableWallet)
#else // ENABLE_WALLET
LogPrintf("No wallet support compiled in!\n");
@@ -1556,7 +1567,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
uiInterface.InitMessage(_("Activating best chain..."));
// scan for better chains in the block chain database, that are not yet connected in the active best chain
CValidationState state;
- if (!ActivateBestChain(state))
+ if (!ActivateBestChain(state, chainparams))
strErrors << "Failed to connect best block";
std::vector<boost::filesystem::path> vImportFiles;
@@ -1603,7 +1614,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
scheduler.scheduleEvery(f, nPowTargetSpacing);
// Generate coins in the background
- GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams);
+ GenerateBitcoins(GetBoolArg("-gen", DEFAULT_GENERATE), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams);
// ********************************************************* Step 12: finished
diff --git a/src/init.h b/src/init.h
index 8cd51b0286..d4872e7794 100644
--- a/src/init.h
+++ b/src/init.h
@@ -23,6 +23,10 @@ bool ShutdownRequested();
/** Interrupt threads */
void Interrupt(boost::thread_group& threadGroup);
void Shutdown();
+//!Initialize the logging infrastructure
+void InitLogging();
+//!Parameter interaction: change current parameters depending on various rules
+void InitParameterInteraction();
bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler);
/** The help message mode determines what help message to show */
diff --git a/src/main.cpp b/src/main.cpp
index 2579b642b8..55b0517349 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -12,6 +12,7 @@
#include "checkpoints.h"
#include "checkqueue.h"
#include "consensus/consensus.h"
+#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.h"
#include "init.h"
@@ -66,10 +67,10 @@ bool fReindex = false;
bool fTxIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
-bool fIsBareMultisigStd = true;
+bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
bool fRequireStandard = true;
bool fCheckBlockIndex = false;
-bool fCheckpointsEnabled = true;
+bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
bool fAlerts = DEFAULT_ALERTS;
@@ -246,6 +247,8 @@ struct CNodeState {
uint256 hashLastUnknownBlock;
//! The last full block we both have.
CBlockIndex *pindexLastCommonBlock;
+ //! The best header we have sent our peer.
+ CBlockIndex *pindexBestHeaderSent;
//! Whether we've started headers synchronization with this peer.
bool fSyncStarted;
//! Since when we're stalling block download progress (in microseconds), or 0.
@@ -255,6 +258,8 @@ struct CNodeState {
int nBlocksInFlightValidHeaders;
//! Whether we consider this a preferred download peer.
bool fPreferredDownload;
+ //! Whether this peer wants invs or headers (when possible) for block announcements.
+ bool fPreferHeaders;
CNodeState() {
fCurrentlyConnected = false;
@@ -263,11 +268,13 @@ struct CNodeState {
pindexBestKnownBlock = NULL;
hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = NULL;
+ pindexBestHeaderSent = NULL;
fSyncStarted = false;
nStallingSince = 0;
nBlocksInFlight = 0;
nBlocksInFlightValidHeaders = 0;
fPreferredDownload = false;
+ fPreferHeaders = false;
}
};
@@ -397,6 +404,22 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
}
}
+// Requires cs_main
+bool CanDirectFetch(const Consensus::Params &consensusParams)
+{
+ return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20;
+}
+
+// Requires cs_main
+bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex)
+{
+ if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight))
+ return true;
+ if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight))
+ return true;
+ return false;
+}
+
/** Find the last common ancestor two blocks have.
* Both pa and pb must be non-NULL. */
CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
@@ -832,15 +855,42 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool");
// Check for conflicts with in-memory transactions
+ set<uint256> setConflicts;
{
LOCK(pool.cs); // protect pool.mapNextTx
- for (unsigned int i = 0; i < tx.vin.size(); i++)
+ BOOST_FOREACH(const CTxIn &txin, tx.vin)
{
- COutPoint outpoint = tx.vin[i].prevout;
- if (pool.mapNextTx.count(outpoint))
+ if (pool.mapNextTx.count(txin.prevout))
{
- // Disable replacement feature for now
- return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict");
+ const CTransaction *ptxConflicting = pool.mapNextTx[txin.prevout].ptx;
+ if (!setConflicts.count(ptxConflicting->GetHash()))
+ {
+ // Allow opt-out of transaction replacement by setting
+ // nSequence >= maxint-1 on all inputs.
+ //
+ // maxint-1 is picked to still allow use of nLockTime by
+ // non-replacable transactions. All inputs rather than just one
+ // is for the sake of multi-party protocols, where we don't
+ // want a single party to be able to disable replacement.
+ //
+ // The opt-out ignores descendants as anyone relying on
+ // first-seen mempool behavior should be checking all
+ // unconfirmed ancestors anyway; doing otherwise is hopelessly
+ // insecure.
+ bool fReplacementOptOut = true;
+ BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin)
+ {
+ if (txin.nSequence < std::numeric_limits<unsigned int>::max()-1)
+ {
+ fReplacementOptOut = false;
+ break;
+ }
+ }
+ if (fReplacementOptOut)
+ return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict");
+
+ setConflicts.insert(ptxConflicting->GetHash());
+ }
}
}
}
@@ -900,9 +950,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut;
- double dPriority = view.GetPriority(tx, chainActive.Height());
+ CAmount inChainInputValue;
+ double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue);
- CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx));
+ CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue);
unsigned int nSize = entry.GetTxSize();
// Don't accept it if it can't get into a block
@@ -914,7 +965,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) {
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
- } else if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
+ } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
// Require that free transactions have sufficient priority to be mined in the next block.
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
}
@@ -936,7 +987,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
- if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
+ if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000)
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction");
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
@@ -958,6 +1009,160 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString);
}
+ // A transaction that spends outputs that would be replaced by it is invalid. Now
+ // that we have the set of all ancestors we can detect this
+ // pathological case by making sure setConflicts and setAncestors don't
+ // intersect.
+ BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors)
+ {
+ const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
+ if (setConflicts.count(hashAncestor))
+ {
+ return state.DoS(10, error("AcceptToMemoryPool: %s spends conflicting transaction %s",
+ hash.ToString(),
+ hashAncestor.ToString()),
+ REJECT_INVALID, "bad-txns-spends-conflicting-tx");
+ }
+ }
+
+ // Check if it's economically rational to mine this transaction rather
+ // than the ones it replaces.
+ CAmount nConflictingFees = 0;
+ size_t nConflictingSize = 0;
+ uint64_t nConflictingCount = 0;
+ CTxMemPool::setEntries allConflicting;
+
+ // If we don't hold the lock allConflicting might be incomplete; the
+ // subsequent RemoveStaged() and addUnchecked() calls don't guarantee
+ // mempool consistency for us.
+ LOCK(pool.cs);
+ if (setConflicts.size())
+ {
+ CFeeRate newFeeRate(nFees, nSize);
+ set<uint256> setConflictsParents;
+ const int maxDescendantsToVisit = 100;
+ CTxMemPool::setEntries setIterConflicting;
+ BOOST_FOREACH(const uint256 &hashConflicting, setConflicts)
+ {
+ CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting);
+ if (mi == pool.mapTx.end())
+ continue;
+
+ // Save these to avoid repeated lookups
+ setIterConflicting.insert(mi);
+
+ // If this entry is "dirty", then we don't have descendant
+ // state for this transaction, which means we probably have
+ // lots of in-mempool descendants.
+ // Don't allow replacements of dirty transactions, to ensure
+ // that we don't spend too much time walking descendants.
+ // This should be rare.
+ if (mi->IsDirty()) {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s; cannot replace tx %s with untracked descendants",
+ hash.ToString(),
+ mi->GetTx().GetHash().ToString()),
+ REJECT_NONSTANDARD, "too many potential replacements");
+ }
+
+ // Don't allow the replacement to reduce the feerate of the
+ // mempool.
+ //
+ // We usually don't want to accept replacements with lower
+ // feerates than what they replaced as that would lower the
+ // feerate of the next block. Requiring that the feerate always
+ // be increased is also an easy-to-reason about way to prevent
+ // DoS attacks via replacements.
+ //
+ // The mining code doesn't (currently) take children into
+ // account (CPFP) so we only consider the feerates of
+ // transactions being directly replaced, not their indirect
+ // descendants. While that does mean high feerate children are
+ // ignored when deciding whether or not to replace, we do
+ // require the replacement to pay more overall fees too,
+ // mitigating most cases.
+ CFeeRate oldFeeRate(mi->GetFee(), mi->GetTxSize());
+ if (newFeeRate <= oldFeeRate)
+ {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s; new feerate %s <= old feerate %s",
+ hash.ToString(),
+ newFeeRate.ToString(),
+ oldFeeRate.ToString()),
+ REJECT_INSUFFICIENTFEE, "insufficient fee");
+ }
+
+ BOOST_FOREACH(const CTxIn &txin, mi->GetTx().vin)
+ {
+ setConflictsParents.insert(txin.prevout.hash);
+ }
+
+ nConflictingCount += mi->GetCountWithDescendants();
+ }
+ // This potentially overestimates the number of actual descendants
+ // but we just want to be conservative to avoid doing too much
+ // work.
+ if (nConflictingCount <= maxDescendantsToVisit) {
+ // If not too many to replace, then calculate the set of
+ // transactions that would have to be evicted
+ BOOST_FOREACH(CTxMemPool::txiter it, setIterConflicting) {
+ pool.CalculateDescendants(it, allConflicting);
+ }
+ BOOST_FOREACH(CTxMemPool::txiter it, allConflicting) {
+ nConflictingFees += it->GetFee();
+ nConflictingSize += it->GetTxSize();
+ }
+ } else {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s; too many potential replacements (%d > %d)\n",
+ hash.ToString(),
+ nConflictingCount,
+ maxDescendantsToVisit),
+ REJECT_NONSTANDARD, "too many potential replacements");
+ }
+
+ for (unsigned int j = 0; j < tx.vin.size(); j++)
+ {
+ // We don't want to accept replacements that require low
+ // feerate junk to be mined first. Ideally we'd keep track of
+ // the ancestor feerates and make the decision based on that,
+ // but for now requiring all new inputs to be confirmed works.
+ if (!setConflictsParents.count(tx.vin[j].prevout.hash))
+ {
+ // Rather than check the UTXO set - potentially expensive -
+ // it's cheaper to just check if the new input refers to a
+ // tx that's in the mempool.
+ if (pool.mapTx.find(tx.vin[j].prevout.hash) != pool.mapTx.end())
+ return state.DoS(0, error("AcceptToMemoryPool: replacement %s adds unconfirmed input, idx %d",
+ hash.ToString(), j),
+ REJECT_NONSTANDARD, "replacement-adds-unconfirmed");
+ }
+ }
+
+ // The replacement must pay greater fees than the transactions it
+ // replaces - if we did the bandwidth used by those conflicting
+ // transactions would not be paid for.
+ if (nFees < nConflictingFees)
+ {
+ return state.DoS(0, error("AcceptToMemoryPool: rejecting replacement %s, less fees than conflicting txs; %s < %s",
+ hash.ToString(), FormatMoney(nFees), FormatMoney(nConflictingFees)),
+ REJECT_INSUFFICIENTFEE, "insufficient fee");
+ }
+
+ // Finally in addition to paying more fees than the conflicts the
+ // new transaction must pay for its own bandwidth.
+ CAmount nDeltaFees = nFees - nConflictingFees;
+ if (nDeltaFees < ::minRelayTxFee.GetFee(nSize))
+ {
+ return state.DoS(0,
+ error("AcceptToMemoryPool: rejecting replacement %s, not enough additional fees to relay; %s < %s",
+ hash.ToString(),
+ FormatMoney(nDeltaFees),
+ FormatMoney(::minRelayTxFee.GetFee(nSize))),
+ REJECT_INSUFFICIENTFEE, "insufficient fee");
+ }
+ }
+
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true))
@@ -978,6 +1183,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
__func__, hash.ToString(), FormatStateMessage(state));
}
+ // Remove conflicting transactions from the mempool
+ BOOST_FOREACH(const CTxMemPool::txiter it, allConflicting)
+ {
+ LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
+ it->GetTx().GetHash().ToString(),
+ hash.ToString(),
+ FormatMoney(nFees - nConflictingFees),
+ (int)nSize - (int)nConflictingSize);
+ }
+ pool.RemoveStaged(allConflicting);
+
// Store transaction in memory
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
@@ -1244,7 +1460,7 @@ void Misbehaving(NodeId pnode, int howmuch)
return;
state->nMisbehavior += howmuch;
- int banscore = GetArg("-banscore", 100);
+ int banscore = GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD);
if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
{
LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
@@ -1947,6 +2163,7 @@ enum FlushStateMode {
* or always and in all cases if we're in prune mode and are deleting files.
*/
bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
+ const CChainParams& chainparams = Params();
LOCK2(cs_main, cs_LastBlockFile);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
@@ -1955,7 +2172,7 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
bool fFlushForPrune = false;
try {
if (fPruneMode && fCheckForPruning && !fReindex) {
- FindFilesToPrune(setFilesToPrune);
+ FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight());
fCheckForPruning = false;
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
@@ -2155,8 +2372,8 @@ static int64_t nTimePostConnect = 0;
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
-bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, const CBlock *pblock) {
- const CChainParams& chainparams = Params();
+bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock)
+{
assert(pindexNew->pprev == chainActive.Tip());
mempool.check(pcoinsTip);
// Read block from disk.
@@ -2288,8 +2505,8 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/
-static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, const CBlock *pblock) {
- const CChainParams& chainparams = Params();
+static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock)
+{
AssertLockHeld(cs_main);
bool fInvalidFound = false;
const CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -2322,7 +2539,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
// Connect new blocks.
BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
- if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) {
+ if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
if (!state.CorruptionPossible())
@@ -2363,46 +2580,69 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
* or an activated best chain. pblock is either NULL or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
-bool ActivateBestChain(CValidationState &state, const CBlock *pblock) {
- CBlockIndex *pindexNewTip = NULL;
+bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
CBlockIndex *pindexMostWork = NULL;
- const CChainParams& chainparams = Params();
do {
boost::this_thread::interruption_point();
+ CBlockIndex *pindexNewTip = NULL;
+ const CBlockIndex *pindexFork;
bool fInitialDownload;
{
LOCK(cs_main);
+ CBlockIndex *pindexOldTip = chainActive.Tip();
pindexMostWork = FindMostWorkChain();
// Whether we have anything to do at all.
if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip())
return true;
- if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL))
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL))
return false;
pindexNewTip = chainActive.Tip();
+ pindexFork = chainActive.FindFork(pindexOldTip);
fInitialDownload = IsInitialBlockDownload();
}
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
// Notifications/callbacks that can run without cs_main
- if (!fInitialDownload) {
- uint256 hashNewTip = pindexNewTip->GetBlockHash();
- // Relay inventory, but don't relay old inventory during initial block download.
- int nBlockEstimate = 0;
- if (fCheckpointsEnabled)
- nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints());
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
- pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
+ // Always notify the UI if a new block tip was connected
+ if (pindexFork != pindexNewTip) {
+ uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
+
+ if (!fInitialDownload) {
+ // Find the hashes of all blocks that weren't previously in the best chain.
+ std::vector<uint256> vHashes;
+ CBlockIndex *pindexToAnnounce = pindexNewTip;
+ while (pindexToAnnounce != pindexFork) {
+ vHashes.push_back(pindexToAnnounce->GetBlockHash());
+ pindexToAnnounce = pindexToAnnounce->pprev;
+ if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) {
+ // Limit announcements in case of a huge reorganization.
+ // Rely on the peer's synchronization mechanism in that case.
+ break;
+ }
+ }
+ // Relay inventory, but don't relay old inventory during initial block download.
+ int nBlockEstimate = 0;
+ if (fCheckpointsEnabled)
+ nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints());
+ {
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes) {
+ if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) {
+ BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
+ pnode->PushBlockHash(hash);
+ }
+ }
+ }
+ }
+ // Notify external listeners about the new tip.
+ if (!vHashes.empty()) {
+ GetMainSignals().UpdatedBlockTip(pindexNewTip);
+ }
}
- // Notify external listeners about the new tip.
- GetMainSignals().UpdatedBlockTip(pindexNewTip);
- uiInterface.NotifyBlockTip(hashNewTip);
}
} while(pindexMostWork != chainActive.Tip());
CheckBlockIndex(chainparams.GetConsensus());
@@ -2683,7 +2923,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
// Check the merkle root.
if (fCheckMerkleRoot) {
bool mutated;
- uint256 hashMerkleRoot2 = block.ComputeMerkleRoot(&mutated);
+ uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
if (block.hashMerkleRoot != hashMerkleRoot2)
return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"),
REJECT_INVALID, "bad-txnmrklroot", true);
@@ -2858,9 +3098,9 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
return true;
}
-bool AcceptBlock(const CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp)
+/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */
+static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp)
{
- const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
CBlockIndex *&pindex = *ppindex;
@@ -2950,7 +3190,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c
// Store to disk
CBlockIndex *pindex = NULL;
- bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp);
+ bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp);
if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
}
@@ -2959,7 +3199,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c
return error("%s: AcceptBlock FAILED", __func__);
}
- if (!ActivateBestChain(state, pblock))
+ if (!ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed", __func__);
return true;
@@ -3049,13 +3289,13 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune)
}
/* Calculate the block/rev files that should be deleted to remain under target*/
-void FindFilesToPrune(std::set<int>& setFilesToPrune)
+void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
{
LOCK2(cs_main, cs_LastBlockFile);
if (chainActive.Tip() == NULL || nPruneTarget == 0) {
return;
}
- if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) {
+ if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) {
return;
}
@@ -3283,9 +3523,8 @@ CVerifyDB::~CVerifyDB()
uiInterface.ShowProgress("", 100);
}
-bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
+bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth)
{
- const CChainParams& chainparams = Params();
LOCK(cs_main);
if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL)
return true;
@@ -3401,9 +3640,8 @@ bool LoadBlockIndex()
return true;
}
-
-bool InitBlockIndex() {
- const CChainParams& chainparams = Params();
+bool InitBlockIndex(const CChainParams& chainparams)
+{
LOCK(cs_main);
// Initialize global variables that cannot be constructed at startup.
@@ -3414,14 +3652,14 @@ bool InitBlockIndex() {
return true;
// Use the provided setting for -txindex in the new database
- fTxIndex = GetBoolArg("-txindex", false);
+ fTxIndex = GetBoolArg("-txindex", DEFAULT_TXINDEX);
pblocktree->WriteFlag("txindex", fTxIndex);
LogPrintf("Initializing databases...\n");
// Only add the genesis block if not reindexing (in which case we reuse the one already on disk)
if (!fReindex) {
try {
- CBlock &block = const_cast<CBlock&>(Params().GenesisBlock());
+ CBlock &block = const_cast<CBlock&>(chainparams.GenesisBlock());
// Start new block file
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
@@ -3433,7 +3671,7 @@ bool InitBlockIndex() {
CBlockIndex *pindex = AddToBlockIndex(block);
if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("LoadBlockIndex(): genesis block not accepted");
- if (!ActivateBestChain(state, &block))
+ if (!ActivateBestChain(state, chainparams, &block))
return error("LoadBlockIndex(): genesis block cannot be activated");
// Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
@@ -3445,11 +3683,8 @@ bool InitBlockIndex() {
return true;
}
-
-
-bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
+bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp)
{
- const CChainParams& chainparams = Params();
// Map of disk positions for blocks with unknown parent (only used for reindex)
static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent;
int64_t nStart = GetTimeMillis();
@@ -3469,10 +3704,10 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
try {
// locate a header
unsigned char buf[MESSAGE_START_SIZE];
- blkdat.FindByte(Params().MessageStart()[0]);
+ blkdat.FindByte(chainparams.MessageStart()[0]);
nRewind = blkdat.GetPos()+1;
blkdat >> FLATDATA(buf);
- if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE))
+ if (memcmp(buf, chainparams.MessageStart(), MESSAGE_START_SIZE))
continue;
// read size
blkdat >> nSize;
@@ -3748,7 +3983,7 @@ std::string GetWarnings(const std::string& strFor)
if (!CLIENT_VERSION_IS_RELEASE)
strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
- if (GetBoolArg("-testsafemode", false))
+ if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE))
strStatusBar = strRPC = "testsafemode enabled";
// Misc warnings like out of disk space and clock is wrong
@@ -3866,7 +4101,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// best equivalent proof of work) than the best header chain we know about.
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) &&
(pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
- (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, Params().GetConsensus()) < nOneMonth);
+ (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
if (!send) {
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
}
@@ -3989,6 +4224,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
+ if (!(nLocalServices & NODE_BLOOM) &&
+ (strCommand == "filterload" ||
+ strCommand == "filteradd" ||
+ strCommand == "filterclear"))
+ {
+ if (pfrom->nVersion >= NO_BLOOM_VERSION) {
+ Misbehaving(pfrom->GetId(), 100);
+ return false;
+ } else if (GetBoolArg("-enforcenodebloom", false)) {
+ pfrom->fDisconnect = true;
+ return false;
+ }
+ }
if (strCommand == "version")
@@ -4131,6 +4379,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LOCK(cs_main);
State(pfrom->GetId())->fCurrentlyConnected = true;
}
+
+ if (pfrom->nVersion >= SENDHEADERS_VERSION) {
+ // Tell our peer we prefer to receive headers rather than inv's
+ // We send this to non-NODE NETWORK peers as well, because even
+ // non-NODE NETWORK peers can announce blocks (such as pruning
+ // nodes)
+ pfrom->PushMessage("sendheaders");
+ }
}
@@ -4200,6 +4456,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fDisconnect = true;
}
+ else if (strCommand == "sendheaders")
+ {
+ LOCK(cs_main);
+ State(pfrom->GetId())->fPreferHeaders = true;
+ }
+
else if (strCommand == "inv")
{
@@ -4244,7 +4506,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// not a direct successor.
pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash);
CNodeState *nodestate = State(pfrom->GetId());
- if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - chainparams.GetConsensus().nPowTargetSpacing * 20 &&
+ if (CanDirectFetch(chainparams.GetConsensus()) &&
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
vToFetch.push_back(inv);
// Mark block as in flight already, even though the actual "getdata" message only goes out
@@ -4352,6 +4614,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id);
return true;
}
+
+ CNodeState *nodestate = State(pfrom->GetId());
CBlockIndex* pindex = NULL;
if (locator.IsNull())
{
@@ -4379,6 +4643,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
+ // pindex can be NULL either if we sent chainActive.Tip() OR
+ // if our peer has chainActive.Tip() (and thus we are sending an empty
+ // headers message). In both cases it's safe to update
+ // pindexBestHeaderSent to be our tip.
+ nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip();
pfrom->PushMessage("headers", vHeaders);
}
@@ -4408,11 +4677,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
mapAlreadyAskedFor.erase(inv);
- // Check for recently rejected (and do other quick existence checks)
- if (AlreadyHave(inv))
- return true;
-
- if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
+ if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs))
{
mempool.check(pcoinsTip);
RelayTransaction(tx);
@@ -4492,13 +4757,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (pfrom->fWhitelisted && GetBoolArg("-whitelistalwaysrelay", DEFAULT_WHITELISTALWAYSRELAY)) {
// Always relay transactions received from whitelisted peers, even
- // if they were rejected from the mempool, allowing the node to
- // function as a gateway for nodes hidden behind it.
+ // if they were already in the mempool or rejected from it due
+ // to policy, allowing the node to function as a gateway for
+ // nodes hidden behind it.
//
- // FIXME: This includes invalid transactions, which means a
- // whitelisted peer could get us banned! We may want to change
- // that.
- RelayTransaction(tx);
+ // Never relay transactions that we would assign a non-zero DoS
+ // score for, as we expect peers to do the same with us in that
+ // case.
+ int nDoS = 0;
+ if (!state.IsInvalid(nDoS) || nDoS == 0) {
+ LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
+ RelayTransaction(tx);
+ } else {
+ LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
+ }
}
}
int nDoS = 0;
@@ -4567,6 +4839,53 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256());
}
+ bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
+ CNodeState *nodestate = State(pfrom->GetId());
+ // If this set of headers is valid and ends in a block with at least as
+ // much work as our tip, download as much as possible.
+ if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
+ vector<CBlockIndex *> vToFetch;
+ CBlockIndex *pindexWalk = pindexLast;
+ // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
+ while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
+ !mapBlocksInFlight.count(pindexWalk->GetBlockHash())) {
+ // We don't have this block, and it's not yet in flight.
+ vToFetch.push_back(pindexWalk);
+ }
+ pindexWalk = pindexWalk->pprev;
+ }
+ // If pindexWalk still isn't on our main chain, we're looking at a
+ // very large reorg at a time we think we're close to caught up to
+ // the main chain -- this shouldn't really happen. Bail out on the
+ // direct fetch and rely on parallel download instead.
+ if (!chainActive.Contains(pindexWalk)) {
+ LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n",
+ pindexLast->GetBlockHash().ToString(),
+ pindexLast->nHeight);
+ } else {
+ vector<CInv> vGetData;
+ // Download as much as possible, from earliest to latest.
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) {
+ if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
+ // Can't download any more from this peer
+ break;
+ }
+ vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash()));
+ MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex);
+ LogPrint("net", "Requesting block %s from peer=%d\n",
+ pindex->GetBlockHash().ToString(), pfrom->id);
+ }
+ if (vGetData.size() > 1) {
+ LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n",
+ pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
+ }
+ if (vGetData.size() > 0) {
+ pfrom->PushMessage("getdata", vGetData);
+ }
+ }
+ }
+
CheckBlockIndex(chainparams.GetConsensus());
}
@@ -4727,7 +5046,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
uint256 alertHash = alert.GetHash();
if (pfrom->setKnown.count(alertHash) == 0)
{
- if (alert.ProcessAlert(Params().AlertKey()))
+ if (alert.ProcessAlert(chainparams.AlertKey()))
{
// Relay
pfrom->setKnown.insert(alertHash);
@@ -4750,21 +5069,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- else if (!(nLocalServices & NODE_BLOOM) &&
- (strCommand == "filterload" ||
- strCommand == "filteradd" ||
- strCommand == "filterclear") &&
- //TODO: Remove this line after reasonable network upgrade
- pfrom->nVersion >= NO_BLOOM_VERSION)
- {
- if (pfrom->nVersion >= NO_BLOOM_VERSION)
- Misbehaving(pfrom->GetId(), 100);
- //TODO: Enable this after reasonable network upgrade
- //else
- // pfrom->fDisconnect = true;
- }
-
-
else if (strCommand == "filterload")
{
CBloomFilter filter;
@@ -5108,6 +5412,100 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
}
//
+ // Try sending block announcements via headers
+ //
+ {
+ // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our
+ // list of block hashes we're relaying, and our peer wants
+ // headers announcements, then find the first header
+ // not yet known to our peer but would connect, and send.
+ // If no header would connect, or if we have too many
+ // blocks, or if the peer doesn't want headers, just
+ // add all to the inv queue.
+ LOCK(pto->cs_inventory);
+ vector<CBlock> vHeaders;
+ bool fRevertToInv = (!state.fPreferHeaders || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE);
+ CBlockIndex *pBestIndex = NULL; // last header queued for delivery
+ ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date
+
+ if (!fRevertToInv) {
+ bool fFoundStartingHeader = false;
+ // Try to find first header that our peer doesn't have, and
+ // then send all headers past that one. If we come across any
+ // headers that aren't on chainActive, give up.
+ BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) {
+ BlockMap::iterator mi = mapBlockIndex.find(hash);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+ if (chainActive[pindex->nHeight] != pindex) {
+ // Bail out if we reorged away from this block
+ fRevertToInv = true;
+ break;
+ }
+ assert(pBestIndex == NULL || pindex->pprev == pBestIndex);
+ pBestIndex = pindex;
+ if (fFoundStartingHeader) {
+ // add this to the headers message
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else if (PeerHasHeader(&state, pindex)) {
+ continue; // keep looking for the first new block
+ } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) {
+ // Peer doesn't have this header but they do have the prior one.
+ // Start sending headers.
+ fFoundStartingHeader = true;
+ vHeaders.push_back(pindex->GetBlockHeader());
+ } else {
+ // Peer doesn't have this header or the prior one -- nothing will
+ // connect, so bail out.
+ fRevertToInv = true;
+ break;
+ }
+ }
+ }
+ if (fRevertToInv) {
+ // If falling back to using an inv, just try to inv the tip.
+ // The last entry in vBlockHashesToAnnounce was our tip at some point
+ // in the past.
+ if (!pto->vBlockHashesToAnnounce.empty()) {
+ const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back();
+ BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce);
+ assert(mi != mapBlockIndex.end());
+ CBlockIndex *pindex = mi->second;
+
+ // Warn if we're announcing a block that is not on the main chain.
+ // This should be very rare and could be optimized out.
+ // Just log for now.
+ if (chainActive[pindex->nHeight] != pindex) {
+ LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n",
+ hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString());
+ }
+
+ // If the peer announced this block to us, don't inv it back.
+ // (Since block announcements may not be via inv's, we can't solely rely on
+ // setInventoryKnown to track this.)
+ if (!PeerHasHeader(&state, pindex)) {
+ pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce));
+ LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__,
+ pto->id, hashToAnnounce.ToString());
+ }
+ }
+ } else if (!vHeaders.empty()) {
+ if (vHeaders.size() > 1) {
+ LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__,
+ vHeaders.size(),
+ vHeaders.front().GetHash().ToString(),
+ vHeaders.back().GetHash().ToString(), pto->id);
+ } else {
+ LogPrint("net", "%s: sending header %s to peer=%d\n", __func__,
+ vHeaders.front().GetHash().ToString(), pto->id);
+ }
+ pto->PushMessage("headers", vHeaders);
+ state.pindexBestHeaderSent = pBestIndex;
+ }
+ pto->vBlockHashesToAnnounce.clear();
+ }
+
+ //
// Message: inventory
//
vector<CInv> vInv;
diff --git a/src/main.h b/src/main.h
index eb61ff9570..bdbfa3826e 100644
--- a/src/main.h
+++ b/src/main.h
@@ -56,8 +56,6 @@ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 101;
static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25;
/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */
static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101;
-/** Default for -maxmempool, maximum megabytes of mempool memory usage */
-static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 72;
/** The maximum size of a blk?????.dat file (since 0.8) */
@@ -66,6 +64,7 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
+
/** Maximum number of script-checking threads allowed */
static const int MAX_SCRIPTCHECK_THREADS = 16;
/** -par default (number of script-checking threads, 0 = auto) */
@@ -88,6 +87,19 @@ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum length of reject messages. */
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
+static const unsigned int DEFAULT_LIMITFREERELAY = 15;
+static const bool DEFAULT_RELAYPRIORITY = true;
+
+/** Default for -permitbaremultisig */
+static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
+static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
+static const bool DEFAULT_TXINDEX = false;
+static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
+
+static const bool DEFAULT_TESTSAFEMODE = false;
+
+/** Maximum number of headers to announce when relaying blocks with headers message.*/
+static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8;
struct BlockHasher
{
@@ -172,9 +184,9 @@ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
/** Import blocks from an external file */
-bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL);
+bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = NULL);
/** Initialize a new block tree database + block data on disk */
-bool InitBlockIndex();
+bool InitBlockIndex(const CChainParams& chainparams);
/** Load the block tree and coins database from disk */
bool LoadBlockIndex();
/** Unload database information */
@@ -199,7 +211,7 @@ std::string GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
-bool ActivateBestChain(CValidationState &state, const CBlock *pblock = NULL);
+bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL);
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
/**
@@ -210,14 +222,14 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
*
* Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
* Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
- * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 10 on regtest).
+ * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
* Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
* The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
* A db flag records the fact that at least some block files have been pruned.
*
* @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
*/
-void FindFilesToPrune(std::set<int>& setFilesToPrune);
+void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
/**
* Actually unlink the specified files
@@ -383,9 +395,6 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
-/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */
-bool AcceptBlock(const CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp);
-
class CBlockFileInfo
{
@@ -446,7 +455,7 @@ class CVerifyDB {
public:
CVerifyDB();
~CVerifyDB();
- bool VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth);
+ bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth);
};
/** Find the last common block between the parameter chain and a locator. */
diff --git a/src/miner.cpp b/src/miner.cpp
index bb6b513372..27a1fbcf80 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -10,6 +10,7 @@
#include "chainparams.h"
#include "coins.h"
#include "consensus/consensus.h"
+#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.h"
#include "main.h"
@@ -153,7 +154,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
// Priority order to process transactions
list<COrphan> vOrphan; // list memory doesn't move
map<uint256, vector<COrphan*> > mapDependers;
- bool fPrintPriority = GetBoolArg("-printpriority", false);
+ bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
// This vector will be sorted into a priority queue:
vector<TxPriority> vecPriority;
@@ -373,7 +374,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = txCoinbase;
- pblock->hashMerkleRoot = pblock->ComputeMerkleRoot();
+ pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
}
//////////////////////////////////////////////////////////////////////////////
diff --git a/src/miner.h b/src/miner.h
index 7b544303e0..16c8e2a976 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -17,8 +17,11 @@ class CScript;
class CWallet;
namespace Consensus { struct Params; };
+static const bool DEFAULT_GENERATE = false;
static const int DEFAULT_GENERATE_THREADS = 1;
+static const bool DEFAULT_PRINTPRIORITY = false;
+
struct CBlockTemplate
{
CBlock block;
diff --git a/src/net.cpp b/src/net.cpp
index cff4c54505..abc7cbb8f4 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -521,12 +521,11 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti
banEntry.banReason = banReason;
if (bantimeoffset <= 0)
{
- bantimeoffset = GetArg("-bantime", 60*60*24); // Default 24-hour ban
+ bantimeoffset = GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME);
sinceUnixEpoch = false;
}
banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
-
LOCK(cs_setBanned);
if (setBanned[subNet].nBanUntil < banEntry.nBanUntil)
setBanned[subNet] = banEntry;
@@ -1414,7 +1413,7 @@ void ThreadDNSAddressSeed()
{
// goal: only query DNS seeds if address need is acute
if ((addrman.size() > 0) &&
- (!GetBoolArg("-forcednsseed", false))) {
+ (!GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) {
MilliSleep(11 * 1000);
LOCK(cs_vNodes);
@@ -2337,8 +2336,8 @@ bool CAddrDB::Read(CAddrMan& addr)
return true;
}
-unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
-unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
+unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); }
+unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); }
CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) :
ssSend(SER_NETWORK, INIT_PROTO_VERSION),
diff --git a/src/net.h b/src/net.h
index 559cdf0878..fb299fb0b4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -65,6 +65,13 @@ static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
/** Default for blocks only*/
static const bool DEFAULT_BLOCKSONLY = false;
+static const bool DEFAULT_FORCEDNSSEED = false;
+static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
+static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
+
+// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
+static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
+
unsigned int ReceiveFloodSize();
unsigned int SendBufferSize();
@@ -383,6 +390,9 @@ public:
std::vector<CInv> vInventoryToSend;
CCriticalSection cs_inventory;
std::multimap<int64_t, CInv> mapAskFor;
+ // Used for headers announcements - unfiltered blocks to relay
+ // Also protected by cs_inventory
+ std::vector<uint256> vBlockHashesToAnnounce;
// Ping time measurement:
// The pong reply we're expecting, or 0 if no pong expected.
@@ -497,6 +507,12 @@ public:
}
}
+ void PushBlockHash(const uint256 &hash)
+ {
+ LOCK(cs_inventory);
+ vBlockHashesToAnnounce.push_back(hash);
+ }
+
void AskFor(const CInv& inv);
// TODO: Document the postcondition of this function. Is cs_vSend locked?
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 83cedfb620..05214cb026 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -40,7 +40,7 @@ static proxyType proxyInfo[NET_MAX];
static proxyType nameProxy;
static CCriticalSection cs_proxyInfos;
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
-bool fNameLookup = false;
+bool fNameLookup = DEFAULT_NAME_LOOKUP;
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
diff --git a/src/netbase.h b/src/netbase.h
index 2a79f82d72..9c2df0338e 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -19,8 +19,10 @@
extern int nConnectTimeout;
extern bool fNameLookup;
-/** -timeout default */
+//! -timeout default
static const int DEFAULT_CONNECT_TIMEOUT = 5000;
+//! -dns default
+static const int DEFAULT_NAME_LOOKUP = true;
#ifdef WIN32
// In MSVC, this is defined as a macro, undefine it to prevent a compile and link error
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index ffe31d1942..980ecf10df 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "policy/fees.h"
+#include "policy/policy.h"
#include "amount.h"
#include "primitives/transaction.h"
@@ -504,6 +505,33 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
return CFeeRate(median);
}
+CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool)
+{
+ if (answerFoundAtTarget)
+ *answerFoundAtTarget = confTarget;
+ // Return failure if trying to analyze a target we're not tracking
+ if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
+ return CFeeRate(0);
+
+ double median = -1;
+ while (median < 0 && (unsigned int)confTarget <= feeStats.GetMaxConfirms()) {
+ median = feeStats.EstimateMedianVal(confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
+ }
+
+ if (answerFoundAtTarget)
+ *answerFoundAtTarget = confTarget - 1;
+
+ // If mempool is limiting txs , return at least the min fee from the mempool
+ CAmount minPoolFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ if (minPoolFee > 0 && minPoolFee > median)
+ return CFeeRate(minPoolFee);
+
+ if (median < 0)
+ return CFeeRate(0);
+
+ return CFeeRate(median);
+}
+
double CBlockPolicyEstimator::estimatePriority(int confTarget)
{
// Return failure if trying to analyze a target we're not tracking
@@ -513,6 +541,30 @@ double CBlockPolicyEstimator::estimatePriority(int confTarget)
return priStats.EstimateMedianVal(confTarget, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
}
+double CBlockPolicyEstimator::estimateSmartPriority(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool)
+{
+ if (answerFoundAtTarget)
+ *answerFoundAtTarget = confTarget;
+ // Return failure if trying to analyze a target we're not tracking
+ if (confTarget <= 0 || (unsigned int)confTarget > priStats.GetMaxConfirms())
+ return -1;
+
+ // If mempool is limiting txs, no priority txs are allowed
+ CAmount minPoolFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ if (minPoolFee > 0)
+ return INF_PRIORITY;
+
+ double median = -1;
+ while (median < 0 && (unsigned int)confTarget <= priStats.GetMaxConfirms()) {
+ median = priStats.EstimateMedianVal(confTarget++, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
+ }
+
+ if (answerFoundAtTarget)
+ *answerFoundAtTarget = confTarget - 1;
+
+ return median;
+}
+
void CBlockPolicyEstimator::Write(CAutoFile& fileout)
{
fileout << nBestSeenHeight;
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 15577d128a..7a293267d4 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -15,6 +15,7 @@
class CAutoFile;
class CFeeRate;
class CTxMemPoolEntry;
+class CTxMemPool;
/** \class CBlockPolicyEstimator
* The BlockPolicyEstimator is used for estimating the fee or priority needed
@@ -182,8 +183,8 @@ static const unsigned int MAX_BLOCK_CONFIRMS = 25;
/** Decay of .998 is a half-life of 346 blocks or about 2.4 days */
static const double DEFAULT_DECAY = .998;
-/** Require greater than 85% of X fee transactions to be confirmed within Y blocks for X to be big enough */
-static const double MIN_SUCCESS_PCT = .85;
+/** Require greater than 95% of X fee transactions to be confirmed within Y blocks for X to be big enough */
+static const double MIN_SUCCESS_PCT = .95;
static const double UNLIKELY_PCT = .5;
/** Require an avg of 1 tx in the combined fee bucket per block to have stat significance */
@@ -242,9 +243,21 @@ public:
/** Return a fee estimate */
CFeeRate estimateFee(int confTarget);
+ /** Estimate fee rate needed to get be included in a block within
+ * confTarget blocks. If no answer can be given at confTarget, return an
+ * estimate at the lowest target where one can be given.
+ */
+ CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool);
+
/** Return a priority estimate */
double estimatePriority(int confTarget);
+ /** Estimate priority needed to get be included in a block within
+ * confTarget blocks. If no answer can be given at confTarget, return an
+ * estimate at the lowest target where one can be given.
+ */
+ double estimateSmartPriority(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool);
+
/** Write estimation data to a file */
void Write(CAutoFile& fileout);
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 4c96fbf5a5..46c7f18942 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -50,7 +50,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
if (m < 1 || m > n)
return false;
} else if (whichType == TX_NULL_DATA &&
- (!GetBoolArg("-datacarrier", true) || scriptPubKey.size() > nMaxDatacarrierBytes))
+ (!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes))
return false;
return whichType != TX_NONSTANDARD;
diff --git a/src/policy/policy.h b/src/policy/policy.h
index f269e8d476..c8d2c1a924 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -25,6 +25,8 @@ static const unsigned int MAX_STANDARD_TX_SIZE = 100000;
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 -maxmempool, maximum megabytes of mempool memory usage */
+static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index 7a58074d24..7280c18f77 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -15,69 +15,6 @@ uint256 CBlockHeader::GetHash() const
return SerializeHash(*this);
}
-uint256 CBlock::ComputeMerkleRoot(bool* fMutated) const
-{
- /* WARNING! If you're reading this because you're learning about crypto
- and/or designing a new system that will use merkle trees, keep in mind
- that the following merkle tree algorithm has a serious flaw related to
- duplicate txids, resulting in a vulnerability (CVE-2012-2459).
-
- The reason is that if the number of hashes in the list at a given time
- is odd, the last one is duplicated before computing the next level (which
- is unusual in Merkle trees). This results in certain sequences of
- transactions leading to the same merkle root. For example, these two
- trees:
-
- A A
- / \ / \
- B C B C
- / \ | / \ / \
- D E F D E F F
- / \ / \ / \ / \ / \ / \ / \
- 1 2 3 4 5 6 1 2 3 4 5 6 5 6
-
- for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and
- 6 are repeated) result in the same root hash A (because the hash of both
- of (F) and (F,F) is C).
-
- The vulnerability results from being able to send a block with such a
- transaction list, with the same merkle root, and the same block hash as
- the original without duplication, resulting in failed validation. If the
- receiving node proceeds to mark that block as permanently invalid
- however, it will fail to accept further unmodified (and thus potentially
- valid) versions of the same block. We defend against this by detecting
- the case where we would hash two identical hashes at the end of the list
- together, and treating that identically to the block having an invalid
- merkle root. Assuming no double-SHA256 collisions, this will detect all
- known ways of changing the transactions without affecting the merkle
- root.
- */
- std::vector<uint256> vMerkleTree;
- vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
- for (std::vector<CTransaction>::const_iterator it(vtx.begin()); it != vtx.end(); ++it)
- vMerkleTree.push_back(it->GetHash());
- int j = 0;
- bool mutated = false;
- for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
- {
- for (int i = 0; i < nSize; i += 2)
- {
- int i2 = std::min(i+1, nSize-1);
- if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j+i] == vMerkleTree[j+i2]) {
- // Two identical hashes at the end of the list at a particular level.
- mutated = true;
- }
- vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
- BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
- }
- j += nSize;
- }
- if (fMutated) {
- *fMutated = mutated;
- }
- return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
-}
-
std::string CBlock::ToString() const
{
std::stringstream s;
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 54731ff557..5c017d436f 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -118,12 +118,6 @@ public:
return block;
}
- // Build the merkle tree for this block and return the merkle root.
- // If non-NULL, *mutated is set to whether mutation was detected in the merkle
- // tree (a duplication of transactions in the block leading to an identical
- // merkle root).
- uint256 ComputeMerkleRoot(bool* mutated = NULL) const;
-
std::string ToString() const;
};
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index bda8acff15..6e6330d2a4 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -201,8 +201,10 @@ public:
/// Create payment server
void createPaymentServer();
#endif
+ /// parameter interaction/setup based on rules
+ void parameterSetup();
/// Create options model
- void createOptionsModel();
+ void createOptionsModel(bool resetSettings);
/// Create main window
void createWindow(const NetworkStyle *networkStyle);
/// Create splash screen
@@ -309,14 +311,8 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv):
// UI per-platform customization
// This must be done inside the BitcoinApplication constructor, or after it, because
// PlatformStyle::instantiate requires a QApplication
-#if defined(Q_OS_MAC)
- std::string platformName = "macosx";
-#elif defined(Q_OS_WIN)
- std::string platformName = "windows";
-#else
- std::string platformName = "other";
-#endif
- platformName = GetArg("-uiplatform", platformName);
+ std::string platformName;
+ platformName = GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM);
platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName));
if (!platformStyle) // Fall back to "other" if specified name not found
platformStyle = PlatformStyle::instantiate("other");
@@ -352,9 +348,9 @@ void BitcoinApplication::createPaymentServer()
}
#endif
-void BitcoinApplication::createOptionsModel()
+void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel();
+ optionsModel = new OptionsModel(NULL, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
@@ -397,6 +393,12 @@ void BitcoinApplication::startThread()
coreThread->start();
}
+void BitcoinApplication::parameterSetup()
+{
+ InitLogging();
+ InitParameterInteraction();
+}
+
void BitcoinApplication::requestInitialize()
{
qDebug() << __func__ << ": Requesting initialize";
@@ -644,13 +646,15 @@ int main(int argc, char *argv[])
// Install qDebug() message handler to route to debug.log
qInstallMessageHandler(DebugMessageHandler);
#endif
+ // Allow parameter interaction before we create the options model
+ app.parameterSetup();
// Load GUI settings from QSettings
- app.createOptionsModel();
+ app.createOptionsModel(mapArgs.count("-resetguisettings") != 0);
// Subscribe to global signals from core
uiInterface.InitMessage.connect(InitMessage);
- if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false))
+ if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
try
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 6f9f6e90d6..b2bd167aea 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -59,6 +59,16 @@
#include <QUrlQuery>
#endif
+const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
+#if defined(Q_OS_MAC)
+ "macosx"
+#elif defined(Q_OS_WIN)
+ "windows"
+#else
+ "other"
+#endif
+ ;
+
const QString BitcoinGUI::DEFAULT_WALLET = "~Default";
BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
@@ -443,8 +453,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
setNumConnections(clientModel->getNumConnections());
connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
- setNumBlocks(clientModel->getNumBlocks(), clientModel->getLastBlockDate());
- connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(setNumBlocks(int,QDateTime)));
+ setNumBlocks(clientModel->getNumBlocks(), clientModel->getLastBlockDate(), clientModel->getVerificationProgress(NULL));
+ connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double)), this, SLOT(setNumBlocks(int,QDateTime,double)));
// Receive and report messages from client model
connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int)));
@@ -672,7 +682,7 @@ void BitcoinGUI::setNumConnections(int count)
labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
}
-void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate)
+void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress)
{
if(!clientModel)
return;
@@ -749,7 +759,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate)
progressBarLabel->setVisible(true);
progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
progressBar->setMaximum(1000000000);
- progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5);
+ progressBar->setValue(nVerificationProgress * 1000000000.0 + 0.5);
progressBar->setVisible(true);
tooltip = tr("Catching up...") + QString("<br>") + tooltip;
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 2b98dabc56..b121a443e7 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -48,6 +48,7 @@ class BitcoinGUI : public QMainWindow
public:
static const QString DEFAULT_WALLET;
+ static const std::string DEFAULT_UIPLATFORM;
explicit BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0);
~BitcoinGUI();
@@ -149,7 +150,7 @@ public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set number of blocks and last block date shown in the UI */
- void setNumBlocks(int count, const QDateTime& blockDate);
+ void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress);
/** Notify the user of an event from the core network or transaction handling code.
@param[in] title the message box / notification title
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 566e8fa62d..d36d129c1a 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -22,17 +22,16 @@
#include <QDebug>
#include <QTimer>
+class CBlockIndex;
+
static const int64_t nClientStartupTime = GetTime();
+static int64_t nLastBlockTipUpdateNotification = 0;
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
QObject(parent),
optionsModel(optionsModel),
peerTableModel(0),
banTableModel(0),
- cachedNumBlocks(0),
- cachedBlockDate(QDateTime()),
- cachedReindexing(0),
- cachedImporting(0),
pollTimer(0)
{
peerTableModel = new PeerTableModel(this);
@@ -99,40 +98,21 @@ size_t ClientModel::getMempoolDynamicUsage() const
return mempool.DynamicMemoryUsage();
}
-double ClientModel::getVerificationProgress() const
+double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const
{
- LOCK(cs_main);
- return Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip());
+ CBlockIndex *tip = const_cast<CBlockIndex *>(tipIn);
+ if (!tip)
+ {
+ LOCK(cs_main);
+ tip = chainActive.Tip();
+ }
+ return Checkpoints::GuessVerificationProgress(Params().Checkpoints(), tip);
}
void ClientModel::updateTimer()
{
- // Get required lock upfront. This avoids the GUI from getting stuck on
- // periodical polls if the core is holding the locks for a longer time -
- // for example, during a wallet rescan.
- TRY_LOCK(cs_main, lockMain);
- if (!lockMain)
- return;
-
- // Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
- // Periodically check and update with a timer.
- int newNumBlocks = getNumBlocks();
- QDateTime newBlockDate = getLastBlockDate();
-
- // check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state
- if (cachedNumBlocks != newNumBlocks ||
- cachedBlockDate != newBlockDate ||
- cachedReindexing != fReindex ||
- cachedImporting != fImporting)
- {
- cachedNumBlocks = newNumBlocks;
- cachedBlockDate = newBlockDate;
- cachedReindexing = fReindex;
- cachedImporting = fImporting;
-
- Q_EMIT numBlocksChanged(newNumBlocks, newBlockDate);
- }
-
+ // no locking required at this point
+ // the following calls will aquire the required lock
Q_EMIT mempoolSizeChanged(getMempoolSize(), getMempoolDynamicUsage());
Q_EMIT bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}
@@ -261,6 +241,23 @@ static void BannedListChanged(ClientModel *clientmodel)
QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
}
+static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CBlockIndex *pIndex)
+{
+ // lock free async UI updates in case we have a new block tip
+ // during initial sync, only update the UI if the last update
+ // was > 250ms (MODEL_UPDATE_DELAY) ago
+ int64_t now = 0;
+ if (initialSync)
+ now = GetTimeMillis();
+
+ // if we are in-sync, update the UI regardless of last update time
+ if (!initialSync || now - nLastBlockTipUpdateNotification > MODEL_UPDATE_DELAY) {
+ //pass a async signal to the UI thread
+ Q_EMIT clientmodel->numBlocksChanged(pIndex->nHeight, QDateTime::fromTime_t(pIndex->GetBlockTime()), clientmodel->getVerificationProgress(pIndex));
+ nLastBlockTipUpdateNotification = now;
+ }
+}
+
void ClientModel::subscribeToCoreSignals()
{
// Connect signals to client
@@ -268,6 +265,7 @@ void ClientModel::subscribeToCoreSignals()
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
+ uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2));
}
void ClientModel::unsubscribeFromCoreSignals()
@@ -277,4 +275,5 @@ void ClientModel::unsubscribeFromCoreSignals()
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
+ uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2));
}
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 493a759331..2d204fdb67 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -15,6 +15,7 @@ class PeerTableModel;
class TransactionTableModel;
class CWallet;
+class CBlockIndex;
QT_BEGIN_NAMESPACE
class QTimer;
@@ -59,7 +60,7 @@ public:
quint64 getTotalBytesRecv() const;
quint64 getTotalBytesSent() const;
- double getVerificationProgress() const;
+ double getVerificationProgress(const CBlockIndex *tip) const;
QDateTime getLastBlockDate() const;
//! Return true if core is doing initial block download
@@ -81,11 +82,6 @@ private:
PeerTableModel *peerTableModel;
BanTableModel *banTableModel;
- int cachedNumBlocks;
- QDateTime cachedBlockDate;
- bool cachedReindexing;
- bool cachedImporting;
-
QTimer *pollTimer;
void subscribeToCoreSignals();
@@ -93,7 +89,7 @@ private:
Q_SIGNALS:
void numConnectionsChanged(int count);
- void numBlocksChanged(int count, const QDateTime& blockDate);
+ void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
void alertsChanged(const QString &warnings);
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 81b2597c3b..cbc41f3416 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -538,7 +538,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
// Priority
- double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
+ double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);
dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority);
@@ -550,10 +550,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// Fee
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
- // Allow free?
- double dPriorityNeeded = mempoolEstimatePriority;
- if (dPriorityNeeded <= 0)
- dPriorityNeeded = AllowFreeThreshold(); // not enough data, back to hard-coded
+ // Allow free? (require at least hard-coded threshold and default to that if no estimate)
+ double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold());
fAllowFree = (dPriority >= dPriorityNeeded);
if (fSendFreeTransactions)
@@ -649,8 +647,9 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
double dFeeVary;
if (payTxFee.GetFeePerK() > 0)
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
- else
- dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), mempool.estimateFee(nTxConfirmTarget).GetFeePerK()) / 1000;
+ else {
+ dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / 1000;
+ }
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
@@ -686,7 +685,7 @@ void CoinControlDialog::updateView()
QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
- double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
+ double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget);
std::map<QString, std::vector<COutput> > mapCoins;
model->listCoins(mapCoins);
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 7d3e48ff32..216f23f139 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -14,6 +14,8 @@ static const int MAX_PASSPHRASE_SIZE = 1024;
/* BitcoinGUI -- Size of icons in status bar */
static const int STATUSBAR_ICONSIZE = 16;
+static const bool DEFAULT_SPLASHSCREEN = true;
+
/* Invalid field background style */
#define STYLE_INVALID "background:#FF8080"
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 4ab87e0f32..ab63e98d40 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -162,7 +162,7 @@ void Intro::pickDataDirectory()
/* 2) Allow QSettings to override default dir */
dataDir = settings.value("strDataDir", dataDir).toString();
- if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", false))
+ if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR))
{
/* If current default data directory does not exist, let the user choose one */
Intro intro;
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 50783f7225..1d49922e93 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -9,6 +9,8 @@
#include <QMutex>
#include <QThread>
+static const bool DEFAULT_CHOOSE_DATADIR = false;
+
class FreespaceChecker;
namespace Ui {
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index d0191fa6d8..647c860bdc 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -34,8 +34,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
QDialog(parent),
ui(new Ui::OptionsDialog),
model(0),
- mapper(0),
- fProxyIpsValid(true)
+ mapper(0)
{
ui->setupUi(this);
@@ -60,12 +59,11 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool)));
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool)));
+ connect(ui->connectSocks, SIGNAL(toggled(bool)), this, SLOT(updateProxyValidationState()));
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyIpTor, SLOT(setEnabled(bool)));
connect(ui->connectSocksTor, SIGNAL(toggled(bool)), ui->proxyPortTor, SLOT(setEnabled(bool)));
-
- ui->proxyIp->installEventFilter(this);
- ui->proxyIpTor->installEventFilter(this);
+ connect(ui->connectSocksTor, SIGNAL(toggled(bool)), this, SLOT(updateProxyValidationState()));
/* Window elements init */
#ifdef Q_OS_MAC
@@ -119,7 +117,12 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
mapper->setOrientation(Qt::Vertical);
/* setup/change UI elements when proxy IPs are invalid/valid */
- connect(this, SIGNAL(proxyIpChecks(QValidatedLineEdit *, int)), this, SLOT(doProxyIpChecks(QValidatedLineEdit *, int)));
+ ui->proxyIp->setCheckValidator(new ProxyAddressValidator(parent));
+ ui->proxyIpTor->setCheckValidator(new ProxyAddressValidator(parent));
+ connect(ui->proxyIp, SIGNAL(validationDidChange(QValidatedLineEdit *)), this, SLOT(updateProxyValidationState()));
+ connect(ui->proxyIpTor, SIGNAL(validationDidChange(QValidatedLineEdit *)), this, SLOT(updateProxyValidationState()));
+ connect(ui->proxyPort, SIGNAL(textChanged(const QString&)), this, SLOT(updateProxyValidationState()));
+ connect(ui->proxyPortTor, SIGNAL(textChanged(const QString&)), this, SLOT(updateProxyValidationState()));
}
OptionsDialog::~OptionsDialog()
@@ -200,18 +203,6 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->thirdPartyTxUrls, OptionsModel::ThirdPartyTxUrls);
}
-void OptionsDialog::enableOkButton()
-{
- /* prevent enabling of the OK button when data modified, if there is an invalid proxy address present */
- if(fProxyIpsValid)
- setOkButtonState(true);
-}
-
-void OptionsDialog::disableOkButton()
-{
- setOkButtonState(false);
-}
-
void OptionsDialog::setOkButtonState(bool fState)
{
ui->okButton->setEnabled(fState);
@@ -269,24 +260,20 @@ void OptionsDialog::clearStatusLabel()
ui->statusLabel->clear();
}
-void OptionsDialog::doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort)
+void OptionsDialog::updateProxyValidationState()
{
- Q_UNUSED(nProxyPort);
-
- CService addrProxy;
-
- /* Check for a valid IPv4 / IPv6 address */
- if (!(fProxyIpsValid = LookupNumeric(pUiProxyIp->text().toStdString().c_str(), addrProxy)))
+ QValidatedLineEdit *pUiProxyIp = ui->proxyIp;
+ QValidatedLineEdit *otherProxyWidget = (pUiProxyIp == ui->proxyIpTor) ? ui->proxyIp : ui->proxyIpTor;
+ if (pUiProxyIp->isValid() && (!ui->proxyPort->isEnabled() || ui->proxyPort->text().toInt() > 0) && (!ui->proxyPortTor->isEnabled() || ui->proxyPortTor->text().toInt() > 0))
{
- disableOkButton();
- pUiProxyIp->setValid(false);
- ui->statusLabel->setStyleSheet("QLabel { color: red; }");
- ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
+ setOkButtonState(otherProxyWidget->isValid()); //only enable ok button if both proxys are valid
+ ui->statusLabel->clear();
}
else
{
- enableOkButton();
- ui->statusLabel->clear();
+ setOkButtonState(false);
+ ui->statusLabel->setStyleSheet("QLabel { color: red; }");
+ ui->statusLabel->setText(tr("The supplied proxy address is invalid."));
}
}
@@ -312,18 +299,18 @@ void OptionsDialog::updateDefaultProxyNets()
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false);
}
-bool OptionsDialog::eventFilter(QObject *object, QEvent *event)
+ProxyAddressValidator::ProxyAddressValidator(QObject *parent) :
+QValidator(parent)
{
- if(event->type() == QEvent::FocusOut)
- {
- if(object == ui->proxyIp)
- {
- Q_EMIT proxyIpChecks(ui->proxyIp, ui->proxyPort->text().toInt());
- }
- else if(object == ui->proxyIpTor)
- {
- Q_EMIT proxyIpChecks(ui->proxyIpTor, ui->proxyPortTor->text().toInt());
- }
- }
- return QDialog::eventFilter(object, event);
+}
+
+QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) const
+{
+ Q_UNUSED(pos);
+ // Validate the proxy
+ proxyType addrProxy = proxyType(CService(input.toStdString(), 9050), true);
+ if (addrProxy.IsValid())
+ return QValidator::Acceptable;
+
+ return QValidator::Invalid;
}
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index 348489c599..489e35da49 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -6,6 +6,7 @@
#define BITCOIN_QT_OPTIONSDIALOG_H
#include <QDialog>
+#include <QValidator>
class OptionsModel;
class QValidatedLineEdit;
@@ -18,6 +19,18 @@ namespace Ui {
class OptionsDialog;
}
+/** Proxy address widget validator, checks for a valid proxy address.
+ */
+class ProxyAddressValidator : public QValidator
+{
+ Q_OBJECT
+
+public:
+ explicit ProxyAddressValidator(QObject *parent);
+
+ State validate(QString &input, int &pos) const;
+};
+
/** Preferences dialog. */
class OptionsDialog : public QDialog
{
@@ -30,14 +43,7 @@ public:
void setModel(OptionsModel *model);
void setMapper();
-protected:
- bool eventFilter(QObject *object, QEvent *event);
-
private Q_SLOTS:
- /* enable OK button */
- void enableOkButton();
- /* disable OK button */
- void disableOkButton();
/* set OK button state (enabled / disabled) */
void setOkButtonState(bool fState);
void on_resetButton_clicked();
@@ -46,7 +52,7 @@ private Q_SLOTS:
void showRestartWarning(bool fPersistent = false);
void clearStatusLabel();
- void doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort);
+ void updateProxyValidationState();
/* query the networks, for which the default proxy is used */
void updateDefaultProxyNets();
@@ -57,7 +63,6 @@ private:
Ui::OptionsDialog *ui;
OptionsModel *model;
QDataWidgetMapper *mapper;
- bool fProxyIpsValid;
};
#endif // BITCOIN_QT_OPTIONSDIALOG_H
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 65e490570e..3e5c6c72b1 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -26,10 +26,10 @@
#include <QSettings>
#include <QStringList>
-OptionsModel::OptionsModel(QObject *parent) :
+OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
QAbstractListModel(parent)
{
- Init();
+ Init(resetSettings);
}
void OptionsModel::addOverriddenOption(const std::string &option)
@@ -38,8 +38,11 @@ void OptionsModel::addOverriddenOption(const std::string &option)
}
// Writes all missing QSettings with their default values
-void OptionsModel::Init()
+void OptionsModel::Init(bool resetSettings)
{
+ if (resetSettings)
+ Reset();
+
QSettings settings;
// Ensure restart flag is unset on client startup
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 8448cad8de..d5bddb1a94 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -24,7 +24,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(QObject *parent = 0);
+ explicit OptionsModel(QObject *parent = 0, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -48,7 +48,7 @@ public:
OptionIDRowCount,
};
- void Init();
+ void Init(bool resetSettings = false);
void Reset();
int rowCount(const QModelIndex & parent = QModelIndex()) const;
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index 78a783dea4..1f54c62b6e 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -145,7 +145,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c
int error = X509_STORE_CTX_get_error(store_ctx);
// For testing payment requests, we allow self signed root certs!
// This option is just shown in the UI options, if -help-debug is enabled.
- if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GetBoolArg("-allowselfsignedrootcertificates", false))) {
+ if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) {
throw SSLVerifyError(X509_verify_cert_error_string(error));
} else {
qDebug() << "PaymentRequestPlus::getMerchant: Allowing self signed root certificate, because -allowselfsignedrootcertificates is true.";
diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h
index 99a7186b85..8a7c4c0623 100644
--- a/src/qt/paymentrequestplus.h
+++ b/src/qt/paymentrequestplus.h
@@ -15,6 +15,8 @@
#include <QList>
#include <QString>
+static const bool DEFAULT_SELFSIGNED_ROOTCERTS = false;
+
//
// Wraps dumb protocol buffer paymentRequest
// with extra methods
diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp
index 346369392c..5658a0bdcf 100644
--- a/src/qt/qvalidatedlineedit.cpp
+++ b/src/qt/qvalidatedlineedit.cpp
@@ -99,9 +99,25 @@ void QValidatedLineEdit::checkValidity()
}
else
setValid(false);
+
+ Q_EMIT validationDidChange(this);
}
void QValidatedLineEdit::setCheckValidator(const QValidator *v)
{
checkValidator = v;
}
+
+bool QValidatedLineEdit::isValid()
+{
+ // use checkValidator in case the QValidatedLineEdit is disabled
+ if (checkValidator)
+ {
+ QString address = text();
+ int pos = 0;
+ if (checkValidator->validate(address, pos) == QValidator::Acceptable)
+ return true;
+ }
+
+ return valid;
+}
diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h
index 8665acda5e..8cb6a425fa 100644
--- a/src/qt/qvalidatedlineedit.h
+++ b/src/qt/qvalidatedlineedit.h
@@ -18,6 +18,7 @@ public:
explicit QValidatedLineEdit(QWidget *parent);
void clear();
void setCheckValidator(const QValidator *v);
+ bool isValid();
protected:
void focusInEvent(QFocusEvent *evt);
@@ -31,6 +32,9 @@ public Q_SLOTS:
void setValid(bool valid);
void setEnabled(bool enabled);
+Q_SIGNALS:
+ void validationDidChange(QValidatedLineEdit *validatedLineEdit);
+
private Q_SLOTS:
void markValid();
void checkValidity();
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 619c8631ae..30e551de19 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -263,7 +263,13 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, SLOT(clear()));
// set library version labels
+
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION));
+#else
+ ui->openSSLVersion->setText(OpenSSL_version(OPENSSL_VERSION));
+#endif
+
#ifdef ENABLE_WALLET
ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0));
#else
@@ -337,8 +343,8 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumConnections(model->getNumConnections());
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
- setNumBlocks(model->getNumBlocks(), model->getLastBlockDate());
- connect(model, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(setNumBlocks(int,QDateTime)));
+ setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(NULL));
+ connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double)), this, SLOT(setNumBlocks(int,QDateTime,double)));
updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
@@ -519,7 +525,7 @@ void RPCConsole::setNumConnections(int count)
ui->numberOfConnections->setText(connections);
}
-void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate)
+void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress)
{
ui->numberOfBlocks->setText(QString::number(count));
ui->lastBlockTime->setText(blockDate.toString());
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 4b242affcd..4aebad480c 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -83,7 +83,7 @@ public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set number of blocks and last block date shown in the UI */
- void setNumBlocks(int count, const QDateTime& blockDate);
+ void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress);
/** Set size (number of transactions and memory usage) of the mempool in the UI */
void setMempoolSize(long numberOfTxs, size_t dynUsage);
/** Go forward or back in history */
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 4b030fdaa0..0fd86da034 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -124,7 +124,7 @@ void SendCoinsDialog::setClientModel(ClientModel *clientModel)
this->clientModel = clientModel;
if (clientModel) {
- connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime)), this, SLOT(updateSmartFeeLabel()));
+ connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double)), this, SLOT(updateSmartFeeLabel()));
}
}
@@ -633,7 +633,8 @@ void SendCoinsDialog::updateSmartFeeLabel()
return;
int nBlocksToConfirm = defaultConfirmTarget - ui->sliderSmartFee->value();
- CFeeRate feeRate = mempool.estimateFee(nBlocksToConfirm);
+ int estimateFoundAtBlocks = nBlocksToConfirm;
+ CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
if (feeRate <= CFeeRate(0)) // not enough data => minfee
{
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)) + "/kB");
@@ -644,7 +645,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
{
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
ui->labelSmartFee2->hide();
- ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", nBlocksToConfirm));
+ ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks));
}
updateFeeMinimizedLabel();
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 5e26f3e01b..f609289749 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -8,6 +8,9 @@
#include "bitcoingui.h"
#include "clientmodel.h"
+#include "guiconstants.h"
+#include "intro.h"
+#include "paymentrequestplus.h"
#include "guiutil.h"
#include "clientversion.h"
@@ -70,7 +73,22 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) :
cursor.insertText(header);
cursor.insertBlock();
- QString coreOptions = QString::fromStdString(HelpMessage(HMM_BITCOIN_QT));
+ std::string strUsage = HelpMessage(HMM_BITCOIN_QT);
+ const bool showDebug = GetBoolArg("-help-debug", false);
+ strUsage += HelpMessageGroup(_("UI Options:"));
+ if (showDebug) {
+ strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS));
+ }
+ strUsage += HelpMessageOpt("-choosedatadir", strprintf(_("Choose data directory on startup (default: %u)"), DEFAULT_CHOOSE_DATADIR));
+ strUsage += HelpMessageOpt("-lang=<lang>", _("Set language, for example \"de_DE\" (default: system locale)"));
+ strUsage += HelpMessageOpt("-min", _("Start minimized"));
+ strUsage += HelpMessageOpt("-rootcertificates=<file>", _("Set SSL root certificates for payment request (default: -system-)"));
+ strUsage += HelpMessageOpt("-splash", strprintf(_("Show splash screen on startup (default: %u)"), DEFAULT_SPLASHSCREEN));
+ strUsage += HelpMessageOpt("-resetguisettings", _("Reset all settings changes made over the GUI"));
+ if (showDebug) {
+ strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM));
+ }
+ QString coreOptions = QString::fromStdString(strUsage);
text = version + "\n" + header + "\n" + coreOptions;
QTextTableFormat tf;
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index 012370ed10..c872822759 100644
--- a/src/rpcblockchain.cpp
+++ b/src/rpcblockchain.cpp
@@ -10,6 +10,7 @@
#include "coins.h"
#include "consensus/validation.h"
#include "main.h"
+#include "policy/policy.h"
#include "primitives/transaction.h"
#include "rpcserver.h"
#include "streams.h"
@@ -566,7 +567,7 @@ UniValue verifychain(const UniValue& params, bool fHelp)
if (params.size() > 1)
nCheckDepth = params[1].get_int();
- return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth);
+ return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth);
}
/** Implementation of IsSuperMajority with better feedback */
@@ -834,7 +835,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp)
}
if (state.IsValid()) {
- ActivateBestChain(state);
+ ActivateBestChain(state, Params());
}
if (!state.IsValid()) {
@@ -873,7 +874,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp)
}
if (state.IsValid()) {
- ActivateBestChain(state);
+ ActivateBestChain(state, Params());
}
if (!state.IsValid()) {
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index 343b6234d4..cab5819017 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -96,6 +96,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getrawmempool", 0 },
{ "estimatefee", 0 },
{ "estimatepriority", 0 },
+ { "estimatesmartfee", 0 },
+ { "estimatesmartpriority", 0 },
{ "prioritisetransaction", 1 },
{ "prioritisetransaction", 2 },
{ "setban", 2 },
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index 3fd07fc374..c8649ec27d 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -99,7 +99,7 @@ UniValue getgenerate(const UniValue& params, bool fHelp)
throw runtime_error(
"getgenerate\n"
"\nReturn if the server is set to generate coins or not. The default is false.\n"
- "It is set with the command line argument -gen (or bitcoin.conf setting gen)\n"
+ "It is set with the command line argument -gen (or " + std::string(BITCOIN_CONF_FILENAME) + " setting gen)\n"
"It can also be set with the setgenerate call.\n"
"\nResult\n"
"true|false (boolean) If the server is set to generate coins or not\n"
@@ -109,7 +109,7 @@ UniValue getgenerate(const UniValue& params, bool fHelp)
);
LOCK(cs_main);
- return GetBoolArg("-gen", false);
+ return GetBoolArg("-gen", DEFAULT_GENERATE);
}
UniValue generate(const UniValue& params, bool fHelp)
@@ -726,3 +726,75 @@ UniValue estimatepriority(const UniValue& params, bool fHelp)
return mempool.estimatePriority(nBlocks);
}
+
+UniValue estimatesmartfee(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() != 1)
+ throw runtime_error(
+ "estimatesmartfee nblocks\n"
+ "\nWARNING: This interface is unstable and may disappear or change!\n"
+ "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
+ "confirmation within nblocks blocks if possible and return the number of blocks\n"
+ "for which the estimate is valid.\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric)\n"
+ "\nResult:\n"
+ "{\n"
+ " \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n"
+ " \"blocks\" : n (numeric) block number where estimate was found\n"
+ "}\n"
+ "\n"
+ "A negative value is returned if not enough transactions and blocks\n"
+ "have been observed to make an estimate for any number of blocks.\n"
+ "However it will not return a value below the mempool reject fee.\n"
+ "\nExample:\n"
+ + HelpExampleCli("estimatesmartfee", "6")
+ );
+
+ RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
+
+ int nBlocks = params[0].get_int();
+
+ UniValue result(UniValue::VOBJ);
+ int answerFound;
+ CFeeRate feeRate = mempool.estimateSmartFee(nBlocks, &answerFound);
+ result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
+ result.push_back(Pair("blocks", answerFound));
+ return result;
+}
+
+UniValue estimatesmartpriority(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() != 1)
+ throw runtime_error(
+ "estimatesmartpriority nblocks\n"
+ "\nWARNING: This interface is unstable and may disappear or change!\n"
+ "\nEstimates the approximate priority a zero-fee transaction needs to begin\n"
+ "confirmation within nblocks blocks if possible and return the number of blocks\n"
+ "for which the estimate is valid.\n"
+ "\nArguments:\n"
+ "1. nblocks (numeric)\n"
+ "\nResult:\n"
+ "{\n"
+ " \"priority\" : x.x, (numeric) estimated priority\n"
+ " \"blocks\" : n (numeric) block number where estimate was found\n"
+ "}\n"
+ "\n"
+ "A negative value is returned if not enough transactions and blocks\n"
+ "have been observed to make an estimate for any number of blocks.\n"
+ "However if the mempool reject fee is set it will return 1e9 * MAX_MONEY.\n"
+ "\nExample:\n"
+ + HelpExampleCli("estimatesmartpriority", "6")
+ );
+
+ RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
+
+ int nBlocks = params[0].get_int();
+
+ UniValue result(UniValue::VOBJ);
+ int answerFound;
+ double priority = mempool.estimateSmartPriority(nBlocks, &answerFound);
+ result.push_back(Pair("priority", priority));
+ result.push_back(Pair("blocks", answerFound));
+ return result;
+}
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 8bda5a0373..83d2c2d503 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -319,6 +319,8 @@ static const CRPCCommand vRPCCommands[] =
{ "util", "verifymessage", &verifymessage, true },
{ "util", "estimatefee", &estimatefee, true },
{ "util", "estimatepriority", &estimatepriority, true },
+ { "util", "estimatesmartfee", &estimatesmartfee, true },
+ { "util", "estimatesmartpriority", &estimatesmartpriority, true },
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, true },
diff --git a/src/rpcserver.h b/src/rpcserver.h
index dde8dfdcc3..fc88f82be8 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -193,6 +193,8 @@ extern UniValue getblocktemplate(const UniValue& params, bool fHelp);
extern UniValue submitblock(const UniValue& params, bool fHelp);
extern UniValue estimatefee(const UniValue& params, bool fHelp);
extern UniValue estimatepriority(const UniValue& params, bool fHelp);
+extern UniValue estimatesmartfee(const UniValue& params, bool fHelp);
+extern UniValue estimatesmartpriority(const UniValue& params, bool fHelp);
extern UniValue getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp
extern UniValue getaccountaddress(const UniValue& params, bool fHelp);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index bfef8afa17..4863b96391 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -16,6 +16,7 @@ using namespace std;
typedef vector<unsigned char> valtype;
+bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER;
unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
diff --git a/src/script/standard.h b/src/script/standard.h
index ae1bbecca0..2b9fbe78dd 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -13,6 +13,8 @@
#include <stdint.h>
+static const bool DEFAULT_ACCEPT_DATACARRIER = true;
+
class CKeyID;
class CScript;
@@ -26,6 +28,7 @@ public:
};
static const unsigned int MAX_OP_RETURN_RELAY = 83; //! bytes (+1 for OP_RETURN, +2 for the pushdata opcodes)
+extern bool fAcceptDatacarrier;
extern unsigned nMaxDatacarrierBytes;
/**
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index f4121f1705..7772a4e9d2 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -75,7 +75,7 @@ TESTS = tests
endif
if USE_ECMULT_STATIC_PRECOMPUTATION
-CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)/
+CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
gen_context_OBJECTS = gen_context.o
diff --git a/src/test/bignum.h b/src/test/bignum.h
deleted file mode 100644
index e7aeee9db6..0000000000
--- a/src/test/bignum.h
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2013 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_TEST_BIGNUM_H
-#define BITCOIN_TEST_BIGNUM_H
-
-#include <algorithm>
-#include <limits>
-#include <stdexcept>
-#include <stdint.h>
-#include <string>
-#include <vector>
-
-#include <openssl/bn.h>
-
-class bignum_error : public std::runtime_error
-{
-public:
- explicit bignum_error(const std::string& str) : std::runtime_error(str) {}
-};
-
-
-/** C++ wrapper for BIGNUM (OpenSSL bignum) */
-class CBigNum : public BIGNUM
-{
-public:
- CBigNum()
- {
- BN_init(this);
- }
-
- CBigNum(const CBigNum& b)
- {
- BN_init(this);
- if (!BN_copy(this, &b))
- {
- BN_clear_free(this);
- throw bignum_error("CBigNum::CBigNum(const CBigNum&): BN_copy failed");
- }
- }
-
- CBigNum& operator=(const CBigNum& b)
- {
- if (!BN_copy(this, &b))
- throw bignum_error("CBigNum::operator=: BN_copy failed");
- return (*this);
- }
-
- ~CBigNum()
- {
- BN_clear_free(this);
- }
-
- CBigNum(long long n) { BN_init(this); setint64(n); }
-
- explicit CBigNum(const std::vector<unsigned char>& vch)
- {
- BN_init(this);
- setvch(vch);
- }
-
- int getint() const
- {
- BN_ULONG n = BN_get_word(this);
- if (!BN_is_negative(this))
- return (n > (BN_ULONG)std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : n);
- else
- return (n > (BN_ULONG)std::numeric_limits<int>::max() ? std::numeric_limits<int>::min() : -(int)n);
- }
-
- void setint64(int64_t sn)
- {
- unsigned char pch[sizeof(sn) + 6];
- unsigned char* p = pch + 4;
- bool fNegative;
- uint64_t n;
-
- if (sn < (int64_t)0)
- {
- // Since the minimum signed integer cannot be represented as positive so long as its type is signed,
- // and it's not well-defined what happens if you make it unsigned before negating it,
- // we instead increment the negative integer by 1, convert it, then increment the (now positive) unsigned integer by 1 to compensate
- n = -(sn + 1);
- ++n;
- fNegative = true;
- } else {
- n = sn;
- fNegative = false;
- }
-
- bool fLeadingZeroes = true;
- for (int i = 0; i < 8; i++)
- {
- unsigned char c = (n >> 56) & 0xff;
- n <<= 8;
- if (fLeadingZeroes)
- {
- if (c == 0)
- continue;
- if (c & 0x80)
- *p++ = (fNegative ? 0x80 : 0);
- else if (fNegative)
- c |= 0x80;
- fLeadingZeroes = false;
- }
- *p++ = c;
- }
- unsigned int nSize = p - (pch + 4);
- pch[0] = (nSize >> 24) & 0xff;
- pch[1] = (nSize >> 16) & 0xff;
- pch[2] = (nSize >> 8) & 0xff;
- pch[3] = (nSize) & 0xff;
- BN_mpi2bn(pch, p - pch, this);
- }
-
- void setvch(const std::vector<unsigned char>& vch)
- {
- std::vector<unsigned char> vch2(vch.size() + 4);
- unsigned int nSize = vch.size();
- // BIGNUM's byte stream format expects 4 bytes of
- // big endian size data info at the front
- vch2[0] = (nSize >> 24) & 0xff;
- vch2[1] = (nSize >> 16) & 0xff;
- vch2[2] = (nSize >> 8) & 0xff;
- vch2[3] = (nSize >> 0) & 0xff;
- // swap data to big endian
- reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4);
- BN_mpi2bn(&vch2[0], vch2.size(), this);
- }
-
- std::vector<unsigned char> getvch() const
- {
- unsigned int nSize = BN_bn2mpi(this, NULL);
- if (nSize <= 4)
- return std::vector<unsigned char>();
- std::vector<unsigned char> vch(nSize);
- BN_bn2mpi(this, &vch[0]);
- vch.erase(vch.begin(), vch.begin() + 4);
- reverse(vch.begin(), vch.end());
- return vch;
- }
-
- friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b);
-};
-
-
-
-inline const CBigNum operator+(const CBigNum& a, const CBigNum& b)
-{
- CBigNum r;
- if (!BN_add(&r, &a, &b))
- throw bignum_error("CBigNum::operator+: BN_add failed");
- return r;
-}
-
-inline const CBigNum operator-(const CBigNum& a, const CBigNum& b)
-{
- CBigNum r;
- if (!BN_sub(&r, &a, &b))
- throw bignum_error("CBigNum::operator-: BN_sub failed");
- return r;
-}
-
-inline const CBigNum operator-(const CBigNum& a)
-{
- CBigNum r(a);
- BN_set_negative(&r, !BN_is_negative(&r));
- return r;
-}
-
-inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); }
-inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); }
-inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); }
-inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); }
-inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); }
-inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); }
-
-#endif // BITCOIN_TEST_BIGNUM_H
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 946f904dfa..9489a19f63 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -165,13 +165,22 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
}
if (insecure_rand() % 100 == 0) {
+ // Every 100 iterations, flush an intermediate cache
+ if (stack.size() > 1 && insecure_rand() % 2 == 0) {
+ unsigned int flushIndex = insecure_rand() % (stack.size() - 1);
+ stack[flushIndex]->Flush();
+ }
+ }
+ if (insecure_rand() % 100 == 0) {
// Every 100 iterations, change the cache stack.
if (stack.size() > 0 && insecure_rand() % 2 == 0) {
+ //Remove the top cache
stack.back()->Flush();
delete stack.back();
stack.pop_back();
}
if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) {
+ //Add a new cache
CCoinsView* tip = &base;
if (stack.size() > 0) {
tip = stack.back();
@@ -305,6 +314,13 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
}
if (insecure_rand() % 100 == 0) {
+ // Every 100 iterations, flush an intermediate cache
+ if (stack.size() > 1 && insecure_rand() % 2 == 0) {
+ unsigned int flushIndex = insecure_rand() % (stack.size() - 1);
+ stack[flushIndex]->Flush();
+ }
+ }
+ if (insecure_rand() % 100 == 0) {
// Every 100 iterations, change the cache stack.
if (stack.size() > 0 && insecure_rand() % 2 == 0) {
stack.back()->Flush();
diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp
index 21ae46d6e9..2b92d239e9 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/main_tests.cpp
@@ -72,5 +72,4 @@ BOOST_AUTO_TEST_CASE(test_combiner_all)
Test.disconnect(&ReturnTrue);
BOOST_CHECK(Test());
}
-
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
new file mode 100644
index 0000000000..1e31f2e679
--- /dev/null
+++ b/src/test/merkle_tests.cpp
@@ -0,0 +1,136 @@
+// Copyright (c) 2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "consensus/merkle.h"
+#include "test/test_bitcoin.h"
+#include "random.h"
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(merkle_tests, TestingSetup)
+
+// Older version of the merkle root computation code, for comparison.
+static uint256 BlockBuildMerkleTree(const CBlock& block, bool* fMutated, std::vector<uint256>& vMerkleTree)
+{
+ vMerkleTree.clear();
+ vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
+ for (std::vector<CTransaction>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it)
+ vMerkleTree.push_back(it->GetHash());
+ int j = 0;
+ bool mutated = false;
+ for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
+ {
+ for (int i = 0; i < nSize; i += 2)
+ {
+ int i2 = std::min(i+1, nSize-1);
+ if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j+i] == vMerkleTree[j+i2]) {
+ // Two identical hashes at the end of the list at a particular level.
+ mutated = true;
+ }
+ vMerkleTree.push_back(Hash(vMerkleTree[j+i].begin(), vMerkleTree[j+i].end(),
+ vMerkleTree[j+i2].begin(), vMerkleTree[j+i2].end()));
+ }
+ j += nSize;
+ }
+ if (fMutated) {
+ *fMutated = mutated;
+ }
+ return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
+}
+
+// Older version of the merkle branch computation code, for comparison.
+static std::vector<uint256> BlockGetMerkleBranch(const CBlock& block, const std::vector<uint256>& vMerkleTree, int nIndex)
+{
+ std::vector<uint256> vMerkleBranch;
+ int j = 0;
+ for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
+ {
+ int i = std::min(nIndex^1, nSize-1);
+ vMerkleBranch.push_back(vMerkleTree[j+i]);
+ nIndex >>= 1;
+ j += nSize;
+ }
+ return vMerkleBranch;
+}
+
+static inline int ctz(uint32_t i) {
+ if (i == 0) return 0;
+ int j = 0;
+ while (!(i & 1)) {
+ j++;
+ i >>= 1;
+ }
+ return j;
+}
+
+BOOST_AUTO_TEST_CASE(merkle_test)
+{
+ for (int i = 0; i < 32; i++) {
+ // Try 32 block sizes: all sizes from 0 to 16 inclusive, and then 15 random sizes.
+ int ntx = (i <= 16) ? i : 17 + (insecure_rand() % 4000);
+ // Try up to 3 mutations.
+ for (int mutate = 0; mutate <= 3; mutate++) {
+ int duplicate1 = mutate >= 1 ? 1 << ctz(ntx) : 0; // The last how many transactions to duplicate first.
+ if (duplicate1 >= ntx) break; // Duplication of the entire tree results in a different root (it adds a level).
+ int ntx1 = ntx + duplicate1; // The resulting number of transactions after the first duplication.
+ int duplicate2 = mutate >= 2 ? 1 << ctz(ntx1) : 0; // Likewise for the second mutation.
+ if (duplicate2 >= ntx1) break;
+ int ntx2 = ntx1 + duplicate2;
+ int duplicate3 = mutate >= 3 ? 1 << ctz(ntx2) : 0; // And for the the third mutation.
+ if (duplicate3 >= ntx2) break;
+ int ntx3 = ntx2 + duplicate3;
+ // Build a block with ntx different transactions.
+ CBlock block;
+ block.vtx.resize(ntx);
+ for (int j = 0; j < ntx; j++) {
+ CMutableTransaction mtx;
+ mtx.nLockTime = j;
+ block.vtx[j] = mtx;
+ }
+ // Compute the root of the block before mutating it.
+ bool unmutatedMutated = false;
+ uint256 unmutatedRoot = BlockMerkleRoot(block, &unmutatedMutated);
+ BOOST_CHECK(unmutatedMutated == false);
+ // Optionally mutate by duplicating the last transactions, resulting in the same merkle root.
+ block.vtx.resize(ntx3);
+ for (int j = 0; j < duplicate1; j++) {
+ block.vtx[ntx + j] = block.vtx[ntx + j - duplicate1];
+ }
+ for (int j = 0; j < duplicate2; j++) {
+ block.vtx[ntx1 + j] = block.vtx[ntx1 + j - duplicate2];
+ }
+ for (int j = 0; j < duplicate3; j++) {
+ block.vtx[ntx2 + j] = block.vtx[ntx2 + j - duplicate3];
+ }
+ // Compute the merkle root and merkle tree using the old mechanism.
+ bool oldMutated = false;
+ std::vector<uint256> merkleTree;
+ uint256 oldRoot = BlockBuildMerkleTree(block, &oldMutated, merkleTree);
+ // Compute the merkle root using the new mechanism.
+ bool newMutated = false;
+ uint256 newRoot = BlockMerkleRoot(block, &newMutated);
+ BOOST_CHECK(oldRoot == newRoot);
+ BOOST_CHECK(newRoot == unmutatedRoot);
+ BOOST_CHECK((newRoot == uint256()) == (ntx == 0));
+ BOOST_CHECK(oldMutated == newMutated);
+ BOOST_CHECK(newMutated == !!mutate);
+ // If no mutation was done (once for every ntx value), try up to 16 branches.
+ if (mutate == 0) {
+ for (int loop = 0; loop < std::min(ntx, 16); loop++) {
+ // If ntx <= 16, try all branches. Otherise, try 16 random ones.
+ int mtx = loop;
+ if (ntx > 16) {
+ mtx = insecure_rand() % ntx;
+ }
+ std::vector<uint256> newBranch = BlockMerkleBranch(block, mtx);
+ std::vector<uint256> oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx);
+ BOOST_CHECK(oldBranch == newBranch);
+ BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx].GetHash(), newBranch, mtx) == oldRoot);
+ }
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index dc20e34634..1d7c9f65c0 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -5,6 +5,7 @@
#include "chainparams.h"
#include "coins.h"
#include "consensus/consensus.h"
+#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "main.h"
#include "miner.h"
@@ -93,7 +94,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
pblock->vtx[0] = CTransaction(txCoinbase);
if (txFirst.size() < 2)
txFirst.push_back(new CTransaction(pblock->vtx[0]));
- pblock->hashMerkleRoot = pblock->ComputeMerkleRoot();
+ pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce;
CValidationState state;
BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL));
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index d9f3c3e467..0d7fb2bc35 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include "consensus/merkle.h"
#include "merkleblock.h"
#include "serialize.h"
#include "streams.h"
@@ -48,7 +49,7 @@ BOOST_AUTO_TEST_CASE(pmt_test1)
}
// calculate actual merkle root and height
- uint256 merkleRoot1 = block.ComputeMerkleRoot();
+ uint256 merkleRoot1 = BlockMerkleRoot(block);
std::vector<uint256> vTxid(nTx, uint256());
for (unsigned int j=0; j<nTx; j++)
vTxid[j] = block.vtx[j].GetHash();
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index c4f6660f6a..644c3da213 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -84,11 +84,18 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
block.clear();
if (blocknum == 30) {
// At this point we should need to combine 5 buckets to get enough data points
- // So estimateFee(1) should fail and estimateFee(2) should return somewhere around
- // 8*baserate
+ // So estimateFee(1,2,3) should fail and estimateFee(4) should return somewhere around
+ // 8*baserate. estimateFee(4) %'s are 100,100,100,100,90 = average 98%
BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
- BOOST_CHECK(mpool.estimateFee(2).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
- BOOST_CHECK(mpool.estimateFee(2).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
+ BOOST_CHECK(mpool.estimateFee(2) == CFeeRate(0));
+ BOOST_CHECK(mpool.estimateFee(3) == CFeeRate(0));
+ BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
+ BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
+ int answerFound;
+ BOOST_CHECK(mpool.estimateSmartFee(1, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(mpool.estimateSmartFee(3, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(mpool.estimateSmartFee(4, &answerFound) == mpool.estimateFee(4) && answerFound == 4);
+ BOOST_CHECK(mpool.estimateSmartFee(8, &answerFound) == mpool.estimateFee(8) && answerFound == 8);
}
}
@@ -97,9 +104,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Highest feerate is 10*baseRate and gets in all blocks,
// second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,
// third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
- // so estimateFee(1) should return 9*baseRate.
- // Third highest feerate has 90% chance of being included by 2 blocks,
- // so estimateFee(2) should return 8*baseRate etc...
+ // so estimateFee(1) should return 10*baseRate.
+ // Second highest feerate has 100% chance of being included by 2 blocks,
+ // so estimateFee(2) should return 9*baseRate etc...
for (int i = 1; i < 10;i++) {
origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK());
origPriEst.push_back(mpool.estimatePriority(i));
@@ -107,10 +114,11 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
BOOST_CHECK(origPriEst[i-1] <= origPriEst[i-2]);
}
- BOOST_CHECK(origFeeEst[i-1] < (10-i)*baseRate.GetFeePerK() + deltaFee);
- BOOST_CHECK(origFeeEst[i-1] > (10-i)*baseRate.GetFeePerK() - deltaFee);
- BOOST_CHECK(origPriEst[i-1] < pow(10,10-i) * basepri + deltaPri);
- BOOST_CHECK(origPriEst[i-1] > pow(10,10-i) * basepri - deltaPri);
+ int mult = 11-i;
+ BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
+ BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
+ BOOST_CHECK(origPriEst[i-1] < pow(10,mult) * basepri + deltaPri);
+ BOOST_CHECK(origPriEst[i-1] > pow(10,mult) * basepri - deltaPri);
}
// Mine 50 more blocks with no transactions happening, estimates shouldn't change
@@ -140,9 +148,12 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.removeForBlock(block, ++blocknum, dummyConflicted);
}
+ int answerFound;
for (int i = 1; i < 10;i++) {
- BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
- BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
+ BOOST_CHECK(mpool.estimateFee(i) == CFeeRate(0) || mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
+ BOOST_CHECK(mpool.estimateSmartFee(i, &answerFound).GetFeePerK() > origFeeEst[answerFound-1] - deltaFee);
+ BOOST_CHECK(mpool.estimatePriority(i) == -1 || mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
+ BOOST_CHECK(mpool.estimateSmartPriority(i, &answerFound) > origPriEst[answerFound-1] - deltaPri);
}
// Mine all those transactions
@@ -162,9 +173,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
}
- // Mine 100 more blocks where everything is mined every block
- // Estimates should be below original estimates (not possible for last estimate)
- while (blocknum < 365) {
+ // Mine 200 more blocks where everything is mined every block
+ // Estimates should be below original estimates
+ while (blocknum < 465) {
for (int j = 0; j < 10; j++) { // For each fee/pri multiple
for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
@@ -178,10 +189,22 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.removeForBlock(block, ++blocknum, dummyConflicted);
block.clear();
}
- for (int i = 1; i < 9; i++) {
+ for (int i = 1; i < 10; i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] - deltaPri);
}
+
+ // Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee
+ // and that estimateSmartPriority returns essentially an infinite value
+ mpool.addUnchecked(tx.GetHash(), entry.Fee(feeV[0][5]).Time(GetTime()).Priority(priV[1][5]).Height(blocknum).FromTx(tx, &mpool));
+ // evict that transaction which should set a mempool min fee of minRelayTxFee + feeV[0][5]
+ mpool.TrimToSize(1);
+ BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[0][5]);
+ for (int i = 1; i < 10; i++) {
+ BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.estimateFee(i).GetFeePerK());
+ BOOST_CHECK(mpool.estimateSmartFee(i).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
+ BOOST_CHECK(mpool.estimateSmartPriority(i) == INF_PRIORITY);
+ }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/scriptnum10.h b/src/test/scriptnum10.h
new file mode 100644
index 0000000000..00419746b7
--- /dev/null
+++ b/src/test/scriptnum10.h
@@ -0,0 +1,183 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2013 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_SCRIPTNUM10_H
+#define BITCOIN_TEST_SCRIPTNUM10_H
+
+#include <algorithm>
+#include <limits>
+#include <stdexcept>
+#include <stdint.h>
+#include <string>
+#include <vector>
+#include "assert.h"
+
+class scriptnum10_error : public std::runtime_error
+{
+public:
+ explicit scriptnum10_error(const std::string& str) : std::runtime_error(str) {}
+};
+
+class CScriptNum10
+{
+/**
+ * The ScriptNum implementation from Bitcoin Core 0.10.0, for cross-comparison.
+ */
+public:
+
+ explicit CScriptNum10(const int64_t& n)
+ {
+ m_value = n;
+ }
+
+ static const size_t nDefaultMaxNumSize = 4;
+
+ explicit CScriptNum10(const std::vector<unsigned char>& vch, bool fRequireMinimal,
+ const size_t nMaxNumSize = nDefaultMaxNumSize)
+ {
+ if (vch.size() > nMaxNumSize) {
+ throw scriptnum10_error("script number overflow");
+ }
+ if (fRequireMinimal && vch.size() > 0) {
+ // Check that the number is encoded with the minimum possible
+ // number of bytes.
+ //
+ // If the most-significant-byte - excluding the sign bit - is zero
+ // then we're not minimal. Note how this test also rejects the
+ // negative-zero encoding, 0x80.
+ if ((vch.back() & 0x7f) == 0) {
+ // One exception: if there's more than one byte and the most
+ // significant bit of the second-most-significant-byte is set
+ // it would conflict with the sign bit. An example of this case
+ // is +-255, which encode to 0xff00 and 0xff80 respectively.
+ // (big-endian).
+ if (vch.size() <= 1 || (vch[vch.size() - 2] & 0x80) == 0) {
+ throw scriptnum10_error("non-minimally encoded script number");
+ }
+ }
+ }
+ m_value = set_vch(vch);
+ }
+
+ inline bool operator==(const int64_t& rhs) const { return m_value == rhs; }
+ inline bool operator!=(const int64_t& rhs) const { return m_value != rhs; }
+ inline bool operator<=(const int64_t& rhs) const { return m_value <= rhs; }
+ inline bool operator< (const int64_t& rhs) const { return m_value < rhs; }
+ inline bool operator>=(const int64_t& rhs) const { return m_value >= rhs; }
+ inline bool operator> (const int64_t& rhs) const { return m_value > rhs; }
+
+ inline bool operator==(const CScriptNum10& rhs) const { return operator==(rhs.m_value); }
+ inline bool operator!=(const CScriptNum10& rhs) const { return operator!=(rhs.m_value); }
+ inline bool operator<=(const CScriptNum10& rhs) const { return operator<=(rhs.m_value); }
+ inline bool operator< (const CScriptNum10& rhs) const { return operator< (rhs.m_value); }
+ inline bool operator>=(const CScriptNum10& rhs) const { return operator>=(rhs.m_value); }
+ inline bool operator> (const CScriptNum10& rhs) const { return operator> (rhs.m_value); }
+
+ inline CScriptNum10 operator+( const int64_t& rhs) const { return CScriptNum10(m_value + rhs);}
+ inline CScriptNum10 operator-( const int64_t& rhs) const { return CScriptNum10(m_value - rhs);}
+ inline CScriptNum10 operator+( const CScriptNum10& rhs) const { return operator+(rhs.m_value); }
+ inline CScriptNum10 operator-( const CScriptNum10& rhs) const { return operator-(rhs.m_value); }
+
+ inline CScriptNum10& operator+=( const CScriptNum10& rhs) { return operator+=(rhs.m_value); }
+ inline CScriptNum10& operator-=( const CScriptNum10& rhs) { return operator-=(rhs.m_value); }
+
+ inline CScriptNum10 operator-() const
+ {
+ assert(m_value != std::numeric_limits<int64_t>::min());
+ return CScriptNum10(-m_value);
+ }
+
+ inline CScriptNum10& operator=( const int64_t& rhs)
+ {
+ m_value = rhs;
+ return *this;
+ }
+
+ inline CScriptNum10& operator+=( const int64_t& rhs)
+ {
+ assert(rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) ||
+ (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs));
+ m_value += rhs;
+ return *this;
+ }
+
+ inline CScriptNum10& operator-=( const int64_t& rhs)
+ {
+ assert(rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) ||
+ (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs));
+ m_value -= rhs;
+ return *this;
+ }
+
+ int getint() const
+ {
+ if (m_value > std::numeric_limits<int>::max())
+ return std::numeric_limits<int>::max();
+ else if (m_value < std::numeric_limits<int>::min())
+ return std::numeric_limits<int>::min();
+ return m_value;
+ }
+
+ std::vector<unsigned char> getvch() const
+ {
+ return serialize(m_value);
+ }
+
+ static std::vector<unsigned char> serialize(const int64_t& value)
+ {
+ if(value == 0)
+ return std::vector<unsigned char>();
+
+ std::vector<unsigned char> result;
+ const bool neg = value < 0;
+ uint64_t absvalue = neg ? -value : value;
+
+ while(absvalue)
+ {
+ result.push_back(absvalue & 0xff);
+ absvalue >>= 8;
+ }
+
+// - If the most significant byte is >= 0x80 and the value is positive, push a
+// new zero-byte to make the significant byte < 0x80 again.
+
+// - If the most significant byte is >= 0x80 and the value is negative, push a
+// new 0x80 byte that will be popped off when converting to an integral.
+
+// - If the most significant byte is < 0x80 and the value is negative, add
+// 0x80 to it, since it will be subtracted and interpreted as a negative when
+// converting to an integral.
+
+ if (result.back() & 0x80)
+ result.push_back(neg ? 0x80 : 0);
+ else if (neg)
+ result.back() |= 0x80;
+
+ return result;
+ }
+
+private:
+ static int64_t set_vch(const std::vector<unsigned char>& vch)
+ {
+ if (vch.empty())
+ return 0;
+
+ int64_t result = 0;
+ for (size_t i = 0; i != vch.size(); ++i)
+ result |= static_cast<int64_t>(vch[i]) << 8*i;
+
+ // If the input vector's most significant byte is 0x80, remove it from
+ // the result's msb and return a negative.
+ if (vch.back() & 0x80)
+ return -((int64_t)(result & ~(0x80ULL << (8 * (vch.size() - 1)))));
+
+ return result;
+ }
+
+ int64_t m_value;
+};
+
+
+#endif // BITCOIN_TEST_BIGNUM_H
diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp
index d95724dbe1..2405ab3ffc 100644
--- a/src/test/scriptnum_tests.cpp
+++ b/src/test/scriptnum_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "bignum.h"
+#include "scriptnum10.h"
#include "script/script.h"
#include "test/test_bitcoin.h"
@@ -16,45 +16,48 @@ static const int64_t values[] = \
{ 0, 1, CHAR_MIN, CHAR_MAX, UCHAR_MAX, SHRT_MIN, USHRT_MAX, INT_MIN, INT_MAX, UINT_MAX, LONG_MIN, LONG_MAX };
static const int64_t offsets[] = { 1, 0x79, 0x80, 0x81, 0xFF, 0x7FFF, 0x8000, 0xFFFF, 0x10000};
-static bool verify(const CBigNum& bignum, const CScriptNum& scriptnum)
+static bool verify(const CScriptNum10& bignum, const CScriptNum& scriptnum)
{
return bignum.getvch() == scriptnum.getvch() && bignum.getint() == scriptnum.getint();
}
static void CheckCreateVch(const int64_t& num)
{
- CBigNum bignum(num);
+ CScriptNum10 bignum(num);
CScriptNum scriptnum(num);
BOOST_CHECK(verify(bignum, scriptnum));
- CBigNum bignum2(bignum.getvch());
+ std::vector<unsigned char> vch = bignum.getvch();
+
+ CScriptNum10 bignum2(bignum.getvch(), false);
+ vch = scriptnum.getvch();
CScriptNum scriptnum2(scriptnum.getvch(), false);
BOOST_CHECK(verify(bignum2, scriptnum2));
- CBigNum bignum3(scriptnum2.getvch());
+ CScriptNum10 bignum3(scriptnum2.getvch(), false);
CScriptNum scriptnum3(bignum2.getvch(), false);
BOOST_CHECK(verify(bignum3, scriptnum3));
}
static void CheckCreateInt(const int64_t& num)
{
- CBigNum bignum(num);
+ CScriptNum10 bignum(num);
CScriptNum scriptnum(num);
BOOST_CHECK(verify(bignum, scriptnum));
- BOOST_CHECK(verify(bignum.getint(), CScriptNum(scriptnum.getint())));
- BOOST_CHECK(verify(scriptnum.getint(), CScriptNum(bignum.getint())));
- BOOST_CHECK(verify(CBigNum(scriptnum.getint()).getint(), CScriptNum(CScriptNum(bignum.getint()).getint())));
+ BOOST_CHECK(verify(CScriptNum10(bignum.getint()), CScriptNum(scriptnum.getint())));
+ BOOST_CHECK(verify(CScriptNum10(scriptnum.getint()), CScriptNum(bignum.getint())));
+ BOOST_CHECK(verify(CScriptNum10(CScriptNum10(scriptnum.getint()).getint()), CScriptNum(CScriptNum(bignum.getint()).getint())));
}
static void CheckAdd(const int64_t& num1, const int64_t& num2)
{
- const CBigNum bignum1(num1);
- const CBigNum bignum2(num2);
+ const CScriptNum10 bignum1(num1);
+ const CScriptNum10 bignum2(num2);
const CScriptNum scriptnum1(num1);
const CScriptNum scriptnum2(num2);
- CBigNum bignum3(num1);
- CBigNum bignum4(num1);
+ CScriptNum10 bignum3(num1);
+ CScriptNum10 bignum4(num1);
CScriptNum scriptnum3(num1);
CScriptNum scriptnum4(num1);
@@ -71,7 +74,7 @@ static void CheckAdd(const int64_t& num1, const int64_t& num2)
static void CheckNegate(const int64_t& num)
{
- const CBigNum bignum(num);
+ const CScriptNum10 bignum(num);
const CScriptNum scriptnum(num);
// -INT64_MIN is undefined
@@ -81,8 +84,8 @@ static void CheckNegate(const int64_t& num)
static void CheckSubtract(const int64_t& num1, const int64_t& num2)
{
- const CBigNum bignum1(num1);
- const CBigNum bignum2(num2);
+ const CScriptNum10 bignum1(num1);
+ const CScriptNum10 bignum2(num2);
const CScriptNum scriptnum1(num1);
const CScriptNum scriptnum2(num2);
bool invalid = false;
@@ -107,8 +110,8 @@ static void CheckSubtract(const int64_t& num1, const int64_t& num2)
static void CheckCompare(const int64_t& num1, const int64_t& num2)
{
- const CBigNum bignum1(num1);
- const CBigNum bignum2(num2);
+ const CScriptNum10 bignum1(num1);
+ const CScriptNum10 bignum2(num2);
const CScriptNum scriptnum1(num1);
const CScriptNum scriptnum2(num2);
@@ -149,7 +152,7 @@ static void RunCreate(const int64_t& num)
CheckCreateVch(num);
else
{
- BOOST_CHECK_THROW (CheckCreateVch(num), scriptnum_error);
+ BOOST_CHECK_THROW (CheckCreateVch(num), scriptnum10_error);
}
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 9a3517a27b..351870014d 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -51,6 +51,7 @@ BasicTestingSetup::~BasicTestingSetup()
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
+ const CChainParams& chainparams = Params();
#ifdef ENABLE_WALLET
bitdb.MakeMock();
#endif
@@ -61,7 +62,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
pblocktree = new CBlockTreeDB(1 << 20, true);
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
- InitBlockIndex();
+ InitBlockIndex(chainparams);
#ifdef ENABLE_WALLET
bool fFirstRun;
pwalletMain = new CWallet("wallet.dat");
@@ -143,8 +144,13 @@ TestChain100Setup::~TestChain100Setup()
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) {
- return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight,
- pool ? pool->HasNoInputsOf(tx) : hadNoDependencies);
+ CTransaction txn(tx);
+ bool hasNoDependencies = pool ? pool->HasNoInputsOf(tx) : hadNoDependencies;
+ // Hack to assume either its completely dependent on other mempool txs or not at all
+ CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
+
+ return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
+ hasNoDependencies, inChainValue);
}
void Shutdown(void* parg)
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 08644f2968..8eccc81e30 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -449,6 +449,15 @@ void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& r
{
if (reply.code == 250) {
LogPrint("tor", "tor: Authentication succesful\n");
+
+ // Now that we know Tor is running setup the proxy for onion addresses
+ // if -onion isn't set to something else.
+ if (GetArg("-onion", "") == "") {
+ proxyType addrOnion = proxyType(CService("127.0.0.1", 9050), true);
+ SetProxy(NET_TOR, addrOnion);
+ SetReachable(NET_TOR);
+ }
+
// Finally - now create the service
if (private_key.empty()) // No private key, generate one
private_key = "NEW:BEST";
@@ -608,7 +617,9 @@ void TorController::disconnected_cb(TorControlConnection& conn)
service = CService();
if (!reconnect)
return;
- LogPrintf("tor: Disconnected from Tor control port %s, trying to reconnect\n", target);
+
+ LogPrint("tor", "tor: Disconnected from Tor control port %s, trying to reconnect\n", target);
+
// Single-shot timer for reconnect. Use exponential backoff.
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index a772e7adea..6d1df0b3d1 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -19,10 +19,10 @@
using namespace std;
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
- int64_t _nTime, double _dPriority,
- unsigned int _nHeight, bool poolHasNoInputsOf):
- tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight),
- hadNoDependencies(poolHasNoInputsOf)
+ int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
+ bool poolHasNoInputsOf, CAmount _inChainInputValue):
+ tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
+ hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue)
{
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
nModSize = tx.CalculateModifiedSize(nTxSize);
@@ -31,6 +31,8 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
nCountWithDescendants = 1;
nSizeWithDescendants = nTxSize;
nFeesWithDescendants = nFee;
+ CAmount nValueIn = tx.GetValueOut()+nFee;
+ assert(inChainInputValue <= nValueIn);
}
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
@@ -41,9 +43,10 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
double
CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
{
- CAmount nValueIn = tx.GetValueOut()+nFee;
- double deltaPriority = ((double)(currentHeight-nHeight)*nValueIn)/nModSize;
- double dResult = dPriority + deltaPriority;
+ double deltaPriority = ((double)(currentHeight-entryHeight)*inChainInputValue)/nModSize;
+ double dResult = entryPriority + deltaPriority;
+ if (dResult < 0) // This should only happen if it was called with a height below entry height
+ dResult = 0;
return dResult;
}
@@ -701,11 +704,21 @@ CFeeRate CTxMemPool::estimateFee(int nBlocks) const
LOCK(cs);
return minerPolicyEstimator->estimateFee(nBlocks);
}
+CFeeRate CTxMemPool::estimateSmartFee(int nBlocks, int *answerFoundAtBlocks) const
+{
+ LOCK(cs);
+ return minerPolicyEstimator->estimateSmartFee(nBlocks, answerFoundAtBlocks, *this);
+}
double CTxMemPool::estimatePriority(int nBlocks) const
{
LOCK(cs);
return minerPolicyEstimator->estimatePriority(nBlocks);
}
+double CTxMemPool::estimateSmartPriority(int nBlocks, int *answerFoundAtBlocks) const
+{
+ LOCK(cs);
+ return minerPolicyEstimator->estimateSmartPriority(nBlocks, answerFoundAtBlocks, *this);
+}
bool
CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const
diff --git a/src/txmempool.h b/src/txmempool.h
index 7b5843a8d0..c470bbe28f 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -63,9 +63,10 @@ private:
size_t nModSize; //! ... and modified size for priority
size_t nUsageSize; //! ... and total memory usage
int64_t nTime; //! Local time when entering the mempool
- double dPriority; //! Priority when entering the mempool
- unsigned int nHeight; //! Chain height when entering the mempool
+ double entryPriority; //! Priority when entering the mempool
+ unsigned int entryHeight; //! Chain height when entering the mempool
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
+ CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain
// Information about descendants of this transaction that are in the
// mempool; if we remove this transaction we must remove all of these
@@ -78,15 +79,20 @@ private:
public:
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
- int64_t _nTime, double _dPriority, unsigned int _nHeight, bool poolHasNoInputsOf = false);
+ int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
+ bool poolHasNoInputsOf, CAmount _inChainInputValue);
CTxMemPoolEntry(const CTxMemPoolEntry& other);
const CTransaction& GetTx() const { return this->tx; }
+ /**
+ * Fast calculation of lower bound of current priority as update
+ * from entry priority. Only inputs that were originally in-chain will age.
+ */
double GetPriority(unsigned int currentHeight) const;
const CAmount& GetFee() const { return nFee; }
size_t GetTxSize() const { return nTxSize; }
int64_t GetTime() const { return nTime; }
- unsigned int GetHeight() const { return nHeight; }
+ unsigned int GetHeight() const { return entryHeight; }
bool WasClearAtEntry() const { return hadNoDependencies; }
size_t DynamicMemoryUsage() const { return nUsageSize; }
@@ -420,6 +426,11 @@ public:
*/
bool CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents = true);
+ /** Populate setDescendants with all in-mempool descendants of hash.
+ * Assumes that setDescendants includes all in-mempool descendants of anything
+ * already in it. */
+ void CalculateDescendants(txiter it, setEntries &setDescendants);
+
/** The minimum fee to get into the mempool, which may itself not be enough
* for larger-sized transactions.
* The minReasonableRelayFee constructor arg is used to bound the time it
@@ -454,9 +465,21 @@ public:
bool lookup(uint256 hash, CTransaction& result) const;
+ /** Estimate fee rate needed to get into the next nBlocks
+ * If no answer can be given at nBlocks, return an estimate
+ * at the lowest number of blocks where one can be given
+ */
+ CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks = NULL) const;
+
/** Estimate fee rate needed to get into the next nBlocks */
CFeeRate estimateFee(int nBlocks) const;
+ /** Estimate priority needed to get into the next nBlocks
+ * If no answer can be given at nBlocks, return an estimate
+ * at the lowest number of blocks where one can be given
+ */
+ double estimateSmartPriority(int nBlocks, int *answerFoundAtBlocks = NULL) const;
+
/** Estimate priority needed to get into the next nBlocks */
double estimatePriority(int nBlocks) const;
@@ -493,10 +516,6 @@ private:
void UpdateForRemoveFromMempool(const setEntries &entriesToRemove);
/** Sever link between specified transaction and direct children. */
void UpdateChildrenForRemoval(txiter entry);
- /** Populate setDescendants with all in-mempool descendants of hash.
- * Assumes that setDescendants includes all in-mempool descendants of anything
- * already in it. */
- void CalculateDescendants(txiter it, setEntries &setDescendants);
/** Before calling removeUnchecked for a given transaction,
* UpdateForRemoveFromMempool must be called on the entire (dependent) set
diff --git a/src/ui_interface.h b/src/ui_interface.h
index e402479933..00d9303124 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -15,6 +15,7 @@
class CBasicKeyStore;
class CWallet;
class uint256;
+class CBlockIndex;
/** General change type (added, updated, removed). */
enum ChangeType
@@ -94,7 +95,7 @@ public:
boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress;
/** New block has been accepted */
- boost::signals2::signal<void (const uint256& hash)> NotifyBlockTip;
+ boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyBlockTip;
/** Banlist did change. */
boost::signals2::signal<void (void)> BannedListChanged;
diff --git a/src/uint256.h b/src/uint256.h
index 6d016ab164..6e37cd5d46 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -12,6 +12,7 @@
#include <stdint.h>
#include <string>
#include <vector>
+#include "crypto/common.h"
/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS>
@@ -119,13 +120,10 @@ public:
* used when the contents are considered uniformly random. It is not appropriate
* when the value can easily be influenced from outside as e.g. a network adversary could
* provide values to trigger worst-case behavior.
- * @note The result of this function is not stable between little and big endian.
*/
uint64_t GetCheapHash() const
{
- uint64_t result;
- memcpy((void*)&result, (void*)data, 8);
- return result;
+ return ReadLE64(data);
}
/** A more secure, salted hash function.
diff --git a/src/util.cpp b/src/util.cpp
index e8514a2ef0..1913181712 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -99,6 +99,9 @@ namespace boost {
using namespace std;
+const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
+const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
+
map<string, string> mapArgs;
map<string, vector<string> > mapMultiArgs;
bool fDebug = false;
@@ -107,9 +110,9 @@ bool fPrintToDebugLog = true;
bool fDaemon = false;
bool fServer = false;
string strMiscWarning;
-bool fLogTimestamps = false;
+bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS;
bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS;
-bool fLogIPs = false;
+bool fLogIPs = DEFAULT_LOGIPS;
volatile bool fReopenDebugLog = false;
CTranslationInterface translationInterface;
@@ -447,7 +450,6 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
std::string message = FormatException(pex, pszThread);
LogPrintf("\n\n************************\n%s\n", message);
fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
- strMiscWarning = message;
}
boost::filesystem::path GetDefaultDataDir()
@@ -521,7 +523,7 @@ void ClearDatadirCache()
boost::filesystem::path GetConfigFile()
{
- boost::filesystem::path pathConfigFile(GetArg("-conf", "bitcoin.conf"));
+ boost::filesystem::path pathConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME));
if (!pathConfigFile.is_complete())
pathConfigFile = GetDataDir(false) / pathConfigFile;
@@ -555,7 +557,7 @@ void ReadConfigFile(map<string, string>& mapSettingsRet,
#ifndef WIN32
boost::filesystem::path GetPidFile()
{
- boost::filesystem::path pathPidFile(GetArg("-pid", "bitcoind.pid"));
+ boost::filesystem::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME));
if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
return pathPidFile;
}
diff --git a/src/util.h b/src/util.h
index b2779fe782..fb154f6660 100644
--- a/src/util.h
+++ b/src/util.h
@@ -29,6 +29,8 @@
#include <boost/thread/exceptions.hpp>
static const bool DEFAULT_LOGTIMEMICROS = false;
+static const bool DEFAULT_LOGIPS = false;
+static const bool DEFAULT_LOGTIMESTAMPS = true;
/** Signals for translation. */
class CTranslationInterface
@@ -51,6 +53,9 @@ extern bool fLogIPs;
extern volatile bool fReopenDebugLog;
extern CTranslationInterface translationInterface;
+extern const char * const BITCOIN_CONF_FILENAME;
+extern const char * const BITCOIN_PID_FILENAME;
+
/**
* Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
* If no translation slot is registered, nothing is returned, and simply return the input.
diff --git a/src/version.h b/src/version.h
index 6cdddf9255..f7cf18d0b6 100644
--- a/src/version.h
+++ b/src/version.h
@@ -9,7 +9,7 @@
* network protocol versioning
*/
-static const int PROTOCOL_VERSION = 70011;
+static const int PROTOCOL_VERSION = 70012;
//! initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
@@ -37,4 +37,7 @@ static const int MEMPOOL_GD_VERSION = 60002;
//! "filter*" commands are disabled without NODE_BLOOM after and including this version
static const int NO_BLOOM_VERSION = 70011;
+//! "sendheaders" command and announcing blocks with headers starts with this version
+static const int SENDHEADERS_VERSION = 70012;
+
#endif // BITCOIN_VERSION_H
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index cf6122813c..4b9dbebddd 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -85,7 +85,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn)
LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
unsigned int nEnvFlags = 0;
- if (GetBoolArg("-privdb", true))
+ if (GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
nEnvFlags |= DB_PRIVATE;
dbenv->set_lg_dir(pathLogDir.string().c_str());
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 46bc0ac0a9..7f58d03f08 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -21,6 +21,7 @@
#include <db_cxx.h>
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
+static const bool DEFAULT_WALLET_PRIVDB = true;
extern unsigned int nWalletDBUpdated;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 1b152f4192..b062226dd9 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -39,9 +39,9 @@ using namespace std;
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
-bool bSpendZeroConfChange = true;
-bool fSendFreeTransactions = false;
-bool fPayAtLeastCustomFee = true;
+bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
+bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
+bool fPayAtLeastCustomFee = false;
/**
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
@@ -2010,13 +2010,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
{
// Not enough fee: enough priority?
- double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
- // Not enough mempool history to estimate: use hard-coded AllowFree.
- if (dPriorityNeeded <= 0 && AllowFree(dPriority))
- break;
-
- // Small enough, and priority high enough, to send for free
- if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded)
+ double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget);
+ // Require at least hard-coded AllowFree.
+ if (dPriority >= dPriorityNeeded && AllowFree(dPriority))
break;
}
@@ -2120,12 +2116,14 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK())
nFeeNeeded = payTxFee.GetFeePerK();
// User didn't set: use -txconfirmtarget to estimate...
- if (nFeeNeeded == 0)
- nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes);
- // ... unless we don't have enough mempool data, in which case fall
- // back to the required fee
- if (nFeeNeeded == 0)
- nFeeNeeded = GetRequiredFee(nTxBytes);
+ if (nFeeNeeded == 0) {
+ int estimateFoundTarget = nConfirmTarget;
+ nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes);
+ // ... unless we don't have enough mempool data for our desired target
+ // so we make sure we're paying at least minTxFee
+ if (nFeeNeeded == 0 || (unsigned int)estimateFoundTarget > nConfirmTarget)
+ nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes));
+ }
// prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee
if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes))
nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes);
@@ -2262,7 +2260,7 @@ bool CWallet::NewKeyPool()
if (IsLocked())
return false;
- int64_t nKeys = max(GetArg("-keypool", 100), (int64_t)0);
+ int64_t nKeys = max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t)0);
for (int i = 0; i < nKeys; i++)
{
int64_t nIndex = i+1;
@@ -2289,7 +2287,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
if (kpSize > 0)
nTargetSize = kpSize;
else
- nTargetSize = max(GetArg("-keypool", 100), (int64_t) 0);
+ nTargetSize = max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
while (setKeyPool.size() < (nTargetSize + 1))
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 7e846569ff..a4199488fc 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -37,6 +37,7 @@ extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
extern bool fPayAtLeastCustomFee;
+static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
//! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB
@@ -47,12 +48,17 @@ static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN;
//! minimum change amount
static const CAmount MIN_CHANGE = CENT;
+//! Default for -spendzeroconfchange
+static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
+//! Default for -sendfreetransactions
+static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false;
//! -txconfirmtarget default
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2;
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
static const CAmount nHighTransactionMaxFeeWarning = 100 * nHighTransactionFeeWarning;
//! Largest (in bytes) free transaction we're willing to create
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
+static const bool DEFAULT_WALLETBROADCAST = true;
class CAccountingEntry;
class CBlockIndex;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 9ce9f53bd9..5c08ee6d6c 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -810,7 +810,7 @@ void ThreadFlushWalletDB(const string& strFile)
if (fOneThread)
return;
fOneThread = true;
- if (!GetBoolArg("-flushwallet", true))
+ if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET))
return;
unsigned int nLastSeen = nWalletDBUpdated;
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 3ebc05afd1..77f7958814 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -16,6 +16,8 @@
#include <utility>
#include <vector>
+static const bool DEFAULT_FLUSHWALLET = true;
+
class CAccount;
class CAccountingEntry;
struct CBlockLocator;
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 09fe3aeb4c..be2aec7d15 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -12,7 +12,7 @@
void zmqError(const char *str)
{
- LogPrint("zmq", "Error: %s, errno=%s\n", str, zmq_strerror(errno));
+ LogPrint("zmq", "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
}
CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(NULL)
@@ -72,7 +72,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::CreateWithArguments(const
// Called at startup to conditionally set up ZMQ socket(s)
bool CZMQNotificationInterface::Initialize()
{
- LogPrint("zmq", "Initialize notification interface\n");
+ LogPrint("zmq", "zmq: Initialize notification interface\n");
assert(!pcontext);
pcontext = zmq_init(1);
@@ -110,7 +110,7 @@ bool CZMQNotificationInterface::Initialize()
// Called during shutdown sequence
void CZMQNotificationInterface::Shutdown()
{
- LogPrint("zmq", "Shutdown notification interface\n");
+ LogPrint("zmq", "zmq: Shutdown notification interface\n");
if (pcontext)
{
for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i)
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index ac788843eb..ddc8fe93e9 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -78,7 +78,7 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
}
else
{
- LogPrint("zmq", " Reuse socket for address %s\n", address);
+ LogPrint("zmq", "zmq: Reusing socket for address %s\n", address);
psocket = i->second->psocket;
mapPublishNotifiers.insert(std::make_pair(address, this));
@@ -120,7 +120,7 @@ void CZMQAbstractPublishNotifier::Shutdown()
bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
- LogPrint("zmq", "Publish hash block %s\n", hash.GetHex());
+ LogPrint("zmq", "zmq: Publish hashblock %s\n", hash.GetHex());
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
@@ -131,7 +131,7 @@ bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint("zmq", "Publish hash transaction %s\n", hash.GetHex());
+ LogPrint("zmq", "zmq: Publish hashtx %s\n", hash.GetHex());
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
@@ -141,7 +141,7 @@ bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &t
bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
- LogPrint("zmq", "Publish raw block %s\n", pindex->GetBlockHash().GetHex());
+ LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex());
const Consensus::Params& consensusParams = Params().GetConsensus();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
@@ -164,7 +164,7 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
- LogPrint("zmq", "Publish raw transaction %s\n", hash.GetHex());
+ LogPrint("zmq", "zmq: Publish rawtx %s\n", hash.GetHex());
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << transaction;
int rc = zmq_send_multipart(psocket, "rawtx", 5, &(*ss.begin()), ss.size(), 0);