diff options
130 files changed, 4538 insertions, 1567 deletions
diff --git a/.travis.yml b/.travis.yml index d2fbfee6f2..e2d43d6330 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ # - A travis bug causes caches to trample eachother when using the same # compiler key (which we don't use anyway). This is worked around for now by # replacing the "compilers" with a build name prefixed by the no-op ":" -# command. See: https://github.com/travis-ci/casher/issues/6 +# command. See: https://github.com/travis-ci/travis-ci/issues/4393 os: linux language: cpp @@ -48,7 +48,7 @@ lots of money. ### Automated Testing -Developers are strongly encouraged to write unit tests for new code, and to +Developers are strongly encouraged to write [unit tests](/doc/unit-tests.md) for new code, and to submit new unit tests for old code. Unit tests can be compiled and run (assuming they weren't disabled in configure) with: `make check` diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md index 07c2ba98b1..6149706596 100644 --- a/contrib/gitian-descriptors/README.md +++ b/contrib/gitian-descriptors/README.md @@ -1,8 +1,7 @@ -### Gavin's notes on getting gitian builds up and running using KVM:### +### Gavin's notes on getting gitian builds up and running using KVM -These instructions distilled from: -[ https://help.ubuntu.com/community/KVM/Installation]( https://help.ubuntu.com/community/KVM/Installation) -... see there for complete details. +These instructions distilled from +[https://help.ubuntu.com/community/KVM/Installation](https://help.ubuntu.com/community/KVM/Installation). You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization. @@ -33,14 +32,14 @@ Once you've got the right hardware and software: # Get inputs (see doc/release-process.md for exact inputs needed and where to get them) ... - # For further build instructions see doc/release-notes.md + # For further build instructions see doc/release-process.md ... --------------------- `gitian-builder` now also supports building using LXC. See -[ https://help.ubuntu.com/12.04/serverguide/lxc.html]( https://help.ubuntu.com/12.04/serverguide/lxc.html) -... for how to get LXC up and running under Ubuntu. +[help.ubuntu.com](https://help.ubuntu.com/14.04/serverguide/lxc.html) +for how to get LXC up and running under Ubuntu. If your main machine is a 64-bit Mac or PC with a few gigabytes of memory and at least 10 gigabytes of free disk space, you can `gitian-build` using diff --git a/contrib/gitian-downloader/linux-download-config b/contrib/gitian-downloader/linux-download-config deleted file mode 100644 index c0048d336e..0000000000 --- a/contrib/gitian-downloader/linux-download-config +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: bitcoin -urls: -- http://bitcoin.org/bitcoin-latest-linux-gitian.zip -rss: -- url: - xpath: //item/link/text() - pattern: bitcoin-\d+.\d+.\d+-linux-gitian.zip -signers: - 0A82509767C7D4A5D14DA2301AE1D35043E08E54: - name: BlueMatt - key: bluematt - BF6273FAEF7CC0BA1F562E50989F6B3048A116B5: - name: Devrandom - key: devrandom - E463A93F5F3117EEDE6C7316BD02942421F4889F: - name: Luke-Jr - key: luke-jr - D762373D24904A3E42F33B08B9A408E71DAAC974: - name: "Pieter Wuille" - key: sipa - 77E72E69DA7EE0A148C06B21B34821D4944DE5F7: - name: tcatm - key: tcatm - 01CDF4627A3B88AAE4A571C87588242FBE38D3A8: - name: "Gavin Andresen" - key: gavinandresen - 71A3B16735405025D447E8F274810B012346C9A6: - name: "Wladimir J. van der Laan" - key: laanwj - AEC1884398647C47413C1C3FB1179EB7347DC10D: - name: "Warren Togami" - key: wtogami - 9692B91BBF0E8D34DFD33B1882C5C009628ECF0C: - name: michagogo - key: michagogo - E944AE667CF960B1004BC32FCA662BE18B877A60: - name: "Andreas Schildbach" - key: aschildbach - C060A6635913D98A3587D7DB1C2491FFEB0EF770: - name: "Cory Fields" - key: "cfields" - 37EC7D7B0A217CDB4B4E007E7FAB114267E4FA04: - name: "Peter Todd" - key: "petertodd" diff --git a/contrib/gitian-downloader/win32-download-config b/contrib/gitian-downloader/win32-download-config deleted file mode 100644 index 0ad3bdd4f3..0000000000 --- a/contrib/gitian-downloader/win32-download-config +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: bitcoin -urls: -- http://bitcoin.org/bitcoin-latest-win32-gitian.zip -rss: -- url: - xpath: //item/link/text() - pattern: bitcoin-\d+.\d+.\d+-win32-gitian.zip -signers: - 0A82509767C7D4A5D14DA2301AE1D35043E08E54: - name: BlueMatt - key: bluematt - BF6273FAEF7CC0BA1F562E50989F6B3048A116B5: - name: Devrandom - key: devrandom - E463A93F5F3117EEDE6C7316BD02942421F4889F: - name: Luke-Jr - key: luke-jr - D762373D24904A3E42F33B08B9A408E71DAAC974: - name: "Pieter Wuille" - key: sipa - 77E72E69DA7EE0A148C06B21B34821D4944DE5F7: - name: tcatm - key: tcatm - 01CDF4627A3B88AAE4A571C87588242FBE38D3A8: - name: "Gavin Andresen" - key: gavinandresen - 71A3B16735405025D447E8F274810B012346C9A6: - name: "Wladimir J. van der Laan" - key: laanwj - AEC1884398647C47413C1C3FB1179EB7347DC10D: - name: "Warren Togami" - key: wtogami - 9692B91BBF0E8D34DFD33B1882C5C009628ECF0C: - name: michagogo - key: michagogo - E944AE667CF960B1004BC32FCA662BE18B877A60: - name: "Andreas Schildbach" - key: aschildbach - C060A6635913D98A3587D7DB1C2491FFEB0EF770: - name: "Cory Fields" - key: "cfields" - 37EC7D7B0A217CDB4B4E007E7FAB114267E4FA04: - name: "Peter Todd" - key: "petertodd" 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/build-osx.md b/doc/build-osx.md index 02498e5c4b..c3cb1b7891 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -5,7 +5,7 @@ This guide will show you how to build bitcoind (headless client) for OS X. Notes ----- -* Tested on OS X 10.7 through 10.10 on 64-bit Intel processors only. +* Tested on OS X 10.7 through 10.11 on 64-bit Intel processors only. * All of the commands should be executed in a Terminal application. The built-in one is located in `/Applications/Utilities`. @@ -24,7 +24,7 @@ be re-done or updated every time Xcode is updated. You will also need to install [Homebrew](http://brew.sh) in order to install library dependencies. -The installation of the actual dependencies is covered in the Instructions +The installation of the actual dependencies is covered in the instructions sections below. Instructions: Homebrew @@ -36,17 +36,19 @@ Instructions: Homebrew NOTE: Building with Qt4 is still supported, however, could result in a broken UI. As such, building with Qt5 is recommended. -### Building `bitcoind` +### Building `bitcoin` 1. Clone the GitHub tree to get the source code and go into the directory. git clone https://github.com/bitcoin/bitcoin.git cd bitcoin -2. Build bitcoind: +2. Build bitcoin-core: + This will configure and build the headless bitcoin binaries as well as the gui (if Qt is found). + You can disable the gui build by passing `--without-gui` to configure. ./autogen.sh - ./configure --with-gui=qt5 + ./configure make 3. It is also a good idea to build and run the unit tests: @@ -60,10 +62,10 @@ NOTE: Building with Qt4 is still supported, however, could result in a broken UI Use Qt Creator as IDE ------------------------ You can use Qt Creator as IDE, for debugging and for manipulating forms, etc. -Download Qt Creator from http://www.qt.io/download/. Download the "community edition" and only install Qt Creator (uncheck the rest during the installation process). +Download Qt Creator from https://www.qt.io/download/. Download the "community edition" and only install Qt Creator (uncheck the rest during the installation process). 1. Make sure you installed everything through Homebrew mentioned above -2. Do a proper ./configure --with-gui=qt5 --enable-debug +2. Do a proper ./configure --enable-debug 3. In Qt Creator do "New Project" -> Import Project -> Import Existing Project 4. Enter "bitcoin-qt" as project name, enter src/qt as location 5. Leave the file selection as it is @@ -79,7 +81,7 @@ You can ignore this section if you are building `bitcoind` for your own use. bitcoind/bitcoin-cli binaries are not included in the Bitcoin-Qt.app bundle. -If you are building `bitcoind` or `Bitcoin-Qt` for others, your build machine should be set up +If you are building `bitcoind` or `Bitcoin Core` for others, your build machine should be set up as follows for maximum compatibility: All dependencies should be compiled with these flags: @@ -88,7 +90,7 @@ All dependencies should be compiled with these flags: -arch x86_64 -isysroot $(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk -Once dependencies are compiled, see [doc/release-process.md](release-process.md) for how the Bitcoin-Qt.app +Once dependencies are compiled, see [doc/release-process.md](release-process.md) for how the Bitcoin Core bundle is packaged and signed to create the .dmg disk image that is distributed. Running diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 7fe292f1f8..358792251b 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 pull requests 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 no crashes happen with run-time option `-disablewallet`. + + - *Rationale*: In RPC code that conditionally uses 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 define the interface for the code in implementation files. 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 009baaed51..96c830d177 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -206,6 +206,33 @@ 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. + +Negative confirmations and conflict detection +--------------------------------------------- + +The wallet will now report a negative number for confirmations that indicates +how deep in the block chain the conflict is found. For example, if a transaction +A has 5 confirmations and spends the same input as a wallet transaction B, B +will be reported as having -5 confirmations. If another wallet transaction C +spends an output from B, it will also be reported as having -5 confirmations. +To detect conflicts with historical transactions in the chain a one-time +`-rescan` may be needed. + +Unlike earlier versions, unconfirmed but non-conflicting transactions will never +get a negative confirmation count. They are not treated as spendable unless +they're coming from ourself (change) and accepted into our local mempool, +however. The new "trusted" field in the `listtransactions` RPC output +indicates whether outputs of an unconfirmed transaction are considered +spendable. + 0.12.0 Change log ================= diff --git a/doc/unit-tests.md b/doc/unit-tests.md index 72613054b9..afaece829c 100644 --- a/doc/unit-tests.md +++ b/doc/unit-tests.md @@ -1,18 +1,18 @@ Compiling/running unit tests ------------------------------------ -Unit tests will be automatically compiled if dependencies were met in configure +Unit tests will be automatically compiled if dependencies were met in `./configure` and tests weren't explicitly disabled. -After configuring, they can be run with 'make check'. +After configuring, they can be run with `make check`. -To run the bitcoind tests manually, launch src/test/test_bitcoin . +To run the bitcoind tests manually, launch `src/test/test_bitcoin`. To add more bitcoind tests, add `BOOST_AUTO_TEST_CASE` functions to the existing -.cpp files in the test/ directory or add new .cpp files that +.cpp files in the `test/` directory or add new .cpp files that implement new BOOST_AUTO_TEST_SUITE sections. -To run the bitcoin-qt tests manually, launch src/qt/test/test_bitcoin-qt +To run the bitcoin-qt tests manually, launch `src/qt/test/test_bitcoin-qt` To add more bitcoin-qt tests, add them to the `src/qt/test/` directory and the `src/qt/test/test_main.cpp` file. diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 3d156a2e7b..df71e44b60 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -22,6 +22,7 @@ For a description of arguments recognized by test scripts, see """ import os +import time import shutil import sys import subprocess @@ -47,6 +48,10 @@ opts = set() passOn = "" p = re.compile("^--") +bold = ("","") +if (os.name == 'posix'): + bold = ('\033[0m', '\033[1m') + for arg in sys.argv[1:]: if arg == '--coverage': ENABLE_COVERAGE = 1 @@ -77,8 +82,9 @@ testScripts = [ 'rawtransactions.py', 'rest.py', 'mempool_spendcoinbase.py', - 'mempool_coinbase_spends.py', + 'mempool_reorg.py', 'httpbasics.py', + 'multi_rpc.py', 'zapwallettxes.py', 'proxy_test.py', 'merkle_blocks.py', @@ -91,6 +97,9 @@ testScripts = [ 'p2p-fullblocktest.py', 'blockchain.py', 'disablewallet.py', + 'sendheaders.py', + 'keypool.py', + 'prioritise_transaction.py', ] testScriptsExt = [ 'bip65-cltv.py', @@ -104,7 +113,6 @@ testScriptsExt = [ 'pruning.py', 'forknotify.py', 'invalidateblock.py', - 'keypool.py', # 'rpcbind_test.py', #temporary, bug in libevent, see #6655 'smartfees.py', 'maxblocksinflight.py', @@ -125,7 +133,7 @@ def runtests(): if ENABLE_COVERAGE: coverage = RPCCoverage() - print("Initializing coverage directory at %s" % coverage.dir) + print("Initializing coverage directory at %s\n" % coverage.dir) if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): rpcTestDir = buildDir + '/qa/rpc-tests/' @@ -140,10 +148,12 @@ def runtests(): or run_extended or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): - print("Running testscript " + testScripts[i] + "...") + print("Running testscript %s%s%s ..." % (bold[1], testScripts[i], bold[0])) + time0 = time.time() subprocess.check_call( rpcTestDir + testScripts[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) # exit if help is called so we print just one set of # instructions @@ -155,12 +165,14 @@ def runtests(): for i in range(len(testScriptsExt)): if (run_extended or testScriptsExt[i] in opts or re.sub(".py$", "", testScriptsExt[i]) in opts): + print( "Running 2nd level testscript " - + testScriptsExt[i] + "...") - + + "%s%s%s ..." % (bold[1], testScriptsExt[i], bold[0])) + time0 = time.time() subprocess.check_call( rpcTestDir + testScriptsExt[i] + flags, shell=True) + print("Duration: %s s\n" % (int(time.time() - time0))) if coverage: coverage.report_rpc_coverage() diff --git a/qa/replace-by-fee/.gitignore b/qa/replace-by-fee/.gitignore deleted file mode 100644 index b2c4f4657a..0000000000 --- a/qa/replace-by-fee/.gitignore +++ /dev/null @@ -1 +0,0 @@ -python-bitcoinlib diff --git a/qa/replace-by-fee/README.md b/qa/replace-by-fee/README.md deleted file mode 100644 index baad86de9a..0000000000 --- a/qa/replace-by-fee/README.md +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100755 index 1ee6c83875..0000000000 --- a/qa/replace-by-fee/rbf-tests.py +++ /dev/null @@ -1,360 +0,0 @@ -#!/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/mempool_packages.py b/qa/rpc-tests/mempool_packages.py index 746c26ff5e..34b316a6a3 100755 --- a/qa/rpc-tests/mempool_packages.py +++ b/qa/rpc-tests/mempool_packages.py @@ -8,9 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -def satoshi_round(amount): - return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) - MAX_ANCESTORS = 25 MAX_DESCENDANTS = 25 diff --git a/qa/rpc-tests/mempool_coinbase_spends.py b/qa/rpc-tests/mempool_reorg.py index c64a15b9f5..fdbaf689ad 100755 --- a/qa/rpc-tests/mempool_coinbase_spends.py +++ b/qa/rpc-tests/mempool_reorg.py @@ -52,16 +52,25 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1 # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase), # and make sure the mempool code behaves correctly. - b = [ self.nodes[0].getblockhash(n) for n in range(102, 105) ] + b = [ self.nodes[0].getblockhash(n) for n in range(101, 105) ] coinbase_txids = [ self.nodes[0].getblock(h)['tx'][0] for h in b ] - spend_101_raw = self.create_tx(coinbase_txids[0], node1_address, 50) - spend_102_raw = self.create_tx(coinbase_txids[1], node0_address, 50) - spend_103_raw = self.create_tx(coinbase_txids[2], node0_address, 50) + spend_101_raw = self.create_tx(coinbase_txids[1], node1_address, 50) + spend_102_raw = self.create_tx(coinbase_txids[2], node0_address, 50) + spend_103_raw = self.create_tx(coinbase_txids[3], node0_address, 50) + + # Create a block-height-locked transaction which will be invalid after reorg + timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[0], "vout": 0}], {node0_address: 50}) + # Set the time lock + timelock_tx = timelock_tx.replace("ffffffff", "11111111", 1) + timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" + timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] + assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) + assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = self.create_tx(spend_102_id, node1_address, 50) @@ -69,7 +78,8 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # Broadcast and mine 103_1: spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw) - self.nodes[0].generate(1) + last_block = self.nodes[0].generate(1) + timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx) # ... now put spend_101 and spend_102_1 in memory pools: spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) @@ -77,7 +87,11 @@ class MempoolCoinbaseTest(BitcoinTestFramework): self.sync_all() - assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id ])) + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id, timelock_tx_id ])) + + for node in self.nodes: + node.invalidateblock(last_block[0]) + assert_equal(set(self.nodes[0].getrawmempool()), set([ spend_101_id, spend_102_1_id, spend_103_1_id ])) # Use invalidateblock to re-org back and make all those coinbase spends # immature/invalid: diff --git a/qa/rpc-tests/multi_rpc.py b/qa/rpc-tests/multi_rpc.py new file mode 100755 index 0000000000..62071d426e --- /dev/null +++ b/qa/rpc-tests/multi_rpc.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python2 +# 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 mulitple rpc user config option rpcauth +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import base64 + +try: + import http.client as httplib +except ImportError: + import httplib +try: + import urllib.parse as urlparse +except ImportError: + import urlparse + +class HTTPBasicsTest (BitcoinTestFramework): + def setup_nodes(self): + return start_nodes(4, self.options.tmpdir) + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain(self.options.tmpdir) + #Append rpcauth to bitcoin.conf before initialization + rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" + rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" + with open(os.path.join(self.options.tmpdir+"/node0", "bitcoin.conf"), 'a') as f: + f.write(rpcauth+"\n") + f.write(rpcauth2+"\n") + + def run_test(self): + + ################################################## + # Check correctness of the rpcauth config option # + ################################################## + url = urlparse.urlparse(self.nodes[0].url) + + #Old authpair + authpair = url.username + ':' + url.password + + #New authpair generated via contrib/rpcuser tool + rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" + password = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM=" + + #Second authpair with different username + rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" + password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" + authpairnew = "rt:"+password + + headers = {"Authorization": "Basic " + base64.b64encode(authpair)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, False) + conn.close() + + #Use new authpair to confirm both work + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, False) + conn.close() + + #Wrong login name with rt's password + authpairnew = "rtwrong:"+password + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, True) + conn.close() + + #Wrong password for rt + authpairnew = "rt:"+password+"wrong" + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, True) + conn.close() + + #Correct for rt2 + authpairnew = "rt2:"+password2 + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, False) + conn.close() + + #Wrong password for rt2 + authpairnew = "rt2:"+password2+"wrong" + headers = {"Authorization": "Basic " + base64.b64encode(authpairnew)} + + conn = httplib.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status==401, True) + conn.close() + + + +if __name__ == '__main__': + HTTPBasicsTest ().main () diff --git a/qa/rpc-tests/prioritise_transaction.py b/qa/rpc-tests/prioritise_transaction.py new file mode 100755 index 0000000000..f376ceee5e --- /dev/null +++ b/qa/rpc-tests/prioritise_transaction.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python2 +# 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 PrioritiseTransaction code +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +COIN = 100000000 + +class PrioritiseTransactionTest(BitcoinTestFramework): + + def __init__(self): + # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create + # So we have big transactions (and therefore can't fit very many into each block) + # create one script_pubkey + script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes + for i in xrange (512): + script_pubkey = script_pubkey + "01" + # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change + self.txouts = "81" + for k in xrange(128): + # add txout value + self.txouts = self.txouts + "0000000000000000" + # add length of script_pubkey + self.txouts = self.txouts + "fd0402" + # add script_pubkey + self.txouts = self.txouts + script_pubkey + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 1) + + def setup_network(self): + self.nodes = [] + self.is_network_split = False + + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-printpriority=1"])) + self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] + + def create_confirmed_utxos(self, count): + self.nodes[0].generate(int(0.5*count)+101) + utxos = self.nodes[0].listunspent() + iterations = count - len(utxos) + addr1 = self.nodes[0].getnewaddress() + addr2 = self.nodes[0].getnewaddress() + if iterations <= 0: + return utxos + for i in xrange(iterations): + t = utxos.pop() + fee = self.relayfee + inputs = [] + inputs.append({ "txid" : t["txid"], "vout" : t["vout"]}) + outputs = {} + send_value = t['amount'] - fee + outputs[addr1] = satoshi_round(send_value/2) + outputs[addr2] = satoshi_round(send_value/2) + raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) + signed_tx = self.nodes[0].signrawtransaction(raw_tx)["hex"] + txid = self.nodes[0].sendrawtransaction(signed_tx) + + while (self.nodes[0].getmempoolinfo()['size'] > 0): + self.nodes[0].generate(1) + + utxos = self.nodes[0].listunspent() + assert(len(utxos) >= count) + return utxos + + def create_lots_of_big_transactions(self, utxos, fee): + addr = self.nodes[0].getnewaddress() + txids = [] + for i in xrange(len(utxos)): + t = utxos.pop() + inputs = [] + inputs.append({ "txid" : t["txid"], "vout" : t["vout"]}) + outputs = {} + send_value = t['amount'] - fee + outputs[addr] = satoshi_round(send_value) + rawtx = self.nodes[0].createrawtransaction(inputs, outputs) + newtx = rawtx[0:92] + newtx = newtx + self.txouts + newtx = newtx + rawtx[94:] + signresult = self.nodes[0].signrawtransaction(newtx, None, None, "NONE") + txid = self.nodes[0].sendrawtransaction(signresult["hex"], True) + txids.append(txid) + return txids + + def run_test(self): + utxos = self.create_confirmed_utxos(90) + base_fee = self.relayfee*100 # our transactions are smaller than 100kb + txids = [] + + # Create 3 batches of transactions at 3 different fee rate levels + for i in xrange(3): + txids.append([]) + txids[i] = self.create_lots_of_big_transactions(utxos[30*i:30*i+30], (i+1)*base_fee) + + # add a fee delta to something in the cheapest bucket and make sure it gets mined + # also check that a different entry in the cheapest bucket is NOT mined (lower + # the priority to ensure its not mined due to priority) + self.nodes[0].prioritisetransaction(txids[0][0], 0, int(3*base_fee*COIN)) + self.nodes[0].prioritisetransaction(txids[0][1], -1e15, 0) + + self.nodes[0].generate(1) + + mempool = self.nodes[0].getrawmempool() + print "Assert that prioritised transasction was mined" + assert(txids[0][0] not in mempool) + assert(txids[0][1] in mempool) + + high_fee_tx = None + for x in txids[2]: + if x not in mempool: + high_fee_tx = x + + # Something high-fee should have been mined! + assert(high_fee_tx != None) + + # Add a prioritisation before a tx is in the mempool (de-prioritising a + # high-fee transaction). + self.nodes[0].prioritisetransaction(high_fee_tx, -1e15, -int(2*base_fee*COIN)) + + # Add everything back to mempool + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + + # Check to make sure our high fee rate tx is back in the mempool + mempool = self.nodes[0].getrawmempool() + assert(high_fee_tx in mempool) + + # Now verify the high feerate transaction isn't mined. + self.nodes[0].generate(5) + + # High fee transaction should not have been mined, but other high fee rate + # transactions should have been. + mempool = self.nodes[0].getrawmempool() + print "Assert that de-prioritised transaction is still in mempool" + assert(high_fee_tx in mempool) + for x in txids[2]: + if (x != high_fee_tx): + assert(x not in mempool) + +if __name__ == '__main__': + PrioritiseTransactionTest().main() diff --git a/qa/rpc-tests/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py index 537a1ed8d9..6e9e0b304c 100755 --- a/qa/rpc-tests/replace-by-fee.py +++ b/qa/rpc-tests/replace-by-fee.py @@ -73,7 +73,12 @@ 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"])) + "-relaypriority=0", "-whitelist=127.0.0.1", + "-limitancestorcount=50", + "-limitancestorsize=101", + "-limitdescendantcount=200", + "-limitdescendantsize=101" + ])) self.is_network_split = False def run_test(self): 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 ecfffc1b45..b209ae0c16 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -19,9 +19,6 @@ P2SH_2 = "2NBdpwq8Aoo1EEKEXPNrKvr5xQr3M9UfcZA" # P2SH of "OP_2 OP_DROP" # 4 bytes of OP_TRUE and push 2-byte redeem script of "OP_1 OP_DROP" or "OP_2 OP_DROP" SCRIPT_SIG = ["0451025175", "0451025275"] -def satoshi_round(amount): - return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) - def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): ''' Create and send a transaction with a random fee. 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..b7e90a8a8b 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 @@ -214,7 +217,8 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary= datadir = os.path.join(dirname, "node"+str(i)) if binary is None: binary = os.getenv("BITCOIND", "bitcoind") - args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest" ] + # RPC tests still depend on free transactions + args = [ binary, "-datadir="+datadir, "-keypool=1", "-discover=0", "-rest", "-blockprioritysize=50000" ] if extra_args is not None: args.extend(extra_args) bitcoind_processes[i] = subprocess.Popen(args) devnull = open(os.devnull, "w") @@ -401,3 +405,6 @@ def assert_raises(exc, fun, *args, **kwds): raise AssertionError("Unexpected exception raised: "+type(e).__name__) else: raise AssertionError("No exception raised") + +def satoshi_round(amount): + return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) diff --git a/qa/rpc-tests/txn_clone.py b/qa/rpc-tests/txn_clone.py index e8ced0e5bb..b1f603a192 100755 --- a/qa/rpc-tests/txn_clone.py +++ b/qa/rpc-tests/txn_clone.py @@ -136,7 +136,7 @@ class TxnMallTest(BitcoinTestFramework): tx2 = self.nodes[0].gettransaction(txid2) # Verify expected confirmations - assert_equal(tx1["confirmations"], -1) + assert_equal(tx1["confirmations"], -2) assert_equal(tx1_clone["confirmations"], 2) assert_equal(tx2["confirmations"], 1) diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py index 36081127b4..d4665b3d42 100755 --- a/qa/rpc-tests/txn_doublespend.py +++ b/qa/rpc-tests/txn_doublespend.py @@ -99,7 +99,7 @@ class TxnMallTest(BitcoinTestFramework): # Now give doublespend and its parents to miner: self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) - self.nodes[2].sendrawtransaction(doublespend["hex"]) + doublespend_txid = self.nodes[2].sendrawtransaction(doublespend["hex"]) # ... mine a block... self.nodes[2].generate(1) @@ -107,14 +107,15 @@ class TxnMallTest(BitcoinTestFramework): connect_nodes(self.nodes[1], 2) self.nodes[2].generate(1) # Mine another block to make sure we sync sync_blocks(self.nodes) + assert_equal(self.nodes[0].gettransaction(doublespend_txid)["confirmations"], 2) # Re-fetch transaction info: tx1 = self.nodes[0].gettransaction(txid1) tx2 = self.nodes[0].gettransaction(txid2) - + # Both transactions should be conflicted - assert_equal(tx1["confirmations"], -1) - assert_equal(tx2["confirmations"], -1) + assert_equal(tx1["confirmations"], -2) + assert_equal(tx2["confirmations"], -2) # Node0's total balance should be starting balance, plus 100BTC for # two more matured blocks, minus 1240 for the double-spend, plus fees (which are 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/share/rpcuser/README.md b/share/rpcuser/README.md new file mode 100644 index 0000000000..7c2c909a42 --- /dev/null +++ b/share/rpcuser/README.md @@ -0,0 +1,11 @@ +RPC Tools +--------------------- + +### [RPCUser](/share/rpcuser) ### + +Create an RPC user login credential. + +Usage: + +./rpcuser.py <username> + diff --git a/share/rpcuser/rpcuser.py b/share/rpcuser/rpcuser.py new file mode 100755 index 0000000000..9fd176908b --- /dev/null +++ b/share/rpcuser/rpcuser.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python2 +# 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. + +import hashlib +import sys +import os +from random import SystemRandom +import base64 +import hmac + +if len(sys.argv) < 2: + sys.stderr.write('Please include username as an argument.\n') + sys.exit(0) + +username = sys.argv[1] + +#This uses os.urandom() underneath +cryptogen = SystemRandom() + +#Create 16 byte hex salt +salt_sequence = [cryptogen.randrange(256) for i in range(16)] +hexseq = list(map(hex, salt_sequence)) +salt = "".join([x[2:] for x in hexseq]) + +#Create 32 byte b64 password +password = base64.urlsafe_b64encode(os.urandom(32)) + +digestmod = hashlib.sha256 + +if sys.version_info.major >= 3: + password = password.decode('utf-8') + digestmod = 'SHA256' + +m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), digestmod) +result = m.hexdigest() + +print("String to be appended to bitcoin.conf:") +print("rpcauth="+username+":"+salt+"$"+result) +print("Your password:\n"+password) diff --git a/src/Makefile.am b/src/Makefile.am index f1e98dabde..bb627a5448 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 \ @@ -123,6 +124,7 @@ BITCOIN_CORE_H = \ policy/fees.h \ policy/policy.h \ pow.h \ + prevector.h \ primitives/block.h \ primitives/transaction.h \ protocol.h \ @@ -268,6 +270,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 fafc1a2944..4d0894b711 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -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 \ @@ -64,6 +65,7 @@ BITCOIN_TESTS =\ test/pmt_tests.cpp \ test/policyestimator_tests.cpp \ test/pow_tests.cpp \ + test/prevector_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ test/sanity_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/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/coincontrol.h b/src/coincontrol.h index bc965f9e19..3945644ce8 100644 --- a/src/coincontrol.h +++ b/src/coincontrol.h @@ -16,6 +16,8 @@ public: bool fAllowOtherInputs; //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria bool fAllowWatchOnly; + //! Minimum absolute fee (not per kilobyte) + CAmount nMinimumTotalFee; CCoinControl() { @@ -28,6 +30,7 @@ public: fAllowOtherInputs = false; fAllowWatchOnly = false; setSelected.clear(); + nMinimumTotalFee = 0; } bool HasSelected() const diff --git a/src/coins.cpp b/src/coins.cpp index f0ea5c0459..122bf4e48d 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -144,6 +144,11 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { return (it != cacheCoins.end() && !it->second.coins.vout.empty()); } +bool CCoinsViewCache::HaveCoinsInCache(const uint256 &txid) const { + CCoinsMap::const_iterator it = cacheCoins.find(txid); + return it != cacheCoins.end(); +} + uint256 CCoinsViewCache::GetBestBlock() const { if (hashBlock.IsNull()) hashBlock = base->GetBestBlock(); @@ -206,6 +211,15 @@ bool CCoinsViewCache::Flush() { return fOk; } +void CCoinsViewCache::Uncache(const uint256& hash) +{ + CCoinsMap::iterator it = cacheCoins.find(hash); + if (it != cacheCoins.end() && it->second.flags == 0) { + cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage(); + cacheCoins.erase(it); + } +} + unsigned int CCoinsViewCache::GetCacheSize() const { return cacheCoins.size(); } @@ -243,8 +257,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; @@ -253,8 +268,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..60c1ba8a78 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) @@ -406,6 +406,13 @@ public: bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); /** + * Check if we have the given tx already loaded in this cache. + * The semantics are the same as HaveCoins(), but no calls to + * the backing CCoinsView are made. + */ + bool HaveCoinsInCache(const uint256 &txid) const; + + /** * Return a pointer to CCoins in the cache, or NULL if not found. This is * more efficient than GetCoins. Modifications to other cache entries are * allowed while accessing the returned pointer. @@ -437,6 +444,12 @@ public: */ bool Flush(); + /** + * Removes the transaction with the given hash from the cache, if it is + * not modified. + */ + void Uncache(const uint256 &txid); + //! Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize() const; @@ -456,8 +469,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/core_memusage.h b/src/core_memusage.h index a05f59ee0c..450537d059 100644 --- a/src/core_memusage.h +++ b/src/core_memusage.h @@ -10,7 +10,7 @@ #include "memusage.h" static inline size_t RecursiveDynamicUsage(const CScript& script) { - return memusage::DynamicUsage(*static_cast<const std::vector<unsigned char>*>(&script)); + return memusage::DynamicUsage(*static_cast<const CScriptBase*>(&script)); } static inline size_t RecursiveDynamicUsage(const COutPoint& out) { diff --git a/src/hash.h b/src/hash.h index 0771555623..daa92a0097 100644 --- a/src/hash.h +++ b/src/hash.h @@ -8,6 +8,7 @@ #include "crypto/ripemd160.h" #include "crypto/sha256.h" +#include "prevector.h" #include "serialize.h" #include "uint256.h" #include "version.h" @@ -118,6 +119,13 @@ inline uint160 Hash160(const std::vector<unsigned char>& vch) return Hash160(vch.begin(), vch.end()); } +/** Compute the 160-bit hash of a vector. */ +template<unsigned int N> +inline uint160 Hash160(const prevector<N, unsigned char>& vch) +{ + return Hash160(vch.begin(), vch.end()); +} + /** A writer stream (for serialization) that computes a 256-bit hash. */ class CHashWriter { diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 98ac750bb1..2920aa26f7 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -10,8 +10,12 @@ #include "util.h" #include "utilstrencodings.h" #include "ui_interface.h" +#include "crypto/hmac_sha256.h" +#include <stdio.h> +#include "utilstrencodings.h" #include <boost/algorithm/string.hpp> // boost::trim +#include <boost/foreach.hpp> //BOOST_FOREACH /** Simple one-shot callback timer to be used by the RPC mechanism to e.g. * re-lock the wellet. @@ -72,6 +76,50 @@ static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const Uni req->WriteReply(nStatus, strReply); } +//This function checks username and password against -rpcauth +//entries from config file. +static bool multiUserAuthorized(std::string strUserPass) +{ + if (strUserPass.find(":") == std::string::npos) { + return false; + } + std::string strUser = strUserPass.substr(0, strUserPass.find(":")); + std::string strPass = strUserPass.substr(strUserPass.find(":") + 1); + + if (mapMultiArgs.count("-rpcauth") > 0) { + //Search for multi-user login/pass "rpcauth" from config + BOOST_FOREACH(std::string strRPCAuth, mapMultiArgs["-rpcauth"]) + { + std::vector<std::string> vFields; + boost::split(vFields, strRPCAuth, boost::is_any_of(":$")); + if (vFields.size() != 3) { + //Incorrect formatting in config file + continue; + } + + std::string strName = vFields[0]; + if (!TimingResistantEqual(strName, strUser)) { + continue; + } + + std::string strSalt = vFields[1]; + std::string strHash = vFields[2]; + + unsigned int KEY_SIZE = 32; + unsigned char *out = new unsigned char[KEY_SIZE]; + + CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out); + std::vector<unsigned char> hexvec(out, out+KEY_SIZE); + std::string strHashFromPass = HexStr(hexvec); + + if (TimingResistantEqual(strHashFromPass, strHash)) { + return true; + } + } + } + return false; +} + static bool RPCAuthorized(const std::string& strAuth) { if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called @@ -81,7 +129,12 @@ static bool RPCAuthorized(const std::string& strAuth) std::string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); std::string strUserPass = DecodeBase64(strUserPass64); - return TimingResistantEqual(strUserPass, strRPCUserColonPass); + + //Check if authorized under single-user field + if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) { + return true; + } + return multiUserAuthorized(strUserPass); } static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) @@ -157,6 +210,7 @@ static bool InitRPCAuthentication() return false; } } else { + LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation."); strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; } return true; diff --git a/src/init.cpp b/src/init.cpp index 3e46958afc..c36cf9efbc 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,33 +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("-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)"), 8333, 18333)); + 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)); @@ -388,22 +392,21 @@ 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)); - if (showDebug) - strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)", + strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + 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))); strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), 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)")); @@ -420,38 +423,38 @@ std::string HelpMessage(HelpMessageMode mode) 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, zmq"; // 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)"), @@ -459,8 +462,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)")); @@ -469,11 +474,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) @@ -481,10 +486,11 @@ 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")); + strUsage += HelpMessageOpt("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times")); strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), 8332, 18332)); strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times")); strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS)); @@ -493,23 +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)")); - strUsage += HelpMessageOpt("-resetguisettings", _("Reset all settings changes made over the GUI")); - if (showDebug) { - strUsage += HelpMessageOpt("-uiplatform", "Select platform to customize UI for (one of windows, macosx, other; default: platform compiled on)"); - } - } - return strUsage; } @@ -526,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 } @@ -643,7 +635,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) } } - if (GetBoolArg("-stopafterblockimport", false)) { + if (GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); StartShutdown(); } @@ -675,7 +667,7 @@ 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; @@ -759,9 +751,9 @@ void InitParameterInteraction() void InitLogging() { fPrintToConsole = GetBoolArg("-printtoconsole", false); - fLogTimestamps = GetBoolArg("-logtimestamps", true); + fLogTimestamps = GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); fLogTimeMicros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); - fLogIPs = GetBoolArg("-logips", false); + 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); @@ -831,11 +823,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 2: parameter interactions const CChainParams& chainparams = Params(); - + // 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)) { @@ -844,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); @@ -896,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); // 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 / 1000.0))); + 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); @@ -994,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); @@ -1151,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", ""); @@ -1186,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) { @@ -1275,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 @@ -1331,7 +1314,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } // 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; } @@ -1557,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"); @@ -1631,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/main.cpp b/src/main.cpp index ceb5cb66f3..c41dd58d11 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) { @@ -766,6 +789,17 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return true; } +void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { + int expired = pool.Expire(GetTime() - age); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + std::vector<uint256> vNoSpendsRemaining; + pool.TrimToSize(limit, &vNoSpendsRemaining); + BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) + pcoinsTip->Uncache(removed); +} + CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) { uint256 hash = tx.GetHash(); @@ -793,7 +827,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned } /** Convert CValidationState to a human-readable message for logging */ -static std::string FormatStateMessage(const CValidationState &state) +std::string FormatStateMessage(const CValidationState &state) { return strprintf("%s%s (code %i)", state.GetRejectReason(), @@ -801,8 +835,9 @@ static std::string FormatStateMessage(const CValidationState &state) state.GetRejectCode()); } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) +bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, + std::vector<uint256>& vHashTxnToUncache) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -883,13 +918,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa view.SetBackend(viewMemPool); // do we already have it? - if (view.HaveCoins(hash)) + bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(hash); + if (view.HaveCoins(hash)) { + if (!fHadTxInCache) + vHashTxnToUncache.push_back(hash); return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known"); + } // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), // and only helps with filling in pfMissingInputs (to determine missing vs spent). BOOST_FOREACH(const CTxIn txin, tx.vin) { + if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash)) + vHashTxnToUncache.push_back(txin.prevout.hash); if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; @@ -927,9 +968,21 @@ 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); + + // Keep track of transactions that spend a coinbase, which we re-scan + // during reorgs to ensure COINBASE_MATURITY is still met. + bool fSpendsCoinbase = false; + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + const CCoins *coins = view.AccessCoins(txin.prevout.hash); + if (coins->IsCoinBase()) { + fSpendsCoinbase = true; + break; + } + } - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx)); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps); unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block @@ -941,7 +994,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"); } @@ -963,7 +1016,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; @@ -1175,12 +1228,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // trim mempool and check if tx was trimmed if (!fOverrideMempoolLimit) { - int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - if (expired != 0) - LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); - - pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); - if (!pool.exists(tx.GetHash())) + LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + if (!pool.exists(hash)) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); } } @@ -1190,6 +1239,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return true; } +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) +{ + std::vector<uint256> vHashTxToUncache; + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, fRejectAbsurdFee, vHashTxToUncache); + if (!res) { + BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) + pcoinsTip->Uncache(hashTx); + } + return res; +} + /** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) { @@ -1436,7 +1497,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); @@ -2286,12 +2347,11 @@ void static UpdateTip(CBlockIndex *pindexNew) { } } -/** Disconnect chainActive's tip. You want to manually re-limit mempool size after this */ +/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */ bool static DisconnectTip(CValidationState& state, const Consensus::Params& consensusParams) { CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); - mempool.check(pcoinsTip); // Read block from disk. CBlock block; if (!ReadBlockFromDisk(block, pindexDelete, consensusParams)) @@ -2326,8 +2386,6 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // UpdateTransactionsFromBlock finds descendants of any transactions in this // block that were added back and cleans up the mempool state. mempool.UpdateTransactionsFromBlock(vHashUpdate); - mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight); - mempool.check(pcoinsTip); // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); // Let wallets know transactions went from 1-confirmed to @@ -2351,7 +2409,6 @@ static int64_t nTimePostConnect = 0; 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. int64_t nTime1 = GetTimeMicros(); CBlock block; @@ -2388,7 +2445,6 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Remove conflicting transactions from the mempool. list<CTransaction> txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); - mempool.check(pcoinsTip); // Update chainActive & related variables. UpdateTip(pindexNew); // Tell wallet about transactions that went from mempool @@ -2501,46 +2557,49 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c bool fContinue = true; int nHeight = pindexFork ? pindexFork->nHeight : -1; while (fContinue && nHeight != pindexMostWork->nHeight) { - // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need - // a few blocks along the way. - int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); - vpindexToConnect.clear(); - vpindexToConnect.reserve(nTargetHeight - nHeight); - CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); - while (pindexIter && pindexIter->nHeight != nHeight) { - vpindexToConnect.push_back(pindexIter); - pindexIter = pindexIter->pprev; - } - nHeight = nTargetHeight; - - // Connect new blocks. - BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { - if (state.IsInvalid()) { - // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); - state = CValidationState(); - fInvalidFound = true; - fContinue = false; - break; + // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need + // a few blocks along the way. + int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); + vpindexToConnect.clear(); + vpindexToConnect.reserve(nTargetHeight - nHeight); + CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + while (pindexIter && pindexIter->nHeight != nHeight) { + vpindexToConnect.push_back(pindexIter); + pindexIter = pindexIter->pprev; + } + nHeight = nTargetHeight; + + // Connect new blocks. + BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { + if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { + if (state.IsInvalid()) { + // The block violates a consensus rule. + if (!state.CorruptionPossible()) + InvalidChainFound(vpindexToConnect.back()); + state = CValidationState(); + fInvalidFound = true; + fContinue = false; + break; + } else { + // A system error occurred (disk space, database error, ...). + return false; + } } else { - // A system error occurred (disk space, database error, ...). - return false; - } - } else { - PruneBlockIndexCandidates(); - if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { - // We're in a better position than we were. Return temporarily to release the lock. - fContinue = false; - break; + PruneBlockIndexCandidates(); + if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { + // We're in a better position than we were. Return temporarily to release the lock. + fContinue = false; + break; + } } } } - } - if (fBlocksDisconnected) - mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + if (fBlocksDisconnected) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + } + mempool.check(pcoinsTip); // Callbacks/notifications for a new best chain. if (fInvalidFound) @@ -2556,16 +2615,17 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c * 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 CChainParams& chainparams, const CBlock* pblock) -{ - CBlockIndex *pindexNewTip = NULL; +bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) { CBlockIndex *pindexMostWork = NULL; 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. @@ -2576,26 +2636,48 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, 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()); @@ -2625,11 +2707,12 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(state, consensusParams)) { + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); return false; } } - mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. @@ -2642,6 +2725,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus } InvalidChainFound(pindex); + mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); return true; } @@ -2876,7 +2960,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); @@ -3248,7 +3332,7 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight if (chainActive.Tip() == NULL || nPruneTarget == 0) { return; } - if (chainActive.Tip()->nHeight <= nPruneAfterHeight) { + if ((uint64_t)chainActive.Tip()->nHeight <= nPruneAfterHeight) { return; } @@ -3605,7 +3689,7 @@ bool InitBlockIndex(const CChainParams& chainparams) 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"); @@ -3932,29 +4016,34 @@ std::string GetWarnings(const std::string& strFor) int nPriority = 0; string strStatusBar; string strRPC; + string strGUI; - 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 (!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"; + strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); + } - if (GetBoolArg("-testsafemode", false)) - strStatusBar = strRPC = "testsafemode enabled"; + if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE)) + strStatusBar = strRPC = strGUI = "testsafemode enabled"; // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { nPriority = 1000; - strStatusBar = strMiscWarning; + strStatusBar = strGUI = strMiscWarning; } if (fLargeWorkForkFound) { nPriority = 2000; - strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); + strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; + strGUI = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); } else if (fLargeWorkInvalidChainFound) { nPriority = 2000; - strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); + strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; + strGUI = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); } // Alerts @@ -3966,12 +4055,14 @@ std::string GetWarnings(const std::string& strFor) if (alert.AppliesToMe() && alert.nPriority > nPriority) { nPriority = alert.nPriority; - strStatusBar = alert.strStatusBar; + strStatusBar = strGUI = alert.strStatusBar; } } } - if (strFor == "statusbar") + if (strFor == "gui") + return strGUI; + else if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") return strRPC; @@ -4332,6 +4423,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"); + } } @@ -4401,6 +4500,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") { @@ -4445,7 +4550,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 @@ -4553,6 +4658,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()) { @@ -4580,6 +4687,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); } @@ -4607,13 +4719,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fMissingInputs = false; CValidationState state; + pfrom->setAskFor.erase(inv.hash); 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); @@ -4693,13 +4802,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; @@ -4714,6 +4830,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } + FlushStateToDisk(state, FLUSH_STATE_PERIODIC); } @@ -4768,6 +4885,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()); } @@ -5294,6 +5458,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; @@ -5411,6 +5669,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("getdata", vGetData); vGetData.clear(); } + } else { + //If we're not going to ask, don't expect a response. + pto->setAskFor.erase(inv.hash); } pto->mapAskFor.erase(pto->mapAskFor.begin()); } diff --git a/src/main.h b/src/main.h index f738e3eb57..19623f4d96 100644 --- a/src/main.h +++ b/src/main.h @@ -64,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) */ @@ -86,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 { @@ -192,7 +206,13 @@ void ThreadScriptCheck(); void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, int64_t nPowTargetSpacing); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); -/** Format a string that describes several potential problems detected by the core */ +/** Format a string that describes several potential problems detected by the core. + * strFor can have three values: + * - "rpc": get critical warnings, which should put the client in safe mode if non-empty + * - "statusbar": get all warnings + * - "gui": get all warnings, translated (where possible) for GUI + * This function only returns the highest priority warning of the set selected by strFor. + */ 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); @@ -208,7 +228,7 @@ 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. @@ -237,6 +257,8 @@ void PruneAndFlush(); bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false); +/** Convert CValidationState to a human-readable message for logging */ +std::string FormatStateMessage(const CValidationState &state); struct CNodeStateStats { int nMisbehavior; @@ -453,7 +475,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus /** Remove invalidity status from a block and its descendants. */ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex); -/** The currently-connected chain of blocks. */ +/** The currently-connected chain of blocks (protected by cs_main). */ extern CChain chainActive; /** Global variable that points to the active CCoinsView (protected by cs_main) */ diff --git a/src/memusage.h b/src/memusage.h index e96c5bf038..49760e64c7 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -46,7 +46,9 @@ template<typename X> static inline size_t DynamicUsage(const X * const &v) { ret static inline size_t MallocUsage(size_t alloc) { // Measured on libc6 2.19 on Linux. - if (sizeof(void*) == 8) { + if (alloc == 0) { + return 0; + } else if (sizeof(void*) == 8) { return ((alloc + 31) >> 4) << 4; } else if (sizeof(void*) == 4) { return ((alloc + 15) >> 3) << 3; @@ -74,6 +76,12 @@ static inline size_t DynamicUsage(const std::vector<X>& v) return MallocUsage(v.capacity() * sizeof(X)); } +template<unsigned int N, typename X, typename S, typename D> +static inline size_t DynamicUsage(const prevector<N, X, S, D>& v) +{ + return MallocUsage(v.allocated_memory()); +} + template<typename X, typename Y> static inline size_t DynamicUsage(const std::set<X, Y>& s) { diff --git a/src/miner.cpp b/src/miner.cpp index bb6b513372..c6db00d301 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" @@ -26,6 +27,7 @@ #include <boost/thread.hpp> #include <boost/tuple/tuple.hpp> +#include <queue> using namespace std; @@ -39,48 +41,18 @@ using namespace std; // transactions in the memory pool. When we select transactions from the // pool, we select by highest priority or fee rate, so we might consider // transactions that depend on transactions that aren't yet in the block. -// The COrphan class keeps track of these 'temporary orphans' while -// CreateBlock is figuring out which transactions to include. -// -class COrphan -{ -public: - const CTransaction* ptx; - set<uint256> setDependsOn; - CFeeRate feeRate; - double dPriority; - - COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0) - { - } -}; uint64_t nLastBlockTx = 0; uint64_t nLastBlockSize = 0; -// We want to sort transactions by priority and fee rate, so: -typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority; -class TxPriorityCompare +class ScoreCompare { - bool byFee; - public: - TxPriorityCompare(bool _byFee) : byFee(_byFee) { } + ScoreCompare() {} - bool operator()(const TxPriority& a, const TxPriority& b) + bool operator()(const CTxMemPool::txiter a, const CTxMemPool::txiter b) { - if (byFee) - { - if (a.get<1>() == b.get<1>()) - return a.get<0>() < b.get<0>(); - return a.get<1>() < b.get<1>(); - } - else - { - if (a.get<0>() == b.get<0>()) - return a.get<1>() < b.get<1>(); - return a.get<0>() < b.get<0>(); - } + return CompareTxMemPoolEntryByScore()(*b,*a); // Convert to less than } }; @@ -140,6 +112,22 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); // Collect memory pool transactions into the block + CTxMemPool::setEntries inBlock; + CTxMemPool::setEntries waitSet; + + // This vector will be sorted into a priority queue: + vector<TxCoinAgePriority> vecPriority; + TxCoinAgePriorityCompare pricomparer; + std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> waitPriMap; + typedef std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>::iterator waitPriIter; + double actualPriority = -1; + + std::priority_queue<CTxMemPool::txiter, std::vector<CTxMemPool::txiter>, ScoreCompare> clearedTxs; + bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); + uint64_t nBlockSize = 1000; + uint64_t nBlockTx = 0; + unsigned int nBlockSigOps = 100; + int lastFewTxs = 0; CAmount nFees = 0; { @@ -148,157 +136,102 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s const int nHeight = pindexPrev->nHeight + 1; pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); - CCoinsViewCache view(pcoinsTip); - - // Priority order to process transactions - list<COrphan> vOrphan; // list memory doesn't move - map<uint256, vector<COrphan*> > mapDependers; - bool fPrintPriority = GetBoolArg("-printpriority", false); - - // This vector will be sorted into a priority queue: - vector<TxPriority> vecPriority; - vecPriority.reserve(mempool.mapTx.size()); - for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); - mi != mempool.mapTx.end(); ++mi) - { - const CTransaction& tx = mi->GetTx(); - - int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) - ? nMedianTimePast - : pblock->GetBlockTime(); - - if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff)) - continue; - - COrphan* porphan = NULL; - double dPriority = 0; - CAmount nTotalIn = 0; - bool fMissingInputs = false; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - // Read prev transaction - if (!view.HaveCoins(txin.prevout.hash)) - { - // This should never happen; all transactions in the memory - // pool should connect to either transactions in the chain - // or other transactions in the memory pool. - if (!mempool.mapTx.count(txin.prevout.hash)) - { - LogPrintf("ERROR: mempool transaction missing input\n"); - if (fDebug) assert("mempool transaction missing input" == 0); - fMissingInputs = true; - if (porphan) - vOrphan.pop_back(); - break; - } - - // Has to wait for dependencies - if (!porphan) - { - // Use list for automatic deletion - vOrphan.push_back(COrphan(&tx)); - porphan = &vOrphan.back(); - } - mapDependers[txin.prevout.hash].push_back(porphan); - porphan->setDependsOn.insert(txin.prevout.hash); - nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue; - continue; - } - const CCoins* coins = view.AccessCoins(txin.prevout.hash); - assert(coins); - - CAmount nValueIn = coins->vout[txin.prevout.n].nValue; - nTotalIn += nValueIn; - - int nConf = nHeight - coins->nHeight; - - dPriority += (double)nValueIn * nConf; - } - if (fMissingInputs) continue; - - // Priority is sum(valuein * age) / modified_txsize - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - dPriority = tx.ComputePriority(dPriority, nTxSize); - - uint256 hash = tx.GetHash(); - mempool.ApplyDeltas(hash, dPriority, nTotalIn); - CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize); + int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) + ? nMedianTimePast + : pblock->GetBlockTime(); - if (porphan) + bool fPriorityBlock = nBlockPrioritySize > 0; + if (fPriorityBlock) { + vecPriority.reserve(mempool.mapTx.size()); + for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); + mi != mempool.mapTx.end(); ++mi) { - porphan->dPriority = dPriority; - porphan->feeRate = feeRate; + double dPriority = mi->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); + vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); } - else - vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx()))); + std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); } - // Collect transactions into block - uint64_t nBlockSize = 1000; - uint64_t nBlockTx = 0; - int nBlockSigOps = 100; - bool fSortedByFee = (nBlockPrioritySize <= 0); + CTxMemPool::indexed_transaction_set::nth_index<3>::type::iterator mi = mempool.mapTx.get<3>().begin(); + CTxMemPool::txiter iter; - TxPriorityCompare comparer(fSortedByFee); - std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); - - while (!vecPriority.empty()) + while (mi != mempool.mapTx.get<3>().end() || !clearedTxs.empty()) { - // Take highest priority transaction off the priority queue: - double dPriority = vecPriority.front().get<0>(); - CFeeRate feeRate = vecPriority.front().get<1>(); - const CTransaction& tx = *(vecPriority.front().get<2>()); - - std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); - vecPriority.pop_back(); - - // Size limits - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - if (nBlockSize + nTxSize >= nBlockMaxSize) - continue; + bool priorityTx = false; + if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize + priorityTx = true; + iter = vecPriority.front().second; + actualPriority = vecPriority.front().first; + std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + vecPriority.pop_back(); + } + else if (clearedTxs.empty()) { // add tx with next highest score + iter = mempool.mapTx.project<0>(mi); + mi++; + } + else { // try to add a previously postponed child tx + iter = clearedTxs.top(); + clearedTxs.pop(); + } - // Legacy limits on sigOps: - unsigned int nTxSigOps = GetLegacySigOpCount(tx); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) - continue; + if (inBlock.count(iter)) + continue; // could have been added to the priorityBlock - // Skip free transactions if we're past the minimum block size: - const uint256& hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) - continue; + const CTransaction& tx = iter->GetTx(); - // Prioritise by fee once past the priority size or we run out of high-priority - // transactions: - if (!fSortedByFee && - ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority))) + bool fOrphan = false; + BOOST_FOREACH(CTxMemPool::txiter parent, mempool.GetMemPoolParents(iter)) { - fSortedByFee = true; - comparer = TxPriorityCompare(fSortedByFee); - std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); + if (!inBlock.count(parent)) { + fOrphan = true; + break; + } } - - if (!view.HaveInputs(tx)) + if (fOrphan) { + if (priorityTx) + waitPriMap.insert(std::make_pair(iter,actualPriority)); + else + waitSet.insert(iter); continue; + } - CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut(); - - nTxSigOps += GetP2SHSigOpCount(tx, view); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + unsigned int nTxSize = iter->GetTxSize(); + if (fPriorityBlock && + (nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) { + fPriorityBlock = false; + waitPriMap.clear(); + } + if (!priorityTx && + (iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize) && nBlockSize >= nBlockMinSize)) { + break; + } + if (nBlockSize + nTxSize >= nBlockMaxSize) { + if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { + break; + } + // Once we're within 1000 bytes of a full block, only look at 50 more txs + // to try to fill the remaining space. + if (nBlockSize > nBlockMaxSize - 1000) { + lastFewTxs++; + } continue; + } - // Note that flags: we don't want to set mempool/IsStandard() - // policy here, but we still have to ensure that the block we - // create only contains transactions that are valid in new blocks. - CValidationState state; - if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) + if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) continue; - UpdateCoins(tx, state, view, nHeight); + unsigned int nTxSigOps = iter->GetSigOpCount(); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { + if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { + break; + } + continue; + } + CAmount nTxFees = iter->GetFee(); // Added pblock->vtx.push_back(tx); pblocktemplate->vTxFees.push_back(nTxFees); @@ -310,31 +243,37 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s if (fPrintPriority) { + double dPriority = iter->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy); LogPrintf("priority %.1f fee %s txid %s\n", - dPriority, feeRate.ToString(), tx.GetHash().ToString()); + dPriority , CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), tx.GetHash().ToString()); } + inBlock.insert(iter); + // Add transactions that depend on this one to the priority queue - if (mapDependers.count(hash)) + BOOST_FOREACH(CTxMemPool::txiter child, mempool.GetMemPoolChildren(iter)) { - BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) - { - if (!porphan->setDependsOn.empty()) - { - porphan->setDependsOn.erase(hash); - if (porphan->setDependsOn.empty()) - { - vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx)); - std::push_heap(vecPriority.begin(), vecPriority.end(), comparer); - } + if (fPriorityBlock) { + waitPriIter wpiter = waitPriMap.find(child); + if (wpiter != waitPriMap.end()) { + vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); + std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + waitPriMap.erase(wpiter); + } + } + else { + if (waitSet.count(child)) { + clearedTxs.push(child); + waitSet.erase(child); } } } } - nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; - LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); + LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps); // Compute final coinbase transaction. txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); @@ -350,8 +289,9 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CValidationState state; - if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) - throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed"); + if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + } } return pblocktemplate.release(); @@ -373,7 +313,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..e5659efc01 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), @@ -2408,8 +2407,12 @@ CNode::~CNode() void CNode::AskFor(const CInv& inv) { - if (mapAskFor.size() > MAPASKFOR_MAX_SZ) + if (mapAskFor.size() > MAPASKFOR_MAX_SZ || setAskFor.size() > SETASKFOR_MAX_SZ) + return; + // a peer may not have multiple non-responded queue positions for a single inv item + if (!setAskFor.insert(inv.hash).second) return; + // We're using mapAskFor as a priority queue, // the key is the earliest time the request can be sent int64_t nRequestTime; @@ -58,6 +58,8 @@ static const bool DEFAULT_UPNP = false; #endif /** The maximum number of entries in mapAskFor */ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; +/** The maximum number of entries in setAskFor (larger due to getdata latency)*/ +static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ; /** The maximum number of peer connections to maintain. */ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; /** The default for -maxuploadtarget. 0 = Unlimited */ @@ -65,6 +67,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(); @@ -382,7 +391,11 @@ public: mruset<CInv> setInventoryKnown; std::vector<CInv> vInventoryToSend; CCriticalSection cs_inventory; + std::set<uint256> setAskFor; 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 +510,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/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 c8d2c1a924..31655f2f3a 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -18,7 +18,7 @@ class CCoinsViewCache; static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; /** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ -static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000; +static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 0; /** The maximum size for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_SIZE = 100000; /** Maximum number of signature check operations in an IsStandard() P2SH script */ diff --git a/src/prevector.h b/src/prevector.h new file mode 100644 index 0000000000..3e80ef5d33 --- /dev/null +++ b/src/prevector.h @@ -0,0 +1,486 @@ +#ifndef _BITCOIN_PREVECTOR_H_ +#define _BITCOIN_PREVECTOR_H_ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include <iterator> + +#pragma pack(push, 1) +/** Implements a drop-in replacement for std::vector<T> which stores up to N + * elements directly (without heap allocation). The types Size and Diff are + * used to store element counts, and can be any unsigned + signed type. + * + * Storage layout is either: + * - Direct allocation: + * - Size _size: the number of used elements (between 0 and N) + * - T direct[N]: an array of N elements of type T + * (only the first _size are initialized). + * - Indirect allocation: + * - Size _size: the number of used elements plus N + 1 + * - Size capacity: the number of allocated elements + * - T* indirect: a pointer to an array of capacity elements of type T + * (only the first _size are initialized). + * + * The data type T must be movable by memmove/realloc(). Once we switch to C++, + * move constructors can be used instead. + */ +template<unsigned int N, typename T, typename Size = uint32_t, typename Diff = int32_t> +class prevector { +public: + typedef Size size_type; + typedef Diff difference_type; + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + class iterator { + T* ptr; + public: + typedef Diff difference_type; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef std::random_access_iterator_tag iterator_category; + iterator(T* ptr_) : ptr(ptr_) {} + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } + T& operator[](size_type pos) { return ptr[pos]; } + const T& operator[](size_type pos) const { return ptr[pos]; } + iterator& operator++() { ptr++; return *this; } + iterator& operator--() { ptr--; return *this; } + iterator operator++(int) { iterator copy(*this); ++(*this); return copy; } + iterator operator--(int) { iterator copy(*this); --(*this); return copy; } + difference_type friend operator-(iterator a, iterator b) { return (&(*a) - &(*b)); } + iterator operator+(size_type n) { return iterator(ptr + n); } + iterator& operator+=(size_type n) { ptr += n; return *this; } + iterator operator-(size_type n) { return iterator(ptr - n); } + iterator& operator-=(size_type n) { ptr -= n; return *this; } + bool operator==(iterator x) const { return ptr == x.ptr; } + bool operator!=(iterator x) const { return ptr != x.ptr; } + bool operator>=(iterator x) const { return ptr >= x.ptr; } + bool operator<=(iterator x) const { return ptr <= x.ptr; } + bool operator>(iterator x) const { return ptr > x.ptr; } + bool operator<(iterator x) const { return ptr < x.ptr; } + }; + + class reverse_iterator { + T* ptr; + public: + typedef Diff difference_type; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef std::bidirectional_iterator_tag iterator_category; + reverse_iterator(T* ptr_) : ptr(ptr_) {} + T& operator*() { return *ptr; } + const T& operator*() const { return *ptr; } + T* operator->() { return ptr; } + const T* operator->() const { return ptr; } + reverse_iterator& operator--() { ptr++; return *this; } + reverse_iterator& operator++() { ptr--; return *this; } + reverse_iterator operator++(int) { reverse_iterator copy(*this); ++(*this); return copy; } + reverse_iterator operator--(int) { reverse_iterator copy(*this); --(*this); return copy; } + bool operator==(reverse_iterator x) const { return ptr == x.ptr; } + bool operator!=(reverse_iterator x) const { return ptr != x.ptr; } + }; + + class const_iterator { + const T* ptr; + public: + typedef Diff difference_type; + typedef const T value_type; + typedef const T* pointer; + typedef const T& reference; + typedef std::random_access_iterator_tag iterator_category; + const_iterator(const T* ptr_) : ptr(ptr_) {} + const_iterator(iterator x) : ptr(&(*x)) {} + const T& operator*() const { return *ptr; } + const T* operator->() const { return ptr; } + const T& operator[](size_type pos) const { return ptr[pos]; } + const_iterator& operator++() { ptr++; return *this; } + const_iterator& operator--() { ptr--; return *this; } + const_iterator operator++(int) { const_iterator copy(*this); ++(*this); return copy; } + const_iterator operator--(int) { const_iterator copy(*this); --(*this); return copy; } + difference_type friend operator-(const_iterator a, const_iterator b) { return (&(*a) - &(*b)); } + const_iterator operator+(size_type n) { return const_iterator(ptr + n); } + const_iterator& operator+=(size_type n) { ptr += n; return *this; } + const_iterator operator-(size_type n) { return const_iterator(ptr - n); } + const_iterator& operator-=(size_type n) { ptr -= n; return *this; } + bool operator==(const_iterator x) const { return ptr == x.ptr; } + bool operator!=(const_iterator x) const { return ptr != x.ptr; } + bool operator>=(const_iterator x) const { return ptr >= x.ptr; } + bool operator<=(const_iterator x) const { return ptr <= x.ptr; } + bool operator>(const_iterator x) const { return ptr > x.ptr; } + bool operator<(const_iterator x) const { return ptr < x.ptr; } + }; + + class const_reverse_iterator { + const T* ptr; + public: + typedef Diff difference_type; + typedef const T value_type; + typedef const T* pointer; + typedef const T& reference; + typedef std::bidirectional_iterator_tag iterator_category; + const_reverse_iterator(T* ptr_) : ptr(ptr_) {} + const_reverse_iterator(reverse_iterator x) : ptr(&(*x)) {} + const T& operator*() const { return *ptr; } + const T* operator->() const { return ptr; } + const_reverse_iterator& operator--() { ptr++; return *this; } + const_reverse_iterator& operator++() { ptr--; return *this; } + const_reverse_iterator operator++(int) { const_reverse_iterator copy(*this); ++(*this); return copy; } + const_reverse_iterator operator--(int) { const_reverse_iterator copy(*this); --(*this); return copy; } + bool operator==(const_reverse_iterator x) const { return ptr == x.ptr; } + bool operator!=(const_reverse_iterator x) const { return ptr != x.ptr; } + }; + +private: + size_type _size; + union { + char direct[sizeof(T) * N]; + struct { + size_type capacity; + char* indirect; + }; + } _union; + + T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; } + const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; } + T* indirect_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.indirect) + pos; } + const T* indirect_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.indirect) + pos; } + bool is_direct() const { return _size <= N; } + + void change_capacity(size_type new_capacity) { + if (new_capacity <= N) { + if (!is_direct()) { + T* indirect = indirect_ptr(0); + T* src = indirect; + T* dst = direct_ptr(0); + memcpy(dst, src, size() * sizeof(T)); + free(indirect); + _size -= N + 1; + } + } else { + if (!is_direct()) { + _union.indirect = static_cast<char*>(realloc(_union.indirect, ((size_t)sizeof(T)) * new_capacity)); + _union.capacity = new_capacity; + } else { + char* new_indirect = static_cast<char*>(malloc(((size_t)sizeof(T)) * new_capacity)); + T* src = direct_ptr(0); + T* dst = reinterpret_cast<T*>(new_indirect); + memcpy(dst, src, size() * sizeof(T)); + _union.indirect = new_indirect; + _union.capacity = new_capacity; + _size += N + 1; + } + } + } + + T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } + const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } + +public: + void assign(size_type n, const T& val) { + clear(); + if (capacity() < n) { + change_capacity(n); + } + while (size() < n) { + _size++; + new(static_cast<void*>(item_ptr(size() - 1))) T(val); + } + } + + template<typename InputIterator> + void assign(InputIterator first, InputIterator last) { + size_type n = last - first; + clear(); + if (capacity() < n) { + change_capacity(n); + } + while (first != last) { + _size++; + new(static_cast<void*>(item_ptr(size() - 1))) T(*first); + ++first; + } + } + + prevector() : _size(0) {} + + explicit prevector(size_type n) : _size(0) { + resize(n); + } + + explicit prevector(size_type n, const T& val = T()) : _size(0) { + change_capacity(n); + while (size() < n) { + _size++; + new(static_cast<void*>(item_ptr(size() - 1))) T(val); + } + } + + template<typename InputIterator> + prevector(InputIterator first, InputIterator last) : _size(0) { + size_type n = last - first; + change_capacity(n); + while (first != last) { + _size++; + new(static_cast<void*>(item_ptr(size() - 1))) T(*first); + ++first; + } + } + + prevector(const prevector<N, T, Size, Diff>& other) : _size(0) { + change_capacity(other.size()); + const_iterator it = other.begin(); + while (it != other.end()) { + _size++; + new(static_cast<void*>(item_ptr(size() - 1))) T(*it); + ++it; + } + } + + prevector& operator=(const prevector<N, T, Size, Diff>& other) { + if (&other == this) { + return *this; + } + resize(0); + change_capacity(other.size()); + const_iterator it = other.begin(); + while (it != other.end()) { + _size++; + new(static_cast<void*>(item_ptr(size() - 1))) T(*it); + ++it; + } + return *this; + } + + size_type size() const { + return is_direct() ? _size : _size - N - 1; + } + + bool empty() const { + return size() == 0; + } + + iterator begin() { return iterator(item_ptr(0)); } + const_iterator begin() const { return const_iterator(item_ptr(0)); } + iterator end() { return iterator(item_ptr(size())); } + const_iterator end() const { return const_iterator(item_ptr(size())); } + + reverse_iterator rbegin() { return reverse_iterator(item_ptr(size() - 1)); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(item_ptr(size() - 1)); } + reverse_iterator rend() { return reverse_iterator(item_ptr(-1)); } + const_reverse_iterator rend() const { return const_reverse_iterator(item_ptr(-1)); } + + size_t capacity() const { + if (is_direct()) { + return N; + } else { + return _union.capacity; + } + } + + T& operator[](size_type pos) { + return *item_ptr(pos); + } + + const T& operator[](size_type pos) const { + return *item_ptr(pos); + } + + void resize(size_type new_size) { + while (size() > new_size) { + item_ptr(size() - 1)->~T(); + _size--; + } + if (new_size > capacity()) { + change_capacity(new_size); + } + while (size() < new_size) { + _size++; + new(static_cast<void*>(item_ptr(size() - 1))) T(); + } + } + + void reserve(size_type new_capacity) { + if (new_capacity > capacity()) { + change_capacity(new_capacity); + } + } + + void shrink_to_fit() { + change_capacity(size()); + } + + void clear() { + resize(0); + } + + iterator insert(iterator pos, const T& value) { + size_type p = pos - begin(); + size_type new_size = size() + 1; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T)); + _size++; + new(static_cast<void*>(item_ptr(p))) T(value); + return iterator(item_ptr(p)); + } + + void insert(iterator pos, size_type count, const T& value) { + size_type p = pos - begin(); + size_type new_size = size() + count; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + _size += count; + for (size_type i = 0; i < count; i++) { + new(static_cast<void*>(item_ptr(p + i))) T(value); + } + } + + template<typename InputIterator> + void insert(iterator pos, InputIterator first, InputIterator last) { + size_type p = pos - begin(); + difference_type count = last - first; + size_type new_size = size() + count; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + _size += count; + while (first != last) { + new(static_cast<void*>(item_ptr(p))) T(*first); + ++p; + ++first; + } + } + + iterator erase(iterator pos) { + (*pos).~T(); + memmove(&(*pos), &(*pos) + 1, ((char*)&(*end())) - ((char*)(1 + &(*pos)))); + _size--; + return pos; + } + + iterator erase(iterator first, iterator last) { + iterator p = first; + char* endp = (char*)&(*end()); + while (p != last) { + (*p).~T(); + _size--; + ++p; + } + memmove(&(*first), &(*last), endp - ((char*)(&(*last)))); + return first; + } + + void push_back(const T& value) { + size_type new_size = size() + 1; + if (capacity() < new_size) { + change_capacity(new_size + (new_size >> 1)); + } + new(item_ptr(size())) T(value); + _size++; + } + + void pop_back() { + _size--; + } + + T& front() { + return *item_ptr(0); + } + + const T& front() const { + return *item_ptr(0); + } + + T& back() { + return *item_ptr(size() - 1); + } + + const T& back() const { + return *item_ptr(size() - 1); + } + + void swap(prevector<N, T, Size, Diff>& other) { + if (_size & other._size & 1) { + std::swap(_union.capacity, other._union.capacity); + std::swap(_union.indirect, other._union.indirect); + } else { + std::swap(_union, other._union); + } + std::swap(_size, other._size); + } + + ~prevector() { + clear(); + if (!is_direct()) { + free(_union.indirect); + _union.indirect = NULL; + } + } + + bool operator==(const prevector<N, T, Size, Diff>& other) const { + if (other.size() != size()) { + return false; + } + const_iterator b1 = begin(); + const_iterator b2 = other.begin(); + const_iterator e1 = end(); + while (b1 != e1) { + if ((*b1) != (*b2)) { + return false; + } + ++b1; + ++b2; + } + return true; + } + + bool operator!=(const prevector<N, T, Size, Diff>& other) const { + return !(*this == other); + } + + bool operator<(const prevector<N, T, Size, Diff>& other) const { + if (size() < other.size()) { + return true; + } + if (size() > other.size()) { + return false; + } + const_iterator b1 = begin(); + const_iterator b2 = other.begin(); + const_iterator e1 = end(); + while (b1 != e1) { + if ((*b1) < (*b2)) { + return true; + } + if ((*b2) < (*b1)) { + return false; + } + ++b1; + ++b2; + } + return false; + } + + size_t allocated_memory() const { + if (is_direct()) { + return 0; + } else { + return ((size_t)(sizeof(T))) * _union.capacity; + } + } +}; +#pragma pack(pop) + +#endif 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/primitives/transaction.h b/src/primitives/transaction.h index 98882d315e..c5d8a64a6d 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -74,7 +74,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(prevout); - READWRITE(scriptSig); + READWRITE(*(CScriptBase*)(&scriptSig)); READWRITE(nSequence); } @@ -119,7 +119,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(nValue); - READWRITE(scriptPubKey); + READWRITE(*(CScriptBase*)(&scriptPubKey)); } void SetNull() diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index d407e539ef..6e6330d2a4 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -311,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"); @@ -660,7 +654,7 @@ int main(int argc, char *argv[]) // 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/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 2449046538..6b5f243668 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -67,6 +67,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Fees (in %s/kB) smaller than this are considered zero fee for relaying, " "mining and transaction creation (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Fees (in %s/kB) smaller than this are considered zero fee for transaction " +"creation (default: %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "How thorough the block verification of -checkblocks is (0-4, default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "If <category> is not supplied or if <category> = 1, output all debugging " @@ -121,6 +124,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Set the number of threads for coin generation if enabled (-1 = all cores, " "default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Support filtering of blocks and transaction with bloom filters (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "The block database contains a block which appears to be from the future. " "This may be due to your computer's date and time being set incorrectly. Only " "rebuild the block database if you are sure that your computer's date and " @@ -152,6 +157,10 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: " "%s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Username and hashed password for JSON-RPC connections. The field <userpw> " +"comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is " +"included in share/rpcuser. This option can be specified multiple times"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "WARNING: abnormally high number of blocks generated, %d blocks received in " "the last %d hours (%d expected)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -178,7 +187,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "mode. This will redownload the entire blockchain"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "(default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"), QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"), @@ -197,7 +205,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -bind address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -whitebind address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"), -QT_TRANSLATE_NOOP("bitcoin-core", "Choose data directory on startup (default: 0)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect through SOCKS5 proxy"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"), @@ -274,15 +281,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."), QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands"), QT_TRANSLATE_NOOP("bitcoin-core", "Send trace/debug info to console instead of debug.log file"), QT_TRANSLATE_NOOP("bitcoin-core", "Send transactions as zero-fee transactions if possible (default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Set SSL root certificates for payment request (default: -system-)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set database cache size in megabytes (%d to %d, default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to <n> (default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Set language, for example \"de_DE\" (default: system locale)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set maximum block size in bytes (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set minimum block size in bytes (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Set the number of threads to service RPC calls (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "Show all debugging options (usage: --help -help-debug)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Show splash screen on startup (default: 1)"), QT_TRANSLATE_NOOP("bitcoin-core", "Shrink debug.log file on client startup (default: 1 when no -debug)"), QT_TRANSLATE_NOOP("bitcoin-core", "Signing transaction failed"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify configuration file (default: %s)"), @@ -292,7 +296,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)"), QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), QT_TRANSLATE_NOOP("bitcoin-core", "Spend unconfirmed change when sending transactions (default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Start minimized"), QT_TRANSLATE_NOOP("bitcoin-core", "The transaction amount is too small to pay the fee"), QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."), @@ -303,7 +306,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amount too small"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must be positive"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large for fee policy"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"), -QT_TRANSLATE_NOOP("bitcoin-core", "UI Options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 566e8fa62d..1271187420 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()); } @@ -178,7 +158,7 @@ enum BlockSource ClientModel::getBlockSource() const QString ClientModel::getStatusBarWarnings() const { - return QString::fromStdString(GetWarnings("statusbar")); + return QString::fromStdString(GetWarnings("gui")); } OptionsModel *ClientModel::getOptionsModel() @@ -261,6 +241,26 @@ 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 + QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, + Q_ARG(int, pIndex->nHeight), + Q_ARG(QDateTime, QDateTime::fromTime_t(pIndex->GetBlockTime())), + Q_ARG(double, clientmodel->getVerificationProgress(pIndex))); + nLastBlockTipUpdateNotification = now; + } +} + void ClientModel::subscribeToCoreSignals() { // Connect signals to client @@ -268,6 +268,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 +278,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 cbc41f3416..0f42243047 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -549,6 +549,9 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // Fee nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + if (nPayFee > 0 && coinControl->nMinimumTotalFee > nPayFee) + nPayFee = coinControl->nMinimumTotalFee; + // Allow free? (require at least hard-coded threshold and default to that if no estimate) double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold()); @@ -619,7 +622,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l6->setText(sPriorityLabel); // Priority l7->setText(fDust ? tr("yes") : tr("no")); // Dust l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change - if (nPayFee > 0 && !(payTxFee.GetFeePerK() > 0 && fPayAtLeastCustomFee && nBytes < 1000)) + if (nPayFee > 0 && (coinControl->nMinimumTotalFee < nPayFee)) { l3->setText(ASYMP_UTF8 + l3->text()); l4->setText(ASYMP_UTF8 + l4->text()); 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/locale/bitcoin_da.ts b/src/qt/locale/bitcoin_da.ts index 93594dcb05..ac194e0520 100644 --- a/src/qt/locale/bitcoin_da.ts +++ b/src/qt/locale/bitcoin_da.ts @@ -1097,7 +1097,7 @@ </message> <message> <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source> - <translation>Forbind til Bitcoin-netværket gennem en separat SOCKS5-proxy for skjulte tjenester via Tor.</translation> + <translation>Forbind til Bitcoin-netværket gennem en separat SOCKS5-proxy for skjulte Tor-tjenester.</translation> </message> <message> <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services:</source> @@ -2733,6 +2733,10 @@ <translation>Kopiér transaktions-ID</translation> </message> <message> + <source>Copy raw transaction</source> + <translation>Kopiér rå transaktion</translation> + </message> + <message> <source>Edit label</source> <translation>Redigér mærkat</translation> </message> @@ -3252,10 +3256,18 @@ <translation>Aktiverer bedste kæde…</translation> </message> <message> + <source>Always relay transactions received from whitelisted peers (default: %d)</source> + <translation>Videresend altid transaktioner, der modtages fra hvidlistede knuder (standard: %d)</translation> + </message> + <message> <source>Attempt to recover private keys from a corrupt wallet.dat on startup</source> <translation>Forsøg at genskabe private nøgler fra en ødelagt wallet.dat under opstart</translation> </message> <message> + <source>Automatically create Tor hidden service (default: %d)</source> + <translation>Opret automatisk skjult Tor-tjeneste (standard: %d)</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>Kan ikke løse -whitebind adresse: "%s"</translation> </message> @@ -3388,6 +3400,14 @@ <translation>Dette er eksperimentelt software.</translation> </message> <message> + <source>Tor control port password (default: empty)</source> + <translation>Adgangskode for Tor kontrolport (standard: tom)</translation> + </message> + <message> + <source>Tor control port to use if onion listening enabled (default: %s)</source> + <translation>Tor kontrolport, der skal bruges, hvis onion-lytning er slået til (standard: %s)</translation> + </message> + <message> <source>Transaction amount too small</source> <translation>Transaktionsbeløb er for lavt</translation> </message> @@ -3428,6 +3448,10 @@ <translation>Advarsel</translation> </message> <message> + <source>Whether to operate in a blocks only mode (default: %u)</source> + <translation>Hvorvidt der skal arbejdes i kun-blokke-tilstand (standard: %u)</translation> + </message> + <message> <source>Zapping all transactions from wallet...</source> <translation>Zapper alle transaktioner fra tegnebog…</translation> </message> @@ -3513,7 +3537,7 @@ </message> <message> <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)</source> - <translation>Brug separat SOCS5-proxy for at nå andre knuder via Tor skjulte tjenester (standard: %s)</translation> + <translation>Brug separat SOCS5-proxy for at nå knuder via skjulte Tor-tjenester (standard: %s)</translation> </message> <message> <source>(default: %s)</source> diff --git a/src/qt/locale/bitcoin_de.ts b/src/qt/locale/bitcoin_de.ts index 42776f2c8b..6b68b3c74a 100644 --- a/src/qt/locale/bitcoin_de.ts +++ b/src/qt/locale/bitcoin_de.ts @@ -226,7 +226,11 @@ <source>IP/Netmask</source> <translation>IP/Netzmaske</translation> </message> - </context> + <message> + <source>Banned Until</source> + <translation>Gesperrt bis</translation> + </message> +</context> <context> <name>BitcoinGUI</name> <message> diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 21df732520..0c5529955f 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -299,7 +299,7 @@ <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+325"/> + <location filename="../bitcoingui.cpp" line="+335"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> @@ -701,7 +701,7 @@ <context> <name>ClientModel</name> <message> - <location filename="../clientmodel.cpp" line="+143"/> + <location filename="../clientmodel.cpp" line="+135"/> <source>Network Alert</source> <translation>Network Alert</translation> </message> @@ -925,7 +925,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+160"/> + <location line="+161"/> <source>This label turns red if the transaction size is greater than 1000 bytes.</source> <translation type="unfinished"></translation> </message> @@ -940,12 +940,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+9"/> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-32"/> + <location line="-33"/> <source>yes</source> <translation type="unfinished"></translation> </message> @@ -971,7 +971,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+58"/> + <location line="+59"/> <location line="+60"/> <source>(no label)</source> <translation type="unfinished">(no label)</translation> @@ -1086,7 +1086,7 @@ <context> <name>HelpMessageDialog</name> <message> - <location filename="../utilitydialog.cpp" line="+33"/> + <location filename="../utilitydialog.cpp" line="+36"/> <source>Bitcoin Core</source> <translation type="unfinished">Bitcoin Core</translation> </message> @@ -1469,17 +1469,17 @@ <translation>&Cancel</translation> </message> <message> - <location filename="../optionsdialog.cpp" line="+83"/> + <location filename="../optionsdialog.cpp" line="+81"/> <source>default</source> <translation>default</translation> </message> <message> - <location line="+59"/> + <location line="+64"/> <source>none</source> <translation type="unfinished"></translation> </message> <message> - <location line="+83"/> + <location line="+71"/> <source>Confirm options reset</source> <translation>Confirm options reset</translation> </message> @@ -1500,7 +1500,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+24"/> + <location line="+25"/> <source>The supplied proxy address is invalid.</source> <translation>The supplied proxy address is invalid.</translation> </message> @@ -1756,7 +1756,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+760"/> + <location line="+763"/> <source>%1 d</source> <translation type="unfinished"></translation> </message> @@ -1830,11 +1830,13 @@ <location line="+26"/> <location line="+23"/> <location line="+23"/> - <location line="+36"/> + <location line="+23"/> <location line="+23"/> <location line="+36"/> <location line="+23"/> - <location line="+533"/> + <location line="+36"/> + <location line="+36"/> + <location line="+534"/> <location line="+23"/> <location line="+23"/> <location line="+23"/> @@ -1855,7 +1857,7 @@ <translation>N/A</translation> </message> <message> - <location line="-1156"/> + <location line="-1216"/> <source>Client version</source> <translation>Client version</translation> </message> @@ -1890,12 +1892,12 @@ <translation>Startup time</translation> </message> <message> - <location line="+29"/> + <location line="+170"/> <source>Network</source> <translation>Network</translation> </message> <message> - <location line="+7"/> + <location line="-147"/> <source>Name</source> <translation type="unfinished"></translation> </message> @@ -1915,12 +1917,27 @@ <translation>Current number of blocks</translation> </message> <message> - <location line="+72"/> + <location line="+52"/> + <source>Memory Pool</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>Current number of transactions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+36"/> + <source>Memory usage</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+48"/> <source>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+231"/> + <location line="+233"/> <location line="+552"/> <source>Received</source> <translation type="unfinished"></translation> @@ -1943,8 +1960,8 @@ </message> <message> <location line="+57"/> - <location filename="../rpcconsole.cpp" line="+281"/> - <location line="+566"/> + <location filename="../rpcconsole.cpp" line="+287"/> + <location line="+578"/> <source>Select a peer to view detailed information.</source> <translation type="unfinished"></translation> </message> @@ -1979,8 +1996,8 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-913"/> - <location line="+821"/> + <location line="-973"/> + <location line="+881"/> <source>User Agent</source> <translation type="unfinished"></translation> </message> @@ -2030,17 +2047,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-904"/> + <location line="-977"/> <source>Last block time</source> <translation>Last block time</translation> </message> <message> - <location line="+52"/> + <location line="+123"/> <source>&Open</source> <translation>&Open</translation> </message> <message> - <location line="+24"/> + <location line="+26"/> <source>&Console</source> <translation>&Console</translation> </message> @@ -2060,7 +2077,7 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../rpcconsole.cpp" line="-333"/> + <location filename="../rpcconsole.cpp" line="-343"/> <source>In:</source> <translation type="unfinished"></translation> </message> @@ -2070,17 +2087,17 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../forms/debugwindow.ui" line="-357"/> + <location filename="../forms/debugwindow.ui" line="-417"/> <source>Build date</source> <translation>Build date</translation> </message> <message> - <location line="+183"/> + <location line="+241"/> <source>Debug log file</source> <translation>Debug log file</translation> </message> <message> - <location line="+83"/> + <location line="+85"/> <source>Clear console</source> <translation>Clear console</translation> </message> @@ -2138,7 +2155,7 @@ <translation>Type <b>help</b> for an overview of available commands.</translation> </message> <message> - <location line="+134"/> + <location line="+144"/> <source>%1 B</source> <translation type="unfinished"></translation> </message> @@ -2706,12 +2723,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+88"/> + <location line="+91"/> <source>Pay only the required fee of %1</source> <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+22"/> + <location line="+23"/> <source>Estimated to begin confirmation within %n block(s).</source> <translation type="unfinished"> <numerusform>Estimated to begin confirmation within %n block.</numerusform> @@ -2719,7 +2736,7 @@ </translation> </message> <message> - <location line="-136"/> + <location line="-140"/> <source>The recipient address is not valid. Please recheck.</source> <translation type="unfinished"></translation> </message> @@ -2729,7 +2746,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+231"/> + <location line="+234"/> <source>Warning: Invalid Bitcoin address</source> <translation type="unfinished"></translation> </message> @@ -2744,7 +2761,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-692"/> + <location line="-695"/> <source>Copy dust</source> <translation type="unfinished"></translation> </message> @@ -2871,7 +2888,7 @@ <context> <name>ShutdownWindow</name> <message> - <location filename="../utilitydialog.cpp" line="+81"/> + <location filename="../utilitydialog.cpp" line="+96"/> <source>Bitcoin Core is shutting down...</source> <translation type="unfinished"></translation> </message> @@ -3703,32 +3720,32 @@ <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+260"/> + <location filename="../bitcoinstrings.cpp" line="+267"/> <source>Options:</source> <translation>Options:</translation> </message> <message> - <location line="+30"/> + <location line="+27"/> <source>Specify data directory</source> <translation>Specify data directory</translation> </message> <message> - <location line="-87"/> + <location line="-84"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+90"/> + <location line="+87"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="-109"/> + <location line="-105"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="-113"/> + <location line="-118"/> <source>If <category> is not supplied or if <category> = 1, output all debugging information.</source> <translation type="unfinished"></translation> </message> @@ -3763,7 +3780,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+112"/> + <location line="+116"/> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation type="unfinished"></translation> </message> @@ -3783,17 +3800,17 @@ <translation>Run in the background as a daemon and accept commands</translation> </message> <message> - <location line="+34"/> + <location line="+29"/> <source>Unable to start HTTP server. See debug log for details.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-123"/> + <location line="-117"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> <message> - <location line="-160"/> + <location line="-168"/> <source>Bind to given address and always listen on it. Use [host]:port notation for IPv6</source> <translation>Bind to given address and always listen on it. Use [host]:port notation for IPv6</translation> </message> @@ -3813,12 +3830,12 @@ <translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation> </message> <message> - <location line="+57"/> + <location line="+60"/> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+8"/> <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source> <translation type="unfinished"></translation> </message> @@ -3838,7 +3855,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+9"/> <source>WARNING: abnormally high number of blocks generated, %d blocks received in the last %d hours (%d expected)</source> <translation type="unfinished"></translation> </message> @@ -3869,11 +3886,6 @@ </message> <message> <location line="+11"/> - <source>(default: 1)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> <source>-maxmempool must be at least %d MB</source> <translation type="unfinished"></translation> </message> @@ -3888,7 +3900,7 @@ <translation>Block creation options:</translation> </message> <message> - <location line="+7"/> + <location line="+6"/> <source>Connect only to the specified node(s)</source> <translation>Connect only to the specified node(s)</translation> </message> @@ -4008,22 +4020,22 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+14"/> + <location line="+13"/> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+2"/> <source>Set maximum block size in bytes (default: %d)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+10"/> <source>Specify wallet file (within data directory)</source> <translation>Specify wallet file (within data directory)</translation> </message> <message> - <location line="+18"/> + <location line="+16"/> <source>Unsupported argument -benchmark ignored, use -debug=bench.</source> <translation type="unfinished"></translation> </message> @@ -4078,7 +4090,7 @@ <translation>You need to rebuild the database using -reindex to change -txindex</translation> </message> <message> - <location line="-304"/> + <location line="-306"/> <source>Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times</source> <translation type="unfinished"></translation> </message> @@ -4123,7 +4135,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+11"/> <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4158,7 +4170,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+10"/> <source>The transaction amount is too small to send after the fee has been deducted</source> <translation type="unfinished"></translation> </message> @@ -4168,7 +4180,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+40"/> + <location line="+44"/> <source>Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway</source> <translation type="unfinished"></translation> </message> @@ -4183,7 +4195,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+5"/> <source>Accept public REST requests (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4213,12 +4225,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> - <source>Choose data directory on startup (default: 0)</source> - <translation type="unfinished">Choose data directory on startup (default: 0)</translation> - </message> - <message> - <location line="+2"/> + <location line="+3"/> <source>Connect through SOCKS5 proxy</source> <translation type="unfinished"></translation> </message> @@ -4328,27 +4335,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Set SSL root certificates for payment request (default: -system-)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> - <source>Set language, for example "de_DE" (default: system locale)</source> - <translation type="unfinished">Set language, for example "de_DE" (default: system locale)</translation> - </message> - <message> - <location line="+4"/> + <location line="+6"/> <source>Show all debugging options (usage: --help -help-debug)</source> <translation type="unfinished"></translation> </message> <message> <location line="+1"/> - <source>Show splash screen on startup (default: 1)</source> - <translation type="unfinished">Show splash screen on startup (default: 1)</translation> - </message> - <message> - <location line="+1"/> <source>Shrink debug.log file on client startup (default: 1 when no -debug)</source> <translation>Shrink debug.log file on client startup (default: 1 when no -debug)</translation> </message> @@ -4359,11 +4351,6 @@ </message> <message> <location line="+8"/> - <source>Start minimized</source> - <translation type="unfinished">Start minimized</translation> - </message> - <message> - <location line="+1"/> <source>The transaction amount is too small to pay the fee</source> <translation type="unfinished"></translation> </message> @@ -4404,11 +4391,6 @@ </message> <message> <location line="+1"/> - <source>UI Options:</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> <source>Unable to bind to %s on this computer (bind returned error %s)</source> <translation type="unfinished"></translation> </message> @@ -4453,27 +4435,27 @@ <translation>wallet.dat corrupt, salvage failed</translation> </message> <message> - <location line="-67"/> + <location line="-62"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-198"/> + <location line="-205"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+234"/> + <location line="+237"/> <source>This help message</source> <translation>This help message</translation> </message> <message> - <location line="-108"/> + <location line="-103"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+60"/> + <location line="+59"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> @@ -4483,7 +4465,7 @@ <translation>Error loading wallet.dat: Wallet corrupted</translation> </message> <message> - <location line="-207"/> + <location line="-214"/> <source>(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)</source> <translation type="unfinished"></translation> </message> @@ -4509,6 +4491,11 @@ </message> <message> <location line="+17"/> + <source>Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>How thorough the block verification of -checkblocks is (0-4, default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4528,7 +4515,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+46"/> + <location line="+32"/> + <source>Support filtering of blocks and transaction with bloom filters (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+16"/> <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source> <translation type="unfinished"></translation> </message> @@ -4548,17 +4540,22 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+28"/> + <location line="+3"/> + <source>Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. This option can be specified multiple times</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+29"/> <source>(default: %s)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+10"/> <source>Always query for peer addresses via DNS lookup (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+28"/> + <location line="+27"/> <source>Error loading wallet.dat</source> <translation>Error loading wallet.dat</translation> </message> @@ -4628,12 +4625,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+7"/> <source>Set key pool size to <n> (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+2"/> <source>Set minimum block size in bytes (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4643,7 +4640,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>Specify configuration file (default: %s)</source> <translation type="unfinished"></translation> </message> @@ -4663,17 +4660,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> + <location line="+4"/> <source>Threshold for disconnecting misbehaving peers (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> + <location line="+9"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unknown network specified in -onlynet: '%s'</translation> </message> <message> - <location line="-113"/> + <location line="-107"/> <source>Cannot resolve -bind address: '%s'</source> <translation>Cannot resolve -bind address: '%s'</translation> </message> @@ -4683,7 +4680,7 @@ <translation>Cannot resolve -externalip address: '%s'</translation> </message> <message> - <location line="+46"/> + <location line="+45"/> <source>Invalid amount for -paytxfee=<amount>: '%s'</source> <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> </message> @@ -4698,17 +4695,17 @@ <translation>Loading block index...</translation> </message> <message> - <location line="-62"/> + <location line="-61"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> <message> - <location line="+63"/> + <location line="+62"/> <source>Loading wallet...</source> <translation>Loading wallet...</translation> </message> <message> - <location line="-56"/> + <location line="-55"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> @@ -4718,7 +4715,7 @@ <translation>Cannot write default address</translation> </message> <message> - <location line="+74"/> + <location line="+73"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> diff --git a/src/qt/locale/bitcoin_es.ts b/src/qt/locale/bitcoin_es.ts index ec8261173b..8883aef989 100644 --- a/src/qt/locale/bitcoin_es.ts +++ b/src/qt/locale/bitcoin_es.ts @@ -2915,6 +2915,10 @@ <translation>(predeterminado: 1)</translation> </message> <message> + <source>-maxmempool must be at least %d MB</source> + <translation>-maxmempool debe ser por lo menos de %d MB</translation> + </message> + <message> <source><category> can be:</source> <translation><category> puede ser:</translation> </message> @@ -3127,6 +3131,10 @@ <translation>Intento de recuperar claves privadas de un wallet.dat corrupto</translation> </message> <message> + <source>Automatically create Tor hidden service (default: %d)</source> + <translation>Automáticamente crea el servicio Tor oculto (por defecto: %d)</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>No se puede resolver -whitebind address: '%s'</translation> </message> diff --git a/src/qt/locale/bitcoin_ja.ts b/src/qt/locale/bitcoin_ja.ts index 140ed2445b..a80874652b 100644 --- a/src/qt/locale/bitcoin_ja.ts +++ b/src/qt/locale/bitcoin_ja.ts @@ -2737,6 +2737,10 @@ <translation>取引 ID をコピー</translation> </message> <message> + <source>Copy raw transaction</source> + <translation>生トランザクションをコピー</translation> + </message> + <message> <source>Edit label</source> <translation>ラベルの編集</translation> </message> @@ -3257,10 +3261,18 @@ <translation>最優良のチェインを有効化しています...</translation> </message> <message> + <source>Always relay transactions received from whitelisted peers (default: %d)</source> + <translation>ホワイトリストにあるピアから受け取ったトランザクションを常にリレーする (初期値: %d)</translation> + </message> + <message> <source>Attempt to recover private keys from a corrupt wallet.dat on startup</source> <translation>起動時に壊れた wallet.dat から秘密鍵を復旧することを試す</translation> </message> <message> + <source>Automatically create Tor hidden service (default: %d)</source> + <translation>Tor秘匿サービスを自動的に作成する (初期値: %d)</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>-whitebind アドレス '%s' を解決できません</translation> </message> @@ -3393,6 +3405,14 @@ <translation>これは実験的なソフトウェアです。</translation> </message> <message> + <source>Tor control port password (default: empty)</source> + <translation>Tor管理ポートのパスワード (初期値: 空文字)</translation> + </message> + <message> + <source>Tor control port to use if onion listening enabled (default: %s)</source> + <translation>Onion のリッスンが有効になっている場合に使用するTor管理ポート (初期値: %s)</translation> + </message> + <message> <source>Transaction amount too small</source> <translation>取引の額が小さ過ぎます</translation> </message> @@ -3433,6 +3453,10 @@ <translation>警告</translation> </message> <message> + <source>Whether to operate in a blocks only mode (default: %u)</source> + <translation>ブロック限定モードにおいて動作を行うかどうか (初期値: %u)</translation> + </message> + <message> <source>Zapping all transactions from wallet...</source> <translation>ウォレットからすべてのトランザクションを消去しています...</translation> </message> diff --git a/src/qt/locale/bitcoin_ko_KR.ts b/src/qt/locale/bitcoin_ko_KR.ts index 8243618f11..0213c09aa7 100644 --- a/src/qt/locale/bitcoin_ko_KR.ts +++ b/src/qt/locale/bitcoin_ko_KR.ts @@ -3,7 +3,7 @@ <name>AddressBookPage</name> <message> <source>Right-click to edit address or label</source> - <translation>지갑 주소나 이름을 수정하려면 우클릭하세요.</translation> + <translation>지갑 주소나 라벨을 수정하려면 우클릭하세요.</translation> </message> <message> <source>Create a new address</source> @@ -27,7 +27,7 @@ </message> <message> <source>&Copy Address</source> - <translation>계좌 복사(&C)</translation> + <translation>주소 복사(&C)</translation> </message> <message> <source>Delete the currently selected address from the list</source> @@ -59,11 +59,11 @@ </message> <message> <source>Sending addresses</source> - <translation>보내는 주소들</translation> + <translation>타인 계좌 주소목록</translation> </message> <message> <source>Receiving addresses</source> - <translation>받은 주소들</translation> + <translation>내 계좌 주소목록</translation> </message> <message> <source>These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins.</source> @@ -75,7 +75,7 @@ </message> <message> <source>Copy &Label</source> - <translation>표 복사</translation> + <translation>라벨 복사</translation> </message> <message> <source>&Edit</source> @@ -87,7 +87,7 @@ </message> <message> <source>Comma separated file (*.csv)</source> - <translation>각각의 파일에 쉼표하기(*.csv)</translation> + <translation>쉼표로 구분된 파일(*.csv)</translation> </message> <message> <source>Exporting Failed</source> @@ -95,14 +95,14 @@ </message> <message> <source>There was an error trying to save the address list to %1. Please try again.</source> - <translation>%1으로 주소 리스트를 저장하는 동안 오류가 발생했습니다. 다시 시도해주세요.</translation> + <translation>%1으로 주소 목록을 저장하는 동안 오류가 발생했습니다. 다시 시도해주세요.</translation> </message> </context> <context> <name>AddressTableModel</name> <message> <source>Label</source> - <translation>표</translation> + <translation>라벨</translation> </message> <message> <source>Address</source> @@ -110,7 +110,7 @@ </message> <message> <source>(no label)</source> - <translation>(표 없음)</translation> + <translation>(라벨 없음)</translation> </message> </context> <context> @@ -129,7 +129,7 @@ </message> <message> <source>Repeat new passphrase</source> - <translation>새 암호 반복</translation> + <translation>새로운 암호 재확인</translation> </message> <message> <source>Encrypt wallet</source> @@ -141,7 +141,7 @@ </message> <message> <source>Unlock wallet</source> - <translation>지갑 열기</translation> + <translation>지갑 잠금해제</translation> </message> <message> <source>This operation needs your wallet passphrase to decrypt the wallet.</source> @@ -157,11 +157,11 @@ </message> <message> <source>Confirm wallet encryption</source> - <translation>지갑의 암호화를 확정</translation> + <translation>지갑 암호화 승인</translation> </message> <message> <source>Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!</source> - <translation>경고: 만약 암호화된 지갑의 비밀번호를 잃어버릴 경우, 모든 비트코인들을 잃어버릴 수 있습니다!</translation> + <translation>경고: 만약 암호화 된 지갑의 비밀번호를 잃어버릴 경우, <b>모든 비트코인들을 잃어버릴 수 있습니다</b>!</translation> </message> <message> <source>Are you sure you wish to encrypt your wallet?</source> @@ -169,17 +169,21 @@ </message> <message> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> - <translation>중요: 본인 지갑파일에서 만든 예전 백업들은 새로 생성한 암화화된 지갑 파일로 교체됩니다. 보안상 이유로 이전에 암호화 하지 않은 지갑 파일 백업은 사용할 수 없게 되니 빠른 시일 내로 새로 암화화된 지갑을 사용하시기 바랍니다.</translation> + <translation>중요: 본인 지갑파일에서 만든 예전 백업들은 새로 생성한 암호화 된 지갑 파일로 교체됩니다. 보안상 이유로 이전에 암호화 하지 않은 지갑 파일 백업은 사용할 수 없게 되니 빠른 시일 내로 새로 암호화 된 지갑을 사용하시기 바랍니다.</translation> </message> <message> <source>Warning: The Caps Lock key is on!</source> - <translation>경고: 캡스록 키가 켜져있습니다!</translation> + <translation>경고: Caps Lock키가 켜져있습니다!</translation> </message> <message> <source>Wallet encrypted</source> <translation>지갑 암호화 완료</translation> </message> <message> + <source>Enter the old passphrase and new passphrase to the wallet.</source> + <translation>지갑의 기존 암호와 새로운 암호를 입력해주세요.</translation> + </message> + <message> <source>Wallet encryption failed</source> <translation>지갑 암호화 실패</translation> </message> @@ -964,6 +968,18 @@ <translation>프록시의 포트번호입니다(예: 9050)</translation> </message> <message> + <source>IPv4</source> + <translation>IPv4</translation> + </message> + <message> + <source>IPv6</source> + <translation>IPv6</translation> + </message> + <message> + <source>Tor</source> + <translation>Tor</translation> + </message> + <message> <source>&Window</source> <translation>창(&W)</translation> </message> @@ -1071,6 +1087,10 @@ <translation>아직 사용 가능하지 않은 채굴된 잔액</translation> </message> <message> + <source>Balances</source> + <translation>잔액</translation> + </message> + <message> <source>Total:</source> <translation>총액:</translation> </message> @@ -1082,6 +1102,10 @@ <source>Your current balance in watch-only addresses</source> <translation>모니터링 지갑의 현재 잔액</translation> </message> + <message> + <source>Recent transactions</source> + <translation>최근 거래</translation> + </message> </context> <context> <name>PaymentServer</name> @@ -1152,10 +1176,18 @@ <translation>비트코인 주소를 입력하기 (예. %1)</translation> </message> <message> + <source>%1 s</source> + <translation>%1 초</translation> + </message> + <message> <source>N/A</source> <translation>없음</translation> </message> - </context> + <message> + <source>%1 ms</source> + <translation>%1 ms</translation> + </message> +</context> <context> <name>QRImageWidget</name> <message> @@ -1203,7 +1235,11 @@ </message> <message> <source>Using OpenSSL version</source> - <translation>오픈SSL 버전을 사용합니다</translation> + <translation>사용중인 OpenSSL 버전</translation> + </message> + <message> + <source>Using BerkeleyDB version</source> + <translation>사용중인 BerkeleyDB 버전</translation> </message> <message> <source>Startup time</source> @@ -1230,6 +1266,22 @@ <translation>현재 블럭 수</translation> </message> <message> + <source>Received</source> + <translation>받음</translation> + </message> + <message> + <source>Sent</source> + <translation>보냄</translation> + </message> + <message> + <source>&Peers</source> + <translation>&피어</translation> + </message> + <message> + <source>Version</source> + <translation>버전</translation> + </message> + <message> <source>Last block time</source> <translation>최종 블럭 시각</translation> </message> @@ -1290,7 +1342,7 @@ </message> <message> <source>&Label:</source> - <translation>표:</translation> + <translation>라벨:</translation> </message> <message> <source>&Message:</source> @@ -1432,7 +1484,7 @@ </message> <message> <source>(no label)</source> - <translation>(표 없음)</translation> + <translation>(라벨 없음)</translation> </message> <message> <source>(no message)</source> @@ -1498,6 +1550,14 @@ <translation>주소변경</translation> </message> <message> + <source>Transaction Fee:</source> + <translation>거래 수수료:</translation> + </message> + <message> + <source>Recommended:</source> + <translation>권장:</translation> + </message> + <message> <source>Send to multiple recipients at once</source> <translation>다수의 수령인들에게 한번에 보내기</translation> </message> diff --git a/src/qt/locale/bitcoin_nb.ts b/src/qt/locale/bitcoin_nb.ts index 6cded5e135..c36e1af1d8 100644 --- a/src/qt/locale/bitcoin_nb.ts +++ b/src/qt/locale/bitcoin_nb.ts @@ -2737,6 +2737,10 @@ <translation>Kopier transaksjons-ID</translation> </message> <message> + <source>Copy raw transaction</source> + <translation>Kopier råtransaksjon</translation> + </message> + <message> <source>Edit label</source> <translation>Rediger merkelapp</translation> </message> @@ -3041,11 +3045,11 @@ </message> <message> <source>Enable publish raw block in <address></source> - <translation>Slå på publish raw block i <address></translation> + <translation>Slå på publisering av råblokk i <address></translation> </message> <message> <source>Enable publish raw transaction in <address></source> - <translation>Slå på publish raw transaction i <address></translation> + <translation>Slå på publisering av råtransaksjon i <address></translation> </message> <message> <source>Error initializing block database</source> @@ -3252,10 +3256,18 @@ <translation>Aktiverer beste kjede...</translation> </message> <message> + <source>Always relay transactions received from whitelisted peers (default: %d)</source> + <translation>Alltid videresend transaksjoner mottatt fra hvitlistede noder (standardverdi: %d)</translation> + </message> + <message> <source>Attempt to recover private keys from a corrupt wallet.dat on startup</source> <translation>Forsøk å berge private nøkler fra en korrupt wallet.dat ved oppstart</translation> </message> <message> + <source>Automatically create Tor hidden service (default: %d)</source> + <translation>Automatisk opprette Tor skjult tjeneste (standardverdi: %d)</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>Kan ikke løse -whitebind-adresse: '%s'</translation> </message> @@ -3388,6 +3400,14 @@ <translation>Dette er eksperimentell programvare.</translation> </message> <message> + <source>Tor control port password (default: empty)</source> + <translation>Passord for Tor-kontrollport (standardverdi: tom)</translation> + </message> + <message> + <source>Tor control port to use if onion listening enabled (default: %s)</source> + <translation>Tor-kontrollport å bruke hvis onion-lytting er aktivert (standardverdi: %s)</translation> + </message> + <message> <source>Transaction amount too small</source> <translation>Transaksjonen er for liten</translation> </message> @@ -3428,6 +3448,10 @@ <translation>Advarsel</translation> </message> <message> + <source>Whether to operate in a blocks only mode (default: %u)</source> + <translation>Hvorvidt å operere i modus med kun blokker (standardverdi: %u)</translation> + </message> + <message> <source>Zapping all transactions from wallet...</source> <translation>Zapper alle transaksjoner fra lommeboken...</translation> </message> diff --git a/src/qt/locale/bitcoin_pl.ts b/src/qt/locale/bitcoin_pl.ts index 228e02a5ee..52196f0372 100644 --- a/src/qt/locale/bitcoin_pl.ts +++ b/src/qt/locale/bitcoin_pl.ts @@ -222,7 +222,15 @@ </context> <context> <name>BanTableModel</name> - </context> + <message> + <source>IP/Netmask</source> + <translation>IP/Maska Sieci</translation> + </message> + <message> + <source>Banned Until</source> + <translation>Blokada do</translation> + </message> +</context> <context> <name>BitcoinGUI</name> <message> @@ -1068,6 +1076,18 @@ <translation>Port proxy (np. 9050)</translation> </message> <message> + <source>IPv4</source> + <translation>IPv4</translation> + </message> + <message> + <source>IPv6</source> + <translation>IPv6</translation> + </message> + <message> + <source>Tor</source> + <translation>Tor</translation> + </message> + <message> <source>&Window</source> <translation>&Okno</translation> </message> @@ -1454,6 +1474,10 @@ <translation>&Węzły</translation> </message> <message> + <source>Banned peers</source> + <translation>Blokowane węzły</translation> + </message> + <message> <source>Select a peer to view detailed information.</source> <translation>Wybierz węzeł żeby zobaczyć szczegóły.</translation> </message> @@ -1542,6 +1566,14 @@ <translation>Wyczyść konsolę</translation> </message> <message> + <source>Ban Node for</source> + <translation>Blokuj węzeł na okres</translation> + </message> + <message> + <source>&Unban Node</source> + <translation>Odblokuj węzeł</translation> + </message> + <message> <source>Welcome to the Bitcoin Core RPC console.</source> <translation>Witaj w konsoli Bitcoin Core RPC.</translation> </message> @@ -1586,6 +1618,14 @@ <translation>Wyjściowy</translation> </message> <message> + <source>Yes</source> + <translation>Tak</translation> + </message> + <message> + <source>No</source> + <translation>Nie</translation> + </message> + <message> <source>Unknown</source> <translation>Nieznany</translation> </message> @@ -1981,6 +2021,10 @@ <source>Payment request expired.</source> <translation>Żądanie płatności upłynęło.</translation> </message> + <message> + <source>Pay only the required fee of %1</source> + <translation>Zapłać tylko wymaganą opłatę %1</translation> + </message> <message numerus="yes"> <source>Estimated to begin confirmation within %n block(s).</source> <translation><numerusform>Przybliżony czas zatwierdzenia: %n bloków.</numerusform><numerusform>Przybliżony czas zatwierdzenia: %n bloków.</numerusform><numerusform>Przybliżony czas zatwierdzenia: %n bloków.</numerusform></translation> diff --git a/src/qt/locale/bitcoin_pt_BR.ts b/src/qt/locale/bitcoin_pt_BR.ts index 4863591ac4..e57c051aa1 100644 --- a/src/qt/locale/bitcoin_pt_BR.ts +++ b/src/qt/locale/bitcoin_pt_BR.ts @@ -1550,6 +1550,14 @@ <translation>A duração de um ping excepcional no momento.</translation> </message> <message> + <source>Ping Wait</source> + <translation>Espera de ping</translation> + </message> + <message> + <source>Time Offset</source> + <translation>Offset de tempo</translation> + </message> + <message> <source>Last block time</source> <translation>Horário do último bloco</translation> </message> @@ -2638,6 +2646,10 @@ <translation>Mostrar ou não endereços Bitcoin na lista de transações.</translation> </message> <message> + <source>User-defined intent/purpose of the transaction.</source> + <translation>Intenção/Propósito definido pelo usuário para a transação</translation> + </message> + <message> <source>Amount removed from or added to balance.</source> <translation>Quantidade debitada ou creditada ao saldo.</translation> </message> @@ -2717,6 +2729,10 @@ <translation>Copiar ID da transação</translation> </message> <message> + <source>Copy raw transaction</source> + <translation>Copia os dados brutos da transação</translation> + </message> + <message> <source>Edit label</source> <translation>Editar rótulo</translation> </message> @@ -2868,6 +2884,18 @@ <translation>Se <category> não for suprida ou se <category> = 1, mostrar toda informação de depuração.</translation> </message> <message> + <source>Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly.</source> + <translation>Por favor verifique se a data e horário estão corretos no seu computador! Se o seu relógio estiver incorreto, a Carteira Bitcoin não irá funcionar corretamente.</translation> + </message> + <message> + <source>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source> + <translation>Corte: a ultima sincronização da carteira foi além do dado comprimido. Você precisa reindexar ( -reindex , faça o download de toda a blockchain novamente)</translation> + </message> + <message> + <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> + <translation>Rescans não são possíveis no modo de corte. Você precisa usar -reindex, que irá fazer o download de toda a blockchain novamente.</translation> + </message> + <message> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation>Erro: Um erro interno fatal ocorreu, veja debug.log para detalhes</translation> </message> @@ -2992,6 +3020,22 @@ <translation>Você quer reconstruir o banco de dados de blocos agora?</translation> </message> <message> + <source>Enable publish hash block in <address></source> + <translation>Abilitar a publicação da hash do block em <endereço></translation> + </message> + <message> + <source>Enable publish hash transaction in <address></source> + <translation>Abilitar a publicação da hash da transação em <endereço></translation> + </message> + <message> + <source>Enable publish raw block in <address></source> + <translation>Abilitar a publicação dos dados brutos do block em <endereço></translation> + </message> + <message> + <source>Enable publish raw transaction in <address></source> + <translation>Abilitar a publicação dos dados brutos da transação em <endereço></translation> + </message> + <message> <source>Error initializing block database</source> <translation>Erro ao inicializar banco de dados de blocos</translation> </message> @@ -3112,6 +3156,10 @@ <translation>Vincular ao endereço fornecido e sempre escutar nele. Use a notação [host]:port para IPv6</translation> </message> <message> + <source>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)</source> + <translation>Conecte ao endereço dado para receber conecções JSON-RPC. Use a notação [destino]:porta para IPv6. Essa opção pode ser especificada várias vezes (padrão: conecte a todas as interfaces)</translation> + </message> + <message> <source>Cannot obtain a lock on data directory %s. Bitcoin Core is probably already running.</source> <translation>Não foi possível obter acesso exclusivo ao diretório de dados %s. Provavelmente Bitcoin já está sendo executado.</translation> </message> @@ -3144,6 +3192,10 @@ <translation>A quantia da transação é muito pequena para mandar </translation> </message> <message> + <source>This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.</source> + <translation>Esse produto inclui software desenvolvido pelo Open SSL Project para uso na OpenSSL Toolkit<https://www.openssl.org/> e software criptográfico escrito por Eric Young e software UPnP escrito por Thomas Bernard. </translation> + </message> + <message> <source>You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain</source> <translation>Você precisa reconstruir o banco de dados usando -reindex para sair do modo prune. Isso irá rebaixar todo o blockchain.</translation> </message> diff --git a/src/qt/locale/bitcoin_ru.ts b/src/qt/locale/bitcoin_ru.ts index b69a3bda54..ad0a7b3b50 100644 --- a/src/qt/locale/bitcoin_ru.ts +++ b/src/qt/locale/bitcoin_ru.ts @@ -218,7 +218,15 @@ </context> <context> <name>BanTableModel</name> - </context> + <message> + <source>IP/Netmask</source> + <translation>IP/префикс</translation> + </message> + <message> + <source>Banned Until</source> + <translation>Заблокировано до</translation> + </message> +</context> <context> <name>BitcoinGUI</name> <message> @@ -421,6 +429,10 @@ <source>Show the Bitcoin Core help message to get a list with possible Bitcoin command-line options</source> <translation>Показать помощь по Bitcoin Core и получить список доступных параметров командной строки.</translation> </message> + <message numerus="yes"> + <source>%n active connection(s) to Bitcoin network</source> + <translation><numerusform>%n активных соединений с сетью Bitcoin</numerusform><numerusform>%n активных соединений с сетью Bitcoin</numerusform><numerusform>%n активных соединений с сетью Bitcoin</numerusform><numerusform>%n активных соединений с сетью Bitcoin</numerusform></translation> + </message> <message> <source>No block source available...</source> <translation>Источник блоков недоступен...</translation> @@ -433,10 +445,22 @@ <source>%n hour(s)</source> <translation><numerusform>%n час</numerusform><numerusform>%n часа</numerusform><numerusform>%n часов</numerusform><numerusform>%n часов</numerusform></translation> </message> + <message numerus="yes"> + <source>%n day(s)</source> + <translation><numerusform>%n день</numerusform><numerusform>%n дня</numerusform><numerusform>%n дней</numerusform><numerusform>%n дней</numerusform></translation> + </message> + <message numerus="yes"> + <source>%n week(s)</source> + <translation><numerusform>%n неделю</numerusform><numerusform>%n недели</numerusform><numerusform>%n недель</numerusform><numerusform>%n недель</numerusform></translation> + </message> <message> <source>%1 and %2</source> <translation>%1 и %2</translation> </message> + <message numerus="yes"> + <source>%n year(s)</source> + <translation><numerusform>%n год</numerusform><numerusform>%n лет</numerusform><numerusform>%n лет</numerusform><numerusform>%n года</numerusform></translation> + </message> <message> <source>%1 behind</source> <translation>%1 позади</translation> @@ -893,7 +917,15 @@ <source>Error</source> <translation>Ошибка</translation> </message> - </context> + <message numerus="yes"> + <source>%n GB of free space available</source> + <translation><numerusform>%n ГБ свободного места доступно</numerusform><numerusform>%n ГБ свободного места доступно</numerusform><numerusform>%n ГБ свободного места доступно</numerusform><numerusform>%n ГБ свободного места доступно</numerusform></translation> + </message> + <message numerus="yes"> + <source>(of %n GB needed)</source> + <translation><numerusform>(из необходимых %n ГБ)</numerusform><numerusform>(из необходимых %n ГБ)</numerusform><numerusform>(из необходимых %n ГБ)</numerusform><numerusform>(из необходимых %n ГБ)</numerusform></translation> + </message> +</context> <context> <name>OpenURIDialog</name> <message> @@ -1044,10 +1076,34 @@ <translation>Порт прокси-сервера (например, 9050)</translation> </message> <message> + <source>Used for reaching peers via:</source> + <translation>Используется для достижения участников через:</translation> + </message> + <message> + <source>Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type.</source> + <translation>Показывается, если включено прокси SOCKS5 по умолчанию, используемое для соединения с участниками по этому типу сети.</translation> + </message> + <message> + <source>IPv4</source> + <translation>IPv4</translation> + </message> + <message> + <source>IPv6</source> + <translation>IPv6</translation> + </message> + <message> <source>Tor</source> <translation>Tor</translation> </message> <message> + <source>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</source> + <translation>Подключаться к сети Bitcoin через прокси SOCKS5 для скрытых сервисов Tor.</translation> + </message> + <message> + <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services:</source> + <translation>Использовать отдельный прокси SOCKS5 для соединения с участниками через скрытые сервисы Tor:</translation> + </message> + <message> <source>&Window</source> <translation>&Окно</translation> </message> @@ -1434,10 +1490,18 @@ <translation>&Участники</translation> </message> <message> + <source>Banned peers</source> + <translation>Заблокированные участники</translation> + </message> + <message> <source>Select a peer to view detailed information.</source> <translation>Выберите участника для просмотра подробностей.</translation> </message> <message> + <source>Whitelisted</source> + <translation>Доверенный</translation> + </message> + <message> <source>Direction</source> <translation>Направление</translation> </message> @@ -1446,6 +1510,18 @@ <translation>Версия</translation> </message> <message> + <source>Starting Block</source> + <translation>Начальный блок</translation> + </message> + <message> + <source>Synced Headers</source> + <translation>Синхронизировано заголовков</translation> + </message> + <message> + <source>Synced Blocks</source> + <translation>Синхронизировано блоков</translation> + </message> + <message> <source>User Agent</source> <translation>Юзер-агент</translation> </message> @@ -1474,6 +1550,14 @@ <translation>Время задержки</translation> </message> <message> + <source>The duration of a currently outstanding ping.</source> + <translation>Длительность текущего пинга.</translation> + </message> + <message> + <source>Ping Wait</source> + <translation>Время задержки</translation> + </message> + <message> <source>Time Offset</source> <translation>Смещение времени</translation> </message> @@ -1522,6 +1606,34 @@ <translation>Очистить консоль</translation> </message> <message> + <source>&Disconnect Node</source> + <translation>&Отключить узел</translation> + </message> + <message> + <source>Ban Node for</source> + <translation>Заблокировать узел на</translation> + </message> + <message> + <source>1 &hour</source> + <translation>1 &час</translation> + </message> + <message> + <source>1 &day</source> + <translation>1 &день</translation> + </message> + <message> + <source>1 &week</source> + <translation>1 &неделю</translation> + </message> + <message> + <source>1 &year</source> + <translation>1 &год</translation> + </message> + <message> + <source>&Unban Node</source> + <translation>&Разблокировать узел</translation> + </message> + <message> <source>Welcome to the Bitcoin Core RPC console.</source> <translation>Добро пожаловать в RPC-консоль Bitcoin Core.</translation> </message> @@ -1550,6 +1662,10 @@ <translation>%1 ГБ</translation> </message> <message> + <source>(node id: %1)</source> + <translation>(номер узла: %1)</translation> + </message> + <message> <source>via %1</source> <translation>через %1</translation> </message> @@ -1942,6 +2058,10 @@ <translation>Копировать размен</translation> </message> <message> + <source>Total Amount %1</source> + <translation>Общая сумма %1</translation> + </message> + <message> <source>or</source> <translation>или</translation> </message> @@ -2287,6 +2407,10 @@ <source>Status</source> <translation>Статус</translation> </message> + <message numerus="yes"> + <source>, broadcast through %n node(s)</source> + <translation><numerusform>, разослано через %n узел</numerusform><numerusform>, разослано через %n узла</numerusform><numerusform>, разослано через %n узлов</numerusform><numerusform>, разослано через %n узлов</numerusform></translation> + </message> <message> <source>Date</source> <translation>Дата</translation> @@ -2323,6 +2447,10 @@ <source>Credit</source> <translation>Кредит</translation> </message> + <message numerus="yes"> + <source>matures in %n more block(s)</source> + <translation><numerusform>будет доступно через %n блок</numerusform><numerusform>будет доступно через %n блока</numerusform><numerusform>будет доступно через %n блоков</numerusform><numerusform>будет доступно через %n блоков</numerusform></translation> + </message> <message> <source>not accepted</source> <translation>не принято</translation> @@ -2395,6 +2523,10 @@ <source>, has not been successfully broadcast yet</source> <translation>, ещё не было успешно разослано</translation> </message> + <message numerus="yes"> + <source>Open for %n more block(s)</source> + <translation><numerusform>Открыто для ещё %n блока</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform></translation> + </message> <message> <source>unknown</source> <translation>неизвестно</translation> @@ -2425,6 +2557,10 @@ <source>Immature (%1 confirmations, will be available after %2)</source> <translation>Незрелый (%1 подтверждений, будет доступен после %2)</translation> </message> + <message numerus="yes"> + <source>Open for %n more block(s)</source> + <translation><numerusform>Открыто для ещё %n блока</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform><numerusform>Открыто для ещё %n блоков</numerusform></translation> + </message> <message> <source>Open until %1</source> <translation>Открыто до %1</translation> @@ -2585,6 +2721,10 @@ <translation>Скопировать ID транзакции</translation> </message> <message> + <source>Copy raw transaction</source> + <translation>Скопировать исходную транзакции</translation> + </message> + <message> <source>Edit label</source> <translation>Изменить метку</translation> </message> @@ -2732,10 +2872,42 @@ <translation>Принимать командную строку и команды JSON-RPC</translation> </message> <message> + <source>If <category> is not supplied or if <category> = 1, output all debugging information.</source> + <translation>Если <category> не предоставлена или равна 1, выводить всю отладочную информацию.</translation> + </message> + <message> + <source>Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)</source> + <translation>Максимальная сумма комиссий (%s) для одной транзакции в бумажнике; слишком низкое значение может вызвать прерывание больших транзакций (по умолчанию: %s)</translation> + </message> + <message> + <source>Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly.</source> + <translation>Внимание: убедитесь, что дата и время на Вашем компьютере выставлены верно! Если Ваши часы идут неправильно, Bitcoin Core будет работать некорректно.</translation> + </message> + <message> + <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source> + <translation>Удаление блоков выставлено ниже, чем минимум в %d Мб. Пожалуйста, используйте большее значение.</translation> + </message> + <message> + <source>Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)</source> + <translation>Удаление: последняя синхронизация кошелька вышла за рамки удаленных данных. Вам нужен -reindex (скачать всю цепь блоков в случае удаленного узла)</translation> + </message> + <message> + <source>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. (default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)</source> + <translation>Уменьшить размер хранилища за счёт удаления (обрезания) старых блоков. Этот режим несовместим с -txindex и -rescan. Внимание: переключение этой опции обратно потребует полной загрузки цепи блоков. (по умолчанию: 0 = отключить удаление блоков, >%u = целевой размер в Мб для файлов блоков)</translation> + </message> + <message> + <source>Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.</source> + <translation>Повторное сканирование не возможно в режиме удаления. Вам надо будет использовать -reindex, который загрузит заново всю цепь блоков.</translation> + </message> + <message> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation>Ошибка: произошла неустранимая ошибка, подробности в debug.log</translation> </message> <message> + <source>Fee (in %s/kB) to add to transactions you send (default: %s)</source> + <translation>Комиссия (в %s/Кб) для добавления к вашим транзакциям (по умолчанию: %s)</translation> + </message> + <message> <source>Pruning blockstore...</source> <translation>Очистка хранилища блоков...</translation> </message> @@ -2744,6 +2916,10 @@ <translation>Запускаться в фоне как демон и принимать команды</translation> </message> <message> + <source>Unable to start HTTP server. See debug log for details.</source> + <translation>Невозможно запустить HTTP сервер. Смотри debug лог для подробностей.</translation> + </message> + <message> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Принимать подключения извне (по умолчанию: 1, если не используется -proxy или -connect)</translation> </message> @@ -2768,6 +2944,10 @@ <translation>Задать число потоков проверки скрипта (от %u до %d, 0=авто, <0 = оставить столько ядер свободными, по умолчанию: %d)</translation> </message> <message> + <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source> + <translation>База данных блоков содержит блок, который появляется из будущего. Это может из-за некорректно установленных даты и времени на вашем компьютере. Остается только перестроивать базу блоков, если вы уверены, что дата и время корректны.</translation> + </message> + <message> <source>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</source> <translation>Это пре-релизная тестовая сборка - используйте на свой страх и риск - не используйте для добычи или торговых приложений</translation> </message> @@ -2776,6 +2956,10 @@ <translation>Не удалось забиндиться на %s на этом компьютере. Возможно, Bitcoin Core уже запущен.</translation> </message> <message> + <source>Use UPnP to map the listening port (default: 1 when listening and no -proxy)</source> + <translation>Использовать UPnP для проброса порта (по умолчанию: 1, если используется прослушивание и нет -proxy)</translation> + </message> + <message> <source>WARNING: abnormally high number of blocks generated, %d blocks received in the last %d hours (%d expected)</source> <translation>ВНИМАНИЕ: сгенерировано ненормально большое число блоков, %d блоков получено за последние %d часов (ожидалось %d)</translation> </message> @@ -2804,6 +2988,10 @@ <translation>(по умолчанию: 1)</translation> </message> <message> + <source>-maxmempool must be at least %d MB</source> + <translation>-maxmempool должен быть как минимум %d MB</translation> + </message> + <message> <source><category> can be:</source> <translation><category> может быть:</translation> </message> @@ -2836,6 +3024,22 @@ <translation>Пересобрать БД блоков прямо сейчас?</translation> </message> <message> + <source>Enable publish hash block in <address></source> + <translation>Включить публичный хеш блока в <address></translation> + </message> + <message> + <source>Enable publish hash transaction in <address></source> + <translation>Включить публичный хеш транзакции в <address></translation> + </message> + <message> + <source>Enable publish raw block in <address></source> + <translation>Включить публичный сырой блок в <address></translation> + </message> + <message> + <source>Enable publish raw transaction in <address></source> + <translation>Включить публичную сырую транзакцию в <address></translation> + </message> + <message> <source>Error initializing block database</source> <translation>Ошибка инициализации БД блоков</translation> </message> @@ -2872,6 +3076,10 @@ <translation>Неверный -onion адрес: '%s'</translation> </message> <message> + <source>Keep the transaction memory pool below <n> megabytes (default: %u)</source> + <translation>Сбрасывать транзакции из памяти на диск каждые <n> мегабайт (по умолчанию: %u)</translation> + </message> + <message> <source>Not enough file descriptors available.</source> <translation>Недостаточно файловых дескрипторов.</translation> </message> @@ -2900,10 +3108,26 @@ <translation>Укажите файл бумажника (внутри каталога данных)</translation> </message> <message> + <source>Unsupported argument -benchmark ignored, use -debug=bench.</source> + <translation>Неподдерживаемый аргумент -benchmark проигнорирован, используйте -debug=bench.</translation> + </message> + <message> + <source>Unsupported argument -debugnet ignored, use -debug=net.</source> + <translation>Неподдерживаемый аргумент -debugnet проигнорирован, используйте -debug=net.</translation> + </message> + <message> + <source>Unsupported argument -tor found, use -onion.</source> + <translation>Обнаружен не поддерживаемый параметр -tor, используйте -onion.</translation> + </message> + <message> <source>Use UPnP to map the listening port (default: %u)</source> <translation>Использовать UPnP для проброса порта (по умолчанию: %u)</translation> </message> <message> + <source>User Agent comment (%s) contains unsafe characters.</source> + <translation>Комментарий пользователя (%s) содержит небезопасные символы.</translation> + </message> + <message> <source>Verifying blocks...</source> <translation>Проверка блоков...</translation> </message> @@ -2960,6 +3184,10 @@ <translation>Выполнить команду, когда приходит соответствующее сообщение о тревоге или наблюдается очень длинное расщепление цепи (%s в команде заменяется на сообщение)</translation> </message> <message> + <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)</source> + <translation>Комиссии (в %s/Кб) меньшие этого значения считаются нулевыми для создания, ретрансляции, получения транзакции (по умолчанию: %s)</translation> + </message> + <message> <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source> <translation>Если paytxfee не задан, включить достаточную комиссию для подтверждения транзакции в среднем за n блоков (по умолчанию: %u)</translation> </message> @@ -3016,6 +3244,18 @@ <translation>Активируется лучшая цепь...</translation> </message> <message> + <source>Always relay transactions received from whitelisted peers (default: %d)</source> + <translation>Всегда транслировать транзакции, полученные из белого списка участников (по умолчанию: %d)</translation> + </message> + <message> + <source>Attempt to recover private keys from a corrupt wallet.dat on startup</source> + <translation>Попытаться восстановить приватные ключи из повреждённого wallet.dat при запуске</translation> + </message> + <message> + <source>Automatically create Tor hidden service (default: %d)</source> + <translation>Автоматически создавать скрытый Tor сервис (по умолчанию: %d)</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>Не удаётся разрешить адрес в параметре -whitebind: '%s'</translation> </message> @@ -3040,6 +3280,10 @@ <translation>Ошибка чтения базы данных, работа завершается.</translation> </message> <message> + <source>Imports blocks from external blk000??.dat file on startup</source> + <translation>Импортировать блоки из внешнего файла blk000?.dat при запуске</translation> + </message> + <message> <source>Information</source> <translation>Информация</translation> </message> @@ -3092,6 +3336,14 @@ <translation>Получать и отображать P2P сетевые тревоги (по умолчанию: %u)</translation> </message> <message> + <source>Reducing -maxconnections from %d to %d, because of system limitations.</source> + <translation>Уменьшите -maxconnections с %d до %d, из-за ограничений системы.</translation> + </message> + <message> + <source>Rescan the block chain for missing wallet transactions on startup</source> + <translation>Перепроверить цепь блоков на предмет отсутствующих в бумажнике транзакций при запуске</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Выводить информацию трассировки/отладки на консоль вместо файла debug.log</translation> </message> @@ -3136,6 +3388,14 @@ <translation>Это экспериментальное ПО.</translation> </message> <message> + <source>Tor control port password (default: empty)</source> + <translation>Пароль контроля порта Tor (по умолчанию: пустой)</translation> + </message> + <message> + <source>Tor control port to use if onion listening enabled (default: %s)</source> + <translation>Порт контроля Tor используется, если включено прослушивание onion (по умолчанию: %s)</translation> + </message> + <message> <source>Transaction amount too small</source> <translation>Сумма транзакции слишком мала</translation> </message> @@ -3160,6 +3420,10 @@ <translation>Невозможно привязаться к %s на этом компьютере (bind вернул ошибку %s)</translation> </message> <message> + <source>Upgrade wallet to latest format on startup</source> + <translation>Обновить бумажник до последнего формата при запуске</translation> + </message> + <message> <source>Username for JSON-RPC connections</source> <translation>Имя для подключений JSON-RPC</translation> </message> @@ -3172,10 +3436,18 @@ <translation>Внимание</translation> </message> <message> + <source>Whether to operate in a blocks only mode (default: %u)</source> + <translation>Будет работать в режиме только блоков (по умолчанию: %u)</translation> + </message> + <message> <source>Zapping all transactions from wallet...</source> <translation>Стираем все транзакции из кошелька...</translation> </message> <message> + <source>ZeroMQ notification options:</source> + <translation>ZeroMQ параметры оповещения:</translation> + </message> + <message> <source>wallet.dat corrupt, salvage failed</source> <translation>wallet.dat повреждён, спасение данных не удалось</translation> </message> @@ -3208,6 +3480,22 @@ <translation>(1 = сохранять метаданные транзакции: например, владельца аккаунта и информацию запроса платежа; 2 = отбросить метаданные)</translation> </message> <message> + <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> + <translation>Установлено очень большое значение -maxtxfee. Такие большие комиссии могут быть уплачены в отдельной транзакции.</translation> + </message> + <message> + <source>-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.</source> + <translation>Установлено очень большое значение -paytxfee. Такие большие комиссии могут быть уплачены в отдельной транзакции.</translation> + </message> + <message> + <source>Do not keep transactions in the mempool longer than <n> hours (default: %u)</source> + <translation>Не хранить транзакции в памяти дольше, чем <n> часов (по умолчанию %u)</translation> + </message> + <message> + <source>Error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> + <translation>Ошибка чтения wallet.dat! Все ключи прочитаны верно, но данные транзакций или записи адресной книги могут отсутствовать или быть неправильными.</translation> + </message> + <message> <source>How thorough the block verification of -checkblocks is (0-4, default: %u)</source> <translation>Насколько тщательна проверка контрольных блоков -checkblocks (0-4, по умолчанию: %u)</translation> </message> @@ -3224,6 +3512,18 @@ <translation>Выводить отладочную информацию (по умолчанию: %u, указание <category> необязательно)</translation> </message> <message> + <source>Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.</source> + <translation>Текущая длина строки версии сети (%i) превышает максимальную длину (%i). Увеливается количество или размер uacomments.</translation> + </message> + <message> + <source>Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)</source> + <translation>Пытается ограничить исходящий трафик до (в МБ за 24ч), 0 = не ограничивать (по умолчанию: %d)</translation> + </message> + <message> + <source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source> + <translation>Обнаружен не поддерживаемый аргумент -socks. Выбор версии SOCKS более невозможен, поддерживаются только прокси SOCKS5.</translation> + </message> + <message> <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)</source> <translation>Использовать отдельный прокси SOCKS5 для соединения с участниками через скрытые сервисы Tor (по умолчанию: %s)</translation> </message> diff --git a/src/qt/locale/bitcoin_sv.ts b/src/qt/locale/bitcoin_sv.ts index 69c175645b..bb7fcf7075 100644 --- a/src/qt/locale/bitcoin_sv.ts +++ b/src/qt/locale/bitcoin_sv.ts @@ -2055,6 +2055,10 @@ Var vänlig och försök igen.</translation> <translation>Kopiera växel</translation> </message> <message> + <source>Total Amount %1</source> + <translation>Total summa %1</translation> + </message> + <message> <source>or</source> <translation>eller</translation> </message> @@ -2086,6 +2090,10 @@ Var vänlig och försök igen.</translation> <source>Payment request expired.</source> <translation>Betalningsbegäran löpte ut.</translation> </message> + <message> + <source>Pay only the required fee of %1</source> + <translation>Betala endast den nödvändiga avgiften på %1</translation> + </message> <message numerus="yes"> <source>Estimated to begin confirmation within %n block(s).</source> <translation><numerusform>Uppskattas till att påbörja bekräftelse inom %n block.</numerusform><numerusform>Uppskattas till att påbörja bekräftelse inom %n block.</numerusform></translation> @@ -2718,6 +2726,10 @@ Var vänlig och försök igen.</translation> <translation>Kopiera transaktions ID</translation> </message> <message> + <source>Copy raw transaction</source> + <translation>Kopiera rå transaktion</translation> + </message> + <message> <source>Edit label</source> <translation>Ändra etikett</translation> </message> @@ -3177,6 +3189,10 @@ Var vänlig och försök igen.</translation> <translation>Exekvera kommando när ett relevant meddelande är mottagen eller när vi ser en väldigt lång förgrening (%s i cmd är utbytt med ett meddelande)</translation> </message> <message> + <source>Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)</source> + <translation>Avgifter (i %s/kB) mindre än detta betraktas som nollavgift för vidarebefordran, mining och transaktionsskapande (förvalt: %s)</translation> + </message> + <message> <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source> <translation>Om paytxfee inte är satt, inkludera tillräcklig avgift så att transaktionen börjar att konfirmeras inom n blocks (förvalt: %u)</translation> </message> @@ -3229,6 +3245,18 @@ Var vänlig och försök igen.</translation> <translation>Aktiverar bästa kedjan...</translation> </message> <message> + <source>Always relay transactions received from whitelisted peers (default: %d)</source> + <translation>Vidarebefordra alltid transaktioner från vitlistade noder (förval: %d)</translation> + </message> + <message> + <source>Attempt to recover private keys from a corrupt wallet.dat on startup</source> + <translation>Försök att rädda privata nycklar från en korrupt wallet.dat vid uppstart</translation> + </message> + <message> + <source>Automatically create Tor hidden service (default: %d)</source> + <translation>Skapa automatiskt dold tjänst i Tor (förval: %d)</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>Kan inte matcha -whitebind adress: '%s'</translation> </message> @@ -3253,6 +3281,10 @@ Var vänlig och försök igen.</translation> <translation>Fel vid läsning från databas, avslutar.</translation> </message> <message> + <source>Imports blocks from external blk000??.dat file on startup</source> + <translation>Importera block från extern blk000??.dat-fil vid uppstart</translation> + </message> + <message> <source>Information</source> <translation>Information</translation> </message> @@ -3309,6 +3341,10 @@ Var vänlig och försök igen.</translation> <translation>Minskar -maxconnections från %d till %d, på grund av systembegränsningar.</translation> </message> <message> + <source>Rescan the block chain for missing wallet transactions on startup</source> + <translation>Sök i blockkedjan efter saknade plånbokstransaktioner vid uppstart</translation> + </message> + <message> <source>Send trace/debug info to console instead of debug.log file</source> <translation>Skicka trace-/debuginformation till terminalen istället för till debug.log</translation> </message> @@ -3353,6 +3389,14 @@ Var vänlig och försök igen.</translation> <translation>Detta är experimentmjukvara.</translation> </message> <message> + <source>Tor control port password (default: empty)</source> + <translation>Lösenord för Tor-kontrollport (förval: inget)</translation> + </message> + <message> + <source>Tor control port to use if onion listening enabled (default: %s)</source> + <translation>Tor-kontrollport att använda om onion är aktiverat (förval: %s)</translation> + </message> + <message> <source>Transaction amount too small</source> <translation>Transaktions belopp för liten</translation> </message> @@ -3377,6 +3421,10 @@ Var vänlig och försök igen.</translation> <translation>Det går inte att binda till %s på den här datorn (bind returnerade felmeddelande %s)</translation> </message> <message> + <source>Upgrade wallet to latest format on startup</source> + <translation>Uppgradera plånbok till senaste formatet vid uppstart</translation> + </message> + <message> <source>Username for JSON-RPC connections</source> <translation>Användarnamn för JSON-RPC-anslutningar</translation> </message> @@ -3389,6 +3437,10 @@ Var vänlig och försök igen.</translation> <translation>Varning</translation> </message> <message> + <source>Whether to operate in a blocks only mode (default: %u)</source> + <translation>Ska allt göras i endast block-läge (förval: %u)</translation> + </message> + <message> <source>Zapping all transactions from wallet...</source> <translation>Töm plånboken på alla transaktioner...</translation> </message> diff --git a/src/qt/locale/bitcoin_zh_TW.ts b/src/qt/locale/bitcoin_zh_TW.ts index b4dbf85a33..adf9071ede 100644 --- a/src/qt/locale/bitcoin_zh_TW.ts +++ b/src/qt/locale/bitcoin_zh_TW.ts @@ -403,7 +403,7 @@ </message> <message> <source>Request payments (generates QR codes and bitcoin: URIs)</source> - <translation>要求付款(產生 QR Code 和位元幣付款協議的 URI)</translation> + <translation>要求付款(產生 QR Code 和位元幣付款協議的資源識別碼: URI)</translation> </message> <message> <source>&About Bitcoin Core</source> @@ -423,7 +423,7 @@ </message> <message> <source>Open a bitcoin: URI or payment request</source> - <translation>開啓 bitcoin 協議的 URI 或付款要求</translation> + <translation>開啓 bitcoin 協議的資源識別碼(URI)或付款要求</translation> </message> <message> <source>&Command-line options</source> @@ -907,7 +907,7 @@ </message> <message> <source>Use a custom data directory:</source> - <translation>使用自定的資料目錄:</translation> + <translation>使用自訂的資料目錄:</translation> </message> <message> <source>Bitcoin Core</source> @@ -997,7 +997,7 @@ </message> <message> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> - <translation>在交易頁籤的情境選單出現的第三方(比如說區塊探索網站)網址連結。網址中的 %s 會被取代為交易的雜湊值。可以用直線符號 | 來分隔多個連結。</translation> + <translation>在交易頁籤的情境選單出現的第三方網址連結(URL),比如說區塊探索網站。網址中的 %s 會被取代為交易的雜湊值。可以用直線符號 | 來分隔多個連結。</translation> </message> <message> <source>Third party transaction URLs</source> @@ -1291,11 +1291,11 @@ </message> <message> <source>Payment request fetch URL is invalid: %1</source> - <translation>取得付款要求的 URL 無效: %1</translation> + <translation>取得付款要求的網址連結(URL)無效: %1</translation> </message> <message> <source>URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters.</source> - <translation>沒辦法解析 URI 位址!可能是因為位元幣位址無效,或是 URI 參數格式錯誤。</translation> + <translation>沒辦法解析資源識別碼(URI)!可能是因為位元幣位址無效,或是 URI 參數格式錯誤。</translation> </message> <message> <source>Payment request file handling</source> @@ -1923,7 +1923,7 @@ </message> <message> <source>Custom change address</source> - <translation>自定找零位址</translation> + <translation>自訂找零位址</translation> </message> <message> <source>Transaction Fee:</source> @@ -2194,11 +2194,11 @@ </message> <message> <source>This is an unauthenticated payment request.</source> - <translation>這是個沒驗證過的付款要求。</translation> + <translation>這是個沒有驗證過身份的付款要求。</translation> </message> <message> <source>This is an authenticated payment request.</source> - <translation>這是個已驗證的付款要求。</translation> + <translation>這是個已經驗證過身份的付款要求。</translation> </message> <message> <source>Enter a label for this address to add it to the list of used addresses</source> @@ -2206,7 +2206,7 @@ </message> <message> <source>A message that was attached to the bitcoin: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Bitcoin network.</source> - <translation>附加在位元幣付款協議 URI 中的訊息,會和交易內容一起存起來,給你自己做參考。注意: 這個訊息不會送到位元幣網路上。</translation> + <translation>附加在位元幣付款協議的資源識別碼(URI)中的訊息,會和交易內容一起存起來,給你自己做參考。注意: 這個訊息不會送到位元幣網路上。</translation> </message> <message> <source>Pay To:</source> @@ -2733,6 +2733,10 @@ <translation>複製交易識別碼</translation> </message> <message> + <source>Copy raw transaction</source> + <translation>複製交易原始資料</translation> + </message> + <message> <source>Edit label</source> <translation>編輯標記</translation> </message> @@ -3094,7 +3098,7 @@ </message> <message> <source>Only connect to nodes in network <net> (ipv4, ipv6 or onion)</source> - <translation>只有連接到網絡節點 <net> (IPv4,IPv6或onion)</translation> + <translation>只和 <net> 網路上的節點連線(ipv4, ipv6, 或 onion)</translation> </message> <message> <source>Prune cannot be configured with a negative value.</source> @@ -3249,10 +3253,18 @@ <translation>啟用最佳鏈結...</translation> </message> <message> + <source>Always relay transactions received from whitelisted peers (default: %d)</source> + <translation>無條件轉發從白名點節點收到的交易(預設值: %d)</translation> + </message> + <message> <source>Attempt to recover private keys from a corrupt wallet.dat on startup</source> <translation>啟動時嘗試從壞掉的錢包檔 wallet.dat 復原密鑰</translation> </message> <message> + <source>Automatically create Tor hidden service (default: %d)</source> + <translation>自動產生 Tor 隱藏服務(預設值: %d)</translation> + </message> + <message> <source>Cannot resolve -whitebind address: '%s'</source> <translation>沒辦法解析 -whitebind 指定的位址: '%s'</translation> </message> @@ -3385,6 +3397,14 @@ <translation>這套軟體屬於實驗性質。</translation> </message> <message> + <source>Tor control port password (default: empty)</source> + <translation>Tor 控制埠密碼(預設值: 空白)</translation> + </message> + <message> + <source>Tor control port to use if onion listening enabled (default: %s)</source> + <translation>開啟聽候 onion 連線時的 Tor 控制埠號碼(預設值: %s)</translation> + </message> + <message> <source>Transaction amount too small</source> <translation>交易金額太小</translation> </message> @@ -3425,6 +3445,10 @@ <translation>警告</translation> </message> <message> + <source>Whether to operate in a blocks only mode (default: %u)</source> + <translation>是否要用只要區塊模式運作(預設值: %u)</translation> + </message> + <message> <source>Zapping all transactions from wallet...</source> <translation>正在砍掉錢包中的所有交易...</translation> </message> 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/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 78a783dea4..1000c143f3 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."; @@ -201,7 +201,7 @@ QList<std::pair<CScript,CAmount> > PaymentRequestPlus::getPayTo() const const unsigned char* scriptStr = (const unsigned char*)details.outputs(i).script().data(); CScript s(scriptStr, scriptStr+details.outputs(i).script().size()); - result.append(make_pair(s, details.outputs(i).amount())); + result.append(std::make_pair(s, details.outputs(i).amount())); } return result; } 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 b2b4fd0fab..30e551de19 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -343,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))); @@ -525,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 7b714be5b1..ec4e598bf9 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())); } } @@ -585,7 +585,7 @@ void SendCoinsDialog::updateFeeSectionControls() ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked()); ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); - ui->radioCustomAtLeast ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); + ui->radioCustomAtLeast ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() && CoinControlDialog::coinControl->HasSelected()); ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } @@ -600,7 +600,10 @@ void SendCoinsDialog::updateGlobalFeeVariables() { nTxConfirmTarget = defaultConfirmTarget; payTxFee = CFeeRate(ui->customFee->value()); - fPayAtLeastCustomFee = ui->radioCustomAtLeast->isChecked(); + + // if user has selected to set a minimum absolute fee, pass the value to coincontrol + // set nMinimumTotalFee to 0 in case of user has selected that the fee is per KB + CoinControlDialog::coinControl->nMinimumTotalFee = ui->radioCustomAtLeast->isChecked() ? ui->customFee->value() : 0; } fSendFreeTransactions = ui->checkBoxFreeTx->isChecked(); @@ -707,8 +710,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) if (!checked && model) // coin control features disabled CoinControlDialog::coinControl->SetNull(); - if (checked) - coinControlUpdateLabels(); + coinControlUpdateLabels(); } // Coin Control: button inputs -> show actual coin control dialog @@ -782,9 +784,24 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) // Coin Control: update labels void SendCoinsDialog::coinControlUpdateLabels() { - if (!model || !model->getOptionsModel() || !model->getOptionsModel()->getCoinControlFeatures()) + if (!model || !model->getOptionsModel()) return; + if (model->getOptionsModel()->getCoinControlFeatures()) + { + // enable minium absolute fee UI controls + ui->radioCustomAtLeast->setVisible(true); + + // only enable the feature if inputs are selected + ui->radioCustomAtLeast->setEnabled(CoinControlDialog::coinControl->HasSelected()); + } + else + { + // in case coin control is disabled (=default), hide minimum absolute fee UI controls + ui->radioCustomAtLeast->setVisible(false); + return; + } + // set pay amounts CoinControlDialog::payAmounts.clear(); CoinControlDialog::fSubtractFeeFromAmount = false; 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 5f77ca00d7..ee04636ce8 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -190,6 +190,7 @@ UniValue mempoolToJSON(bool fVerbose = false) UniValue info(UniValue::VOBJ); info.push_back(Pair("size", (int)e.GetTxSize())); info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); + info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee()))); info.push_back(Pair("time", e.GetTime())); info.push_back(Pair("height", (int)e.GetHeight())); info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); @@ -247,6 +248,7 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) " \"transactionid\" : { (json object)\n" " \"size\" : n, (numeric) transaction size in bytes\n" " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" + " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when transaction entered pool\n" " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 19b031b860..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) diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 3bda459245..1f2d77aef0 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -62,6 +62,7 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { entry.push_back(Pair("txid", tx.GetHash().GetHex())); + entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); UniValue vin(UniValue::VARR); @@ -133,6 +134,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) "{\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" + " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" @@ -429,6 +431,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"txid\" : \"id\", (string) The transaction id\n" + " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 8dcab832cb..57e0edc4b4 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1023,7 +1023,7 @@ public: // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScript(), nType, nVersion); + ::Serialize(s, CScriptBase(), nType, nVersion); else SerializeScriptCode(s, nType, nVersion); // Serialize the nSequence diff --git a/src/script/script.cpp b/src/script/script.cpp index 263c89defe..9c77ed9fc1 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -205,9 +205,9 @@ bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: return (this->size() == 23 && - this->at(0) == OP_HASH160 && - this->at(1) == 0x14 && - this->at(22) == OP_EQUAL); + (*this)[0] == OP_HASH160 && + (*this)[1] == 0x14 && + (*this)[22] == OP_EQUAL); } bool CScript::IsPushOnly(const_iterator pc) const diff --git a/src/script/script.h b/src/script/script.h index a38d33a189..3650957fc9 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -7,6 +7,7 @@ #define BITCOIN_SCRIPT_SCRIPT_H #include "crypto/common.h" +#include "prevector.h" #include <assert.h> #include <climits> @@ -354,8 +355,10 @@ private: int64_t m_value; }; +typedef prevector<28, unsigned char> CScriptBase; + /** Serialized script, used inside transaction inputs and outputs */ -class CScript : public std::vector<unsigned char> +class CScript : public CScriptBase { protected: CScript& push_int64(int64_t n) @@ -376,9 +379,10 @@ protected: } public: CScript() { } - CScript(const CScript& b) : std::vector<unsigned char>(b.begin(), b.end()) { } - CScript(const_iterator pbegin, const_iterator pend) : std::vector<unsigned char>(pbegin, pend) { } - CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector<unsigned char>(pbegin, pend) { } + CScript(const CScript& b) : CScriptBase(b.begin(), b.end()) { } + CScript(const_iterator pbegin, const_iterator pend) : CScriptBase(pbegin, pend) { } + CScript(std::vector<unsigned char>::const_iterator pbegin, std::vector<unsigned char>::const_iterator pend) : CScriptBase(pbegin, pend) { } + CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } CScript& operator+=(const CScript& b) { @@ -611,7 +615,7 @@ public: void clear() { // The default std::vector::clear() does not release memory. - std::vector<unsigned char>().swap(*this); + CScriptBase().swap(*this); } }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 8b43183b6d..90f557fc60 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -16,7 +16,7 @@ using namespace std; -typedef vector<unsigned char> valtype; +typedef std::vector<unsigned char> valtype; TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn) {} @@ -118,7 +118,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu bool fSolved = SignStep(creator, subscript, scriptSig, subType) && subType != TX_SCRIPTHASH; // Append serialized subscript whether or not it is completely signed: - scriptSig << static_cast<valtype>(subscript); + scriptSig << valtype(subscript.begin(), subscript.end()); if (!fSolved) return false; } 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/serialize.h b/src/serialize.h index 53d8af142f..5fe7fc1f35 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -20,7 +20,7 @@ #include <utility> #include <vector> -class CScript; +#include "prevector.h" static const unsigned int MAX_SIZE = 0x02000000; @@ -49,26 +49,26 @@ inline T* NCONST_PTR(const T* val) * @note These functions avoid the undefined case of indexing into an empty * vector, as well as that of indexing after the end of the vector. */ -template <class T, class TAl> -inline T* begin_ptr(std::vector<T,TAl>& v) +template <typename V> +inline typename V::value_type* begin_ptr(V& v) { return v.empty() ? NULL : &v[0]; } /** Get begin pointer of vector (const version) */ -template <class T, class TAl> -inline const T* begin_ptr(const std::vector<T,TAl>& v) +template <typename V> +inline const typename V::value_type* begin_ptr(const V& v) { return v.empty() ? NULL : &v[0]; } /** Get end pointer of vector (non-const version) */ -template <class T, class TAl> -inline T* end_ptr(std::vector<T,TAl>& v) +template <typename V> +inline typename V::value_type* end_ptr(V& v) { return v.empty() ? NULL : (&v[0] + v.size()); } /** Get end pointer of vector (const version) */ -template <class T, class TAl> -inline const T* end_ptr(const std::vector<T,TAl>& v) +template <typename V> +inline const typename V::value_type* end_ptr(const V& v) { return v.empty() ? NULL : (&v[0] + v.size()); } @@ -391,6 +391,12 @@ public: pbegin = (char*)begin_ptr(v); pend = (char*)end_ptr(v); } + template <unsigned int N, typename T, typename S, typename D> + explicit CFlatData(prevector<N, T, S, D> &v) + { + pbegin = (char*)begin_ptr(v); + pend = (char*)end_ptr(v); + } char* begin() { return pbegin; } const char* begin() const { return pbegin; } char* end() { return pend; } @@ -486,6 +492,20 @@ template<typename Stream, typename C> void Serialize(Stream& os, const std::basi template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_string<C>& str, int, int=0); /** + * prevector + * prevectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. + */ +template<unsigned int N, typename T> unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const unsigned char&); +template<unsigned int N, typename T, typename V> unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const V&); +template<unsigned int N, typename T> inline unsigned int GetSerializeSize(const prevector<N, T>& v, int nType, int nVersion); +template<typename Stream, unsigned int N, typename T> void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const unsigned char&); +template<typename Stream, unsigned int N, typename T, typename V> void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const V&); +template<typename Stream, unsigned int N, typename T> inline void Serialize(Stream& os, const prevector<N, T>& v, int nType, int nVersion); +template<typename Stream, unsigned int N, typename T> void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const unsigned char&); +template<typename Stream, unsigned int N, typename T, typename V> void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const V&); +template<typename Stream, unsigned int N, typename T> inline void Unserialize(Stream& is, prevector<N, T>& v, int nType, int nVersion); + +/** * vector * vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob. */ @@ -500,13 +520,6 @@ template<typename Stream, typename T, typename A, typename V> void Unserialize_i template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersion); /** - * others derived from vector - */ -extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion); -template<typename Stream> void Serialize(Stream& os, const CScript& v, int nType, int nVersion); -template<typename Stream> void Unserialize(Stream& is, CScript& v, int nType, int nVersion); - -/** * pair */ template<typename K, typename T> unsigned int GetSerializeSize(const std::pair<K, T>& item, int nType, int nVersion); @@ -588,6 +601,96 @@ void Unserialize(Stream& is, std::basic_string<C>& str, int, int) /** + * prevector + */ +template<unsigned int N, typename T> +unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const unsigned char&) +{ + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template<unsigned int N, typename T, typename V> +unsigned int GetSerializeSize_impl(const prevector<N, T>& v, int nType, int nVersion, const V&) +{ + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename prevector<N, T>::const_iterator vi = v.begin(); vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template<unsigned int N, typename T> +inline unsigned int GetSerializeSize(const prevector<N, T>& v, int nType, int nVersion) +{ + return GetSerializeSize_impl(v, nType, nVersion, T()); +} + + +template<typename Stream, unsigned int N, typename T> +void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const unsigned char&) +{ + WriteCompactSize(os, v.size()); + if (!v.empty()) + os.write((char*)&v[0], v.size() * sizeof(T)); +} + +template<typename Stream, unsigned int N, typename T, typename V> +void Serialize_impl(Stream& os, const prevector<N, T>& v, int nType, int nVersion, const V&) +{ + WriteCompactSize(os, v.size()); + for (typename prevector<N, T>::const_iterator vi = v.begin(); vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template<typename Stream, unsigned int N, typename T> +inline void Serialize(Stream& os, const prevector<N, T>& v, int nType, int nVersion) +{ + Serialize_impl(os, v, nType, nVersion, T()); +} + + +template<typename Stream, unsigned int N, typename T> +void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const unsigned char&) +{ + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) + { + unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char*)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template<typename Stream, unsigned int N, typename T, typename V> +void Unserialize_impl(Stream& is, prevector<N, T>& v, int nType, int nVersion, const V&) +{ + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) + { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) + nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template<typename Stream, unsigned int N, typename T> +inline void Unserialize(Stream& is, prevector<N, T>& v, int nType, int nVersion) +{ + Unserialize_impl(is, v, nType, nVersion, T()); +} + + + +/** * vector */ template<typename T, typename A> @@ -678,28 +781,6 @@ inline void Unserialize(Stream& is, std::vector<T, A>& v, int nType, int nVersio /** - * others derived from vector - */ -inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVersion) -{ - return GetSerializeSize((const std::vector<unsigned char>&)v, nType, nVersion); -} - -template<typename Stream> -void Serialize(Stream& os, const CScript& v, int nType, int nVersion) -{ - Serialize(os, (const std::vector<unsigned char>&)v, nType, nVersion); -} - -template<typename Stream> -void Unserialize(Stream& is, CScript& v, int nType, int nVersion) -{ - Unserialize(is, (std::vector<unsigned char>&)v, nType, nVersion); -} - - - -/** * pair */ template<typename K, typename T> 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/mempool_tests.cpp b/src/test/mempool_tests.cpp index 896e1237ed..e9f7378f74 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -102,12 +102,13 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) removed.clear(); } +template<int index> void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder) { BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size()); - CTxMemPool::indexed_transaction_set::nth_index<1>::type::iterator it = pool.mapTx.get<1>().begin(); + typename CTxMemPool::indexed_transaction_set::nth_index<index>::type::iterator it = pool.mapTx.get<index>().begin(); int count=0; - for (; it != pool.mapTx.get<1>().end(); ++it, ++count) { + for (; it != pool.mapTx.get<index>().end(); ++it, ++count) { BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]); } } @@ -163,7 +164,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder[2] = tx1.GetHash().ToString(); // 10000 sortedOrder[3] = tx4.GetHash().ToString(); // 15000 sortedOrder[4] = tx2.GetHash().ToString(); // 20000 - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); /* low fee but with high fee child */ /* tx6 -> tx7 -> tx8, tx9 -> tx10 */ @@ -175,7 +176,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) BOOST_CHECK_EQUAL(pool.size(), 6); // Check that at this point, tx6 is sorted low sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); CTxMemPool::setEntries setAncestors; setAncestors.insert(pool.mapTx.find(tx6.GetHash())); @@ -201,7 +202,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.erase(sortedOrder.begin()); sortedOrder.push_back(tx6.GetHash().ToString()); sortedOrder.push_back(tx7.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx8 = CMutableTransaction(); @@ -216,7 +217,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now tx8 should be sorted low, but tx6/tx both high sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx9 = CMutableTransaction(); @@ -231,7 +232,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // tx9 should be sorted low BOOST_CHECK_EQUAL(pool.size(), 9); sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); std::vector<std::string> snapshotOrder = sortedOrder; @@ -273,7 +274,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6 - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); // there should be 10 transactions in the mempool BOOST_CHECK_EQUAL(pool.size(), 10); @@ -281,9 +282,42 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now try removing tx10 and verify the sort order returns to normal std::list<CTransaction> removed; pool.remove(pool.mapTx.find(tx10.GetHash())->GetTx(), removed, true); - CheckSort(pool, snapshotOrder); + CheckSort<1>(pool, snapshotOrder); + + pool.remove(pool.mapTx.find(tx9.GetHash())->GetTx(), removed, true); + pool.remove(pool.mapTx.find(tx8.GetHash())->GetTx(), removed, true); + /* Now check the sort on the mining score index. + * Final order should be: + * + * tx7 (2M) + * tx2 (20k) + * tx4 (15000) + * tx1/tx5 (10000) + * tx3/6 (0) + * (Ties resolved by hash) + */ + sortedOrder.clear(); + sortedOrder.push_back(tx7.GetHash().ToString()); + sortedOrder.push_back(tx2.GetHash().ToString()); + sortedOrder.push_back(tx4.GetHash().ToString()); + if (tx1.GetHash() < tx5.GetHash()) { + sortedOrder.push_back(tx5.GetHash().ToString()); + sortedOrder.push_back(tx1.GetHash().ToString()); + } else { + sortedOrder.push_back(tx1.GetHash().ToString()); + sortedOrder.push_back(tx5.GetHash().ToString()); + } + if (tx3.GetHash() < tx6.GetHash()) { + sortedOrder.push_back(tx6.GetHash().ToString()); + sortedOrder.push_back(tx3.GetHash().ToString()); + } else { + sortedOrder.push_back(tx3.GetHash().ToString()); + sortedOrder.push_back(tx6.GetHash().ToString()); + } + CheckSort<3>(pool, sortedOrder); } + BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) { CTxMemPool pool(CFeeRate(1000)); 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..19ddb5b79c 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)); @@ -118,7 +119,23 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase + // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails + mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + tx.vin[0].prevout.hash = hash; + } + BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + mempool.clear(); + + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vout[0].nValue = 5000000000LL; + for (unsigned int i = 0; i < 1001; ++i) + { + tx.vout[0].nValue -= 1000000; + hash = tx.GetHash(); + bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase + // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes + mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); @@ -138,18 +155,18 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { tx.vout[0].nValue -= 10000000; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase + mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); - // orphan in mempool + // orphan in mempool, template creation fails hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).FromTx(tx)); + BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); // child with higher priority than parent @@ -157,7 +174,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[1]->GetHash(); tx.vout[0].nValue = 4900000000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(100000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; @@ -165,23 +182,23 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].prevout.n = 0; tx.vout[0].nValue = 5900000000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(400000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); - // coinbase in mempool + // coinbase in mempool, template creation fails tx.vin.resize(1); tx.vin[0].prevout.SetNull(); tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + // give it a fee so it'll get mined + mempool.addUnchecked(hash, entry.Fee(100000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); - // invalid (pre-p2sh) txn in mempool + // invalid (pre-p2sh) txn in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vin[0].scriptSig = CScript() << OP_1; @@ -189,28 +206,26 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(10000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; - tx.vin[0].scriptSig = CScript() << (std::vector<unsigned char>)script; + tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end()); tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); - // double spend txn pair in mempool + // double spend txn pair in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; tx.vout[0].nValue = 4900000000LL; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); // subsidy changing @@ -234,7 +249,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = chainActive.Tip()->nHeight+1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST)); // time locked @@ -248,7 +263,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx2.vout[0].scriptPubKey = CScript() << OP_1; tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1; hash = tx2.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx2)); + mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST)); BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); 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 1315146f10..644c3da213 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) // 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(), CTxMemPoolEntry(tx, feeV[0][5], GetTime(), priV[1][5], blocknum, mpool.HasNoInputsOf(tx))); + 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]); diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp new file mode 100644 index 0000000000..01a45b540d --- /dev/null +++ b/src/test/prevector_tests.cpp @@ -0,0 +1,217 @@ +// 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 <vector> +#include "prevector.h" +#include "random.h" + +#include "serialize.h" +#include "streams.h" + +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(PrevectorTests, TestingSetup) + +template<unsigned int N, typename T> +class prevector_tester { + typedef std::vector<T> realtype; + realtype real_vector; + + typedef prevector<N, T> pretype; + pretype pre_vector; + + typedef typename pretype::size_type Size; + + void test() { + const pretype& const_pre_vector = pre_vector; + BOOST_CHECK_EQUAL(real_vector.size(), pre_vector.size()); + BOOST_CHECK_EQUAL(real_vector.empty(), pre_vector.empty()); + for (Size s = 0; s < real_vector.size(); s++) { + BOOST_CHECK(real_vector[s] == pre_vector[s]); + BOOST_CHECK(&(pre_vector[s]) == &(pre_vector.begin()[s])); + BOOST_CHECK(&(pre_vector[s]) == &*(pre_vector.begin() + s)); + BOOST_CHECK(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size())); + } + // BOOST_CHECK(realtype(pre_vector) == real_vector); + BOOST_CHECK(pretype(real_vector.begin(), real_vector.end()) == pre_vector); + BOOST_CHECK(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector); + size_t pos = 0; + BOOST_FOREACH(const T& v, pre_vector) { + BOOST_CHECK(v == real_vector[pos++]); + } + BOOST_REVERSE_FOREACH(const T& v, pre_vector) { + BOOST_CHECK(v == real_vector[--pos]); + } + BOOST_FOREACH(const T& v, const_pre_vector) { + BOOST_CHECK(v == real_vector[pos++]); + } + BOOST_REVERSE_FOREACH(const T& v, const_pre_vector) { + BOOST_CHECK(v == real_vector[--pos]); + } + CDataStream ss1(SER_DISK, 0); + CDataStream ss2(SER_DISK, 0); + ss1 << real_vector; + ss2 << pre_vector; + BOOST_CHECK_EQUAL(ss1.size(), ss2.size()); + for (Size s = 0; s < ss1.size(); s++) { + BOOST_CHECK_EQUAL(ss1[s], ss2[s]); + } + } + +public: + void resize(Size s) { + real_vector.resize(s); + BOOST_CHECK_EQUAL(real_vector.size(), s); + pre_vector.resize(s); + BOOST_CHECK_EQUAL(pre_vector.size(), s); + test(); + } + + void reserve(Size s) { + real_vector.reserve(s); + BOOST_CHECK(real_vector.capacity() >= s); + pre_vector.reserve(s); + BOOST_CHECK(pre_vector.capacity() >= s); + test(); + } + + void insert(Size position, const T& value) { + real_vector.insert(real_vector.begin() + position, value); + pre_vector.insert(pre_vector.begin() + position, value); + test(); + } + + void insert(Size position, Size count, const T& value) { + real_vector.insert(real_vector.begin() + position, count, value); + pre_vector.insert(pre_vector.begin() + position, count, value); + test(); + } + + template<typename I> + void insert_range(Size position, I first, I last) { + real_vector.insert(real_vector.begin() + position, first, last); + pre_vector.insert(pre_vector.begin() + position, first, last); + test(); + } + + void erase(Size position) { + real_vector.erase(real_vector.begin() + position); + pre_vector.erase(pre_vector.begin() + position); + test(); + } + + void erase(Size first, Size last) { + real_vector.erase(real_vector.begin() + first, real_vector.begin() + last); + pre_vector.erase(pre_vector.begin() + first, pre_vector.begin() + last); + test(); + } + + void update(Size pos, const T& value) { + real_vector[pos] = value; + pre_vector[pos] = value; + test(); + } + + void push_back(const T& value) { + real_vector.push_back(value); + pre_vector.push_back(value); + test(); + } + + void pop_back() { + real_vector.pop_back(); + pre_vector.pop_back(); + test(); + } + + void clear() { + real_vector.clear(); + pre_vector.clear(); + } + + void assign(Size n, const T& value) { + real_vector.assign(n, value); + pre_vector.assign(n, value); + } + + Size size() { + return real_vector.size(); + } + + Size capacity() { + return pre_vector.capacity(); + } + + void shrink_to_fit() { + pre_vector.shrink_to_fit(); + test(); + } +}; + +BOOST_AUTO_TEST_CASE(PrevectorTestInt) +{ + for (int j = 0; j < 64; j++) { + prevector_tester<8, int> test; + for (int i = 0; i < 2048; i++) { + int r = insecure_rand(); + if ((r % 4) == 0) { + test.insert(insecure_rand() % (test.size() + 1), insecure_rand()); + } + if (test.size() > 0 && ((r >> 2) % 4) == 1) { + test.erase(insecure_rand() % test.size()); + } + if (((r >> 4) % 8) == 2) { + int new_size = std::max<int>(0, std::min<int>(30, test.size() + (insecure_rand() % 5) - 2)); + test.resize(new_size); + } + if (((r >> 7) % 8) == 3) { + test.insert(insecure_rand() % (test.size() + 1), 1 + (insecure_rand() % 2), insecure_rand()); + } + if (((r >> 10) % 8) == 4) { + int del = std::min<int>(test.size(), 1 + (insecure_rand() % 2)); + int beg = insecure_rand() % (test.size() + 1 - del); + test.erase(beg, beg + del); + } + if (((r >> 13) % 16) == 5) { + test.push_back(insecure_rand()); + } + if (test.size() > 0 && ((r >> 17) % 16) == 6) { + test.pop_back(); + } + if (((r >> 21) % 32) == 7) { + int values[4]; + int num = 1 + (insecure_rand() % 4); + for (int i = 0; i < num; i++) { + values[i] = insecure_rand(); + } + test.insert_range(insecure_rand() % (test.size() + 1), values, values + num); + } + if (((r >> 26) % 32) == 8) { + int del = std::min<int>(test.size(), 1 + (insecure_rand() % 4)); + int beg = insecure_rand() % (test.size() + 1 - del); + test.erase(beg, beg + del); + } + r = insecure_rand(); + if (r % 32 == 9) { + test.reserve(insecure_rand() % 32); + } + if ((r >> 5) % 64 == 10) { + test.shrink_to_fit(); + } + if (test.size() > 0) { + test.update(insecure_rand() % test.size(), insecure_rand()); + } + if (((r >> 11) & 1024) == 11) { + test.clear(); + } + if (((r >> 21) & 512) == 12) { + test.assign(insecure_rand() % 32, insecure_rand()); + } + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 2a486f08e4..ce22975005 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -72,6 +72,7 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error); string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx)); + BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").get_int(), 193); BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1); BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0); BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error); diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index cb1a427db0..fc07aa72c1 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -40,6 +40,7 @@ static void MicroSleep(uint64_t n) #endif } +#if 0 /* Disabled for now because there is a race condition issue in this test - see #6540 */ BOOST_AUTO_TEST_CASE(manythreads) { seed_insecure_rand(false); @@ -115,5 +116,6 @@ BOOST_AUTO_TEST_CASE(manythreads) } BOOST_CHECK_EQUAL(counterSum, 200); } +#endif BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 16c9a4a868..e36aca8dfa 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -25,7 +25,7 @@ using namespace std; static std::vector<unsigned char> Serialize(const CScript& s) { - std::vector<unsigned char> sSerialized(s); + std::vector<unsigned char> sSerialized(s.begin(), s.end()); return sSerialized; } @@ -339,8 +339,8 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) // SignSignature doesn't know how to sign these. We're // not testing validating signatures, so just create // dummy signatures that DO include the correct P2SH scripts: - txTo.vin[3].scriptSig << OP_11 << OP_11 << static_cast<vector<unsigned char> >(oneAndTwo); - txTo.vin[4].scriptSig << static_cast<vector<unsigned char> >(fifteenSigops); + txTo.vin[3].scriptSig << OP_11 << OP_11 << vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end()); + txTo.vin[4].scriptSig << vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end()); BOOST_CHECK(::AreInputsStandard(txTo, coins)); // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4] @@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd1.vin.resize(1); txToNonStd1.vin[0].prevout.n = 5; txToNonStd1.vin[0].prevout.hash = txFrom.GetHash(); - txToNonStd1.vin[0].scriptSig << static_cast<vector<unsigned char> >(sixteenSigops); + txToNonStd1.vin[0].scriptSig << vector<unsigned char>(sixteenSigops.begin(), sixteenSigops.end()); BOOST_CHECK(!::AreInputsStandard(txToNonStd1, coins)); BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd1, coins), 16U); @@ -374,7 +374,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd2.vin.resize(1); txToNonStd2.vin[0].prevout.n = 6; txToNonStd2.vin[0].prevout.hash = txFrom.GetHash(); - txToNonStd2.vin[0].scriptSig << static_cast<vector<unsigned char> >(twentySigops); + txToNonStd2.vin[0].scriptSig << vector<unsigned char>(twentySigops.begin(), twentySigops.end()); BOOST_CHECK(!::AreInputsStandard(txToNonStd2, coins)); BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd2, coins), 20U); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 882f9eb199..0059e4a998 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -260,7 +260,7 @@ public: TestBuilder& PushRedeem() { - DoPush(static_cast<std::vector<unsigned char> >(scriptPubKey)); + DoPush(std::vector<unsigned char>(scriptPubKey.begin(), scriptPubKey.end())); return *this; } @@ -892,7 +892,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); // dummy scriptSigCopy with placeholder, should always choose non-placeholder: - scriptSigCopy = CScript() << OP_0 << static_cast<vector<unsigned char> >(pkSingle); + scriptSigCopy = CScript() << OP_0 << vector<unsigned char>(pkSingle.begin(), pkSingle.end()); combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); BOOST_CHECK(combined == scriptSig); combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, scriptSigCopy); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index b26fed99f2..ea2b9b795f 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -20,7 +20,7 @@ using namespace std; static std::vector<unsigned char> Serialize(const CScript& s) { - std::vector<unsigned char> sSerialized(s); + std::vector<unsigned char> sSerialized(s.begin(), s.end()); return sSerialized; } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 2fe190f885..2147dbb065 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -144,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, spendsCoinbase, sigOpCount); } void Shutdown(void* parg) diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 815b227411..273bfdd7f4 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -65,11 +65,13 @@ struct TestMemPoolEntryHelper double dPriority; unsigned int nHeight; bool hadNoDependencies; - + bool spendsCoinbase; + unsigned int sigOpCount; + TestMemPoolEntryHelper() : nFee(0), nTime(0), dPriority(0.0), nHeight(1), - hadNoDependencies(false) { } - + hadNoDependencies(false), spendsCoinbase(false), sigOpCount(1) { } + CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL); // Change the default value @@ -78,5 +80,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; } TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; } + TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } + TestMemPoolEntryHelper &SigOps(unsigned int _sigops) { sigOpCount = _sigops; return *this; } }; #endif diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 31a2917203..8eccc81e30 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -617,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 ec7971c2f1..fea5da8029 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -11,6 +11,7 @@ #include "main.h" #include "policy/fees.h" #include "streams.h" +#include "timedata.h" #include "util.h" #include "utilmoneystr.h" #include "utiltime.h" @@ -19,10 +20,12 @@ 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, + bool _spendsCoinbase, unsigned int _sigOps): + tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), + hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), + spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); nModSize = tx.CalculateModifiedSize(nTxSize); @@ -31,6 +34,10 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; nFeesWithDescendants = nFee; + CAmount nValueIn = tx.GetValueOut()+nFee; + assert(inChainInputValue <= nValueIn); + + feeDelta = 0; } CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) @@ -41,12 +48,18 @@ 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; } +void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) +{ + feeDelta = newFeeDelta; +} + // Update the given tx for any in-mempool descendants. // Assumes that setMemPoolChildren is correct for the given tx and all // descendants. @@ -386,6 +399,15 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, } UpdateAncestorsOf(true, newit, setAncestors); + // Update transaction's score for any feeDelta created by PrioritiseTransaction + std::map<uint256, std::pair<double, CAmount> >::const_iterator pos = mapDeltas.find(hash); + if (pos != mapDeltas.end()) { + const std::pair<double, CAmount> &deltas = pos->second; + if (deltas.second) { + mapTx.modify(newit, update_fee_delta(deltas.second)); + } + } + nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); minerPolicyEstimator->processTransaction(entry, fCurrentEstimate); @@ -475,22 +497,26 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem } } -void CTxMemPool::removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight) +void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) { - // Remove transactions spending a coinbase which are now immature + // Remove transactions spending a coinbase which are now immature and no-longer-final transactions LOCK(cs); list<CTransaction> transactionsToRemove; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { const CTransaction& tx = it->GetTx(); - BOOST_FOREACH(const CTxIn& txin, tx.vin) { - indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); - if (it2 != mapTx.end()) - continue; - const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash); - if (nCheckFrequency != 0) assert(coins); - if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) { - transactionsToRemove.push_back(tx); - break; + if (!CheckFinalTx(tx, flags)) { + transactionsToRemove.push_back(tx); + } else if (it->GetSpendsCoinbase()) { + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); + if (it2 != mapTx.end()) + continue; + const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash); + if (nCheckFrequency != 0) assert(coins); + if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) { + transactionsToRemove.push_back(tx); + break; + } } } } @@ -759,6 +785,10 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, std::pair<double, CAmount> &deltas = mapDeltas[hash]; deltas.first += dPriorityDelta; deltas.second += nFeeDelta; + txiter it = mapTx.find(hash); + if (it != mapTx.end()) { + mapTx.modify(it, update_fee_delta(deltas.second)); + } } LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta)); } @@ -808,8 +838,8 @@ bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const { size_t CTxMemPool::DynamicMemoryUsage() const { LOCK(cs); - // Estimate the overhead of mapTx to be 9 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented. - return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 9 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + cachedInnerUsage; + // Estimate the overhead of mapTx to be 12 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented. + return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + cachedInnerUsage; } void CTxMemPool::RemoveStaged(setEntries &stage) { @@ -914,7 +944,7 @@ void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { } } -void CTxMemPool::TrimToSize(size_t sizelimit) { +void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining) { LOCK(cs); unsigned nTxnRemoved = 0; @@ -933,8 +963,26 @@ void CTxMemPool::TrimToSize(size_t sizelimit) { setEntries stage; CalculateDescendants(mapTx.project<0>(it), stage); - RemoveStaged(stage); nTxnRemoved += stage.size(); + + std::vector<CTransaction> txn; + if (pvNoSpendsRemaining) { + txn.reserve(stage.size()); + BOOST_FOREACH(txiter it, stage) + txn.push_back(it->GetTx()); + } + RemoveStaged(stage); + if (pvNoSpendsRemaining) { + BOOST_FOREACH(const CTransaction& tx, txn) { + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + if (exists(txin.prevout.hash)) + continue; + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0)); + if (it == mapNextTx.end() || it->first.hash != txin.prevout.hash) + pvNoSpendsRemaining->push_back(txin.prevout.hash); + } + } + } } if (maxFeeRateRemoved > CFeeRate(0)) diff --git a/src/txmempool.h b/src/txmempool.h index 7f43120f7f..9203171868 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -63,9 +63,13 @@ 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 + bool spendsCoinbase; //! keep track of transactions that spend a coinbase + unsigned int sigOpCount; //! Legacy sig ops plus P2SH sig op count + int64_t feeDelta; //! Used for determining the priority of the transaction for mining in a block // Information about descendants of this transaction that are in the // mempool; if we remove this transaction we must remove all of these @@ -78,20 +82,30 @@ 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, bool spendsCoinbase, + unsigned int nSigOps); 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; } + unsigned int GetSigOpCount() const { return sigOpCount; } + int64_t GetModifiedFee() const { return nFee + feeDelta; } size_t DynamicMemoryUsage() const { return nUsageSize; } // Adjusts the descendant state, if this entry is not dirty. void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); + // Updates the fee delta used for mining priority score + void UpdateFeeDelta(int64_t feeDelta); /** We can set the entry to be dirty if doing the full calculation of in- * mempool descendants will be too expensive, which can potentially happen @@ -103,6 +117,8 @@ public: uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } CAmount GetFeesWithDescendants() const { return nFeesWithDescendants; } + + bool GetSpendsCoinbase() const { return spendsCoinbase; } }; // Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index. @@ -127,6 +143,16 @@ struct set_dirty { e.SetDirty(); } }; +struct update_fee_delta +{ + update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { } + + void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); } + +private: + int64_t feeDelta; +}; + // extracts a TxMemPoolEntry's transaction hash struct mempoolentry_txid { @@ -174,6 +200,24 @@ public: } }; +/** \class CompareTxMemPoolEntryByScore + * + * Sort by score of entry ((fee+delta)/size) in descending order + */ +class CompareTxMemPoolEntryByScore +{ +public: + bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) + { + double f1 = (double)a.GetModifiedFee() * b.GetTxSize(); + double f2 = (double)b.GetModifiedFee() * a.GetTxSize(); + if (f1 == f2) { + return b.GetTx().GetHash() < a.GetTx().GetHash(); + } + return f1 > f2; + } +}; + class CompareTxMemPoolEntryByEntryTime { public: @@ -211,10 +255,11 @@ public: * * CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping: * - * mapTx is a boost::multi_index that sorts the mempool on 3 criteria: + * mapTx is a boost::multi_index that sorts the mempool on 4 criteria: * - transaction hash * - feerate [we use max(feerate of tx, feerate of tx with all descendants)] * - time in mempool + * - mining score (feerate modified by any fee deltas from PrioritiseTransaction) * * Note: the term "descendant" refers to in-mempool transactions that depend on * this one, while "ancestor" refers to in-mempool transactions that a given @@ -311,6 +356,11 @@ public: boost::multi_index::ordered_non_unique< boost::multi_index::identity<CTxMemPoolEntry>, CompareTxMemPoolEntryByEntryTime + >, + // sorted by score (for mining prioritization) + boost::multi_index::ordered_unique< + boost::multi_index::identity<CTxMemPoolEntry>, + CompareTxMemPoolEntryByScore > > > indexed_transaction_set; @@ -325,6 +375,8 @@ public: }; typedef std::set<txiter, CompareIteratorByHash> setEntries; + const setEntries & GetMemPoolParents(txiter entry) const; + const setEntries & GetMemPoolChildren(txiter entry) const; private: typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap; @@ -336,8 +388,6 @@ private: typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap; txlinksMap mapLinks; - const setEntries & GetMemPoolParents(txiter entry) const; - const setEntries & GetMemPoolChildren(txiter entry) const; void UpdateParent(txiter entry, txiter parent, bool add); void UpdateChild(txiter entry, txiter child, bool add); @@ -370,7 +420,7 @@ public: bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true); void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false); - void removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight); + void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed); void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight, std::list<CTransaction>& conflicts, bool fCurrentEstimate = true); @@ -433,8 +483,11 @@ public: */ CFeeRate GetMinFee(size_t sizelimit) const; - /** Remove transactions from the mempool until its dynamic size is <= sizelimit. */ - void TrimToSize(size_t sizelimit); + /** Remove transactions from the mempool until its dynamic size is <= sizelimit. + * pvNoSpendsRemaining, if set, will be populated with the list of transactions + * which are not in mempool which no longer have any spends in this mempool. + */ + void TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining=NULL); /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */ int Expire(int64_t time); @@ -537,4 +590,17 @@ public: bool HaveCoins(const uint256 &txid) const; }; +// We want to sort transactions by coin age priority +typedef std::pair<double, CTxMemPool::txiter> TxCoinAgePriority; + +struct TxCoinAgePriorityCompare +{ + bool operator()(const TxCoinAgePriority& a, const TxCoinAgePriority& b) + { + if (a.first == b.first) + return CompareTxMemPoolEntryByScore()(*(b.second), *(a.second)); //Reverse order to make sort less than + return a.first < b.first; + } +}; + #endif // BITCOIN_TXMEMPOOL_H 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/util.cpp b/src/util.cpp index a852bc3176..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; @@ -520,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; @@ -554,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/utiltime.cpp b/src/utiltime.cpp index 3202c47f1d..7d9f6210eb 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -20,7 +20,9 @@ int64_t GetTime() { if (nMockTime) return nMockTime; - return time(NULL); + time_t now = time(NULL); + assert(now > 0); + return now; } void SetMockTime(int64_t nMockTimeIn) @@ -30,14 +32,18 @@ void SetMockTime(int64_t nMockTimeIn) int64_t GetTimeMillis() { - return (boost::posix_time::microsec_clock::universal_time() - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); + int64_t now = (boost::posix_time::microsec_clock::universal_time() - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); + assert(now > 0); + return now; } int64_t GetTimeMicros() { - return (boost::posix_time::microsec_clock::universal_time() - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); + int64_t now = (boost::posix_time::microsec_clock::universal_time() - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); + assert(now > 0); + return now; } /** Return a time useful for the debug log */ 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/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b6eaca80b3..db60e498dd 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -65,6 +65,8 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); entry.push_back(Pair("blockindex", wtx.nIndex)); entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); + } else { + entry.push_back(Pair("trusted", wtx.IsTrusted())); } uint256 hash = wtx.GetHash(); entry.push_back(Pair("txid", hash.GetHex())); @@ -1421,7 +1423,9 @@ UniValue listtransactions(const UniValue& params, bool fHelp) " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" - " 'receive' category of transactions.\n" + " 'receive' category of transactions. Negative confirmations indicate the\n" + " transation conflicts with the block chain\n" + " \"trusted\": xxx (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" " category of transactions.\n" " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" @@ -2174,7 +2178,7 @@ UniValue settxfee(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( "settxfee amount\n" - "\nSet the transaction fee per kB.\n" + "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n" "\nArguments:\n" "1. amount (numeric, required) The transaction fee in " + CURRENCY_UNIT + "/kB rounded to the nearest 0.00000001\n" "\nResult\n" diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c5246d909e..30b9869be0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -39,9 +39,8 @@ 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; /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) @@ -608,6 +607,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD wtx.BindWallet(this); wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); AddToSpends(hash); + BOOST_FOREACH(const CTxIn& txin, wtx.vin) { + if (mapWallet.count(txin.prevout.hash)) { + CWalletTx& prevtx = mapWallet[txin.prevout.hash]; + if (prevtx.nIndex == -1 && !prevtx.hashBlock.IsNull()) { + MarkConflicted(prevtx.hashBlock, wtx.GetHash()); + } + } + } } else { @@ -727,6 +734,20 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl { { AssertLockHeld(cs_wallet); + + if (pblock) { + BOOST_FOREACH(const CTxIn& txin, tx.vin) { + std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout); + while (range.first != range.second) { + if (range.first->second != tx.GetHash()) { + LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pblock->GetHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); + MarkConflicted(pblock->GetHash(), range.first->second); + } + range.first++; + } + } + } + bool fExisted = mapWallet.count(tx.GetHash()) != 0; if (fExisted && !fUpdate) return false; if (fExisted || IsMine(tx) || IsFromMe(tx)) @@ -747,9 +768,57 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl return false; } +void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) +{ + LOCK2(cs_main, cs_wallet); + + CBlockIndex* pindex; + assert(mapBlockIndex.count(hashBlock)); + pindex = mapBlockIndex[hashBlock]; + int conflictconfirms = 0; + if (chainActive.Contains(pindex)) { + conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); + } + assert(conflictconfirms < 0); + + // Do not flush the wallet here for performance reasons + CWalletDB walletdb(strWalletFile, "r+", false); + + std::deque<uint256> todo; + std::set<uint256> done; + + todo.push_back(hashTx); + + while (!todo.empty()) { + uint256 now = todo.front(); + todo.pop_front(); + done.insert(now); + assert(mapWallet.count(now)); + CWalletTx& wtx = mapWallet[now]; + int currentconfirm = wtx.GetDepthInMainChain(); + if (conflictconfirms < currentconfirm) { + // Block is 'more conflicted' than current confirm; update. + // Mark transaction as conflicted with this block. + wtx.nIndex = -1; + wtx.hashBlock = hashBlock; + wtx.MarkDirty(); + wtx.WriteToDisk(&walletdb); + // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too + TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); + while (iter != mapTxSpends.end() && iter->first.hash == now) { + if (!done.count(iter->second)) { + todo.push_back(iter->second); + } + iter++; + } + } + } +} + void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) { LOCK2(cs_main, cs_wallet); + if (!AddToWalletIfInvolvingMe(tx, pblock, true)) return; // Not one of ours @@ -1089,7 +1158,7 @@ void CWallet::ReacceptWalletTransactions() int nDepth = wtx.GetDepthInMainChain(); - if (!wtx.IsCoinBase() && nDepth < 0) { + if (!wtx.IsCoinBase() && nDepth == 0) { mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); } } @@ -1303,6 +1372,14 @@ bool CWalletTx::IsTrusted() const if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit return false; + // Don't trust unconfirmed transactions from us unless they are in the mempool. + { + LOCK(mempool.cs); + if (!mempool.exists(GetHash())) { + return false; + } + } + // Trusted if all inputs are from us and are in the mempool: BOOST_FOREACH(const CTxIn& txin, vin) { @@ -1879,6 +1956,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt //a chance at a free transaction. //But mempool inputs might still be in the mempool, so their age stays 0 int age = pcoin.first->GetDepthInMainChain(); + assert(age >= 0); if (age != 0) age += 1; dPriority += (double)nCredit * age; @@ -2017,6 +2095,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt } CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -2112,9 +2193,6 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge { // payTxFee is user-set "I want to pay this much" CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); - // user selected total at least (default=true) - if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK()) - nFeeNeeded = payTxFee.GetFeePerK(); // User didn't set: use -txconfirmtarget to estimate... if (nFeeNeeded == 0) { int estimateFoundTarget = nConfirmTarget; @@ -2260,7 +2338,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; @@ -2287,7 +2365,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)) { @@ -2814,9 +2892,9 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block) return chainActive.Height() - pindex->nHeight + 1; } -int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const +int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const { - if (hashBlock.IsNull() || nIndex == -1) + if (hashBlock.IsNull()) return 0; AssertLockHeld(cs_main); @@ -2829,17 +2907,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const return 0; pindexRet = pindex; - return chainActive.Height() - pindex->nHeight + 1; -} - -int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const -{ - AssertLockHeld(cs_main); - int nResult = GetDepthInMainChainINTERNAL(pindexRet); - if (nResult == 0 && !mempool.exists(GetHash())) - return -1; // Not in chain, not in mempool - - return nResult; + return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); } int CMerkleTx::GetBlocksToMaturity() const diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7e846569ff..859788893c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -35,8 +35,8 @@ extern CAmount maxTxFee; extern unsigned int nTxConfirmTarget; 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 +47,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; @@ -150,11 +155,14 @@ struct COutputEntry /** A transaction with a merkle branch linking it to the block chain. */ class CMerkleTx : public CTransaction { -private: - int GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const; - public: uint256 hashBlock; + + /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest + * block in the chain we know this or any in-wallet dependency conflicts + * with. Older clients interpret nIndex == -1 as unconfirmed for backward + * compatibility. + */ int nIndex; CMerkleTx() @@ -187,16 +195,15 @@ public: int SetMerkleBranch(const CBlock& block); - /** * Return depth of transaction in blockchain: - * -1 : not in blockchain, and not in memory pool (conflicted transaction) + * <0 : conflicts with a transaction this deep in the blockchain * 0 : in memory pool, waiting to be included in a block * >=1 : this many blocks deep in the main chain */ int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } - bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; } + bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; } int GetBlocksToMaturity() const; bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectAbsurdFee=true); }; @@ -475,6 +482,10 @@ private: void AddToSpends(const COutPoint& outpoint, const uint256& wtxid); void AddToSpends(const uint256& wtxid); + /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ + void MarkConflicted(const uint256& hashBlock, const uint256& hashTx); + + void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>); public: diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 9ce9f53bd9..e2e827d816 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -113,19 +113,19 @@ bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); + return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); } bool CWalletDB::WriteWatchOnly(const CScript &dest) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("watchs"), dest), '1'); + return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); } bool CWalletDB::EraseWatchOnly(const CScript &dest) { nWalletDBUpdated++; - return Erase(std::make_pair(std::string("watchs"), dest)); + return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) @@ -421,7 +421,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, else if (strType == "watchs") { CScript script; - ssKey >> script; + ssKey >> *(CScriptBase*)(&script); char fYes; ssValue >> fYes; if (fYes == '1') @@ -575,7 +575,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, uint160 hash; ssKey >> hash; CScript script; - ssValue >> script; + ssValue >> *(CScriptBase*)(&script); if (!pwallet->LoadCScript(script)) { strErr = "Error reading wallet database: LoadCScript failed"; @@ -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; |