aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml9
-rwxr-xr-xci/test/00_setup_env_native_nowallet.sh6
-rwxr-xr-xci/test/00_setup_env_native_qt5.sh6
-rw-r--r--doc/dependencies.md4
-rw-r--r--doc/developer-notes.md32
-rw-r--r--doc/release-notes-23065.md15
-rw-r--r--doc/release-notes.md18
-rw-r--r--src/addrdb.cpp2
-rw-r--r--src/bech32.cpp34
-rw-r--r--src/bench/addrman.cpp38
-rw-r--r--src/bench/bech32.cpp1
-rw-r--r--src/bench/bench.cpp17
-rw-r--r--src/bench/bench.h3
-rw-r--r--src/bench/bench_bitcoin.cpp64
-rw-r--r--src/bench/crypto_hash.cpp8
-rw-r--r--src/bench/nanobench.h43
-rw-r--r--src/bench/peer_eviction.cpp10
-rw-r--r--src/bench/rollingbloom.cpp16
-rw-r--r--src/bitcoin-cli.cpp6
-rw-r--r--src/bitcoin-tx.cpp2
-rw-r--r--src/bloom.cpp59
-rw-r--r--src/bloom.h20
-rw-r--r--src/chainparams.cpp49
-rw-r--r--src/chainparamsbase.cpp2
-rw-r--r--src/core_io.h6
-rw-r--r--src/core_write.cpp46
-rw-r--r--src/crc32c/.travis.yml18
-rw-r--r--src/crc32c/.ycm_extra_conf.py4
-rw-r--r--src/crc32c/README.md2
-rw-r--r--src/crc32c/src/crc32c_arm64_check.h10
-rw-r--r--src/hash.cpp1
-rw-r--r--src/httpserver.cpp8
-rw-r--r--src/init.cpp54
-rw-r--r--src/interfaces/wallet.h4
-rw-r--r--src/miner.cpp4
-rw-r--r--src/net.cpp2
-rw-r--r--src/net_processing.cpp6
-rw-r--r--src/node/interfaces.cpp14
-rw-r--r--src/qt/addressbookpage.cpp8
-rw-r--r--src/qt/bitcoin.cpp19
-rw-r--r--src/qt/bitcoin.h5
-rw-r--r--src/qt/bitcoingui.cpp21
-rw-r--r--src/qt/bitcoingui.h1
-rw-r--r--src/qt/coincontroldialog.cpp18
-rw-r--r--src/qt/coincontroldialog.h4
-rw-r--r--src/qt/forms/optionsdialog.ui20
-rw-r--r--src/qt/guiutil.cpp22
-rw-r--r--src/qt/guiutil.h6
-rw-r--r--src/qt/initexecutor.cpp49
-rw-r--r--src/qt/initexecutor.h1
-rw-r--r--src/qt/intro.cpp4
-rw-r--r--src/qt/optionsdialog.cpp5
-rw-r--r--src/qt/optionsdialog.h1
-rw-r--r--src/qt/optionsmodel.cpp15
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/qt/psbtoperationsdialog.cpp7
-rw-r--r--src/qt/rpcconsole.cpp14
-rw-r--r--src/qt/sendcoinsdialog.cpp13
-rw-r--r--src/qt/test/test_main.cpp1
-rw-r--r--src/qt/transactionview.cpp28
-rw-r--r--src/qt/walletframe.cpp5
-rw-r--r--src/qt/walletmodel.cpp7
-rw-r--r--src/qt/walletview.cpp24
-rw-r--r--src/rpc/blockchain.cpp19
-rw-r--r--src/rpc/blockchain.h4
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp7
-rw-r--r--src/rpc/rawtransaction.cpp44
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/script/sigcache.cpp2
-rw-r--r--src/script/standard.cpp41
-rw-r--r--src/script/standard.h18
-rw-r--r--src/test/bloom_tests.cpp2
-rw-r--r--src/test/fuzz/muhash.cpp73
-rw-r--r--src/test/fuzz/script.cpp62
-rw-r--r--src/test/fuzz/system.cpp12
-rw-r--r--src/test/fuzz/transaction.cpp4
-rw-r--r--src/test/getarg_tests.cpp18
-rw-r--r--src/test/script_standard_tests.cpp61
-rw-r--r--src/test/txvalidationcache_tests.cpp9
-rw-r--r--src/test/util/setup_common.cpp8
-rw-r--r--src/test/util/setup_common.h6
-rw-r--r--src/test/util_tests.cpp14
-rw-r--r--src/timedata.cpp2
-rw-r--r--src/txdb.cpp4
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/util/system.h2
-rw-r--r--src/validation.cpp26
-rw-r--r--src/wallet/bdb.cpp2
-rw-r--r--src/wallet/coinselection.cpp26
-rw-r--r--src/wallet/coinselection.h15
-rw-r--r--src/wallet/interfaces.cpp10
-rw-r--r--src/wallet/rpcdump.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp37
-rw-r--r--src/wallet/scriptpubkeyman.cpp6
-rw-r--r--src/wallet/spend.cpp9
-rw-r--r--src/wallet/sqlite.cpp4
-rw-r--r--src/wallet/test/coinselector_tests.cpp19
-rw-r--r--src/wallet/wallet.cpp110
-rw-r--r--src/wallet/wallet.h21
-rw-r--r--src/wallet/walletdb.cpp17
-rw-r--r--src/wallet/walletdb.h4
-rw-r--r--src/zmq/zmqnotificationinterface.cpp2
-rwxr-xr-xtest/functional/feature_addrman.py3
-rwxr-xr-xtest/functional/feature_asmap.py6
-rwxr-xr-xtest/functional/feature_bip68_sequence.py10
-rwxr-xr-xtest/functional/feature_block.py5
-rwxr-xr-xtest/functional/feature_cltv.py5
-rwxr-xr-xtest/functional/feature_csv_activation.py5
-rwxr-xr-xtest/functional/feature_dersig.py10
-rwxr-xr-xtest/functional/feature_fee_estimation.py10
-rwxr-xr-xtest/functional/feature_nulldummy.py2
-rwxr-xr-xtest/functional/feature_presegwit_node_upgrade.py6
-rwxr-xr-xtest/functional/feature_rbf.py18
-rwxr-xr-xtest/functional/feature_segwit.py63
-rwxr-xr-xtest/functional/mempool_persist.py62
-rwxr-xr-xtest/functional/p2p_filter.py34
-rwxr-xr-xtest/functional/p2p_segwit.py30
-rwxr-xr-xtest/functional/rpc_addresses_deprecation.py56
-rwxr-xr-xtest/functional/rpc_blockchain.py34
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py78
-rwxr-xr-xtest/functional/rpc_psbt.py10
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py3
-rw-r--r--test/functional/test_framework/authproxy.py4
-rw-r--r--test/functional/test_framework/blocktools.py5
-rwxr-xr-xtest/functional/test_framework/script_util.py57
-rwxr-xr-xtest/functional/test_framework/test_framework.py25
-rw-r--r--test/functional/test_framework/util.py11
-rw-r--r--test/functional/test_framework/wallet.py35
-rwxr-xr-xtest/functional/test_runner.py7
-rwxr-xr-xtest/functional/wallet_basic.py40
-rwxr-xr-xtest/functional/wallet_transactiontime_rescan.py161
-rwxr-xr-xtest/functional/wallet_txn_clone.py13
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py13
-rw-r--r--test/lint/README.md6
135 files changed, 1472 insertions, 1024 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index f15afca005..48747ac138 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -93,7 +93,6 @@ task:
QTBASEDIR: 'C:\Qt5.12.11_x64_static_vs2019_160900'
x64_NATIVE_TOOLS: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"'
IgnoreWarnIntDirInTempDetected: 'true'
- EXCLUDE_TESTS: 'feature_addrman.py,feature_bip68_sequence.py,feature_fee_estimation.py,mining_prioritisetransaction.py,p2p_getaddr_caching.py,p2p_invalid_locator.py,p2p_invalid_tx.py,rpc_misc.py,rpc_net.py,wallet_avoidreuse.py,wallet_descriptor.py,wallet_groups.py,wallet_keypool.py'
merge_script:
- git config --global user.email "ci@ci.ci"
- git config --global user.name "ci"
@@ -147,8 +146,12 @@ task:
- python test\util\test_runner.py
- python test\util\rpcauth-test.py
functional_tests_script:
- # TODO enable '--extended' and drop '--exclude'.
- - python test\functional\test_runner.py --nocleanup --ci --quiet --combinedlogslen=4000 --jobs=4 --timeout-factor=8 --exclude %EXCLUDE_TESTS% --failfast
+ # Increase the dynamic port range to the maximum allowed value to mitigate "OSError: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted".
+ # See: https://docs.microsoft.com/en-us/biztalk/technical-guides/settings-that-can-be-modified-to-improve-network-performance
+ - netsh int ipv4 set dynamicport tcp start=1025 num=64511
+ - netsh int ipv6 set dynamicport tcp start=1025 num=64511
+ # Exclude feature_dbcrash for now due to timeout
+ - python test\functional\test_runner.py --nocleanup --ci --quiet --combinedlogslen=4000 --jobs=4 --timeout-factor=8 --failfast --extended --exclude feature_dbcrash
task:
name: 'ARM [unit tests, no functional tests] [bullseye]'
diff --git a/ci/test/00_setup_env_native_nowallet.sh b/ci/test/00_setup_env_native_nowallet.sh
index 5edc6ae2da..e9a20fca7d 100755
--- a/ci/test/00_setup_env_native_nowallet.sh
+++ b/ci/test/00_setup_env_native_nowallet.sh
@@ -8,7 +8,7 @@ export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_nowallet
export DOCKER_NAME_TAG=ubuntu:18.04 # Use bionic to have one config run the tests in python3.6, see doc/dependencies.md
-export PACKAGES="python3-zmq clang-5.0 llvm-5.0" # Use clang-5 to test C++17 compatibility, see doc/dependencies.md
-export DEP_OPTS="NO_WALLET=1"
+export PACKAGES="python3-zmq clang-7 llvm-7 libc++abi-7-dev libc++-7-dev" # Use clang-7 to test C++17 compatibility, see doc/dependencies.md
+export DEP_OPTS="NO_WALLET=1 CC=clang-7 CXX='clang++-7 -stdlib=libc++'"
export GOAL="install"
-export BITCOIN_CONFIG="--enable-reduce-exports CC=clang-5.0 CXX=clang++-5.0"
+export BITCOIN_CONFIG="--enable-reduce-exports CC=clang-7 CXX='clang++-7 -stdlib=libc++'"
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index 7d73f2d0b2..8176179f0b 100755
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -7,8 +7,8 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_qt5
-export DOCKER_NAME_TAG=ubuntu:18.04 # Check that bionic gcc-7 can compile our c++17 and run our functional tests in python3, see doc/dependencies.md
-export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev"
+export DOCKER_NAME_TAG=ubuntu:18.04 # Check that bionic gcc-8 can compile our C++17 and run our functional tests in python3, see doc/dependencies.md
+export PACKAGES="gcc-8 g++-8 python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev"
export DEP_OPTS="NO_QT=1 NO_UPNP=1 NO_NATPMP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
export TEST_RUNNER_EXTRA="--previous-releases --coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
export RUN_UNIT_TESTS_SEQUENTIAL="true"
@@ -16,4 +16,4 @@ export RUN_UNIT_TESTS="false"
export GOAL="install"
export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.15.2 v0.16.3 v0.17.2 v0.18.1 v0.19.1 v0.20.1"
export BITCOIN_CONFIG="--enable-zmq --with-libs=no --with-gui=qt5 --enable-reduce-exports
---enable-debug --disable-fuzz-binary CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
+--enable-debug --disable-fuzz-binary CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" CC=gcc-8 CXX=g++-8"
diff --git a/doc/dependencies.md b/doc/dependencies.md
index abdbeee3ce..0c1fd6ba98 100644
--- a/doc/dependencies.md
+++ b/doc/dependencies.md
@@ -7,11 +7,11 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| --- | --- | --- | --- | --- | --- |
| Berkeley DB | [4.8.30](https://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | |
| Boost | [1.71.0](https://www.boost.org/users/download/) | [1.64.0](https://github.com/bitcoin/bitcoin/pull/22320) | No | | |
-| Clang<sup>[ \* ](#note1)</sup> | | [5.0+](https://releases.llvm.org/download.html) (C++17 support) | | | |
+| Clang<sup>[ \* ](#note1)</sup> | | [7.0](https://releases.llvm.org/download.html) (C++17 & std::filesystem support) | | | |
| Expat | [2.2.7](https://libexpat.github.io/) | | No | Yes | |
| fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | |
| FreeType | [2.7.1](https://download.savannah.gnu.org/releases/freetype) | | No | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Android only) |
-| GCC | | [7+](https://gcc.gnu.org/) (C++17 support) | | | |
+| GCC | | [8.1](https://gcc.gnu.org/) (C++17 & std::filesystem support) | | | |
| glibc | | [2.17](https://www.gnu.org/software/libc/) | | | | |
| HarfBuzz-NG | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| libevent | [2.1.12-stable](https://github.com/libevent/libevent/releases) | [2.0.21](https://github.com/bitcoin/bitcoin/pull/18676) | No | | |
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index ffb6632e21..0a5a7066ab 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -963,37 +963,41 @@ Subtrees
Several parts of the repository are subtrees of software maintained elsewhere.
-Some of these are maintained by active developers of Bitcoin Core, in which case changes should probably go
-directly upstream without being PRed directly against the project. They will be merged back in the next
-subtree merge.
+Some of these are maintained by active developers of Bitcoin Core, in which case
+changes should go directly upstream without being PRed directly against the project.
+They will be merged back in the next subtree merge.
-Others are external projects without a tight relationship with our project. Changes to these should also
-be sent upstream, but bugfixes may also be prudent to PR against Bitcoin Core so that they can be integrated
-quickly. Cosmetic changes should be purely taken upstream.
+Others are external projects without a tight relationship with our project. Changes
+to these should also be sent upstream, but bugfixes may also be prudent to PR against
+a Bitcoin Core subtree, so that they can be integrated quickly. Cosmetic changes
+should be taken upstream.
-There is a tool in `test/lint/git-subtree-check.sh` ([instructions](../test/lint#git-subtree-checksh)) to check a subtree directory for consistency with
-its upstream repository.
+There is a tool in `test/lint/git-subtree-check.sh` ([instructions](../test/lint#git-subtree-checksh))
+to check a subtree directory for consistency with its upstream repository.
Current subtrees include:
- src/leveldb
- - Upstream at https://github.com/google/leveldb ; Maintained by Google, but
- open important PRs to Core to avoid delay.
+ - Subtree at https://github.com/bitcoin-core/leveldb-subtree ; maintained by Core contributors.
+ - Upstream at https://github.com/google/leveldb ; maintained by Google. Open
+ important PRs to the subtree to avoid delay.
- **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb) when
merging upstream changes to the LevelDB subtree.
- src/crc32c
- Used by leveldb for hardware acceleration of CRC32C checksums for data integrity.
- - Upstream at https://github.com/google/crc32c ; Maintained by Google.
+ - Subtree at https://github.com/bitcoin-core/crc32c-subtree ; maintained by Core contributors.
+ - Upstream at https://github.com/google/crc32c ; maintained by Google.
- src/secp256k1
- - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintained by Core contributors.
+ - Upstream at https://github.com/bitcoin-core/secp256k1/ ; maintained by Core contributors.
- src/crypto/ctaes
- - Upstream at https://github.com/bitcoin-core/ctaes ; actively maintained by Core contributors.
+ - Upstream at https://github.com/bitcoin-core/ctaes ; maintained by Core contributors.
- src/univalue
- - Upstream at https://github.com/bitcoin-core/univalue ; actively maintained by Core contributors, deviates from upstream https://github.com/jgarzik/univalue
+ - Subtree at https://github.com/bitcoin-core/univalue-subtree ; maintained by Core contributors.
+ - Deviates from upstream https://github.com/jgarzik/univalue.
Upgrading LevelDB
---------------------
diff --git a/doc/release-notes-23065.md b/doc/release-notes-23065.md
new file mode 100644
index 0000000000..6ec002b2df
--- /dev/null
+++ b/doc/release-notes-23065.md
@@ -0,0 +1,15 @@
+Notable changes
+===============
+
+Updated RPCs
+------------
+
+- `lockunspent` now optionally takes a third parameter, `persistent`, which
+causes the lock to be written persistently to the wallet database. This
+allows UTXOs to remain locked even after node restarts or crashes.
+
+GUI changes
+-----------
+
+- UTXOs which are locked via the GUI are now stored persistently in the
+wallet database, so are not lost on node shutdown or crash. \ No newline at end of file
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 915bda2ea3..a0c1ed3b31 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -64,6 +64,12 @@ P2P and network changes
Updated RPCs
------------
+- The `-deprecatedrpc=addresses` configuration option has been removed. RPCs
+ `gettxout`, `getrawtransaction`, `decoderawtransaction`, `decodescript`,
+ `gettransaction verbose=true` and REST endpoints `/rest/tx`, `/rest/getutxos`,
+ `/rest/block` no longer return the `addresses` and `reqSigs` fields, which
+ were previously deprecated in 22.0. (#22650)
+
New RPCs
--------
@@ -86,6 +92,12 @@ New settings
Updated settings
----------------
+- In previous releases, the meaning of the command line option
+ `-persistmempool` (without a value provided) incorrectly disabled mempool
+ persistence. `-persistmempool` is now treated like other boolean options to
+ mean `-persistmempool=1`. Passing `-persistmempool=0`, `-persistmempool=1`
+ and `-nopersistmempool` is unaffected. (#23061)
+
Tools and Utilities
-------------------
@@ -113,10 +125,8 @@ Tests
-----
- For the `regtest` network the activation heights of several softforks were
- changed.
- * BIP 34 (blockheight in coinbase) from 500 to 2 (#16333)
- * BIP 66 (DERSIG) from 1251 to 102 (#22632)
- * BIP 65 (CLTV) from 1351 to 111 (#21862)
+ set to block height 1. They can be changed by the runtime setting
+ `-testactivationheight=name@height`. (#22818)
Credits
=======
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 1e73750ce3..94c77a6d89 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -183,7 +183,7 @@ void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman)
{
- auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
+ auto check_addrman = std::clamp<int32_t>(args.GetIntArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
int64_t nStart = GetTimeMillis();
diff --git a/src/bech32.cpp b/src/bech32.cpp
index 288b14e023..9da2488ef2 100644
--- a/src/bech32.cpp
+++ b/src/bech32.cpp
@@ -66,6 +66,26 @@ uint32_t PolyMod(const data& v)
// the above example, `c` initially corresponds to 1 mod g(x), and after processing 2 inputs of
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
// for `c`.
+
+ // The following Sage code constructs the generator used:
+ //
+ // B = GF(2) # Binary field
+ // BP.<b> = B[] # Polynomials over the binary field
+ // F_mod = b**5 + b**3 + 1
+ // F.<f> = GF(32, modulus=F_mod, repr='int') # GF(32) definition
+ // FP.<x> = F[] # Polynomials over GF(32)
+ // E_mod = x**2 + F.fetch_int(9)*x + F.fetch_int(23)
+ // E.<e> = F.extension(E_mod) # GF(1024) extension field definition
+ // for p in divisors(E.order() - 1): # Verify e has order 1023.
+ // assert((e**p == 1) == (p % 1023 == 0))
+ // G = lcm([(e**i).minpoly() for i in range(997,1000)])
+ // print(G) # Print out the generator
+ //
+ // It demonstrates that g(x) is the least common multiple of the minimal polynomials
+ // of 3 consecutive powers (997,998,999) of a primitive element (e) of GF(1024).
+ // That guarantees it is, in fact, the generator of a primitive BCH code with cycle
+ // length 1023 and distance 4. See https://en.wikipedia.org/wiki/BCH_code for more details.
+
uint32_t c = 1;
for (const auto v_i : v) {
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
@@ -88,12 +108,21 @@ uint32_t PolyMod(const data& v)
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
c = ((c & 0x1ffffff) << 5) ^ v_i;
- // Finally, for each set bit n in c0, conditionally add {2^n}k(x):
+ // Finally, for each set bit n in c0, conditionally add {2^n}k(x). These constants can be
+ // computed using the following Sage code (continuing the code above):
+ //
+ // for i in [1,2,4,8,16]: # Print out {1,2,4,8,16}*(g(x) mod x^6), packed in hex integers.
+ // v = 0
+ // for coef in reversed((F.fetch_int(i)*(G % x**6)).coefficients(sparse=True)):
+ // v = v*32 + coef.integer_representation()
+ // print("0x%x" % v)
+ //
if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
+
}
return c;
}
@@ -125,7 +154,8 @@ Encoding VerifyChecksum(const std::string& hrp, const data& values)
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
// list of values would result in a new valid list. For that reason, Bech32 requires the
- // resulting checksum to be 1 instead. In Bech32m, this constant was amended.
+ // resulting checksum to be 1 instead. In Bech32m, this constant was amended. See
+ // https://gist.github.com/sipa/14c248c288c3880a3b191f978a34508e for details.
const uint32_t check = PolyMod(Cat(ExpandHRP(hrp), values));
if (check == EncodingConstant(Encoding::BECH32)) return Encoding::BECH32;
if (check == EncodingConstant(Encoding::BECH32M)) return Encoding::BECH32M;
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index e5dd571a4c..bebf86a09d 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -103,41 +103,33 @@ static void AddrManGetAddr(benchmark::Bench& bench)
});
}
-static void AddrManGood(benchmark::Bench& bench)
+static void AddrManAddThenGood(benchmark::Bench& bench)
{
- /* Create many CAddrMan objects - one to be modified at each loop iteration.
- * This is necessary because the CAddrMan::Good() method modifies the
- * object, affecting the timing of subsequent calls to the same method and
- * we want to do the same amount of work in every loop iteration. */
-
- bench.epochs(5).epochIterations(1);
- const uint64_t addrman_count{bench.epochs() * bench.epochIterations()};
- Assert(addrman_count == 5U);
-
- std::vector<std::unique_ptr<CAddrMan>> addrmans(addrman_count);
- for (size_t i{0}; i < addrman_count; ++i) {
- addrmans[i] = std::make_unique<CAddrMan>(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
- FillAddrMan(*addrmans[i]);
- }
-
auto markSomeAsGood = [](CAddrMan& addrman) {
for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
- if (addr_i % 32 == 0) {
- addrman.Good(g_addresses[source_i][addr_i]);
- }
+ addrman.Good(g_addresses[source_i][addr_i]);
}
}
};
- uint64_t i = 0;
+ CreateAddresses();
+
bench.run([&] {
- markSomeAsGood(*addrmans.at(i));
- ++i;
+ // To make the benchmark independent of the number of evaluations, we always prepare a new addrman.
+ // This is necessary because CAddrMan::Good() method modifies the object, affecting the timing of subsequent calls
+ // to the same method and we want to do the same amount of work in every loop iteration.
+ //
+ // This has some overhead (exactly the result of AddrManAdd benchmark), but that overhead is constant so improvements in
+ // CAddrMan::Good() will still be noticeable.
+ CAddrMan addrman(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ AddAddressesToAddrMan(addrman);
+
+ markSomeAsGood(addrman);
});
}
BENCHMARK(AddrManAdd);
BENCHMARK(AddrManSelect);
BENCHMARK(AddrManGetAddr);
-BENCHMARK(AddrManGood);
+BENCHMARK(AddrManAddThenGood);
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
index 8e10862a37..bc3685818e 100644
--- a/src/bench/bech32.cpp
+++ b/src/bench/bech32.cpp
@@ -3,7 +3,6 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <bench/nanobench.h>
#include <bech32.h>
#include <util/strencodings.h>
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 012057e792..030bc43396 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -4,11 +4,18 @@
#include <bench/bench.h>
-#include <chainparams.h>
#include <test/util/setup_common.h>
-#include <validation.h>
+#include <chrono>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <map>
#include <regex>
+#include <string>
+#include <vector>
+
+using namespace std::chrono_literals;
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
@@ -61,6 +68,12 @@ void benchmark::BenchRunner::RunAll(const Args& args)
Bench bench;
bench.name(p.first);
+ if (args.min_time > 0ms) {
+ // convert to nanos before dividing to reduce rounding errors
+ std::chrono::nanoseconds min_time_ns = args.min_time;
+ bench.minEpochTime(min_time_ns / bench.epochs());
+ }
+
if (args.asymptote.empty()) {
p.second(bench);
} else {
diff --git a/src/bench/bench.h b/src/bench/bench.h
index c4fcd80e33..b0e4006ee3 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -41,11 +41,12 @@ using ankerl::nanobench::Bench;
typedef std::function<void(Bench&)> BenchFunction;
struct Args {
- std::string regex_filter;
bool is_list_only;
+ std::chrono::milliseconds min_time;
std::vector<double> asymptote;
std::string output_csv;
std::string output_json;
+ std::string regex_filter;
};
class BenchRunner
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index aab777cac1..0b43ea1fd5 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -4,21 +4,28 @@
#include <bench/bench.h>
+#include <clientversion.h>
#include <crypto/sha256.h>
#include <util/strencodings.h>
#include <util/system.h>
-#include <memory>
+#include <chrono>
+#include <cstdint>
+#include <iostream>
+#include <sstream>
+#include <vector>
static const char* DEFAULT_BENCH_FILTER = ".*";
+static constexpr int64_t DEFAULT_MIN_TIME_MS{10};
static void SetupBenchArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
- argsman.AddArg("-asymptote=n1,n2,n3,...", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-asymptote=<n1,n2,n3,...>", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
+ argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
@@ -48,17 +55,62 @@ int main(int argc, char** argv)
}
if (HelpRequested(argsman)) {
- std::cout << argsman.GetHelpMessage();
+ std::cout << "Usage: bench_bitcoin [options]\n"
+ "\n"
+ << argsman.GetHelpMessage()
+ << "Description:\n"
+ "\n"
+ " bench_bitcoin executes microbenchmarks. The quality of the benchmark results\n"
+ " highly depend on the stability of the machine. It can sometimes be difficult\n"
+ " to get stable, repeatable results, so here are a few tips:\n"
+ "\n"
+ " * Use pyperf [1] to disable frequency scaling, turbo boost etc. For best\n"
+ " results, use CPU pinning and CPU isolation (see [2]).\n"
+ "\n"
+ " * Each call of run() should do exactly the same work. E.g. inserting into\n"
+ " a std::vector doesn't do that as it will reallocate on certain calls. Make\n"
+ " sure each run has exactly the same preconditions.\n"
+ "\n"
+ " * If results are still not reliable, increase runtime with e.g.\n"
+ " -min_time=5000 to let a benchmark run for at least 5 seconds.\n"
+ "\n"
+ " * bench_bitcoin uses nanobench [3] for which there is extensive\n"
+ " documentation available online.\n"
+ "\n"
+ "Environment Variables:\n"
+ "\n"
+ " To attach a profiler you can run a benchmark in endless mode. This can be\n"
+ " done with the environment variable NANOBENCH_ENDLESS. E.g. like so:\n"
+ "\n"
+ " NANOBENCH_ENDLESS=MuHash ./bench_bitcoin -filter=MuHash\n"
+ "\n"
+ " In rare cases it can be useful to suppress stability warnings. This can be\n"
+ " done with the environment variable NANOBENCH_SUPPRESS_WARNINGS, e.g:\n"
+ "\n"
+ " NANOBENCH_SUPPRESS_WARNINGS=1 ./bench_bitcoin\n"
+ "\n"
+ "Notes:\n"
+ "\n"
+ " 1. pyperf\n"
+ " https://github.com/psf/pyperf\n"
+ "\n"
+ " 2. CPU pinning & isolation\n"
+ " https://pyperf.readthedocs.io/en/latest/system.html\n"
+ "\n"
+ " 3. nanobench\n"
+ " https://github.com/martinus/nanobench\n"
+ "\n";
return EXIT_SUCCESS;
}
benchmark::Args args;
- args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
- args.is_list_only = argsman.GetBoolArg("-list", false);
args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
+ args.is_list_only = argsman.GetBoolArg("-list", false);
+ args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min_time", DEFAULT_MIN_TIME_MS));
args.output_csv = argsman.GetArg("-output_csv", "");
args.output_json = argsman.GetArg("-output_json", "");
+ args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
benchmark::BenchRunner::RunAll(args);
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 30fe11be6b..d36e504bfc 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -110,9 +110,9 @@ static void MuHash(benchmark::Bench& bench)
{
MuHash3072 acc;
unsigned char key[32] = {0};
- int i = 0;
+ uint32_t i = 0;
bench.run([&] {
- key[0] = ++i;
+ key[0] = ++i & 0xFF;
acc *= MuHash3072(key);
});
}
@@ -134,10 +134,6 @@ static void MuHashDiv(benchmark::Bench& bench)
FastRandomContext rng(true);
MuHash3072 muhash{rng.randbytes(32)};
- for (size_t i = 0; i < bench.epochIterations(); ++i) {
- acc *= muhash;
- }
-
bench.run([&] {
acc /= muhash;
});
diff --git a/src/bench/nanobench.h b/src/bench/nanobench.h
index 030d6ebf6a..27df08fb69 100644
--- a/src/bench/nanobench.h
+++ b/src/bench/nanobench.h
@@ -33,7 +33,7 @@
// see https://semver.org/
#define ANKERL_NANOBENCH_VERSION_MAJOR 4 // incompatible API changes
#define ANKERL_NANOBENCH_VERSION_MINOR 3 // backwards-compatible changes
-#define ANKERL_NANOBENCH_VERSION_PATCH 4 // backwards-compatible bug fixes
+#define ANKERL_NANOBENCH_VERSION_PATCH 6 // backwards-compatible bug fixes
///////////////////////////////////////////////////////////////////////////////////////////////////
// public facing api - as minimal as possible
@@ -88,13 +88,15 @@
} while (0)
#endif
-#if defined(__linux__) && defined(PERF_EVENT_IOC_ID) && defined(PERF_COUNT_HW_REF_CPU_CYCLES) && defined(PERF_FLAG_FD_CLOEXEC) && \
- !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
-// only enable perf counters on kernel 3.14 which seems to have all the necessary defines. The three PERF_... defines are not in
-// kernel 2.6.32 (all others are).
-# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
-#else
-# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
+#define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
+#if defined(__linux__) && !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
+# include <linux/version.h>
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+// PERF_COUNT_HW_REF_CPU_CYCLES only available since kernel 3.3
+// PERF_FLAG_FD_CLOEXEC since kernel 3.14
+# undef ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS
+# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
+# endif
#endif
#if defined(__clang__)
@@ -2210,20 +2212,20 @@ struct IterationLogic::Impl {
columns.emplace_back(10, 1, "err%", "%", rErrorMedian * 100.0);
double rInsMedian = -1.0;
- if (mResult.has(Result::Measure::instructions)) {
+ if (mBench.performanceCounters() && mResult.has(Result::Measure::instructions)) {
rInsMedian = mResult.median(Result::Measure::instructions);
columns.emplace_back(18, 2, "ins/" + mBench.unit(), "", rInsMedian / mBench.batch());
}
double rCycMedian = -1.0;
- if (mResult.has(Result::Measure::cpucycles)) {
+ if (mBench.performanceCounters() && mResult.has(Result::Measure::cpucycles)) {
rCycMedian = mResult.median(Result::Measure::cpucycles);
columns.emplace_back(18, 2, "cyc/" + mBench.unit(), "", rCycMedian / mBench.batch());
}
if (rInsMedian > 0.0 && rCycMedian > 0.0) {
columns.emplace_back(9, 3, "IPC", "", rCycMedian <= 0.0 ? 0.0 : rInsMedian / rCycMedian);
}
- if (mResult.has(Result::Measure::branchinstructions)) {
+ if (mBench.performanceCounters() && mResult.has(Result::Measure::branchinstructions)) {
double rBraMedian = mResult.median(Result::Measure::branchinstructions);
columns.emplace_back(17, 2, "bra/" + mBench.unit(), "", rBraMedian / mBench.batch());
if (mResult.has(Result::Measure::branchmisses)) {
@@ -2402,6 +2404,14 @@ public:
return (a + divisor / 2) / divisor;
}
+ ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
+ static inline uint32_t mix(uint32_t x) noexcept {
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return x;
+ }
+
template <typename Op>
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
void calibrate(Op&& op) {
@@ -2441,15 +2451,10 @@ public:
uint64_t const numIters = 100000U + (std::random_device{}() & 3);
uint64_t n = numIters;
uint32_t x = 1234567;
- auto fn = [&]() {
- x ^= x << 13;
- x ^= x >> 17;
- x ^= x << 5;
- };
beginMeasure();
while (n-- > 0) {
- fn();
+ x = mix(x);
}
endMeasure();
detail::doNotOptimizeAway(x);
@@ -2459,8 +2464,8 @@ public:
beginMeasure();
while (n-- > 0) {
// we now run *twice* so we can easily calculate the overhead
- fn();
- fn();
+ x = mix(x);
+ x = mix(x);
}
endMeasure();
detail::doNotOptimizeAway(x);
diff --git a/src/bench/peer_eviction.cpp b/src/bench/peer_eviction.cpp
index 46fd9d999e..8429f18613 100644
--- a/src/bench/peer_eviction.cpp
+++ b/src/bench/peer_eviction.cpp
@@ -20,19 +20,17 @@ static void EvictionProtectionCommon(
{
using Candidates = std::vector<NodeEvictionCandidate>;
FastRandomContext random_context{true};
- bench.warmup(100).epochIterations(1100);
Candidates candidates{GetRandomNodeEvictionCandidates(num_candidates, random_context)};
for (auto& c : candidates) {
candidate_setup_fn(c);
}
- std::vector<Candidates> copies{
- static_cast<size_t>(bench.epochs() * bench.epochIterations()), candidates};
- size_t i{0};
+
bench.run([&] {
- ProtectEvictionCandidatesByRatio(copies.at(i));
- ++i;
+ // creating a copy has an overhead of about 3%, so it does not influence the benchmark results much.
+ auto copy = candidates;
+ ProtectEvictionCandidatesByRatio(copy);
});
}
diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp
index 997ab56549..28167767db 100644
--- a/src/bench/rollingbloom.cpp
+++ b/src/bench/rollingbloom.cpp
@@ -13,16 +13,16 @@ static void RollingBloom(benchmark::Bench& bench)
uint32_t count = 0;
bench.run([&] {
count++;
- data[0] = count;
- data[1] = count >> 8;
- data[2] = count >> 16;
- data[3] = count >> 24;
+ data[0] = count & 0xFF;
+ data[1] = (count >> 8) & 0xFF;
+ data[2] = (count >> 16) & 0xFF;
+ data[3] = (count >> 24) & 0xFF;
filter.insert(data);
- data[0] = count >> 24;
- data[1] = count >> 16;
- data[2] = count >> 8;
- data[3] = count;
+ data[0] = (count >> 24) & 0xFF;
+ data[1] = (count >> 16) & 0xFF;
+ data[2] = (count >> 8) & 0xFF;
+ data[3] = count & 0xFF;
filter.contains(data);
});
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index d6e7298fd0..3c22ee0f67 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -702,7 +702,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
// 3. default port for chain
uint16_t port{BaseParams().RPCPort()};
SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
- port = static_cast<uint16_t>(gArgs.GetArg("-rpcport", port));
+ port = static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", port));
// Obtain event base
raii_event_base base = obtain_event_base();
@@ -712,7 +712,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
// Set connection timeout
{
- const int timeout = gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
+ const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
if (timeout > 0) {
evhttp_connection_set_timeout(evcon.get(), timeout);
} else {
@@ -822,7 +822,7 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
UniValue response(UniValue::VOBJ);
// Execute and handle connection failures with -rpcwait.
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
- const int timeout = gArgs.GetArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
+ const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
const auto deadline{GetTime<std::chrono::microseconds>() + 1s * timeout};
do {
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 58c51bd8e0..98916460aa 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -727,7 +727,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
static void OutputTxJSON(const CTransaction& tx)
{
UniValue entry(UniValue::VOBJ);
- TxToUniv(tx, uint256(), /* include_addresses */ false, entry);
+ TxToUniv(tx, uint256(), entry);
std::string jsonOutput = entry.write(4);
tfm::format(std::cout, "%s\n", jsonOutput);
diff --git a/src/bloom.cpp b/src/bloom.cpp
index d0128a26d7..15e06389de 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -4,20 +4,22 @@
#include <bloom.h>
-#include <primitives/transaction.h>
#include <hash.h>
+#include <primitives/transaction.h>
+#include <random.h>
#include <script/script.h>
#include <script/standard.h>
-#include <random.h>
+#include <span.h>
#include <streams.h>
-#include <math.h>
-#include <stdlib.h>
-
#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <vector>
-#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
-#define LN2 0.6931471805599453094172321214581765680755001343602552
+static constexpr double LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
+static constexpr double LN2 = 0.6931471805599453094172321214581765680755001343602552;
CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweakIn, unsigned char nFlagsIn) :
/**
@@ -37,13 +39,13 @@ CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, c
{
}
-inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
+inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, Span<const unsigned char> vDataToHash) const
{
// 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
}
-void CBloomFilter::insert(const std::vector<unsigned char>& vKey)
+void CBloomFilter::insert(Span<const unsigned char> vKey)
{
if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700)
return;
@@ -59,17 +61,10 @@ void CBloomFilter::insert(const COutPoint& outpoint)
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- std::vector<unsigned char> data(stream.begin(), stream.end());
- insert(data);
+ insert(stream);
}
-void CBloomFilter::insert(const uint256& hash)
-{
- std::vector<unsigned char> data(hash.begin(), hash.end());
- insert(data);
-}
-
-bool CBloomFilter::contains(const std::vector<unsigned char>& vKey) const
+bool CBloomFilter::contains(Span<const unsigned char> vKey) const
{
if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700)
return true;
@@ -87,14 +82,7 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- std::vector<unsigned char> data(stream.begin(), stream.end());
- return contains(data);
-}
-
-bool CBloomFilter::contains(const uint256& hash) const
-{
- std::vector<unsigned char> data(hash.begin(), hash.end());
- return contains(data);
+ return contains(MakeUCharSpan(stream));
}
bool CBloomFilter::IsWithinSizeConstraints() const
@@ -198,7 +186,8 @@ CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const dou
}
/* Similar to CBloomFilter::Hash */
-static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, const std::vector<unsigned char>& vDataToHash) {
+static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, Span<const unsigned char> vDataToHash)
+{
return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash);
}
@@ -210,7 +199,7 @@ static inline uint32_t FastMod(uint32_t x, size_t n) {
return ((uint64_t)x * (uint64_t)n) >> 32;
}
-void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
+void CRollingBloomFilter::insert(Span<const unsigned char> vKey)
{
if (nEntriesThisGeneration == nEntriesPerGeneration) {
nEntriesThisGeneration = 0;
@@ -241,13 +230,7 @@ void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey)
}
}
-void CRollingBloomFilter::insert(const uint256& hash)
-{
- std::vector<unsigned char> vData(hash.begin(), hash.end());
- insert(vData);
-}
-
-bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
+bool CRollingBloomFilter::contains(Span<const unsigned char> vKey) const
{
for (int n = 0; n < nHashFuncs; n++) {
uint32_t h = RollingBloomHash(n, nTweak, vKey);
@@ -261,12 +244,6 @@ bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const
return true;
}
-bool CRollingBloomFilter::contains(const uint256& hash) const
-{
- std::vector<unsigned char> vData(hash.begin(), hash.end());
- return contains(vData);
-}
-
void CRollingBloomFilter::reset()
{
nTweak = GetRand(std::numeric_limits<unsigned int>::max());
diff --git a/src/bloom.h b/src/bloom.h
index fdaa8abfb2..422646d8b9 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -6,16 +6,16 @@
#define BITCOIN_BLOOM_H
#include <serialize.h>
+#include <span.h>
#include <vector>
class COutPoint;
class CTransaction;
-class uint256;
//! 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%
-static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes
-static const unsigned int MAX_HASH_FUNCS = 50;
+static constexpr unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes
+static constexpr unsigned int MAX_HASH_FUNCS = 50;
/**
* First two bits of nFlags control how much IsRelevantAndUpdate actually updates
@@ -49,7 +49,7 @@ private:
unsigned int nTweak;
unsigned char nFlags;
- unsigned int Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const;
+ unsigned int Hash(unsigned int nHashNum, Span<const unsigned char> vDataToHash) const;
public:
/**
@@ -66,13 +66,11 @@ public:
SERIALIZE_METHODS(CBloomFilter, obj) { READWRITE(obj.vData, obj.nHashFuncs, obj.nTweak, obj.nFlags); }
- void insert(const std::vector<unsigned char>& vKey);
+ void insert(Span<const unsigned char> vKey);
void insert(const COutPoint& outpoint);
- void insert(const uint256& hash);
- bool contains(const std::vector<unsigned char>& vKey) const;
+ bool contains(Span<const unsigned char> vKey) const;
bool contains(const COutPoint& outpoint) const;
- bool contains(const uint256& hash) const;
//! True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS
//! (catch a filter which was just deserialized which was too big)
@@ -112,10 +110,8 @@ class CRollingBloomFilter
public:
CRollingBloomFilter(const unsigned int nElements, const double nFPRate);
- void insert(const std::vector<unsigned char>& vKey);
- void insert(const uint256& hash);
- bool contains(const std::vector<unsigned char>& vKey) const;
- bool contains(const uint256& hash) const;
+ void insert(Span<const unsigned char> vKey);
+ bool contains(Span<const unsigned char> vKey) const;
void reset();
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 4cc37560a3..b155745794 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -280,8 +280,10 @@ public:
if (!args.IsArgSet("-signetchallenge")) {
bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
+ vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl");
+
+ // Hardcoded nodes can be removed once there are more DNS seeds
vSeeds.emplace_back("178.128.221.177");
- vSeeds.emplace_back("2a01:7c8:d005:390::5");
vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333");
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000008546553c03");
@@ -390,12 +392,12 @@ public:
consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Exception = uint256();
- consensus.BIP34Height = 2; // BIP34 activated on regtest (Block at height 1 not enforced for testing purposes)
+ consensus.BIP34Height = 1; // Always active unless overridden
consensus.BIP34Hash = uint256();
- consensus.BIP65Height = 111; // BIP65 activated on regtest (Block at height 110 and earlier not enforced for testing purposes)
- consensus.BIP66Height = 102; // BIP66 activated on regtest (Block at height 101 and earlier not enforced for testing purposes)
- consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests)
- consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden
+ consensus.BIP65Height = 1; // Always active unless overridden
+ consensus.BIP66Height = 1; // Always active unless overridden
+ consensus.CSVHeight = 1; // Always active unless overridden
+ consensus.SegwitHeight = 1; // Always active unless overridden
consensus.MinBIP9WarningHeight = 0;
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
@@ -487,15 +489,38 @@ public:
void UpdateActivationParametersFromArgs(const ArgsManager& args);
};
-void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
+static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& consensus)
{
- if (args.IsArgSet("-segwitheight")) {
- int64_t height = args.GetArg("-segwitheight", consensus.SegwitHeight);
- if (height < 0 || height >= std::numeric_limits<int>::max()) {
- throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range.", height));
+ for (const std::string& arg : args.GetArgs("-testactivationheight")) {
+ const auto found{arg.find('@')};
+ if (found == std::string::npos) {
+ throw std::runtime_error(strprintf("Invalid format (%s) for -testactivationheight=name@height.", arg));
+ }
+ const auto name{arg.substr(0, found)};
+ const auto value{arg.substr(found + 1)};
+ int32_t height;
+ if (!ParseInt32(value, &height) || height < 0 || height >= std::numeric_limits<int>::max()) {
+ throw std::runtime_error(strprintf("Invalid height value (%s) for -testactivationheight=name@height.", arg));
+ }
+ if (name == "segwit") {
+ consensus.SegwitHeight = int{height};
+ } else if (name == "bip34") {
+ consensus.BIP34Height = int{height};
+ } else if (name == "dersig") {
+ consensus.BIP66Height = int{height};
+ } else if (name == "cltv") {
+ consensus.BIP65Height = int{height};
+ } else if (name == "csv") {
+ consensus.CSVHeight = int{height};
+ } else {
+ throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
}
- consensus.SegwitHeight = static_cast<int>(height);
}
+}
+
+void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
+{
+ MaybeUpdateHeights(args, consensus);
if (!args.IsArgSet("-vbparams")) return;
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 79c1bc25bc..dc484f5c03 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -20,7 +20,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
diff --git a/src/core_io.h b/src/core_io.h
index 3b9b66574c..f00f155249 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -44,8 +44,8 @@ UniValue ValueFromAmount(const CAmount amount);
std::string FormatScript(const CScript& script);
std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
std::string SighashToStr(unsigned char sighash_type);
-void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex, bool include_addresses);
-void ScriptToUniv(const CScript& script, UniValue& out, bool include_address);
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, bool include_addresses, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
+void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include_hex, bool include_address = true);
+void ScriptToUniv(const CScript& script, UniValue& out);
+void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
#endif // BITCOIN_CORE_IO_H
diff --git a/src/core_write.cpp b/src/core_write.cpp
index b35f835f42..d92c970cb6 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -141,56 +141,28 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
return HexStr(ssTx);
}
-void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
+void ScriptToUniv(const CScript& script, UniValue& out)
{
- out.pushKV("asm", ScriptToAsmStr(script));
- out.pushKV("hex", HexStr(script));
-
- std::vector<std::vector<unsigned char>> solns;
- TxoutType type = Solver(script, solns);
- out.pushKV("type", GetTxnOutputType(type));
-
- CTxDestination address;
- if (include_address && ExtractDestination(script, address) && type != TxoutType::PUBKEY) {
- out.pushKV("address", EncodeDestination(address));
- }
+ ScriptPubKeyToUniv(script, out, /* include_hex */ true, /* include_address */ false);
}
-// TODO: from v23 ("addresses" and "reqSigs" deprecated) this method should be refactored to remove the `include_addresses` option
-// this method can also be combined with `ScriptToUniv` as they will overlap
-void ScriptPubKeyToUniv(const CScript& scriptPubKey,
- UniValue& out, bool fIncludeHex, bool include_addresses)
+void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include_hex, bool include_address)
{
- TxoutType type;
CTxDestination address;
- std::vector<CTxDestination> addresses;
- int nRequired;
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
- if (fIncludeHex)
- out.pushKV("hex", HexStr(scriptPubKey));
+ if (include_hex) out.pushKV("hex", HexStr(scriptPubKey));
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TxoutType::PUBKEY) {
- out.pushKV("type", GetTxnOutputType(type));
- return;
- }
+ std::vector<std::vector<unsigned char>> solns;
+ const TxoutType type{Solver(scriptPubKey, solns)};
- if (ExtractDestination(scriptPubKey, address)) {
+ if (include_address && ExtractDestination(scriptPubKey, address) && type != TxoutType::PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}
out.pushKV("type", GetTxnOutputType(type));
-
- if (include_addresses) {
- UniValue a(UniValue::VARR);
- for (const CTxDestination& addr : addresses) {
- a.push_back(EncodeDestination(addr));
- }
- out.pushKV("addresses", a);
- out.pushKV("reqSigs", nRequired);
- }
}
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, bool include_addresses, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
+void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
{
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
@@ -249,7 +221,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, bool include_add
out.pushKV("n", (int64_t)i);
UniValue o(UniValue::VOBJ);
- ScriptPubKeyToUniv(txout.scriptPubKey, o, true, include_addresses);
+ ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
out.pushKV("scriptPubKey", o);
vout.push_back(out);
diff --git a/src/crc32c/.travis.yml b/src/crc32c/.travis.yml
index d990a89f07..183a5fba45 100644
--- a/src/crc32c/.travis.yml
+++ b/src/crc32c/.travis.yml
@@ -4,7 +4,7 @@
language: cpp
dist: bionic
-osx_image: xcode10.3
+osx_image: xcode12.5
compiler:
- gcc
@@ -24,20 +24,20 @@ env:
addons:
apt:
sources:
- - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main'
+ - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-12 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
- sourceline: 'ppa:ubuntu-toolchain-r/test'
packages:
- - clang-9
+ - clang-12
- cmake
- - gcc-9
- - g++-9
+ - gcc-11
+ - g++-11
- ninja-build
homebrew:
packages:
- cmake
- - gcc@9
- - llvm@9
+ - gcc@11
+ - llvm@12
- ninja
update: true
@@ -48,14 +48,14 @@ install:
export PATH="$(brew --prefix llvm)/bin:$PATH";
fi
# /usr/bin/gcc points to an older compiler on both Linux and macOS.
-- if [ "$CXX" = "g++" ]; then export CXX="g++-9" CC="gcc-9"; fi
+- if [ "$CXX" = "g++" ]; then export CXX="g++-11" CC="gcc-11"; fi
# /usr/bin/clang points to an older compiler on both Linux and macOS.
#
# Homebrew's llvm package doesn't ship a versioned clang++ binary, so the values
# below don't work on macOS. Fortunately, the path change above makes the
# default values (clang and clang++) resolve to the correct compiler on macOS.
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
- if [ "$CXX" = "clang++" ]; then export CXX="clang++-9" CC="clang-9"; fi;
+ if [ "$CXX" = "clang++" ]; then export CXX="clang++-12" CC="clang-12"; fi;
fi
- echo ${CC}
- echo ${CXX}
diff --git a/src/crc32c/.ycm_extra_conf.py b/src/crc32c/.ycm_extra_conf.py
index 536aadcec8..62daa8a4ac 100644
--- a/src/crc32c/.ycm_extra_conf.py
+++ b/src/crc32c/.ycm_extra_conf.py
@@ -4,10 +4,10 @@
"""YouCompleteMe configuration that interprets a .clang_complete file.
This module implementes the YouCompleteMe configuration API documented at:
-https://github.com/Valloric/ycmd#ycm_extra_confpy-specification
+https://github.com/ycm-core/ycmd#ycm_extra_confpy-specification
The implementation loads and processes a .clang_complete file, documented at:
-https://github.com/Rip-Rip/clang_complete/blob/master/README.md
+https://github.com/xavierd/clang_complete/blob/master/README.md
"""
import os
diff --git a/src/crc32c/README.md b/src/crc32c/README.md
index 0bd69f7f09..58ba38e611 100644
--- a/src/crc32c/README.md
+++ b/src/crc32c/README.md
@@ -65,7 +65,7 @@ apm install autocomplete-clang build build-cmake clang-format language-cmake \
If you don't mind more setup in return for more speed, replace
`autocomplete-clang` and `linter-clang` with `you-complete-me`. This requires
-[setting up ycmd](https://github.com/Valloric/ycmd#building).
+[setting up ycmd](https://github.com/ycm-core/ycmd#building).
```bash
apm install autocomplete-plus build build-cmake clang-format language-cmake \
diff --git a/src/crc32c/src/crc32c_arm64_check.h b/src/crc32c/src/crc32c_arm64_check.h
index 62a07aba09..6b80f70037 100644
--- a/src/crc32c/src/crc32c_arm64_check.h
+++ b/src/crc32c/src/crc32c_arm64_check.h
@@ -40,7 +40,15 @@ inline bool CanUseArm64Crc32() {
// From 'arch/arm64/include/uapi/asm/hwcap.h' in Linux kernel source code.
constexpr unsigned long kHWCAP_PMULL = 1 << 4;
constexpr unsigned long kHWCAP_CRC32 = 1 << 7;
- unsigned long hwcap = (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0;
+ unsigned long hwcap =
+#if HAVE_STRONG_GETAUXVAL
+ // Some compilers warn on (&getauxval != nullptr) in the block below.
+ getauxval(AT_HWCAP);
+#elif HAVE_WEAK_GETAUXVAL
+ (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0;
+#else
+#error This is supposed to be nested inside a check for HAVE_*_GETAUXVAL.
+#endif // HAVE_STRONG_GETAUXVAL
return (hwcap & (kHWCAP_PMULL | kHWCAP_CRC32)) ==
(kHWCAP_PMULL | kHWCAP_CRC32);
#elif defined(__APPLE__)
diff --git a/src/hash.cpp b/src/hash.cpp
index 3465caa3a9..92c923fbd2 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <hash.h>
+#include <span.h>
#include <crypto/common.h>
#include <crypto/hmac_sha512.h>
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index fa0379f612..36bbece0ea 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -289,7 +289,7 @@ static bool ThreadHTTP(struct event_base* base)
/** Bind HTTP server to specified addresses */
static bool HTTPBindAddresses(struct evhttp* http)
{
- uint16_t http_port{static_cast<uint16_t>(gArgs.GetArg("-rpcport", BaseParams().RPCPort()))};
+ uint16_t http_port{static_cast<uint16_t>(gArgs.GetIntArg("-rpcport", BaseParams().RPCPort()))};
std::vector<std::pair<std::string, uint16_t>> endpoints;
// Determine what addresses to bind to
@@ -374,7 +374,7 @@ bool InitHTTPServer()
return false;
}
- evhttp_set_timeout(http, gArgs.GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
+ evhttp_set_timeout(http, gArgs.GetIntArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
evhttp_set_max_body_size(http, MAX_SIZE);
evhttp_set_gencb(http, http_request_cb, nullptr);
@@ -385,7 +385,7 @@ bool InitHTTPServer()
}
LogPrint(BCLog::HTTP, "Initialized HTTP server\n");
- int workQueueDepth = std::max((long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
+ int workQueueDepth = std::max((long)gArgs.GetIntArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
g_work_queue = std::make_unique<WorkQueue<HTTPClosure>>(workQueueDepth);
@@ -415,7 +415,7 @@ static std::vector<std::thread> g_thread_http_workers;
void StartHTTPServer()
{
LogPrint(BCLog::HTTP, "Starting HTTP server\n");
- int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
+ int rpcThreads = std::max((long)gArgs.GetIntArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
g_thread_http = std::thread(ThreadHTTP, eventBase);
diff --git a/src/init.cpp b/src/init.cpp
index ff36ec805c..df0c753b04 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -218,7 +218,7 @@ void Shutdown(NodeContext& node)
node.banman.reset();
node.addrman.reset();
- if (node.mempool && node.mempool->IsLoaded() && node.args->GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ if (node.mempool && node.mempool->IsLoaded() && node.args->GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool(*node.mempool);
}
@@ -843,7 +843,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
// if using block pruning, then disallow txindex and coinstatsindex
- if (args.GetArg("-prune", 0)) {
+ if (args.GetIntArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX))
@@ -868,11 +868,9 @@ bool AppInitParameterInteraction(const ArgsManager& args)
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
- nUserMaxConnections = args.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
+ nUserMaxConnections = args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
- // Trim requested connection counts, to fit into system limitations
- // <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS + nBind + NUM_FDS_MESSAGE_CAPTURE);
#ifdef USE_POLL
@@ -880,6 +878,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
#else
int fd_max = FD_SETSIZE;
#endif
+ // Trim requested connection counts, to fit into system limitations
+ // <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
@@ -915,8 +915,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
// mempool limits
- int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
- int64_t nMempoolSizeMin = args.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
+ int64_t nMempoolSizeMax = args.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMin = args.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
@@ -930,7 +930,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
- int64_t nPruneArg = args.GetArg("-prune", 0);
+ int64_t nPruneArg = args.GetIntArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(_("Prune cannot be configured with a negative value."));
}
@@ -947,12 +947,12 @@ bool AppInitParameterInteraction(const ArgsManager& args)
fPruneMode = true;
}
- nConnectTimeout = args.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
+ nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
- peer_connect_timeout = args.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
+ peer_connect_timeout = args.GetIntArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated("peertimeout cannot be configured with a negative value."));
}
@@ -992,27 +992,27 @@ bool AppInitParameterInteraction(const ArgsManager& args)
if (!chainparams.IsTestChain() && !fRequireStandard) {
return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()));
}
- nBytesPerSigOp = args.GetArg("-bytespersigop", nBytesPerSigOp);
+ nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp);
if (!g_wallet_init_interface.ParameterInteraction()) return false;
fIsBareMultisigStd = args.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = args.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
- nMaxDatacarrierBytes = args.GetArg("-datacarriersize", nMaxDatacarrierBytes);
+ nMaxDatacarrierBytes = args.GetIntArg("-datacarriersize", nMaxDatacarrierBytes);
// Option to startup with mocktime set (used for regression testing):
- SetMockTime(args.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
+ SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
- if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
+ if (args.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError(Untranslated("rpcserialversion must be non-negative."));
- if (args.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
+ if (args.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
return InitError(Untranslated("Unknown rpcserialversion requested."));
- nMaxTipAge = args.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
+ nMaxTipAge = args.GetIntArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
return InitError(_("No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
@@ -1101,7 +1101,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
InitSignatureCache();
InitScriptExecutionCache();
- int script_threads = args.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
+ int script_threads = args.GetIntArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (script_threads <= 0) {
// -par=0 means autodetect (number of cores - 1 script threads)
// -par=-n means "leave n cores free" (number of cores - n - 1 script threads)
@@ -1208,7 +1208,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
assert(!node.banman);
- node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
+ node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), *node.addrman, args.GetBoolArg("-networkactive", true));
@@ -1218,7 +1218,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (!ignores_incoming_txs) node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
assert(!node.mempool);
- int check_ratio = std::min<int>(std::max<int>(args.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
+ int check_ratio = std::min<int>(std::max<int>(args.GetIntArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get(), check_ratio);
assert(!node.chainman);
@@ -1325,7 +1325,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
- int64_t nTotalCache = (args.GetArg("-dbcache", nDefaultDbCache) << 20);
+ int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
@@ -1343,7 +1343,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
nTotalCache -= nCoinDBCache;
int64_t nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
- int64_t nMempoolSizeMax = args.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ int64_t nMempoolSizeMax = args.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1f MiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
@@ -1499,7 +1499,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
for (CChainState* chainstate : chainman.GetAll()) {
if (!is_coinsview_empty(chainstate)) {
uiInterface.InitMessage(_("Verifying blocks…").translated);
- if (fHavePruned && args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
+ if (fHavePruned && args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
MIN_BLOCKS_TO_KEEP);
}
@@ -1516,8 +1516,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (!CVerifyDB().VerifyDB(
*chainstate, chainparams, chainstate->CoinsDB(),
- args.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
- args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
+ args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
+ args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
failed_verification = true;
break;
@@ -1709,11 +1709,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
connOptions.m_msgproc = node.peerman.get();
- connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
- connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
+ connOptions.nSendBufferMaxSize = 1000 * args.GetIntArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
+ connOptions.nReceiveFloodSize = 1000 * args.GetIntArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode");
- connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
+ connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetIntArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
connOptions.m_peer_connect_timeout = peer_connect_timeout;
for (const std::string& bind_arg : args.GetArgs("-bind")) {
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index a85db04b8b..6766e0510f 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -122,10 +122,10 @@ public:
virtual bool displayAddress(const CTxDestination& dest) = 0;
//! Lock coin.
- virtual void lockCoin(const COutPoint& output) = 0;
+ virtual bool lockCoin(const COutPoint& output, const bool write_to_db) = 0;
//! Unlock coin.
- virtual void unlockCoin(const COutPoint& output) = 0;
+ virtual bool unlockCoin(const COutPoint& output) = 0;
//! Return whether coin is locked.
virtual bool isLockedCoin(const COutPoint& output) = 0;
diff --git a/src/miner.cpp b/src/miner.cpp
index 38c7b4b8cc..33d115f279 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -72,7 +72,7 @@ static BlockAssembler::Options DefaultOptions()
// Block resource limits
// If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT
BlockAssembler::Options options;
- options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
+ options.nBlockMaxWeight = gArgs.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
if (gArgs.IsArgSet("-blockmintxfee")) {
std::optional<CAmount> parsed = ParseMoney(gArgs.GetArg("-blockmintxfee", ""));
options.blockMinFeeRate = CFeeRate{parsed.value_or(DEFAULT_BLOCK_MIN_TX_FEE)};
@@ -125,7 +125,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
- pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion);
+ pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
diff --git a/src/net.cpp b/src/net.cpp
index cc8f4c4316..b8ff0b13ea 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -123,7 +123,7 @@ void CConnman::AddAddrFetch(const std::string& strDest)
uint16_t GetListenPort()
{
- return static_cast<uint16_t>(gArgs.GetArg("-port", Params().GetDefaultPort()));
+ return static_cast<uint16_t>(gArgs.GetIntArg("-port", Params().GetDefaultPort()));
}
// find 'best' local address for a particular peer
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 80655c61e7..e130272ff1 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1300,7 +1300,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
{
- size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
+ size_t max_extra_txn = gArgs.GetIntArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
return;
if (!vExtraTxnForCompact.size())
@@ -3315,7 +3315,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
- unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
+ unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetIntArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
unsigned int nEvicted = m_orphanage.LimitOrphans(nMaxOrphanTx);
if (nEvicted > 0) {
LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
@@ -4416,7 +4416,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds c
// peers with the forcerelay permission should not filter txs to us
if (pto.HasPermission(NetPermissionFlags::ForceRelay)) return;
- CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
+ CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index d7860f0115..5b6d8416a7 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -583,8 +583,8 @@ public:
}
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
{
- limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ limit_ancestor_count = gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ limit_descendant_count = gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
}
bool checkChainLimits(const CTransactionRef& tx) override
{
@@ -592,10 +592,10 @@ public:
LockPoints lp;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
CTxMemPool::setEntries ancestors;
- auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
- auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
- auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
+ auto limit_ancestor_count = gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ auto limit_ancestor_size = gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
+ auto limit_descendant_count = gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ auto limit_descendant_size = gArgs.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
std::string unused_error_string;
LOCK(m_node.mempool->cs);
return m_node.mempool->CalculateMemPoolAncestors(
@@ -615,7 +615,7 @@ public:
CFeeRate mempoolMinFee() override
{
if (!m_node.mempool) return {};
- return m_node.mempool->GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ return m_node.mempool->GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index e78594390b..a617bb4451 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -182,14 +182,14 @@ void AddressBookPage::onEditAction()
if(indexes.isEmpty())
return;
- EditAddressDialog dlg(
+ auto dlg = new EditAddressDialog(
tab == SendingTab ?
EditAddressDialog::EditSendingAddress :
EditAddressDialog::EditReceivingAddress, this);
- dlg.setModel(model);
+ dlg->setModel(model);
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
- dlg.loadRow(origIndex.row());
- dlg.exec();
+ dlg->loadRow(origIndex.row());
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void AddressBookPage::on_newAddress_clicked()
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index d4895ea6ff..00c9fd3059 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -55,6 +55,7 @@
#include <QThread>
#include <QTimer>
#include <QTranslator>
+#include <QWindow>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@@ -259,6 +260,7 @@ void BitcoinApplication::createOptionsModel(bool resetSettings)
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
{
window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
+ connect(window, &BitcoinGUI::quitRequested, this, &BitcoinApplication::requestShutdown);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown);
@@ -296,7 +298,7 @@ void BitcoinApplication::startThread()
/* communication to and from thread */
connect(&m_executor.value(), &InitExecutor::initializeResult, this, &BitcoinApplication::initializeResult);
- connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &BitcoinApplication::shutdownResult);
+ connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &QCoreApplication::quit);
connect(&m_executor.value(), &InitExecutor::runawayException, this, &BitcoinApplication::handleRunawayException);
connect(this, &BitcoinApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
connect(this, &BitcoinApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
@@ -326,13 +328,17 @@ void BitcoinApplication::requestInitialize()
void BitcoinApplication::requestShutdown()
{
+ for (const auto w : QGuiApplication::topLevelWindows()) {
+ w->hide();
+ }
+
// Show a simple window indicating shutdown status
// Do this first as some of the steps may take some time below,
// for example the RPC console may still be executing a command.
shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
qDebug() << __func__ << ": Requesting shutdown";
- window->hide();
+
// Must disconnect node signals otherwise current thread can deadlock since
// no event loop is running.
window->unsubscribeFromCoreSignals();
@@ -409,15 +415,10 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
pollShutdownTimer->start(200);
} else {
Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
- quit(); // Exit first main loop invocation
+ requestShutdown();
}
}
-void BitcoinApplication::shutdownResult()
-{
- quit(); // Exit second main loop invocation after shutdown finished
-}
-
void BitcoinApplication::handleRunawayException(const QString &message)
{
QMessageBox::critical(
@@ -640,8 +641,6 @@ int GuiMain(int argc, char* argv[])
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
#endif
app.exec();
- app.requestShutdown();
- app.exec();
rv = app.getReturnValue();
} else {
// A dialog with detailed error will have been shown by InitError()
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 602b76052c..5678ca90d2 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -61,8 +61,6 @@ public:
/// Request core initialization
void requestInitialize();
- /// Request core shutdown
- void requestShutdown();
/// Get process return value
int getReturnValue() const { return returnValue; }
@@ -77,7 +75,8 @@ public:
public Q_SLOTS:
void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
- void shutdownResult();
+ /// Request core shutdown
+ void requestShutdown();
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 862bdd3bfe..af05f35759 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -171,7 +171,9 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
frameBlocksLayout->addWidget(unitDisplayControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelWalletEncryptionIcon);
+ labelWalletEncryptionIcon->hide();
frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
+ labelWalletHDStatusIcon->hide();
}
frameBlocksLayout->addWidget(labelProxyIcon);
frameBlocksLayout->addStretch();
@@ -371,7 +373,7 @@ void BitcoinGUI::createActions()
m_mask_values_action->setStatusTip(tr("Mask the values in the Overview tab"));
m_mask_values_action->setCheckable(true);
- connect(quitAction, &QAction::triggered, qApp, QApplication::quit);
+ connect(quitAction, &QAction::triggered, this, &BitcoinGUI::quitRequested);
connect(aboutAction, &QAction::triggered, this, &BitcoinGUI::aboutClicked);
connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt);
connect(optionsAction, &QAction::triggered, this, &BitcoinGUI::optionsClicked);
@@ -848,8 +850,8 @@ void BitcoinGUI::aboutClicked()
if(!clientModel)
return;
- HelpMessageDialog dlg(this, true);
- dlg.exec();
+ auto dlg = new HelpMessageDialog(this, /* about */ true);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void BitcoinGUI::showDebugWindow()
@@ -990,10 +992,11 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
if (!clientModel || !clientModel->getOptionsModel())
return;
- OptionsDialog dlg(this, enableWallet);
- dlg.setCurrentTab(tab);
- dlg.setModel(clientModel->getOptionsModel());
- dlg.exec();
+ auto dlg = new OptionsDialog(this, enableWallet);
+ connect(dlg, &OptionsDialog::quitOnReset, this, &BitcoinGUI::quitRequested);
+ dlg->setCurrentTab(tab);
+ dlg->setModel(clientModel->getOptionsModel());
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state)
@@ -1216,7 +1219,7 @@ void BitcoinGUI::closeEvent(QCloseEvent *event)
// close rpcConsole in case it was open to make some space for the shutdown window
rpcConsole->close();
- QApplication::quit();
+ Q_EMIT quitRequested();
}
else
{
@@ -1410,7 +1413,7 @@ void BitcoinGUI::detectShutdown()
{
if(rpcConsole)
rpcConsole->hide();
- qApp->quit();
+ Q_EMIT quitRequested();
}
}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index c83cd446a0..27045f5cc3 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -214,6 +214,7 @@ private:
void openOptionsDialogWithTab(OptionsDialog::Tab tab);
Q_SIGNALS:
+ void quitRequested();
/** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri);
/** Signal raised when RPC console shown */
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index d2a9365890..e93fedad28 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -55,7 +55,7 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m
contextMenu->addAction(tr("&Copy address"), this, &CoinControlDialog::copyAddress);
contextMenu->addAction(tr("Copy &label"), this, &CoinControlDialog::copyLabel);
contextMenu->addAction(tr("Copy &amount"), this, &CoinControlDialog::copyAmount);
- copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction &ID"), this, &CoinControlDialog::copyTransactionHash);
+ m_copy_transaction_outpoint_action = contextMenu->addAction(tr("Copy transaction &ID and output index"), this, &CoinControlDialog::copyTransactionOutpoint);
contextMenu->addSeparator();
lockAction = contextMenu->addAction(tr("L&ock unspent"), this, &CoinControlDialog::lockCoin);
unlockAction = contextMenu->addAction(tr("&Unlock unspent"), this, &CoinControlDialog::unlockCoin);
@@ -180,7 +180,7 @@ void CoinControlDialog::showMenu(const QPoint &point)
// disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
if (item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
- copyTransactionHashAction->setEnabled(true);
+ m_copy_transaction_outpoint_action->setEnabled(true);
if (model->wallet().isLockedCoin(COutPoint(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt())))
{
lockAction->setEnabled(false);
@@ -194,7 +194,7 @@ void CoinControlDialog::showMenu(const QPoint &point)
}
else // this means click on parent node in tree mode -> disable all
{
- copyTransactionHashAction->setEnabled(false);
+ m_copy_transaction_outpoint_action->setEnabled(false);
lockAction->setEnabled(false);
unlockAction->setEnabled(false);
}
@@ -228,10 +228,14 @@ void CoinControlDialog::copyAddress()
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_ADDRESS));
}
-// context menu action: copy transaction id
-void CoinControlDialog::copyTransactionHash()
+// context menu action: copy transaction id and vout index
+void CoinControlDialog::copyTransactionOutpoint()
{
- GUIUtil::setClipboard(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString());
+ const QString address = contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString();
+ const QString vout = contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toString();
+ const QString outpoint = QString("%1:%2").arg(address).arg(vout);
+
+ GUIUtil::setClipboard(outpoint);
}
// context menu action: lock coin
@@ -241,7 +245,7 @@ void CoinControlDialog::lockCoin()
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
- model->wallet().lockCoin(outpt);
+ model->wallet().lockCoin(outpt, /* write_to_db = */ true);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
updateLabelLocked();
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index 3a03341c9e..bcaf45df42 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -63,7 +63,7 @@ private:
QMenu *contextMenu;
QTreeWidgetItem *contextMenuItem;
- QAction *copyTransactionHashAction;
+ QAction* m_copy_transaction_outpoint_action;
QAction *lockAction;
QAction *unlockAction;
@@ -95,7 +95,7 @@ private Q_SLOTS:
void copyAmount();
void copyLabel();
void copyAddress();
- void copyTransactionHash();
+ void copyTransactionOutpoint();
void lockCoin();
void unlockCoin();
void clipboardQuantity();
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 59d220636d..1c22124616 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -33,7 +33,7 @@
<string>Automatically start %1 after logging in to the system.</string>
</property>
<property name="text">
- <string>&amp;Start %1 on system login</string>
+ <string>Start %1 on system &amp;login</string>
</property>
</widget>
</item>
@@ -179,7 +179,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
- <height>20</height>
+ <height>40</height>
</size>
</property>
</spacer>
@@ -187,6 +187,16 @@
</layout>
</item>
<item>
+ <widget class="QCheckBox" name="enableServer">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for Options window setting that enables the RPC server.">This allows you or a third party tool to communicate with the node through command-line and JSON-RPC commands.</string>
+ </property>
+ <property name="text">
+ <string extracomment="An Options window setting to enable the RPC server.">Enable RPC &amp;server</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer_Main">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -729,10 +739,10 @@
<item>
<widget class="QLabel" name="thirdPartyTxUrlsLabel">
<property name="toolTip">
- <string>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 |.</string>
+ <string>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 |.</string>
</property>
<property name="text">
- <string>&amp;Third party transaction URLs</string>
+ <string>&amp;Third-party transaction URLs</string>
</property>
<property name="buddy">
<cstring>thirdPartyTxUrls</cstring>
@@ -742,7 +752,7 @@
<item>
<widget class="QLineEdit" name="thirdPartyTxUrls">
<property name="toolTip">
- <string>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 |.</string>
+ <string>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 |.</string>
</property>
<property name="placeholderText">
<string notr="true">https://example.com/tx/%s</string>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index e98e50ba14..7b1384b485 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -36,6 +36,7 @@
#include <QClipboard>
#include <QDateTime>
#include <QDesktopServices>
+#include <QDialog>
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFont>
@@ -673,14 +674,26 @@ QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction
{
QString prefix;
if (prepend_direction) {
- prefix = (conn_type == ConnectionType::INBOUND) ? QObject::tr("Inbound") : QObject::tr("Outbound") + " ";
+ prefix = (conn_type == ConnectionType::INBOUND) ?
+ /*: An inbound connection from a peer. An inbound connection
+ is a connection initiated by a peer. */
+ QObject::tr("Inbound") :
+ /*: An outbound connection to a peer. An outbound connection
+ is a connection initiated by us. */
+ QObject::tr("Outbound") + " ";
}
switch (conn_type) {
case ConnectionType::INBOUND: return prefix;
+ //: Peer connection type that relays all network information.
case ConnectionType::OUTBOUND_FULL_RELAY: return prefix + QObject::tr("Full Relay");
+ /*: Peer connection type that relays network information about
+ blocks and not transactions or addresses. */
case ConnectionType::BLOCK_RELAY: return prefix + QObject::tr("Block Relay");
+ //: Peer connection type established manually through one of several methods.
case ConnectionType::MANUAL: return prefix + QObject::tr("Manual");
+ //: Short-lived peer connection type that tests the aliveness of known addresses.
case ConnectionType::FEELER: return prefix + QObject::tr("Feeler");
+ //: Short-lived peer connection type that solicits known addresses from a peer.
case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch");
} // no default case, so the compiler can warn about missing cases
assert(false);
@@ -958,4 +971,11 @@ void PrintSlotException(
PrintExceptionContinue(exception, description.c_str());
}
+void ShowModalDialogAndDeleteOnClose(QDialog* dialog)
+{
+ dialog->setAttribute(Qt::WA_DeleteOnClose);
+ dialog->setWindowModality(Qt::ApplicationModal);
+ dialog->show();
+}
+
} // namespace GUIUtil
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 06a3b63668..274f0bdcbf 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -41,6 +41,7 @@ class QAbstractButton;
class QAbstractItemView;
class QAction;
class QDateTime;
+class QDialog;
class QFont;
class QKeySequence;
class QLineEdit;
@@ -417,6 +418,11 @@ namespace GUIUtil
type);
}
+ /**
+ * Shows a QDialog instance asynchronously, and deletes it on close.
+ */
+ void ShowModalDialogAndDeleteOnClose(QDialog* dialog);
+
} // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H
diff --git a/src/qt/initexecutor.cpp b/src/qt/initexecutor.cpp
index 7060f74dab..24ae7ba73d 100644
--- a/src/qt/initexecutor.cpp
+++ b/src/qt/initexecutor.cpp
@@ -5,6 +5,7 @@
#include <qt/initexecutor.h>
#include <interfaces/node.h>
+#include <qt/guiutil.h>
#include <util/system.h>
#include <util/threadnames.h>
@@ -18,7 +19,7 @@
InitExecutor::InitExecutor(interfaces::Node& node)
: QObject(), m_node(node)
{
- this->moveToThread(&m_thread);
+ m_context.moveToThread(&m_thread);
m_thread.start();
}
@@ -38,29 +39,33 @@ void InitExecutor::handleRunawayException(const std::exception* e)
void InitExecutor::initialize()
{
- try {
- util::ThreadRename("qt-init");
- qDebug() << __func__ << ": Running initialization in thread";
- interfaces::BlockAndHeaderTipInfo tip_info;
- bool rv = m_node.appInitMain(&tip_info);
- Q_EMIT initializeResult(rv, tip_info);
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
+ GUIUtil::ObjectInvoke(&m_context, [this] {
+ try {
+ util::ThreadRename("qt-init");
+ qDebug() << "Running initialization in thread";
+ interfaces::BlockAndHeaderTipInfo tip_info;
+ bool rv = m_node.appInitMain(&tip_info);
+ Q_EMIT initializeResult(rv, tip_info);
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+ });
}
void InitExecutor::shutdown()
{
- try {
- qDebug() << __func__ << ": Running Shutdown in thread";
- m_node.appShutdown();
- qDebug() << __func__ << ": Shutdown finished";
- Q_EMIT shutdownResult();
- } catch (const std::exception& e) {
- handleRunawayException(&e);
- } catch (...) {
- handleRunawayException(nullptr);
- }
+ GUIUtil::ObjectInvoke(&m_context, [this] {
+ try {
+ qDebug() << "Running Shutdown in thread";
+ m_node.appShutdown();
+ qDebug() << "Shutdown finished";
+ Q_EMIT shutdownResult();
+ } catch (const std::exception& e) {
+ handleRunawayException(&e);
+ } catch (...) {
+ handleRunawayException(nullptr);
+ }
+ });
}
diff --git a/src/qt/initexecutor.h b/src/qt/initexecutor.h
index 319ce40465..410c44fa2d 100644
--- a/src/qt/initexecutor.h
+++ b/src/qt/initexecutor.h
@@ -40,6 +40,7 @@ private:
void handleRunawayException(const std::exception* e);
interfaces::Node& m_node;
+ QObject m_context;
QThread m_thread;
};
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index a698a96857..4c78fba752 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -113,7 +113,7 @@ namespace {
//! Return pruning size that will be used if automatic pruning is enabled.
int GetPruneTargetGB()
{
- int64_t prune_target_mib = gArgs.GetArg("-prune", 0);
+ int64_t prune_target_mib = gArgs.GetIntArg("-prune", 0);
// >1 means automatic pruning is enabled by config, 1 means manual pruning, 0 means no pruning.
return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib) : DEFAULT_PRUNE_TARGET_GB;
}
@@ -142,7 +142,7 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si
const int min_prune_target_GB = std::ceil(MIN_DISK_SPACE_FOR_BLOCK_FILES / 1e9);
ui->pruneGB->setRange(min_prune_target_GB, std::numeric_limits<int>::max());
- if (gArgs.GetArg("-prune", 0) > 1) { // -prune=1 means enabled, above that it's a size in MiB
+ if (gArgs.GetIntArg("-prune", 0) > 1) { // -prune=1 means enabled, above that it's a size in MiB
ui->prune->setChecked(true);
ui->prune->setEnabled(false);
}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 92644ef24b..0cc2d61df6 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -210,6 +210,7 @@ void OptionsDialog::setModel(OptionsModel *_model)
connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Network */
connect(ui->allowIncoming, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
+ connect(ui->enableServer, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning);
/* Display */
@@ -246,6 +247,7 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp);
mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
+ mapper->addMapping(ui->enableServer, OptionsModel::Server);
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
@@ -290,7 +292,8 @@ void OptionsDialog::on_resetButton_clicked()
/* reset all options and close GUI */
model->Reset();
- QApplication::quit();
+ close();
+ Q_EMIT quitOnReset();
}
}
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index ba35ff3b67..f14aec3449 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -68,6 +68,7 @@ private Q_SLOTS:
Q_SIGNALS:
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, uint16_t nProxyPort);
+ void quitOnReset();
private:
Ui::OptionsDialog *ui;
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index d87fc1f84a..9e2f38f7ec 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -149,6 +149,13 @@ void OptionsModel::Init(bool resetSettings)
if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
addOverriddenOption("-listen");
+ if (!settings.contains("server")) {
+ settings.setValue("server", false);
+ }
+ if (!gArgs.SoftSetBoolArg("-server", settings.value("server").toBool())) {
+ addOverriddenOption("-server");
+ }
+
if (!settings.contains("fUseProxy"))
settings.setValue("fUseProxy", false);
if (!settings.contains("addrProxy"))
@@ -363,6 +370,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return settings.value("nThreadsScriptVerif");
case Listen:
return settings.value("fListen");
+ case Server:
+ return settings.value("server");
default:
return QVariant();
}
@@ -528,6 +537,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
setRestartRequired(true);
}
break;
+ case Server:
+ if (settings.value("server") != value) {
+ settings.setValue("server", value);
+ setRestartRequired(true);
+ }
+ break;
default:
break;
}
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 203ee27ad8..65544acfbd 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -69,6 +69,7 @@ public:
ExternalSignerPath, // QString
SpendZeroConfChange, // bool
Listen, // bool
+ Server, // bool
OptionIDRowCount,
};
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 289fb9f7c8..34d56e5506 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -71,6 +71,9 @@ void PSBTOperationsDialog::signTransaction()
{
bool complete;
size_t n_signed;
+
+ WalletModel::UnlockContext ctx(m_wallet_model->requestUnlock());
+
TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, &n_signed, m_transaction_data, complete);
if (err != TransactionError::OK) {
@@ -81,7 +84,9 @@ void PSBTOperationsDialog::signTransaction()
updateTransactionDisplay();
- if (!complete && n_signed < 1) {
+ if (!complete && !ctx.isValid()) {
+ showStatus(tr("Cannot sign inputs while wallet is locked."), StatusLevel::WARN);
+ } else if (!complete && n_signed < 1) {
showStatus(tr("Could not sign any more inputs."), StatusLevel::WARN);
} else if (!complete) {
showStatus(tr("Signed %1 inputs, but more signatures are still required.").arg(n_signed),
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 4554f11a41..1c8ed22ada 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -495,14 +495,28 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
+ //: Explanatory text for an inbound peer connection.
tr("Inbound: initiated by peer"),
+ /*: Explanatory text for an outbound peer connection that
+ relays all network information. This is the default behavior for
+ outbound connections. */
tr("Outbound Full Relay: default"),
+ /*: Explanatory text for an outbound peer connection that relays
+ network information about blocks and not transactions or addresses. */
tr("Outbound Block Relay: does not relay transactions or addresses"),
+ /*: Explanatory text for an outbound peer connection that was
+ established manually through one of several methods. The numbered
+ arguments are stand-ins for the methods available to establish
+ manual connections. */
tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options")
.arg("addnode")
.arg(QString(nonbreaking_hyphen) + "addnode")
.arg(QString(nonbreaking_hyphen) + "connect"),
+ /*: Explanatory text for a short-lived outbound peer connection that
+ is used to test the aliveness of known addresses. */
tr("Outbound Feeler: short-lived, for testing addresses"),
+ /*: Explanatory text for a short-lived outbound peer connection that is used
+ to request addresses from a peer. */
tr("Outbound Address Fetch: short-lived, for soliciting addresses")};
const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list));
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index ff53d8160f..2718392940 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -399,9 +399,10 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
- SendConfirmationDialog confirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
- confirmationDialog.exec();
- QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
+ auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
+ confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
+ // TODO: Replace QDialog::exec() with safer QDialog::show().
+ const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
if(retval != QMessageBox::Yes)
{
@@ -914,9 +915,9 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
// Coin Control: button inputs -> show actual coin control dialog
void SendCoinsDialog::coinControlButtonClicked()
{
- CoinControlDialog dlg(*m_coin_control, model, platformStyle);
- dlg.exec();
- coinControlUpdateLabels();
+ auto dlg = new CoinControlDialog(*m_coin_control, model, platformStyle);
+ connect(dlg, &QDialog::finished, this, &SendCoinsDialog::coinControlUpdateLabels);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
// Coin Control: checkbox custom change address
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 884ed25637..55d00bb37e 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -9,7 +9,6 @@
#include <interfaces/init.h>
#include <interfaces/node.h>
#include <qt/bitcoin.h>
-#include <qt/initexecutor.h>
#include <qt/test/apptests.h>
#include <qt/test/rpcnestedtests.h>
#include <qt/test/uritests.h>
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 2f16e6edb4..653f3dda6d 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -222,17 +222,21 @@ void TransactionView::setModel(WalletModel *_model)
{
// Add third party transaction URLs to context menu
QStringList listUrls = GUIUtil::SplitSkipEmptyParts(_model->getOptionsModel()->getThirdPartyTxUrls(), "|");
+ bool actions_created = false;
for (int i = 0; i < listUrls.size(); ++i)
{
QString url = listUrls[i].trimmed();
QString host = QUrl(url, QUrl::StrictMode).host();
if (!host.isEmpty())
{
- QAction *thirdPartyTxUrlAction = new QAction(host, this); // use host as menu item label
- if (i == 0)
+ if (!actions_created) {
contextMenu->addSeparator();
- contextMenu->addAction(thirdPartyTxUrlAction);
- connect(thirdPartyTxUrlAction, &QAction::triggered, [this, url] { openThirdPartyTxUrl(url); });
+ actions_created = true;
+ }
+ /*: Transactions table context menu action to show the
+ selected transaction in a third-party block explorer.
+ %1 is a stand-in argument for the URL of the explorer. */
+ contextMenu->addAction(tr("Show in %1").arg(host), [this, url] { openThirdPartyTxUrl(url); });
}
}
}
@@ -500,22 +504,22 @@ void TransactionView::editLabel()
// Determine type of address, launch appropriate editor dialog type
QString type = modelIdx.data(AddressTableModel::TypeRole).toString();
- EditAddressDialog dlg(
+ auto dlg = new EditAddressDialog(
type == AddressTableModel::Receive
? EditAddressDialog::EditReceivingAddress
: EditAddressDialog::EditSendingAddress, this);
- dlg.setModel(addressBook);
- dlg.loadRow(idx);
- dlg.exec();
+ dlg->setModel(addressBook);
+ dlg->loadRow(idx);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
else
{
// Add sending address
- EditAddressDialog dlg(EditAddressDialog::NewSendingAddress,
+ auto dlg = new EditAddressDialog(EditAddressDialog::NewSendingAddress,
this);
- dlg.setModel(addressBook);
- dlg.setAddress(address);
- dlg.exec();
+ dlg->setModel(addressBook);
+ dlg->setAddress(address);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
}
}
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 5eeb2d5308..4ff92bf82c 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -221,10 +221,9 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard)
return;
}
- PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
+ auto dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
dlg->openWithPSBT(psbtx);
- dlg->setAttribute(Qt::WA_DeleteOnClose);
- dlg->exec();
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void WalletFrame::encryptWallet()
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 967dd588b4..052453cf65 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -506,9 +506,10 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
questionString.append(tr("Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy."));
}
- SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString);
- confirmationDialog.exec();
- QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
+ auto confirmationDialog = new SendConfirmationDialog(tr("Confirm fee bump"), questionString);
+ confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
+ // TODO: Replace QDialog::exec() with safer QDialog::show().
+ const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
// cancel sign&broadcast if user doesn't want to bump the fee
if (retval != QMessageBox::Yes) {
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 309806a1c4..7813b89e41 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -205,11 +205,10 @@ void WalletView::showOutOfSyncWarning(bool fShow)
void WalletView::encryptWallet()
{
- AskPassphraseDialog dlg(AskPassphraseDialog::Encrypt, this);
- dlg.setModel(walletModel);
- dlg.exec();
-
- Q_EMIT encryptionStatusChanged();
+ auto dlg = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, this);
+ dlg->setModel(walletModel);
+ connect(dlg, &QDialog::finished, this, &WalletView::encryptionStatusChanged);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void WalletView::backupWallet()
@@ -234,19 +233,18 @@ void WalletView::backupWallet()
void WalletView::changePassphrase()
{
- AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
- dlg.setModel(walletModel);
- dlg.exec();
+ auto dlg = new AskPassphraseDialog(AskPassphraseDialog::ChangePass, this);
+ dlg->setModel(walletModel);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
void WalletView::unlockWallet()
{
// Unlock wallet when requested by wallet model
- if (walletModel->getEncryptionStatus() == WalletModel::Locked)
- {
- AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
- dlg.setModel(walletModel);
- dlg.exec();
+ if (walletModel->getEncryptionStatus() == WalletModel::Locked) {
+ auto dlg = new AskPassphraseDialog(AskPassphraseDialog::Unlock, this);
+ dlg->setModel(walletModel);
+ GUIUtil::ShowModalDialogAndDeleteOnClose(dlg);
}
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 08d21e0f71..3370afc75f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1256,11 +1256,8 @@ static RPCHelpMan gettxout()
{RPCResult::Type::OBJ, "scriptPubKey", "", {
{RPCResult::Type::STR, "asm", ""},
{RPCResult::Type::STR_HEX, "hex", ""},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
{RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {{RPCResult::Type::STR, "address", "bitcoin address"}}},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
{RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
}},
@@ -1503,7 +1500,7 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("pruneheight", block->nHeight);
// if 0, execution bypasses the whole if block.
- bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1);
+ bool automatic_pruning = (gArgs.GetIntArg("-prune", 0) != 1);
obj.pushKV("automatic_pruning", automatic_pruning);
if (automatic_pruning) {
obj.pushKV("prune_target_size", nPruneTarget);
@@ -1650,7 +1647,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
- size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
+ size_t maxmempool = gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("maxmempool", (int64_t) maxmempool);
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
@@ -1933,16 +1930,6 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
}
}
-void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
-{
- ScriptPubKeyToUniv(scriptPubKey, out, fIncludeHex, IsDeprecatedRPCEnabled("addresses"));
-}
-
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
-{
- TxToUniv(tx, hashBlock, IsDeprecatedRPCEnabled("addresses"), entry, include_hex, serialize_flags, txundo);
-}
-
template<typename T>
static inline bool SetHasKeys(const std::set<T>& set) {return false;}
template<typename T, typename Tk, typename... Args>
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index ffb6f03b47..4b0d855685 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -6,7 +6,6 @@
#define BITCOIN_RPC_BLOCKCHAIN_H
#include <amount.h>
-#include <core_io.h>
#include <streams.h>
#include <sync.h>
@@ -53,9 +52,6 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
/** Used by getblockstats to get feerates at different percentiles by weight */
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
-void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
-void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
-
NodeContext& EnsureAnyNodeContext(const std::any& context);
CTxMemPool& EnsureMemPool(const NodeContext& node);
CTxMemPool& EnsureAnyMemPool(const std::any& context);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index d6943e066a..93e49cb9a8 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -131,6 +131,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettxoutsetinfo", 2, "use_index"},
{ "lockunspent", 0, "unlock" },
{ "lockunspent", 1, "transactions" },
+ { "lockunspent", 2, "persistent" },
{ "send", 0, "outputs" },
{ "send", 1, "conf_target" },
{ "send", 3, "fee_rate"},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index eb2d5b9c5f..066a60b71b 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1103,6 +1103,8 @@ static RPCHelpMan estimatesmartfee()
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
+ const NodeContext& node = EnsureAnyNodeContext(request.context);
+ const CTxMemPool& mempool = EnsureMemPool(node);
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
@@ -1118,7 +1120,10 @@ static RPCHelpMan estimatesmartfee()
UniValue result(UniValue::VOBJ);
UniValue errors(UniValue::VARR);
FeeCalculation feeCalc;
- CFeeRate feeRate = fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative);
+ CFeeRate feeRate{fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative)};
+ CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)};
+ CFeeRate min_relay_feerate{::minRelayTxFee};
+ feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate});
if (feeRate != CFeeRate(0)) {
result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK()));
} else {
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 737abf6ca9..b32bc670b6 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -131,13 +131,8 @@ static RPCHelpMan getrawtransaction()
{
{RPCResult::Type::STR, "asm", "the asm"},
{RPCResult::Type::STR, "hex", "the hex"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "bitcoin address"},
- }},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
}},
}},
@@ -495,13 +490,8 @@ static RPCHelpMan decoderawtransaction()
{
{RPCResult::Type::STR, "asm", "the asm"},
{RPCResult::Type::STR_HEX, "hex", "the hex"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "bitcoin address"},
- }},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
}},
}},
@@ -554,24 +544,14 @@ static RPCHelpMan decodescript()
{
{RPCResult::Type::STR, "asm", "Script public key"},
{RPCResult::Type::STR, "type", "The output type (e.g. "+GetAllOutputTypes()+")"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "bitcoin address"},
- }},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
{RPCResult::Type::STR, "p2sh", /* optional */ true, "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"},
{RPCResult::Type::OBJ, "segwit", /* optional */ true, "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)",
{
{RPCResult::Type::STR, "asm", "String representation of the script public key"},
{RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"},
{RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"},
- {RPCResult::Type::STR, "address", /* optional */ true, "bitcoin address (only if a well-defined address exists)"},
- {RPCResult::Type::NUM, "reqSigs", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Number of required signatures"},
- {RPCResult::Type::ARR, "addresses", /* optional */ true, "(DEPRECATED, returned only if config option -deprecatedrpc=addresses is passed) Array of bitcoin addresses",
- {
- {RPCResult::Type::STR, "address", "segwit address"},
- }},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
{RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"},
}},
}
@@ -592,7 +572,7 @@ static RPCHelpMan decodescript()
} else {
// Empty scripts are valid
}
- ScriptPubKeyToUniv(script, r, /* fIncludeHex */ false);
+ ScriptPubKeyToUniv(script, r, /* include_hex */ false);
UniValue type;
type = find_value(r, "type");
@@ -626,7 +606,7 @@ static RPCHelpMan decodescript()
// Newer segwit program versions should be considered when then become available.
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
- ScriptPubKeyToUniv(segwitScr, sr, /* fIncludeHex */ true);
+ ScriptPubKeyToUniv(segwitScr, sr, /* include_hex */ true);
sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(segwitScr)));
r.pushKV("segwit", sr);
}
@@ -1061,7 +1041,7 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::STR, "asm", "The asm"},
{RPCResult::Type::STR_HEX, "hex", "The hex"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
- {RPCResult::Type::STR, "address", /*optional=*/true, "Bitcoin address if there is one"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"},
}},
}},
{RPCResult::Type::OBJ_DYN, "partial_signatures", /* optional */ true, "",
@@ -1181,7 +1161,7 @@ static RPCHelpMan decodepsbt()
txout = input.witness_utxo;
UniValue o(UniValue::VOBJ);
- ScriptToUniv(txout.scriptPubKey, o, true);
+ ScriptPubKeyToUniv(txout.scriptPubKey, o, /* include_hex */ true);
UniValue out(UniValue::VOBJ);
out.pushKV("amount", ValueFromAmount(txout.nValue));
@@ -1228,12 +1208,12 @@ static RPCHelpMan decodepsbt()
// Redeem script and witness script
if (!input.redeem_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(input.redeem_script, r, false);
+ ScriptToUniv(input.redeem_script, r);
in.pushKV("redeem_script", r);
}
if (!input.witness_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(input.witness_script, r, false);
+ ScriptToUniv(input.witness_script, r);
in.pushKV("witness_script", r);
}
@@ -1288,12 +1268,12 @@ static RPCHelpMan decodepsbt()
// Redeem script and witness script
if (!output.redeem_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(output.redeem_script, r, false);
+ ScriptToUniv(output.redeem_script, r);
out.pushKV("redeem_script", r);
}
if (!output.witness_script.empty()) {
UniValue r(UniValue::VOBJ);
- ScriptToUniv(output.witness_script, r, false);
+ ScriptToUniv(output.witness_script, r);
out.pushKV("witness_script", r);
}
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index cf80b08b96..0d02ec5c47 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -540,7 +540,7 @@ void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nS
int RPCSerializationFlags()
{
int flag = 0;
- if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
+ if (gArgs.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
return flag;
}
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 65867c1c14..6f911f4fe7 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -96,7 +96,7 @@ void InitSignatureCache()
{
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
// setup_bytes creates the minimum possible cache (2 elements).
- size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 67a79a157c..d9656c781d 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -266,47 +266,6 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
assert(false);
}
-// TODO: from v23 ("addresses" and "reqSigs" deprecated) "ExtractDestinations" should be removed
-bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
-{
- addressRet.clear();
- std::vector<valtype> vSolutions;
- typeRet = Solver(scriptPubKey, vSolutions);
- if (typeRet == TxoutType::NONSTANDARD) {
- return false;
- } else if (typeRet == TxoutType::NULL_DATA) {
- // This is data, not addresses
- return false;
- }
-
- if (typeRet == TxoutType::MULTISIG)
- {
- nRequiredRet = vSolutions.front()[0];
- for (unsigned int i = 1; i < vSolutions.size()-1; i++)
- {
- CPubKey pubKey(vSolutions[i]);
- if (!pubKey.IsValid())
- continue;
-
- CTxDestination address = PKHash(pubKey);
- addressRet.push_back(address);
- }
-
- if (addressRet.empty())
- return false;
- }
- else
- {
- nRequiredRet = 1;
- CTxDestination address;
- if (!ExtractDestination(scriptPubKey, address))
- return false;
- addressRet.push_back(address);
- }
-
- return true;
-}
-
namespace {
class CScriptVisitor
{
diff --git a/src/script/standard.h b/src/script/standard.h
index 78492733db..a8e57231bf 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -176,28 +176,12 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
/**
* Parse a standard scriptPubKey for the destination address. Assigns result to
- * the addressRet parameter and returns true if successful. For multisig
- * scripts, instead use ExtractDestinations. Currently only works for P2PK,
+ * the addressRet parameter and returns true if successful. Currently only works for P2PK,
* P2PKH, P2SH, P2WPKH, and P2WSH scripts.
*/
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
/**
- * Parse a standard scriptPubKey with one or more destination addresses. For
- * multisig scripts, this populates the addressRet vector with the pubkey IDs
- * and nRequiredRet with the n required to spend. For other destinations,
- * addressRet is populated with a single value and nRequiredRet is set to 1.
- * Returns true if successful.
- *
- * Note: this function confuses destinations (a subset of CScripts that are
- * encodable as an address) with key identifiers (of keys involved in a
- * CScript), and its use should be phased out.
- *
- * TODO: from v23 ("addresses" and "reqSigs" deprecated) "ExtractDestinations" should be removed
- */
-bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
-
-/**
* Generate a Bitcoin scriptPubKey for the given CTxDestination. Returns a P2PKH
* script for a CKeyID destination, a P2SH script for a CScriptID, and an empty
* script for CNoDestination.
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 5a98558240..23ef2062ef 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
CBloomFilter filter(2, 0.001, 0, BLOOM_UPDATE_ALL);
filter.insert(vchPubKey);
uint160 hash = pubkey.GetID();
- filter.insert(std::vector<unsigned char>(hash.begin(), hash.end()));
+ filter.insert(hash);
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << filter;
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
index 4ea9511870..8304e6fdb8 100644
--- a/src/test/fuzz/muhash.cpp
+++ b/src/test/fuzz/muhash.cpp
@@ -12,52 +12,47 @@
FUZZ_TARGET(muhash)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
- std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- std::vector<uint8_t> data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- if (data.empty()) {
- data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
- }
- if (data2.empty()) {
- data2.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
- }
-
- data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ std::vector<uint8_t> data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ std::vector<uint8_t> data2{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
MuHash3072 muhash;
- // Test that MuHash result is consistent independent of order of operations
muhash.Insert(data);
muhash.Insert(data2);
+ const std::string initial_state_hash{"dd5ad2a105c2d29495f577245c357409002329b9f4d6182c0af3dc2f462555c8"};
uint256 out;
- muhash.Finalize(out);
-
- muhash = MuHash3072();
- muhash.Insert(data2);
- muhash.Insert(data);
-
uint256 out2;
- muhash.Finalize(out2);
-
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ // Test that MuHash result is consistent independent of order of operations
+ muhash.Finalize(out);
+
+ muhash = MuHash3072();
+ muhash.Insert(data2);
+ muhash.Insert(data);
+ muhash.Finalize(out2);
+ },
+ [&] {
+ // Test that multiplication with the initial state never changes the finalized result
+ muhash.Finalize(out);
+ MuHash3072 muhash3;
+ muhash3 *= muhash;
+ muhash3.Finalize(out2);
+ },
+ [&] {
+ // Test that dividing a MuHash by itself brings it back to it's initial state
+ muhash /= muhash;
+ muhash.Finalize(out);
+ out2 = uint256S(initial_state_hash);
+ },
+ [&] {
+ // Test that removing all added elements brings the object back to it's initial state
+ muhash.Remove(data);
+ muhash.Remove(data2);
+ muhash.Finalize(out);
+ out2 = uint256S(initial_state_hash);
+ });
assert(out == out2);
- MuHash3072 muhash3;
- muhash3 *= muhash;
- uint256 out3;
- muhash3.Finalize(out3);
- assert(out == out3);
-
- // Test that removing all added elements brings the object back to it's initial state
- muhash /= muhash;
- muhash.Finalize(out);
-
- MuHash3072 muhash2;
- muhash2.Finalize(out2);
-
- assert(out == out2);
-
- muhash3.Remove(data);
- muhash3.Remove(data2);
- muhash3.Finalize(out3);
- assert(out == out3);
}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 950ee45d1d..74c576322a 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -56,46 +56,8 @@ FUZZ_TARGET_INIT(script, initialize_script)
assert(script == decompressed_script);
}
- CTxDestination address;
- TxoutType type_ret;
- std::vector<CTxDestination> addresses;
- int required_ret;
- bool extract_destinations_ret = ExtractDestinations(script, type_ret, addresses, required_ret);
- bool extract_destination_ret = ExtractDestination(script, address);
- if (!extract_destinations_ret) {
- assert(!extract_destination_ret);
- if (type_ret == TxoutType::MULTISIG) {
- assert(addresses.empty() && required_ret == 0);
- } else {
- assert(type_ret == TxoutType::PUBKEY ||
- type_ret == TxoutType::NONSTANDARD ||
- type_ret == TxoutType::NULL_DATA);
- }
- } else {
- assert(required_ret >= 1 && required_ret <= 16);
- assert((unsigned long)required_ret == addresses.size());
- assert(type_ret == TxoutType::MULTISIG || required_ret == 1);
- }
- if (type_ret == TxoutType::NONSTANDARD || type_ret == TxoutType::NULL_DATA) {
- assert(!extract_destinations_ret);
- }
- if (!extract_destination_ret) {
- assert(type_ret == TxoutType::PUBKEY ||
- type_ret == TxoutType::NONSTANDARD ||
- type_ret == TxoutType::NULL_DATA ||
- type_ret == TxoutType::MULTISIG);
- } else {
- assert(address == addresses[0]);
- }
- if (type_ret == TxoutType::NONSTANDARD ||
- type_ret == TxoutType::NULL_DATA ||
- type_ret == TxoutType::MULTISIG) {
- assert(!extract_destination_ret);
- }
-
TxoutType which_type;
bool is_standard_ret = IsStandard(script, which_type);
- assert(type_ret == which_type);
if (!is_standard_ret) {
assert(which_type == TxoutType::NONSTANDARD ||
which_type == TxoutType::NULL_DATA ||
@@ -112,6 +74,20 @@ FUZZ_TARGET_INIT(script, initialize_script)
which_type == TxoutType::NONSTANDARD);
}
+ CTxDestination address;
+ bool extract_destination_ret = ExtractDestination(script, address);
+ if (!extract_destination_ret) {
+ assert(which_type == TxoutType::PUBKEY ||
+ which_type == TxoutType::NONSTANDARD ||
+ which_type == TxoutType::NULL_DATA ||
+ which_type == TxoutType::MULTISIG);
+ }
+ if (which_type == TxoutType::NONSTANDARD ||
+ which_type == TxoutType::NULL_DATA ||
+ which_type == TxoutType::MULTISIG) {
+ assert(!extract_destination_ret);
+ }
+
const FlatSigningProvider signing_provider;
(void)InferDescriptor(script, signing_provider);
(void)IsSegWitOutput(signing_provider, script);
@@ -133,15 +109,11 @@ FUZZ_TARGET_INIT(script, initialize_script)
(void)ScriptToAsmStr(script, true);
UniValue o1(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o1, true, true);
- ScriptPubKeyToUniv(script, o1, true, false);
+ ScriptPubKeyToUniv(script, o1, true);
UniValue o2(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o2, false, true);
- ScriptPubKeyToUniv(script, o2, false, false);
+ ScriptPubKeyToUniv(script, o2, false);
UniValue o3(UniValue::VOBJ);
- ScriptToUniv(script, o3, true);
- UniValue o4(UniValue::VOBJ);
- ScriptToUniv(script, o4, false);
+ ScriptToUniv(script, o3);
{
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp
index 0f53939eac..dc3f9c8b8f 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -5,6 +5,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <cstdint>
@@ -12,6 +13,11 @@
#include <vector>
namespace {
+void initialize_system()
+{
+ static const auto testing_setup = MakeNoLogFileContext<>();
+}
+
std::string GetArgumentName(const std::string& name)
{
size_t idx = name.find('=');
@@ -20,9 +26,8 @@ std::string GetArgumentName(const std::string& name)
}
return name.substr(0, idx);
}
-} // namespace
-FUZZ_TARGET(system)
+FUZZ_TARGET_INIT(system, initialize_system)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
ArgsManager args_manager{};
@@ -97,7 +102,7 @@ FUZZ_TARGET(system)
const int64_t i64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
const bool b = fuzzed_data_provider.ConsumeBool();
- (void)args_manager.GetArg(s1, i64);
+ (void)args_manager.GetIntArg(s1, i64);
(void)args_manager.GetArg(s1, s2);
(void)args_manager.GetArgFlags(s1);
(void)args_manager.GetArgs(s1);
@@ -114,3 +119,4 @@ FUZZ_TARGET(system)
(void)HelpRequested(args_manager);
}
+} // namespace
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index ff34cc87b2..a21e5cea0c 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -103,6 +103,6 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
(void)IsWitnessStandard(tx, coins_view_cache);
UniValue u(UniValue::VOBJ);
- TxToUniv(tx, /* hashBlock */ uint256::ZERO, /* include_addresses */ true, u);
- TxToUniv(tx, /* hashBlock */ uint256::ONE, /* include_addresses */ false, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ZERO, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ONE, u);
}
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 2a217f3455..17e904fcff 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -137,20 +137,20 @@ BOOST_AUTO_TEST_CASE(intarg)
const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
SetupArgs({foo, bar});
ResetArgs("");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 11);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 11), 11);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 0), 0);
ResetArgs("-foo -bar");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 11), 0);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 0);
ResetArgs("-foo=11 -bar=12");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 0), 11);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 12);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 0), 11);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 12);
ResetArgs("-foo=NaN -bar=NotANumber");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", 1), 0);
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-foo", 1), 0);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 11), 0);
}
BOOST_AUTO_TEST_CASE(doubledash)
@@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(doubledash)
ResetArgs("--foo=verbose --bar=1");
BOOST_CHECK_EQUAL(m_local_args.GetArg("-foo", ""), "verbose");
- BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 0), 1);
+ BOOST_CHECK_EQUAL(m_local_args.GetIntArg("-bar", 0), 1);
}
BOOST_AUTO_TEST_CASE(boolargno)
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index a01d3fa03a..bf8ff5f5e2 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -252,67 +252,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(std::get<WitnessUnknown>(address) == unk);
}
-BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
-{
- CKey keys[3];
- CPubKey pubkeys[3];
- for (int i = 0; i < 3; i++) {
- keys[i].MakeNewKey(true);
- pubkeys[i] = keys[i].GetPubKey();
- }
-
- CScript s;
- TxoutType whichType;
- std::vector<CTxDestination> addresses;
- int nRequired;
-
- // TxoutType::PUBKEY
- s.clear();
- s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY);
- BOOST_CHECK_EQUAL(addresses.size(), 1U);
- BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
-
- // TxoutType::PUBKEYHASH
- s.clear();
- s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH);
- BOOST_CHECK_EQUAL(addresses.size(), 1U);
- BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
-
- // TxoutType::SCRIPTHASH
- CScript redeemScript(s); // initialize with leftover P2PKH script
- s.clear();
- s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH);
- BOOST_CHECK_EQUAL(addresses.size(), 1U);
- BOOST_CHECK_EQUAL(nRequired, 1);
- BOOST_CHECK(std::get<ScriptHash>(addresses[0]) == ScriptHash(redeemScript));
-
- // TxoutType::MULTISIG
- s.clear();
- s << OP_2 <<
- ToByteVector(pubkeys[0]) <<
- ToByteVector(pubkeys[1]) <<
- OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG);
- BOOST_CHECK_EQUAL(addresses.size(), 2U);
- BOOST_CHECK_EQUAL(nRequired, 2);
- BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
- BOOST_CHECK(std::get<PKHash>(addresses[1]) == PKHash(pubkeys[1]));
-
- // TxoutType::NULL_DATA
- s.clear();
- s << OP_RETURN << std::vector<unsigned char>({75});
- BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
-}
-
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
{
CKey keys[3];
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 1924ea55b1..afb3ad0cfd 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -13,6 +13,11 @@
#include <boost/test/unit_test.hpp>
+struct Dersig100Setup : public TestChain100Setup {
+ Dersig100Setup()
+ : TestChain100Setup{{"-testactivationheight=dersig@102"}} {}
+};
+
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
@@ -20,7 +25,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
-BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
+BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
{
// Make sure skipping validation of transactions that were
// validated going into the memory pool does not allow
@@ -153,7 +158,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
}
}
-BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
+BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
{
// Test that passing CheckInputScripts with one set of script flags doesn't imply
// that we would pass again with a different set of flags.
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index df4f003227..97e614379c 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -205,7 +205,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup()
+TestChain100Setup::TestChain100Setup(const std::vector<const char*>& extra_args)
+ : TestingSetup{CBaseChainParams::REGTEST, extra_args}
{
SetMockTime(1598887952);
constexpr std::array<unsigned char, 32> vchKey = {
@@ -321,11 +322,6 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
return mempool_txn;
}
-TestChain100Setup::~TestChain100Setup()
-{
- gArgs.ForceSetArg("-segwitheight", "0");
-}
-
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
{
return FromTx(MakeTransactionRef(tx));
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index acb96e54df..7518cdb042 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -113,8 +113,8 @@ class CScript;
/**
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
-struct TestChain100Setup : public RegTestingSetup {
- TestChain100Setup();
+struct TestChain100Setup : public TestingSetup {
+ TestChain100Setup(const std::vector<const char*>& extra_args = {});
/**
* Create a new block with just given transactions, coinbase paying to
@@ -156,8 +156,6 @@ struct TestChain100Setup : public RegTestingSetup {
CAmount output_amount = CAmount(1 * COIN),
bool submit = true);
- ~TestChain100Setup();
-
std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index a62abf9b9c..a5c9d2ef6f 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -300,9 +300,9 @@ public:
}
if (expect.default_int) {
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), 99999);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), 99999);
} else if (expect.int_value) {
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), *expect.int_value);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), *expect.int_value);
} else {
BOOST_CHECK(!success);
}
@@ -432,8 +432,8 @@ static void TestParse(const std::string& str, bool expected_bool, int64_t expect
BOOST_CHECK(test.ParseParameters(2, (char**)argv, error));
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), expected_bool);
BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), expected_bool);
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99998), expected_int);
- BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), expected_int);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99998), expected_int);
+ BOOST_CHECK_EQUAL(test.GetIntArg("-value", 99999), expected_int);
}
// Test bool and int parsing.
@@ -784,9 +784,9 @@ BOOST_AUTO_TEST_CASE(util_GetArg)
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
- BOOST_CHECK_EQUAL(testArgs.GetArg("inttest1", -1), 12345);
- BOOST_CHECK_EQUAL(testArgs.GetArg("inttest2", -1), 81985529216486895LL);
- BOOST_CHECK_EQUAL(testArgs.GetArg("inttest3", -1), -1);
+ BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest1", -1), 12345);
+ BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest2", -1), 81985529216486895LL);
+ BOOST_CHECK_EQUAL(testArgs.GetIntArg("inttest3", -1), -1);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest1", false), true);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false);
BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false);
diff --git a/src/timedata.cpp b/src/timedata.cpp
index f53fbe231b..69d0273e79 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -75,7 +75,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
int64_t nMedian = vTimeOffsets.median();
std::vector<int64_t> vSorted = vTimeOffsets.sorted();
// Only let other nodes change our time by so much
- int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT));
+ int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetIntArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT));
if (nMedian >= -max_adjustment && nMedian <= max_adjustment) {
nTimeOffset = nMedian;
} else {
diff --git a/src/txdb.cpp b/src/txdb.cpp
index cfa864668a..3839c9083c 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -107,8 +107,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
CDBBatch batch(*m_db);
size_t count = 0;
size_t changed = 0;
- size_t batch_size = (size_t)gArgs.GetArg("-dbbatchsize", nDefaultDbBatchSize);
- int crash_simulate = gArgs.GetArg("-dbcrashratio", 0);
+ size_t batch_size = (size_t)gArgs.GetIntArg("-dbbatchsize", nDefaultDbBatchSize);
+ int crash_simulate = gArgs.GetIntArg("-dbcrashratio", 0);
assert(!hashBlock.IsNull());
uint256 old_tip = GetBestBlock();
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 08f62f1da7..4defeed4ce 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -591,7 +591,7 @@ std::string ArgsManager::GetArg(const std::string& strArg, const std::string& st
return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str();
}
-int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
+int64_t ArgsManager::GetIntArg(const std::string& strArg, int64_t nDefault) const
{
const util::SettingsValue value = GetSetting(strArg);
return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : atoi64(value.get_str());
diff --git a/src/util/system.h b/src/util/system.h
index 3c1399629c..2e217f6f90 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -327,7 +327,7 @@ protected:
* @param nDefault (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
- int64_t GetArg(const std::string& strArg, int64_t nDefault) const;
+ int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const;
/**
* Return boolean argument or default value
diff --git a/src/validation.cpp b/src/validation.cpp
index 51cb1d373c..4504d2ca0a 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -373,8 +373,8 @@ void CChainState::MaybeUpdateMempoolForReorg(
LimitMempoolSize(
*m_mempool,
this->CoinsTip(),
- gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
- std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
+ std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
}
/**
@@ -425,10 +425,10 @@ class MemPoolAccept
{
public:
explicit MemPoolAccept(CTxMemPool& mempool, CChainState& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate),
- m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
- m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
- m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
- m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
+ m_limit_ancestors(gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
+ m_limit_ancestor_size(gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
+ m_limit_descendants(gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
+ m_limit_descendant_size(gArgs.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -510,7 +510,7 @@ private:
// Compare a package's feerate against minimum allowed.
bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
{
- CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
+ CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
}
@@ -911,7 +911,7 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
- LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
if (!m_pool.exists(hash))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
}
@@ -1273,7 +1273,7 @@ void InitScriptExecutionCache() {
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
// setup_bytes creates the minimum possible cache (2 elements).
- size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
size_t nElems = g_scriptExecutionCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
@@ -1898,7 +1898,7 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
{
return this->GetCoinsCacheSizeState(
m_coinstip_cache_size_bytes,
- gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
@@ -2552,7 +2552,7 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
- int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
+ int nStopAtHeight = gArgs.GetIntArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
// Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during
@@ -3740,7 +3740,7 @@ bool BlockManager::LoadBlockIndexDB(std::set<CBlockIndex*, CBlockIndexWorkCompar
void CChainState::LoadMempool(const ArgsManager& args)
{
if (!m_mempool) return;
- if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ if (args.GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
::LoadMempool(*m_mempool, *this);
}
m_mempool->SetIsLoaded(!ShutdownRequested());
@@ -4458,7 +4458,7 @@ static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function)
{
const CChainParams& chainparams = Params();
- int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
+ int64_t nExpiryTimeout = gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat", "rb")};
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 1dc23374e3..2eb4d3106c 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -375,7 +375,7 @@ void BerkeleyBatch::Flush()
nMinutes = 1;
if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
- env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
+ env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetIntArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
}
}
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index 1699424657..d2f30abf23 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -5,9 +5,11 @@
#include <wallet/coinselection.h>
#include <policy/feerate.h>
+#include <util/check.h>
#include <util/system.h>
#include <util/moneystr.h>
+#include <numeric>
#include <optional>
// Descending order comparator
@@ -168,6 +170,30 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selectio
return true;
}
+std::optional<std::pair<std::set<CInputCoin>, CAmount>> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value)
+{
+ std::set<CInputCoin> out_set;
+ CAmount value_ret = 0;
+
+ std::vector<size_t> indexes;
+ indexes.resize(utxo_pool.size());
+ std::iota(indexes.begin(), indexes.end(), 0);
+ Shuffle(indexes.begin(), indexes.end(), FastRandomContext());
+
+ CAmount selected_eff_value = 0;
+ for (const size_t i : indexes) {
+ const OutputGroup& group = utxo_pool.at(i);
+ Assume(group.GetSelectionAmount() > 0);
+ selected_eff_value += group.GetSelectionAmount();
+ value_ret += group.m_value;
+ util::insert(out_set, group.m_outputs);
+ if (selected_eff_value >= target_value) {
+ return std::make_pair(out_set, value_ret);
+ }
+ }
+ return std::nullopt;
+}
+
static void ApproximateBestSubset(const std::vector<OutputGroup>& groups, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 35617d455b..a28bee622e 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -10,6 +10,8 @@
#include <primitives/transaction.h>
#include <random.h>
+#include <optional>
+
//! target minimum change amount
static constexpr CAmount MIN_CHANGE{COIN / 100};
//! final minimum change amount after paying for fees
@@ -174,7 +176,9 @@ struct OutputGroup
* change_cost = effective_feerate * change_output_size + long_term_feerate * change_spend_size
*
* @param[in] inputs The selected inputs
- * @param[in] change_cost The cost of creating change and spending it in the future. Only used if there is change. Must be 0 if there is no change.
+ * @param[in] change_cost The cost of creating change and spending it in the future.
+ * Only used if there is change, in which case it must be positive.
+ * Must be 0 if there is no change.
* @param[in] target The amount targeted by the coin selection algorithm.
* @param[in] use_effective_value Whether to use the input's effective value (when true) or the real value (when false).
* @return The waste
@@ -183,6 +187,15 @@ struct OutputGroup
bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change, std::set<CInputCoin>& out_set, CAmount& value_ret);
+/** Select coins by Single Random Draw. OutputGroups are selected randomly from the eligible
+ * outputs until the target is satisfied
+ *
+ * @param[in] utxo_pool The positive effective value OutputGroups eligible for selection
+ * @param[in] target_value The target value to select for
+ * @returns If successful, a pair of set of outputs and total selected value, otherwise, std::nullopt
+ */
+std::optional<std::pair<std::set<CInputCoin>, CAmount>> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value);
+
// Original coin selection algorithm as a fallback
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index 9a8c1e3c02..d9fc6de79b 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -214,15 +214,17 @@ public:
LOCK(m_wallet->cs_wallet);
return m_wallet->DisplayAddress(dest);
}
- void lockCoin(const COutPoint& output) override
+ bool lockCoin(const COutPoint& output, const bool write_to_db) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->LockCoin(output);
+ std::unique_ptr<WalletBatch> batch = write_to_db ? std::make_unique<WalletBatch>(m_wallet->GetDatabase()) : nullptr;
+ return m_wallet->LockCoin(output, batch.get());
}
- void unlockCoin(const COutPoint& output) override
+ bool unlockCoin(const COutPoint& output) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->UnlockCoin(output);
+ std::unique_ptr<WalletBatch> batch = std::make_unique<WalletBatch>(m_wallet->GetDatabase());
+ return m_wallet->UnlockCoin(output, batch.get());
}
bool isLockedCoin(const COutPoint& output) override
{
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 72c60c8fe2..382e8b6116 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1488,7 +1488,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
} else {
warnings.push_back("Range not given, using default keypool range");
range_start = 0;
- range_end = gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE);
+ range_end = gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE);
}
next_index = range_start;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 77757e79a3..7bc2dc7b21 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1762,7 +1762,7 @@ static RPCHelpMan gettransaction()
if (verbose) {
UniValue decoded(UniValue::VOBJ);
- TxToUniv(*wtx.tx, uint256(), pwallet->chain().rpcEnableDeprecated("addresses"), decoded, false);
+ TxToUniv(*wtx.tx, uint256(), decoded, false);
entry.pushKV("decoded", decoded);
}
@@ -2140,8 +2140,9 @@ static RPCHelpMan lockunspent()
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
"Manually selected coins are automatically unlocked.\n"
- "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
- "is always cleared (by virtue of process exit) when a node stops or fails.\n"
+ "Locks are stored in memory only, unless persistent=true, in which case they will be written to the\n"
+ "wallet database and loaded on node start. Unwritten (persistent=false) locks are always cleared\n"
+ "(by virtue of process exit) when a node stops or fails. Unlocking will clear both persistent and not.\n"
"Also see the listunspent call\n",
{
{"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Whether to unlock (true) or lock (false) the specified transactions"},
@@ -2155,6 +2156,7 @@ static RPCHelpMan lockunspent()
},
},
},
+ {"persistent", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to write/erase this lock in the wallet database, or keep the change in memory only. Ignored for unlocking."},
},
RPCResult{
RPCResult::Type::BOOL, "", "Whether the command was successful or not"
@@ -2168,6 +2170,8 @@ static RPCHelpMan lockunspent()
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
+ "\nLock the transaction persistently in the wallet database\n"
+ + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\" true") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
},
@@ -2186,9 +2190,13 @@ static RPCHelpMan lockunspent()
bool fUnlock = request.params[0].get_bool();
+ const bool persistent{request.params[2].isNull() ? false : request.params[2].get_bool()};
+
if (request.params[1].isNull()) {
- if (fUnlock)
- pwallet->UnlockAllCoins();
+ if (fUnlock) {
+ if (!pwallet->UnlockAllCoins())
+ throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coins failed");
+ }
return true;
}
@@ -2239,17 +2247,24 @@ static RPCHelpMan lockunspent()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output");
}
- if (!fUnlock && is_locked) {
+ if (!fUnlock && is_locked && !persistent) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked");
}
outputs.push_back(outpt);
}
+ std::unique_ptr<WalletBatch> batch = nullptr;
+ // Unlock is always persistent
+ if (fUnlock || persistent) batch = std::make_unique<WalletBatch>(pwallet->GetDatabase());
+
// Atomically set (un)locked status for the outputs.
for (const COutPoint& outpt : outputs) {
- if (fUnlock) pwallet->UnlockCoin(outpt);
- else pwallet->LockCoin(outpt);
+ if (fUnlock) {
+ if (!pwallet->UnlockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Unlocking coin failed");
+ } else {
+ if (!pwallet->LockCoin(outpt, batch.get())) throw JSONRPCError(RPC_WALLET_ERROR, "Locking coin failed");
+ }
}
return true;
@@ -3784,7 +3799,6 @@ public:
obj.pushKV("embedded", std::move(subobj));
} else if (which_type == TxoutType::MULTISIG) {
// Also report some information on multisig scripts (which do not have a corresponding address).
- // TODO: abstract out the common functionality between this logic and ExtractDestinations.
obj.pushKV("sigsrequired", solutions_data[0][0]);
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
@@ -4405,7 +4419,7 @@ static RPCHelpMan walletprocesspsbt()
HELP_REQUIRING_PASSPHRASE,
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction base64 string"},
- {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating"},
+ {"sign", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also sign the transaction when updating (requires wallet to be unlocked)"},
{"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type to sign with if not specified by the PSBT. Must be one of\n"
" \"DEFAULT\"\n"
" \"ALL\"\n"
@@ -4452,6 +4466,9 @@ static RPCHelpMan walletprocesspsbt()
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool complete = true;
+
+ if (sign) EnsureWalletIsUnlocked(*pwallet);
+
const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs)};
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index fe41f9b8cc..fdfb36bb0a 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -331,7 +331,7 @@ bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t i
CHDChain& chain = it->second;
// Top up key pool
- int64_t target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
+ int64_t target_size = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
// "size" of the keypools. Not really the size, actually the difference between index and the chain counter
// Since chain counter is 1 based and index is 0 based, one of them needs to be offset by 1.
@@ -1259,7 +1259,7 @@ bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize)
if (kpSize > 0)
nTargetSize = kpSize;
else
- nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
+ nTargetSize = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);
// count amount of available keys (internal, external)
// make sure the keypool of external and internal keys fits the user selected target (-keypool)
@@ -1764,7 +1764,7 @@ bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)
if (size > 0) {
target_size = size;
} else {
- target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
+ target_size = std::max(gArgs.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1);
}
// Calculate the new range_end
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 4a7a268982..1724375f4c 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -387,6 +387,15 @@ bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const
results.emplace_back(std::make_tuple(waste, std::move(knapsack_coins), knapsack_value));
}
+ // We include the minimum final change for SRD as we do want to avoid making really small change.
+ // KnapsackSolver does not need this because it includes MIN_CHANGE internally.
+ const CAmount srd_target = nTargetValue + coin_selection_params.m_change_fee + MIN_FINAL_CHANGE;
+ auto srd_result = SelectCoinsSRD(positive_groups, srd_target);
+ if (srd_result != std::nullopt) {
+ const auto waste = GetSelectionWaste(srd_result->first, coin_selection_params.m_cost_of_change, srd_target, !coin_selection_params.m_subtract_fee_outputs);
+ results.emplace_back(std::make_tuple(waste, std::move(srd_result->first), srd_result->second));
+ }
+
if (results.size() == 0) {
// No solution found
return false;
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 2e60aca017..815d17967c 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -212,6 +212,10 @@ void SQLiteDatabase::Open()
if (ret != SQLITE_OK) {
throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
}
+ ret = sqlite3_extended_result_codes(m_db, 1);
+ if (ret != SQLITE_OK) {
+ throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable extended result codes: %s\n", sqlite3_errstr(ret)));
+ }
}
if (sqlite3_db_readonly(m_db, "main") != 0) {
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 5d51809241..40d9e90d56 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -724,12 +724,25 @@ BOOST_AUTO_TEST_CASE(waste_test)
BOOST_CHECK_LT(waste_nochange2, waste_nochange1);
selection.clear();
- // 0 Waste only when fee == long term fee, no change, and no excess
+ // No Waste when fee == long_term_fee, no change, and no excess
add_coin(1 * COIN, 1, selection, fee, fee);
add_coin(2 * COIN, 2, selection, fee, fee);
- const CAmount exact_target = in_amt - 2 * fee;
- BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, 0, exact_target));
+ const CAmount exact_target{in_amt - fee * 2};
+ BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change_cost */ 0, exact_target));
+ selection.clear();
+ // No Waste when (fee - long_term_fee) == (-cost_of_change), and no excess
+ const CAmount new_change_cost{fee_diff * 2};
+ add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
+ add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
+ BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, new_change_cost, target));
+ selection.clear();
+
+ // No Waste when (fee - long_term_fee) == (-excess), no change cost
+ const CAmount new_target{in_amt - fee * 2 - fee_diff * 2};
+ add_coin(1 * COIN, 1, selection, fee, fee + fee_diff);
+ add_coin(2 * COIN, 2, selection, fee, fee + fee_diff);
+ BOOST_CHECK_EQUAL(0, GetSelectionWaste(selection, /* change cost */ 0, new_target));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 70349b2455..6885499be2 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -589,11 +589,16 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
return false;
}
-void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
+void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch)
{
mapTxSpends.insert(std::make_pair(outpoint, wtxid));
- setLockedCoins.erase(outpoint);
+ if (batch) {
+ UnlockCoin(outpoint, batch);
+ } else {
+ WalletBatch temp_batch(GetDatabase());
+ UnlockCoin(outpoint, &temp_batch);
+ }
std::pair<TxSpends::iterator, TxSpends::iterator> range;
range = mapTxSpends.equal_range(outpoint);
@@ -601,7 +606,7 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
}
-void CWallet::AddToSpends(const uint256& wtxid)
+void CWallet::AddToSpends(const uint256& wtxid, WalletBatch* batch)
{
auto it = mapWallet.find(wtxid);
assert(it != mapWallet.end());
@@ -610,7 +615,7 @@ void CWallet::AddToSpends(const uint256& wtxid)
return;
for (const CTxIn& txin : thisTx.tx->vin)
- AddToSpends(txin.prevout, wtxid);
+ AddToSpends(txin.prevout, wtxid, batch);
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
@@ -879,7 +884,7 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
return false;
}
-CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose)
+CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose, bool rescanning_old_block)
{
LOCK(cs_wallet);
@@ -909,8 +914,8 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
wtx.nTimeReceived = chain().getAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&batch);
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
- wtx.nTimeSmart = ComputeTimeSmart(wtx);
- AddToSpends(hash);
+ wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
+ AddToSpends(hash, &batch);
}
if (!fInsertedNew)
@@ -1026,7 +1031,7 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx
return true;
}
-bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate, bool rescanning_old_block)
{
const CTransaction& tx = *ptx;
{
@@ -1064,7 +1069,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co
// Block disconnection override an abandoned tx as unconfirmed
// which means user may have to call abandontransaction again
- return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false);
+ return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false, rescanning_old_block);
}
}
return false;
@@ -1193,9 +1198,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
}
}
-void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx)
+void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx, bool rescanning_old_block)
{
- if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx))
+ if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx, rescanning_old_block))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1638,7 +1643,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate);
+ SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate, /* rescanning_old_block */ true);
}
// scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash;
@@ -2260,22 +2265,36 @@ bool CWallet::DisplayAddress(const CTxDestination& dest)
return signer_spk_man->DisplayAddress(scriptPubKey, signer);
}
-void CWallet::LockCoin(const COutPoint& output)
+bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch)
{
AssertLockHeld(cs_wallet);
setLockedCoins.insert(output);
+ if (batch) {
+ return batch->WriteLockedUTXO(output);
+ }
+ return true;
}
-void CWallet::UnlockCoin(const COutPoint& output)
+bool CWallet::UnlockCoin(const COutPoint& output, WalletBatch* batch)
{
AssertLockHeld(cs_wallet);
- setLockedCoins.erase(output);
+ bool was_locked = setLockedCoins.erase(output);
+ if (batch && was_locked) {
+ return batch->EraseLockedUTXO(output);
+ }
+ return true;
}
-void CWallet::UnlockAllCoins()
+bool CWallet::UnlockAllCoins()
{
AssertLockHeld(cs_wallet);
+ bool success = true;
+ WalletBatch batch(GetDatabase());
+ for (auto it = setLockedCoins.begin(); it != setLockedCoins.end(); ++it) {
+ success &= batch.EraseLockedUTXO(*it);
+ }
setLockedCoins.clear();
+ return success;
}
bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
@@ -2365,6 +2384,8 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
* - If sending a transaction, assign its timestamp to the current time.
* - If receiving a transaction outside a block, assign its timestamp to the
* current time.
+ * - If receiving a transaction during a rescanning process, assign all its
+ * (not already known) transactions' timestamps to the block time.
* - If receiving a block with a future timestamp, assign all its (not already
* known) transactions' timestamps to the current time.
* - If receiving a block with a past timestamp, before the most recent known
@@ -2379,38 +2400,43 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
* https://bitcointalk.org/?topic=54527, or
* https://github.com/bitcoin/bitcoin/pull/1393.
*/
-unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
+unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const
{
unsigned int nTimeSmart = wtx.nTimeReceived;
if (!wtx.isUnconfirmed() && !wtx.isAbandoned()) {
int64_t blocktime;
- if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime))) {
- int64_t latestNow = wtx.nTimeReceived;
- int64_t latestEntry = 0;
-
- // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
- int64_t latestTolerated = latestNow + 300;
- const TxItems& txOrdered = wtxOrdered;
- for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
- CWalletTx* const pwtx = it->second;
- if (pwtx == &wtx) {
- continue;
- }
- int64_t nSmartTime;
- nSmartTime = pwtx->nTimeSmart;
- if (!nSmartTime) {
- nSmartTime = pwtx->nTimeReceived;
- }
- if (nSmartTime <= latestTolerated) {
- latestEntry = nSmartTime;
- if (nSmartTime > latestNow) {
- latestNow = nSmartTime;
+ int64_t block_max_time;
+ if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime).maxTime(block_max_time))) {
+ if (rescanning_old_block) {
+ nTimeSmart = block_max_time;
+ } else {
+ int64_t latestNow = wtx.nTimeReceived;
+ int64_t latestEntry = 0;
+
+ // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
+ int64_t latestTolerated = latestNow + 300;
+ const TxItems& txOrdered = wtxOrdered;
+ for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
+ CWalletTx* const pwtx = it->second;
+ if (pwtx == &wtx) {
+ continue;
+ }
+ int64_t nSmartTime;
+ nSmartTime = pwtx->nTimeSmart;
+ if (!nSmartTime) {
+ nSmartTime = pwtx->nTimeReceived;
+ }
+ if (nSmartTime <= latestTolerated) {
+ latestEntry = nSmartTime;
+ if (nSmartTime > latestNow) {
+ latestNow = nSmartTime;
+ }
+ break;
}
- break;
}
- }
- nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
+ nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
+ }
} else {
WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.m_confirm.hashBlock.ToString());
}
@@ -2716,7 +2742,7 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
_("The wallet will avoid paying less than the minimum relay fee."));
}
- walletInstance->m_confirm_target = args.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
+ walletInstance->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
walletInstance->m_signal_rbf = args.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 2dc9eff712..15a5933424 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -256,8 +256,8 @@ private:
*/
typedef std::multimap<COutPoint, uint256> TxSpends;
TxSpends mapTxSpends GUARDED_BY(cs_wallet);
- void AddToSpends(const COutPoint& outpoint, const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void AddToSpends(const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AddToSpends(const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
@@ -271,8 +271,11 @@ private:
* abandoned is an indication that it is not safe to be considered abandoned.
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
+ *
+ * Should be called with rescanning_old_block set to true, if the transaction is
+ * not discovered in real time, but during a rescan of old blocks.
*/
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate, bool rescanning_old_block) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
@@ -284,7 +287,7 @@ private:
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** WalletFlags set on this wallet. */
std::atomic<uint64_t> m_wallet_flags{0};
@@ -449,9 +452,9 @@ public:
bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool LockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool UnlockCoin(const COutPoint& output, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ListLockedCoins(std::vector<COutPoint>& vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/*
@@ -484,7 +487,7 @@ public:
bool EncryptWallet(const SecureString& strWalletPassphrase);
void GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
+ unsigned int ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const;
/**
* Increment the next transaction order id
@@ -503,7 +506,7 @@ public:
//! @return true if wtx is changed and needs to be saved to disk, otherwise false
using UpdateWalletTxFn = std::function<bool(CWalletTx& wtx, bool new_tx)>;
- CWalletTx* AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true);
+ CWalletTx* AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true, bool rescanning_old_block = false);
bool LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override;
void blockConnected(const CBlock& block, int height) override;
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 03464cd2c8..c697534c06 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -40,6 +40,7 @@ const std::string FLAGS{"flags"};
const std::string HDCHAIN{"hdchain"};
const std::string KEYMETA{"keymeta"};
const std::string KEY{"key"};
+const std::string LOCKED_UTXO{"lockedutxo"};
const std::string MASTER_KEY{"mkey"};
const std::string MINVERSION{"minversion"};
const std::string NAME{"name"};
@@ -284,6 +285,16 @@ bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const Descri
return true;
}
+bool WalletBatch::WriteLockedUTXO(const COutPoint& output)
+{
+ return WriteIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)), uint8_t{'1'});
+}
+
+bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
+{
+ return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)));
+}
+
class CWalletScanState {
public:
unsigned int nKeys{0};
@@ -701,6 +712,12 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.m_descriptor_crypt_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(pubkey, privkey)));
wss.fIsEncrypted = true;
+ } else if (strType == DBKeys::LOCKED_UTXO) {
+ uint256 hash;
+ uint32_t n;
+ ssKey >> hash;
+ ssKey >> n;
+ pwallet->LockCoin(COutPoint(hash, n));
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 25c2ec5909..a549c8039c 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -65,6 +65,7 @@ extern const std::string FLAGS;
extern const std::string HDCHAIN;
extern const std::string KEY;
extern const std::string KEYMETA;
+extern const std::string LOCKED_UTXO;
extern const std::string MASTER_KEY;
extern const std::string MINVERSION;
extern const std::string NAME;
@@ -250,6 +251,9 @@ public:
bool WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index);
bool WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache);
+ bool WriteLockedUTXO(const COutPoint& output);
+ bool EraseLockedUTXO(const COutPoint& output);
+
/// Write destination data key,value tuple to database
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);
/// Erase destination data tuple from wallet database
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 86f47d71f3..a53de34db4 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -47,7 +47,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
notifier->SetType(entry.first);
notifier->SetAddress(address);
- notifier->SetOutboundMessageHighWaterMark(static_cast<int>(gArgs.GetArg(arg + "hwm", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM)));
+ notifier->SetOutboundMessageHighWaterMark(static_cast<int>(gArgs.GetIntArg(arg + "hwm", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM)));
notifiers.push_back(std::move(notifier));
}
}
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index 55d3e48c64..5a8394db2e 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -5,6 +5,7 @@
"""Test addrman functionality"""
import os
+import re
import struct
from test_framework.messages import ser_uint256, hash256
@@ -56,7 +57,7 @@ class AddrmanTest(BitcoinTestFramework):
init_error = lambda reason: (
f"Error: Invalid or corrupt peers.dat \\({reason}\\). If you believe this "
f"is a bug, please report it to {self.config['environment']['PACKAGE_BUGREPORT']}. "
- f'As a workaround, you can move the file \\("{peers_dat}"\\) out of the way \\(rename, '
+ f'As a workaround, you can move the file \\("{re.escape(peers_dat)}"\\) out of the way \\(rename, '
"move, or delete\\) to have a new one created on the next start."
)
diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py
index 2dc1e3a7cb..debd87962f 100755
--- a/test/functional/feature_asmap.py
+++ b/test/functional/feature_asmap.py
@@ -42,8 +42,8 @@ class AsmapTest(BitcoinTestFramework):
self.extra_args = [["-checkaddrman=1"]] # Do addrman checks on all operations.
def fill_addrman(self, node_id):
- """Add 2 tried addresses to the addrman, followed by 2 new addresses."""
- for addr, tried in [[0, True], [1, True], [2, False], [3, False]]:
+ """Add 1 tried address to the addrman, followed by 1 new address."""
+ for addr, tried in [[0, True], [1, False]]:
self.nodes[node_id].addpeeraddress(address=f"101.{addr}.0.0", tried=tried, port=8333)
def test_without_asmap_arg(self):
@@ -89,7 +89,7 @@ class AsmapTest(BitcoinTestFramework):
self.restart_node(0, ["-asmap", "-checkaddrman=1"])
with self.node.assert_debug_log(
expected_msgs=[
- "Addrman checks started: new 2, tried 2, total 4",
+ "Addrman checks started: new 1, tried 1, total 2",
"Addrman checks completed successfully",
]
):
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index 09cda8444a..ee2c71cd42 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -41,8 +41,14 @@ class BIP68Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [
- ["-acceptnonstdtxn=1"],
- ["-acceptnonstdtxn=0"],
+ [
+ '-testactivationheight=csv@432',
+ "-acceptnonstdtxn=1",
+ ],
+ [
+ '-testactivationheight=csv@432',
+ "-acceptnonstdtxn=0",
+ ],
]
def skip_test_if_missing_module(self):
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 777787ed32..b06ea8542b 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -82,7 +82,10 @@ class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-acceptnonstdtxn=1']] # This is a consensus block test, we don't care about tx policy
+ self.extra_args = [[
+ '-acceptnonstdtxn=1', # This is a consensus block test, we don't care about tx policy
+ '-testactivationheight=bip34@2',
+ ]]
def run_test(self):
node = self.nodes[0] # convenience reference to the node
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 2c3ef9b88b..3dc858f5d2 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -8,7 +8,6 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates.
"""
from test_framework.blocktools import (
- CLTV_HEIGHT,
create_block,
create_coinbase,
)
@@ -76,10 +75,14 @@ def cltv_validate(tx, height):
cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
+CLTV_HEIGHT = 111
+
+
class BIP65Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [[
+ f'-testactivationheight=cltv@{CLTV_HEIGHT}',
'-whitelist=noban@127.0.0.1',
'-par=1', # Use only one script thread to get the exact reject reason for testing
'-acceptnonstdtxn=1', # cltv_invalidate is nonstandard
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index d2b3fe45d1..5255b13bd1 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -41,7 +41,6 @@ from itertools import product
import time
from test_framework.blocktools import (
- CSV_ACTIVATION_HEIGHT,
create_block,
create_coinbase,
)
@@ -89,12 +88,16 @@ def all_rlt_txs(txs):
return [tx['tx'] for tx in txs]
+CSV_ACTIVATION_HEIGHT = 432
+
+
class BIP68_112_113Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
self.extra_args = [[
'-whitelist=noban@127.0.0.1',
+ f'-testactivationheight=csv@{CSV_ACTIVATION_HEIGHT}',
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
self.supports_cli = False
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 595d26611a..28aff1f2f9 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -8,7 +8,6 @@ Test the DERSIG soft-fork activation on regtest.
"""
from test_framework.blocktools import (
- DERSIG_HEIGHT,
create_block,
create_coinbase,
)
@@ -42,10 +41,14 @@ def unDERify(tx):
tx.vin[0].scriptSig = CScript(newscript)
+DERSIG_HEIGHT = 102
+
+
class BIP66Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [[
+ f'-testactivationheight=dersig@{DERSIG_HEIGHT}',
'-whitelist=noban@127.0.0.1',
'-par=1', # Use only one script thread to get the exact log msg for testing
]]
@@ -83,7 +86,6 @@ class BIP66Test(BitcoinTestFramework):
tip = self.nodes[0].getbestblockhash()
block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time)
- block.nVersion = 2
block.vtx.append(spendtx)
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
@@ -110,7 +112,7 @@ class BIP66Test(BitcoinTestFramework):
peer.sync_with_ping()
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
- block.nVersion = 3
+ block.nVersion = 4
spendtx = self.create_tx(self.coinbase_txids[1])
unDERify(spendtx)
@@ -139,7 +141,7 @@ class BIP66Test(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
peer.sync_with_ping()
- self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
+ self.log.info("Test that a block with a DERSIG-compliant transaction is accepted")
block.vtx[1] = self.create_tx(self.coinbase_txids[1])
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index c4610f98bd..9c225dc687 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -132,9 +132,13 @@ def check_smart_estimates(node, fees_seen):
delta = 1.0e-6 # account for rounding error
last_feerate = float(max(fees_seen))
all_smart_estimates = [node.estimatesmartfee(i) for i in range(1, 26)]
+ mempoolMinFee = node.getmempoolinfo()['mempoolminfee']
+ minRelaytxFee = node.getmempoolinfo()['minrelaytxfee']
for i, e in enumerate(all_smart_estimates): # estimate is for i+1
feerate = float(e["feerate"])
assert_greater_than(feerate, 0)
+ assert_greater_than_or_equal(feerate, float(mempoolMinFee))
+ assert_greater_than_or_equal(feerate, float(minRelaytxFee))
if feerate + delta < min(fees_seen) or feerate - delta > max(fees_seen):
raise AssertionError(f"Estimated fee ({feerate}) out of range ({min(fees_seen)},{max(fees_seen)})")
@@ -275,6 +279,12 @@ class EstimateFeeTest(BitcoinTestFramework):
self.log.info("Final estimates after emptying mempools")
check_estimates(self.nodes[1], self.fees_per_kb)
+ # check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee
+ self.log.info("Test fee rate estimation after restarting node with high MempoolMinFee")
+ high_val = 3*self.nodes[1].estimatesmartfee(1)['feerate']
+ self.restart_node(1, extra_args=[f'-minrelaytxfee={high_val}'])
+ check_estimates(self.nodes[1], self.fees_per_kb)
+
self.log.info("Testing that fee estimation is disabled in blocksonly.")
self.restart_node(0, ["-blocksonly"])
assert_raises_rpc_error(-32603, "Fee estimation disabled",
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index 96984e1e64..217a38050d 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -52,7 +52,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
self.extra_args = [[
- f'-segwitheight={COINBASE_MATURITY + 5}',
+ f'-testactivationheight=segwit@{COINBASE_MATURITY + 5}',
'-addresstype=legacy',
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
diff --git a/test/functional/feature_presegwit_node_upgrade.py b/test/functional/feature_presegwit_node_upgrade.py
index fd6b8620d4..aac42d4dbf 100755
--- a/test/functional/feature_presegwit_node_upgrade.py
+++ b/test/functional/feature_presegwit_node_upgrade.py
@@ -16,7 +16,7 @@ class SegwitUpgradeTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [["-segwitheight=10"]]
+ self.extra_args = [["-testactivationheight=segwit@10"]]
def run_test(self):
"""A pre-segwit node with insufficiently validated blocks needs to redownload blocks"""
@@ -37,14 +37,14 @@ class SegwitUpgradeTest(BitcoinTestFramework):
# Restarting the node (with segwit activation height set to 5) should result in a shutdown
# because the blockchain consists of 3 insufficiently validated blocks per segwit consensus rules.
node.assert_start_raises_init_error(
- extra_args=["-segwitheight=5"],
+ extra_args=["-testactivationheight=segwit@5"],
expected_msg=": Witness data for blocks after height 5 requires "
f"validation. Please restart with -reindex..{os.linesep}"
"Please restart with -reindex or -reindex-chainstate to recover.",
)
# As directed, the user restarts the node with -reindex
- self.start_node(0, extra_args=["-reindex", "-segwitheight=5"])
+ self.start_node(0, extra_args=["-reindex", "-testactivationheight=segwit@5"])
# With the segwit consensus rules, the node is able to validate only up to block 4
assert_equal(node.getblockcount(), 4)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index a6e9cd2ed1..d759a5aab5 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -19,7 +19,6 @@ from test_framework.script import CScript, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- assert_greater_than,
assert_raises_rpc_error,
)
from test_framework.script_util import (
@@ -96,23 +95,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def make_utxo(self, node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
"""Create a txout with a given amount and scriptPubKey
- Assumes that MiniWallet has enough funds to cover the amount and the fixed fee
- (from it's internal utxos, the one with the largest value is taken).
-
confirmed - txouts created will be confirmed in the blockchain;
unconfirmed otherwise.
"""
- # MiniWallet only supports sweeping utxos to its own internal scriptPubKey, so in
- # order to create an output with arbitrary amount/scriptPubKey, we have to add it
- # manually after calling the create_self_transfer method. The MiniWallet output's
- # nValue has to be adapted accordingly (amount and fee deduction). To keep things
- # simple, we use a fixed fee of 1000 Satoshis here.
- fee = 1000
- tx = self.wallet.create_self_transfer(from_node=node, fee_rate=0, mempool_valid=False)['tx']
- assert_greater_than(tx.vout[0].nValue, amount + fee)
- tx.vout[0].nValue -= (amount + fee) # change output -> MiniWallet
- tx.vout.append(CTxOut(amount, scriptPubKey)) # desired output -> to be returned
- txid = self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
+ txid, n = self.wallet.send_to(from_node=node, scriptPubKey=scriptPubKey, amount=amount)
# If requested, ensure txouts are confirmed.
if confirmed:
@@ -125,7 +111,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert new_size < mempool_size
mempool_size = new_size
- return COutPoint(int(txid, 16), 1)
+ return COutPoint(int(txid, 16), n)
def test_simple_doublespend(self):
"""Simple doublespend"""
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 2b79b3284c..25d1cb2bf1 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -44,6 +44,7 @@ from test_framework.script_util import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than_or_equal,
assert_is_hex_string,
assert_raises_rpc_error,
try_rpc,
@@ -54,12 +55,14 @@ NODE_2 = 2
P2WPKH = 0
P2WSH = 1
+
def getutxo(txid):
utxo = {}
utxo["vout"] = 0
utxo["txid"] = txid
return utxo
+
def find_spendable_utxo(node, min_value):
for utxo in node.listunspent(query_options={'minimumAmount': min_value}):
if utxo['spendable']:
@@ -67,7 +70,9 @@ def find_spendable_utxo(node, min_value):
raise AssertionError(f"Unspent output equal or higher than {min_value} not found")
-txs_mined = {} # txindex from txid to blockhash
+
+txs_mined = {} # txindex from txid to blockhash
+
class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
@@ -78,18 +83,18 @@ class SegWitTest(BitcoinTestFramework):
[
"-acceptnonstdtxn=1",
"-rpcserialversion=0",
- "-segwitheight=432",
+ "-testactivationheight=segwit@432",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
"-rpcserialversion=1",
- "-segwitheight=432",
+ "-testactivationheight=segwit@432",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
- "-segwitheight=432",
+ "-testactivationheight=segwit@432",
"-addresstype=legacy",
],
]
@@ -124,18 +129,18 @@ class SegWitTest(BitcoinTestFramework):
self.log.info("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
- assert tmpl['sizelimit'] == 1000000
+ assert_equal(tmpl['sizelimit'], 1000000)
assert 'weightlimit' not in tmpl
- assert tmpl['sigoplimit'] == 20000
- assert tmpl['transactions'][0]['hash'] == txid
- assert tmpl['transactions'][0]['sigops'] == 2
+ assert_equal(tmpl['sigoplimit'], 20000)
+ assert_equal(tmpl['transactions'][0]['hash'], txid)
+ assert_equal(tmpl['transactions'][0]['sigops'], 2)
assert '!segwit' not in tmpl['rules']
self.generate(self.nodes[0], 1) # block 162
balance_presetup = self.nodes[0].getbalance()
self.pubkey = []
- p2sh_ids = [] # p2sh_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE embedded in p2sh
- wit_ids = [] # wit_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE via bare witness
+ p2sh_ids = [] # p2sh_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE embedded in p2sh
+ wit_ids = [] # wit_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE via bare witness
for i in range(3):
newaddress = self.nodes[i].getnewaddress()
self.pubkey.append(self.nodes[i].getaddressinfo(newaddress)["pubkey"])
@@ -215,7 +220,7 @@ class SegWitTest(BitcoinTestFramework):
witnesses = coinbase_tx["decoded"]["vin"][0]["txinwitness"]
assert_equal(len(witnesses), 1)
assert_is_hex_string(witnesses[0])
- assert_equal(witnesses[0], '00'*32)
+ assert_equal(witnesses[0], '00' * 32)
self.log.info("Verify witness txs without witness data are invalid after the fork")
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False)
@@ -231,12 +236,14 @@ class SegWitTest(BitcoinTestFramework):
self.log.info("Verify sigops are counted in GBT with BIP141 rules after the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
+ raw_tx = self.nodes[0].getrawtransaction(txid, True)
tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']})
- assert tmpl['sizelimit'] >= 3999577 # actual maximum size is lower due to minimum mandatory non-witness data
- assert tmpl['weightlimit'] == 4000000
- assert tmpl['sigoplimit'] == 80000
- assert tmpl['transactions'][0]['txid'] == txid
- assert tmpl['transactions'][0]['sigops'] == 8
+ assert_greater_than_or_equal(tmpl['sizelimit'], 3999577) # actual maximum size is lower due to minimum mandatory non-witness data
+ assert_equal(tmpl['weightlimit'], 4000000)
+ assert_equal(tmpl['sigoplimit'], 80000)
+ assert_equal(tmpl['transactions'][0]['txid'], txid)
+ expected_sigops = 9 if 'txinwitness' in raw_tx["vin"][0] else 8
+ assert_equal(tmpl['transactions'][0]['sigops'], expected_sigops)
assert '!segwit' in tmpl['rules']
self.generate(self.nodes[0], 1) # Mine a block to clear the gbt cache
@@ -356,7 +363,7 @@ class SegWitTest(BitcoinTestFramework):
for i in compressed_spendable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
# p2sh multisig with compressed keys should always be spendable
spendable_anytime.extend([p2sh])
@@ -375,7 +382,7 @@ class SegWitTest(BitcoinTestFramework):
for i in uncompressed_spendable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
# p2sh multisig with uncompressed keys should always be spendable
spendable_anytime.extend([p2sh])
@@ -394,7 +401,7 @@ class SegWitTest(BitcoinTestFramework):
for i in compressed_solvable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
# Multisig without private is not seen after addmultisigaddress, but seen after importaddress
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
@@ -407,7 +414,7 @@ class SegWitTest(BitcoinTestFramework):
for i in uncompressed_solvable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
# Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress
solvable_after_importaddress.extend([bare, p2sh])
@@ -446,7 +453,7 @@ class SegWitTest(BitcoinTestFramework):
importlist = []
for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
bare = bytes.fromhex(v['hex'])
importlist.append(bare.hex())
importlist.append(script_to_p2wsh_script(bare).hex())
@@ -509,7 +516,7 @@ class SegWitTest(BitcoinTestFramework):
for i in compressed_spendable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
@@ -519,7 +526,7 @@ class SegWitTest(BitcoinTestFramework):
for i in uncompressed_spendable_address + uncompressed_solvable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
# P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
unseen_anytime.extend([p2wsh, p2sh_p2wsh])
@@ -530,7 +537,7 @@ class SegWitTest(BitcoinTestFramework):
for i in compressed_solvable_address:
v = self.nodes[0].getaddressinfo(i)
- if (v['isscript']):
+ if v['isscript']:
[bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
premature_witaddress.append(script_to_p2sh(p2wsh))
else:
@@ -597,13 +604,13 @@ class SegWitTest(BitcoinTestFramework):
watchcount = 0
spendcount = 0
for i in self.nodes[0].listunspent():
- if (i['txid'] == txid):
+ if i['txid'] == txid:
watchcount += 1
if i['spendable']:
spendcount += 1
- if (ismine == 2):
+ if ismine == 2:
assert_equal(spendcount, len(script_list))
- elif (ismine == 1):
+ elif ismine == 1:
assert_equal(watchcount, len(script_list))
assert_equal(spendcount, 0)
else:
@@ -615,7 +622,7 @@ class SegWitTest(BitcoinTestFramework):
p2sh = CScript(bytes.fromhex(v['scriptPubKey']))
p2wsh = script_to_p2wsh_script(bare)
p2sh_p2wsh = script_to_p2sh_script(p2wsh)
- return([bare, p2sh, p2wsh, p2sh_p2wsh])
+ return [bare, p2sh, p2wsh, p2sh_p2wsh]
def p2pkh_address_to_script(self, v):
pubkey = bytes.fromhex(v['pubkey'])
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 56f7cbe6a5..71a132dca3 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -46,6 +46,7 @@ from test_framework.util import (
assert_greater_than_or_equal,
assert_raises_rpc_error,
)
+from test_framework.wallet import MiniWallet
class MempoolPersistTest(BitcoinTestFramework):
@@ -53,15 +54,26 @@ class MempoolPersistTest(BitcoinTestFramework):
self.num_nodes = 3
self.extra_args = [[], ["-persistmempool=0"], []]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
+ self.mini_wallet = MiniWallet(self.nodes[2])
+ self.mini_wallet.rescan_utxos()
+ if self.is_sqlite_compiled():
+ self.nodes[2].createwallet(
+ wallet_name="watch",
+ descriptors=True,
+ disable_private_keys=True,
+ load_on_startup=False,
+ )
+ wallet_watch = self.nodes[2].get_wallet_rpc("watch")
+ assert_equal([{'success': True}], wallet_watch.importdescriptors([{'desc': self.mini_wallet.get_descriptor(), 'timestamp': 0}]))
+
self.log.debug("Send 5 transactions from node2 (to its own address)")
tx_creation_time_lower = int(time.time())
for _ in range(5):
- last_txid = self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10"))
- node2_balance = self.nodes[2].getbalance()
+ last_txid = self.mini_wallet.send_self_transfer(from_node=self.nodes[2])["txid"]
+ if self.is_sqlite_compiled():
+ self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
+ node2_balance = wallet_watch.getbalance()
self.sync_all()
tx_creation_time_higher = int(time.time())
@@ -82,16 +94,16 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_equal(total_fee_old, self.nodes[0].getmempoolinfo()['total_fee'])
assert_equal(total_fee_old, sum(v['fees']['base'] for k, v in self.nodes[0].getrawmempool(verbose=True).items()))
- tx_creation_time = self.nodes[0].getmempoolentry(txid=last_txid)['time']
+ last_entry = self.nodes[0].getmempoolentry(txid=last_txid)
+ tx_creation_time = last_entry['time']
assert_greater_than_or_equal(tx_creation_time, tx_creation_time_lower)
assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time)
# disconnect nodes & make a txn that remains in the unbroadcast set.
self.disconnect_nodes(0, 1)
- assert(len(self.nodes[0].getpeerinfo()) == 0)
- assert(len(self.nodes[0].p2ps) == 0)
- self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("12"))
- self.connect_nodes(0, 2)
+ assert_equal(len(self.nodes[0].getpeerinfo()), 0)
+ assert_equal(len(self.nodes[0].p2ps), 0)
+ self.mini_wallet.send_self_transfer(from_node=self.nodes[0])
self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
self.stop_nodes()
@@ -111,17 +123,19 @@ class MempoolPersistTest(BitcoinTestFramework):
fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees']
assert_equal(fees['base'] + Decimal('0.00001000'), fees['modified'])
- self.log.debug('Verify time is loaded correctly')
- assert_equal(tx_creation_time, self.nodes[0].getmempoolentry(txid=last_txid)['time'])
+ self.log.debug('Verify all fields are loaded correctly')
+ assert_equal(last_entry, self.nodes[0].getmempoolentry(txid=last_txid))
# Verify accounting of mempool transactions after restart is correct
- self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
- assert_equal(node2_balance, self.nodes[2].getbalance())
+ if self.is_sqlite_compiled():
+ self.nodes[2].loadwallet("watch")
+ wallet_watch = self.nodes[2].get_wallet_rpc("watch")
+ self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
+ assert_equal(node2_balance, wallet_watch.getbalance())
- # start node0 with wallet disabled so wallet transactions don't get resubmitted
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
self.stop_nodes()
- self.start_node(0, extra_args=["-persistmempool=0", "-disablewallet"])
+ self.start_node(0, extra_args=["-persistmempool=0"])
assert self.nodes[0].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[0].getrawmempool()), 0)
@@ -141,7 +155,7 @@ class MempoolPersistTest(BitcoinTestFramework):
self.log.debug("Stop nodes, make node1 use mempool.dat from node0. Verify it has 6 transactions")
os.rename(mempooldat0, mempooldat1)
self.stop_nodes()
- self.start_node(1, extra_args=[])
+ self.start_node(1, extra_args=["-persistmempool"])
assert self.nodes[1].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[1].getrawmempool()), 6)
@@ -164,18 +178,18 @@ class MempoolPersistTest(BitcoinTestFramework):
# ensure node0 doesn't have any connections
# make a transaction that will remain in the unbroadcast set
- assert(len(node0.getpeerinfo()) == 0)
- assert(len(node0.p2ps) == 0)
- node0.sendtoaddress(self.nodes[1].getnewaddress(), Decimal("12"))
+ assert_equal(len(node0.getpeerinfo()), 0)
+ assert_equal(len(node0.p2ps), 0)
+ self.mini_wallet.send_self_transfer(from_node=node0)
# shutdown, then startup with wallet disabled
- self.stop_nodes()
- self.start_node(0, extra_args=["-disablewallet"])
+ self.restart_node(0, extra_args=["-disablewallet"])
# check that txn gets broadcast due to unbroadcast logic
conn = node0.add_p2p_connection(P2PTxInvStore())
- node0.mockscheduler(16*60) # 15 min + 1 for buffer
+ node0.mockscheduler(16 * 60) # 15 min + 1 for buffer
self.wait_until(lambda: len(conn.get_invs()) == 1)
-if __name__ == '__main__':
+
+if __name__ == "__main__":
MempoolPersistTest().main()
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index a040665fba..0d8c298bea 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -8,6 +8,7 @@ Test BIP 37
from test_framework.messages import (
CInv,
+ COIN,
MAX_BLOOM_FILTER_SIZE,
MAX_BLOOM_HASH_FUNCS,
MSG_BLOCK,
@@ -28,11 +29,15 @@ from test_framework.p2p import (
)
from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.wallet import (
+ MiniWallet,
+ random_p2wpkh,
+)
class P2PBloomFilter(P2PInterface):
# This is a P2SH watch-only wallet
- watch_script_pubkey = 'a914ffffffffffffffffffffffffffffffffffffffff87'
+ watch_script_pubkey = bytes.fromhex('a914ffffffffffffffffffffffffffffffffffffffff87')
# The initial filter (n=10, fp=0.000001) with just the above scriptPubKey added
watch_filter_init = msg_filterload(
data=
@@ -93,8 +98,9 @@ class FilterTest(BitcoinTestFramework):
'-whitelist=noban@127.0.0.1', # immediate tx relay
]]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ def generatetoscriptpubkey(self, scriptpubkey):
+ """Helper to generate a single block to the given scriptPubKey."""
+ return self.generatetodescriptor(self.nodes[0], 1, f'raw({scriptpubkey.hex()})')[0]
def test_size_limits(self, filter_peer):
self.log.info('Check that too large filter is rejected')
@@ -130,8 +136,7 @@ class FilterTest(BitcoinTestFramework):
filter_peer = P2PBloomFilter()
self.log.debug("Create a tx relevant to the peer before connecting")
- filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address']
- txid = self.nodes[0].sendtoaddress(filter_address, 90)
+ txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN)
self.log.debug("Send a mempool msg after connecting and check that the tx is received")
self.nodes[0].add_p2p_connection(filter_peer)
@@ -142,8 +147,7 @@ class FilterTest(BitcoinTestFramework):
def test_frelay_false(self, filter_peer):
self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set")
filter_peer.tx_received = False
- filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address']
- self.nodes[0].sendtoaddress(filter_address, 90)
+ self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN)
# Sync to make sure the reason filter_peer doesn't receive the tx is not p2p delays
filter_peer.sync_with_ping()
assert not filter_peer.tx_received
@@ -156,45 +160,44 @@ class FilterTest(BitcoinTestFramework):
filter_peer.send_and_ping(filter_peer.watch_filter_init)
# If fRelay is not already True, sending filterload sets it to True
assert self.nodes[0].getpeerinfo()[0]['relaytxes']
- filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address']
self.log.info('Check that we receive merkleblock and tx if the filter matches a tx in a block')
- block_hash = self.generatetoaddress(self.nodes[0], 1, filter_address)[0]
+ block_hash = self.generatetoscriptpubkey(filter_peer.watch_script_pubkey)
txid = self.nodes[0].getblock(block_hash)['tx'][0]
filter_peer.wait_for_merkleblock(block_hash)
filter_peer.wait_for_tx(txid)
self.log.info('Check that we only receive a merkleblock if the filter does not match a tx in a block')
filter_peer.tx_received = False
- block_hash = self.generatetoaddress(self.nodes[0], 1, self.nodes[0].getnewaddress())[0]
+ block_hash = self.generatetoscriptpubkey(random_p2wpkh())
filter_peer.wait_for_merkleblock(block_hash)
assert not filter_peer.tx_received
self.log.info('Check that we not receive a tx if the filter does not match a mempool tx')
filter_peer.merkleblock_received = False
filter_peer.tx_received = False
- self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 90)
+ self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=random_p2wpkh(), amount=7 * COIN)
filter_peer.sync_send_with_ping()
assert not filter_peer.merkleblock_received
assert not filter_peer.tx_received
self.log.info('Check that we receive a tx if the filter matches a mempool tx')
filter_peer.merkleblock_received = False
- txid = self.nodes[0].sendtoaddress(filter_address, 90)
+ txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN)
filter_peer.wait_for_tx(txid)
assert not filter_peer.merkleblock_received
self.log.info('Check that after deleting filter all txs get relayed again')
filter_peer.send_and_ping(msg_filterclear())
for _ in range(5):
- txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 7)
+ txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=random_p2wpkh(), amount=7 * COIN)
filter_peer.wait_for_tx(txid)
self.log.info('Check that request for filtered blocks is ignored if no filter is set')
filter_peer.merkleblock_received = False
filter_peer.tx_received = False
with self.nodes[0].assert_debug_log(expected_msgs=['received getdata']):
- block_hash = self.generatetoaddress(self.nodes[0], 1, self.nodes[0].getnewaddress())[0]
+ block_hash = self.generatetoscriptpubkey(random_p2wpkh())
filter_peer.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))])
filter_peer.sync_with_ping()
assert not filter_peer.merkleblock_received
@@ -210,6 +213,9 @@ class FilterTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+ self.wallet.rescan_utxos()
+
filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter())
self.log.info('Test filter size limits')
self.test_size_limits(filter_peer)
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 26f769e646..aa3b95fc4f 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -84,10 +84,6 @@ from test_framework.util import (
assert_raises_rpc_error,
)
-# The versionbit bit used to signal activation of SegWit
-VB_WITNESS_BIT = 1
-VB_TOP_BITS = 0x20000000
-
MAX_SIGOP_COST = 80000
SEGWIT_HEIGHT = 120
@@ -197,8 +193,8 @@ class SegWitTest(BitcoinTestFramework):
self.num_nodes = 2
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
- ["-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT), "-whitelist=noban@127.0.0.1"],
- ["-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
+ ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-whitelist=noban@127.0.0.1"],
+ ["-acceptnonstdtxn=0", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}"],
]
self.supports_cli = False
@@ -207,13 +203,13 @@ class SegWitTest(BitcoinTestFramework):
# Helper functions
- def build_next_block(self, version=4):
+ def build_next_block(self):
"""Build a block on top of node0's tip."""
tip = self.nodes[0].getbestblockhash()
height = self.nodes[0].getblockcount() + 1
block_time = self.nodes[0].getblockheader(tip)["mediantime"] + 1
block = create_block(int(tip, 16), create_coinbase(height), block_time)
- block.nVersion = version
+ block.nVersion = 4
block.rehash()
return block
@@ -299,7 +295,7 @@ class SegWitTest(BitcoinTestFramework):
# Mine a block with an anyone-can-spend coinbase,
# let it mature, then try to spend it.
- block = self.build_next_block(version=1)
+ block = self.build_next_block()
block.solve()
self.test_node.send_and_ping(msg_no_witness_block(block)) # make sure the block was processed
txid = block.vtx[0].sha256
@@ -337,8 +333,8 @@ class SegWitTest(BitcoinTestFramework):
tx.rehash()
assert tx.sha256 != tx.calc_sha256(with_witness=True)
- # Construct a segwit-signaling block that includes the transaction.
- block = self.build_next_block(version=(VB_TOP_BITS | (1 << VB_WITNESS_BIT)))
+ # Construct a block that includes the transaction.
+ block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
# Sending witness data before activation is not allowed (anti-spam
# rule).
@@ -365,7 +361,7 @@ class SegWitTest(BitcoinTestFramework):
# test_node has set NODE_WITNESS, so all getdata requests should be for
# witness blocks.
# Test announcing a block via inv results in a getdata, and that
- # announcing a version 4 or random VB block with a header results in a getdata
+ # announcing a block with a header results in a getdata
block1 = self.build_next_block()
block1.solve()
@@ -373,19 +369,13 @@ class SegWitTest(BitcoinTestFramework):
assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block1, True)
- block2 = self.build_next_block(version=4)
+ block2 = self.build_next_block()
block2.solve()
self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True)
assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block2, True)
- block3 = self.build_next_block(version=(VB_TOP_BITS | (1 << 15)))
- block3.solve()
- self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True)
- assert self.test_node.last_message["getdata"].inv[0].type == blocktype
- test_witness_block(self.nodes[0], self.test_node, block3, True)
-
# Check that we can getdata for witness blocks or regular blocks,
# and the right thing happens.
if not self.segwit_active:
@@ -430,7 +420,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(rpc_details["weight"], block.get_weight())
# Upgraded node should not ask for blocks from unupgraded
- block4 = self.build_next_block(version=4)
+ block4 = self.build_next_block()
block4.solve()
self.old_node.getdataset = set()
diff --git a/test/functional/rpc_addresses_deprecation.py b/test/functional/rpc_addresses_deprecation.py
deleted file mode 100755
index e566fb0aa7..0000000000
--- a/test/functional/rpc_addresses_deprecation.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2020 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 deprecation of reqSigs and addresses RPC fields."""
-
-from test_framework.messages import (
- tx_from_hex,
-)
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
-)
-
-
-class AddressesDeprecationTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 2
- self.extra_args = [[], ["-deprecatedrpc=addresses"]]
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
- def run_test(self):
- self.test_addresses_deprecation()
-
- def test_addresses_deprecation(self):
- node = self.nodes[0]
- coin = node.listunspent().pop()
-
- inputs = [{'txid': coin['txid'], 'vout': coin['vout']}]
- outputs = {node.getnewaddress(): 0.99}
- raw = node.createrawtransaction(inputs, outputs)
- signed = node.signrawtransactionwithwallet(raw)['hex']
-
- # This transaction is derived from test/util/data/txcreatemultisig1.json
- tx = tx_from_hex(signed)
- tx.vout[0].scriptPubKey = bytes.fromhex("522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae")
- tx_signed = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
- txid = node.sendrawtransaction(hexstring=tx_signed, maxfeerate=0)
-
- self.log.info("Test RPCResult scriptPubKey no longer returns the fields addresses or reqSigs by default")
- hash = self.generateblock(node, output=node.getnewaddress(), transactions=[txid])['hash']
- # Ensure both nodes have the newly generated block on disk.
- self.sync_blocks()
- script_pub_key = node.getblock(blockhash=hash, verbose=2)['tx'][-1]['vout'][0]['scriptPubKey']
- assert 'addresses' not in script_pub_key and 'reqSigs' not in script_pub_key
-
- self.log.info("Test RPCResult scriptPubKey returns the addresses field with -deprecatedrpc=addresses")
- script_pub_key = self.nodes[1].getblock(blockhash=hash, verbose=2)['tx'][-1]['vout'][0]['scriptPubKey']
- assert_equal(script_pub_key['addresses'], ['mvKDK6D54HU8wQumJBLHY95eq5iHFqXSBz', 'mv3rHCQSwKp2BLSuMHD8uCS32LW5xiNAA5', 'mirrsyhAQYzo5CwVhcaYJKwUJu1WJRCRJe'])
- assert_equal(script_pub_key['reqSigs'], 2)
-
-
-if __name__ == "__main__":
- AddressesDeprecationTest().main()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 0600d8b9c5..c3c6ade684 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -27,8 +27,6 @@ import subprocess
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.blocktools import (
- CLTV_HEIGHT,
- DERSIG_HEIGHT,
create_block,
create_coinbase,
TIME_GENESIS_BLOCK,
@@ -129,7 +127,29 @@ class BlockchainTest(BitcoinTestFramework):
# should have exact keys
assert_equal(sorted(res.keys()), keys)
- self.restart_node(0, ['-stopatheight=207', '-prune=550'])
+ self.stop_node(0)
+ self.nodes[0].assert_start_raises_init_error(
+ extra_args=['-testactivationheight=name@2'],
+ expected_msg='Error: Invalid name (name@2) for -testactivationheight=name@height.',
+ )
+ self.nodes[0].assert_start_raises_init_error(
+ extra_args=['-testactivationheight=bip34@-2'],
+ expected_msg='Error: Invalid height value (bip34@-2) for -testactivationheight=name@height.',
+ )
+ self.nodes[0].assert_start_raises_init_error(
+ extra_args=['-testactivationheight='],
+ expected_msg='Error: Invalid format () for -testactivationheight=name@height.',
+ )
+ self.start_node(0, extra_args=[
+ '-stopatheight=207',
+ '-prune=550',
+ '-testactivationheight=bip34@2',
+ '-testactivationheight=dersig@3',
+ '-testactivationheight=cltv@4',
+ '-testactivationheight=csv@5',
+ '-testactivationheight=segwit@6',
+ ])
+
res = self.nodes[0].getblockchaininfo()
# result should have these additional pruning keys if prune=550
assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys))
@@ -143,10 +163,10 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(res['softforks'], {
'bip34': {'type': 'buried', 'active': True, 'height': 2},
- 'bip66': {'type': 'buried', 'active': True, 'height': DERSIG_HEIGHT},
- 'bip65': {'type': 'buried', 'active': True, 'height': CLTV_HEIGHT},
- 'csv': {'type': 'buried', 'active': False, 'height': 432},
- 'segwit': {'type': 'buried', 'active': True, 'height': 0},
+ 'bip66': {'type': 'buried', 'active': True, 'height': 3},
+ 'bip65': {'type': 'buried', 'active': True, 'height': 4},
+ 'csv': {'type': 'buried', 'active': True, 'height': 5},
+ 'segwit': {'type': 'buried', 'active': True, 'height': 6},
'testdummy': {
'type': 'bip9',
'bip9': {
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 56312dc6e5..cda0ae0eeb 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -47,7 +47,40 @@ class RawTransactionsTest(BitcoinTestFramework):
self.connect_nodes(0, 2)
self.connect_nodes(0, 3)
+ def lock_outputs_type(self, wallet, outputtype):
+ """
+ Only allow UTXOs of the given type
+ """
+ if outputtype in ["legacy", "p2pkh", "pkh"]:
+ prefixes = ["pkh(", "sh(multi("]
+ elif outputtype in ["p2sh-segwit", "sh_wpkh"]:
+ prefixes = ["sh(wpkh(", "sh(wsh("]
+ elif outputtype in ["bech32", "wpkh"]:
+ prefixes = ["wpkh(", "wsh("]
+ else:
+ assert False, f"Unknown output type {outputtype}"
+
+ to_lock = []
+ for utxo in wallet.listunspent():
+ if "desc" in utxo:
+ for prefix in prefixes:
+ if utxo["desc"].startswith(prefix):
+ to_lock.append({"txid": utxo["txid"], "vout": utxo["vout"]})
+ wallet.lockunspent(False, to_lock)
+
+ def unlock_utxos(self, wallet):
+ """
+ Unlock all UTXOs except the watchonly one
+ """
+ to_keep = []
+ if self.watchonly_txid is not None and self.watchonly_vout is not None:
+ to_keep.append({"txid": self.watchonly_txid, "vout": self.watchonly_vout})
+ wallet.lockunspent(True)
+ wallet.lockunspent(False, to_keep)
+
def run_test(self):
+ self.watchonly_txid = None
+ self.watchonly_vout = None
self.log.info("Connect nodes, set fees, generate blocks, and sync")
self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
# This test is not meant to test fee estimation and we'd like
@@ -373,6 +406,7 @@ class RawTransactionsTest(BitcoinTestFramework):
def test_fee_p2pkh(self):
"""Compare fee of a standard pubkeyhash transaction."""
self.log.info("Test fundrawtxn p2pkh fee")
+ self.lock_outputs_type(self.nodes[0], "p2pkh")
inputs = []
outputs = {self.nodes[1].getnewaddress():1.1}
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
@@ -386,9 +420,12 @@ class RawTransactionsTest(BitcoinTestFramework):
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
+ self.unlock_utxos(self.nodes[0])
+
def test_fee_p2pkh_multi_out(self):
"""Compare fee of a standard pubkeyhash transaction with multiple outputs."""
self.log.info("Test fundrawtxn p2pkh fee with multiple outputs")
+ self.lock_outputs_type(self.nodes[0], "p2pkh")
inputs = []
outputs = {
self.nodes[1].getnewaddress():1.1,
@@ -409,8 +446,11 @@ class RawTransactionsTest(BitcoinTestFramework):
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
+ self.unlock_utxos(self.nodes[0])
+
def test_fee_p2sh(self):
"""Compare fee of a 2-of-2 multisig p2sh transaction."""
+ self.lock_outputs_type(self.nodes[0], "p2pkh")
# Create 2-of-2 addr.
addr1 = self.nodes[1].getnewaddress()
addr2 = self.nodes[1].getnewaddress()
@@ -433,9 +473,12 @@ class RawTransactionsTest(BitcoinTestFramework):
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
+ self.unlock_utxos(self.nodes[0])
+
def test_fee_4of5(self):
"""Compare fee of a standard pubkeyhash transaction."""
self.log.info("Test fundrawtxn fee with 4-of-5 addresses")
+ self.lock_outputs_type(self.nodes[0], "p2pkh")
# Create 4-of-5 addr.
addr1 = self.nodes[1].getnewaddress()
@@ -474,6 +517,8 @@ class RawTransactionsTest(BitcoinTestFramework):
feeDelta = Decimal(fundedTx['fee']) - Decimal(signedFee)
assert feeDelta >= 0 and feeDelta <= self.fee_tolerance
+ self.unlock_utxos(self.nodes[0])
+
def test_spend_2of2(self):
"""Spend a 2-of-2 multisig transaction over fundraw."""
self.log.info("Test fundpsbt spending 2-of-2 multisig")
@@ -542,15 +587,18 @@ class RawTransactionsTest(BitcoinTestFramework):
# Drain the keypool.
self.nodes[1].getnewaddress()
self.nodes[1].getrawchangeaddress()
- inputs = []
- outputs = {self.nodes[0].getnewaddress():1.19999500}
+
+ # Choose 2 inputs
+ inputs = self.nodes[1].listunspent()[0:2]
+ value = sum(inp["amount"] for inp in inputs) - Decimal("0.00000500") # Pay a 500 sat fee
+ outputs = {self.nodes[0].getnewaddress():value}
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
# fund a transaction that does not require a new key for the change output
self.nodes[1].fundrawtransaction(rawtx)
# fund a transaction that requires a new key for the change output
# creating the key must be impossible because the wallet is locked
- outputs = {self.nodes[0].getnewaddress():1.1}
+ outputs = {self.nodes[0].getnewaddress():value - Decimal("0.1")}
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", self.nodes[1].fundrawtransaction, rawtx)
@@ -944,31 +992,31 @@ class RawTransactionsTest(BitcoinTestFramework):
# We receive unconfirmed funds from external keys (unsafe outputs).
addr = wallet.getnewaddress()
- txid1 = self.nodes[2].sendtoaddress(addr, 6)
- txid2 = self.nodes[2].sendtoaddress(addr, 4)
- self.sync_all()
- vout1 = find_vout_for_address(wallet, txid1, addr)
- vout2 = find_vout_for_address(wallet, txid2, addr)
+ inputs = []
+ for i in range(0, 2):
+ txid = self.nodes[2].sendtoaddress(addr, 5)
+ self.sync_mempools()
+ vout = find_vout_for_address(wallet, txid, addr)
+ inputs.append((txid, vout))
# Unsafe inputs are ignored by default.
- rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 5}])
+ rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 7.5}])
assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, rawtx)
# But we can opt-in to use them for funding.
fundedtx = wallet.fundrawtransaction(rawtx, {"include_unsafe": True})
tx_dec = wallet.decoderawtransaction(fundedtx['hex'])
- assert any([txin['txid'] == txid1 and txin['vout'] == vout1 for txin in tx_dec['vin']])
+ assert all((txin["txid"], txin["vout"]) in inputs for txin in tx_dec["vin"])
signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
- wallet.sendrawtransaction(signedtx['hex'])
+ assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]
# And we can also use them once they're confirmed.
self.generate(self.nodes[0], 1)
- rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 3}])
- fundedtx = wallet.fundrawtransaction(rawtx, {"include_unsafe": True})
+ fundedtx = wallet.fundrawtransaction(rawtx, {"include_unsafe": False})
tx_dec = wallet.decoderawtransaction(fundedtx['hex'])
- assert any([txin['txid'] == txid2 and txin['vout'] == vout2 for txin in tx_dec['vin']])
+ assert all((txin["txid"], txin["vout"]) in inputs for txin in tx_dec["vin"])
signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
- wallet.sendrawtransaction(signedtx['hex'])
+ assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]
def test_22670(self):
# In issue #22670, it was observed that ApproximateBestSubset may
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 2b1892c121..f330bbf1c3 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -108,6 +108,16 @@ class PSBTTest(BitcoinTestFramework):
psbtx = self.nodes[1].walletprocesspsbt(psbtx1)['psbt']
assert_equal(psbtx1, psbtx)
+ # Node 0 should not be able to sign the transaction with the wallet is locked
+ self.nodes[0].encryptwallet("password")
+ assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].walletprocesspsbt, psbtx)
+
+ # Node 0 should be able to process without signing though
+ unsigned_tx = self.nodes[0].walletprocesspsbt(psbtx, False)
+ assert_equal(unsigned_tx['complete'], False)
+
+ self.nodes[0].walletpassphrase(passphrase="password", timeout=1000000)
+
# Sign the transaction and send
signed_tx = self.nodes[0].walletprocesspsbt(psbtx)['psbt']
final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex']
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 8f17b29ff4..18abece253 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -6,7 +6,6 @@
from test_framework.blocktools import (
COINBASE_MATURITY,
- CSV_ACTIVATION_HEIGHT,
)
from test_framework.address import (
script_to_p2sh,
@@ -18,7 +17,6 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
find_vout_for_address,
- generate_to_height,
)
from test_framework.messages import (
CTxInWitness,
@@ -273,7 +271,6 @@ class SignRawTransactionsTest(BitcoinTestFramework):
getcontext().prec = 8
# Make sure CSV is active
- generate_to_height(self, self.nodes[0], CSV_ACTIVATION_HEIGHT)
assert self.nodes[0].getblockchaininfo()['softforks']['csv']['active']
# Create a P2WSH script with CSV
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index 81eb881234..c4ffd1fbf6 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -113,10 +113,8 @@ class AuthServiceProxy():
self.__conn.request(method, path, postdata, headers)
return self._get_response()
except OSError as e:
- retry = (
- '[WinError 10053] An established connection was aborted by the software in your host machine' in str(e))
# Workaround for a bug on macOS. See https://bugs.python.org/issue33450
- retry = retry or ('[Errno 41] Protocol wrong type for socket' in str(e))
+ retry = '[Errno 41] Protocol wrong type for socket' in str(e)
if retry:
self.__conn.close()
self.__conn.request(method, path, postdata, headers)
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 25b36b6a91..6de372cd8e 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -53,11 +53,6 @@ TIME_GENESIS_BLOCK = 1296688602
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
COINBASE_MATURITY = 100
-# Soft-fork activation heights
-DERSIG_HEIGHT = 102 # BIP 66
-CLTV_HEIGHT = 111 # BIP 65
-CSV_ACTIVATION_HEIGHT = 432
-
# From BIP141
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py
index 5d1d7ea45c..e0dfce4c2f 100755
--- a/test/functional/test_framework/script_util.py
+++ b/test/functional/test_framework/script_util.py
@@ -3,7 +3,17 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Useful Script constants and utils."""
-from test_framework.script import CScript, hash160, sha256, OP_0, OP_DUP, OP_HASH160, OP_CHECKSIG, OP_EQUAL, OP_EQUALVERIFY
+from test_framework.script import (
+ CScript,
+ hash160,
+ sha256,
+ OP_0,
+ OP_DUP,
+ OP_HASH160,
+ OP_CHECKSIG,
+ OP_EQUAL,
+ OP_EQUALVERIFY,
+)
# To prevent a "tx-size-small" policy rule error, a transaction has to have a
# non-witness size of at least 82 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in
@@ -25,28 +35,34 @@ from test_framework.script import CScript, hash160, sha256, OP_0, OP_DUP, OP_HAS
DUMMY_P2WPKH_SCRIPT = CScript([b'a' * 21])
DUMMY_2_P2WPKH_SCRIPT = CScript([b'b' * 21])
-def keyhash_to_p2pkh_script(hash, main = False):
+
+def keyhash_to_p2pkh_script(hash):
assert len(hash) == 20
return CScript([OP_DUP, OP_HASH160, hash, OP_EQUALVERIFY, OP_CHECKSIG])
-def scripthash_to_p2sh_script(hash, main = False):
+
+def scripthash_to_p2sh_script(hash):
assert len(hash) == 20
return CScript([OP_HASH160, hash, OP_EQUAL])
-def key_to_p2pkh_script(key, main = False):
+
+def key_to_p2pkh_script(key):
key = check_key(key)
- return keyhash_to_p2pkh_script(hash160(key), main)
+ return keyhash_to_p2pkh_script(hash160(key))
-def script_to_p2sh_script(script, main = False):
+
+def script_to_p2sh_script(script):
script = check_script(script)
- return scripthash_to_p2sh_script(hash160(script), main)
+ return scripthash_to_p2sh_script(hash160(script))
+
-def key_to_p2sh_p2wpkh_script(key, main = False):
+def key_to_p2sh_p2wpkh_script(key):
key = check_key(key)
p2shscript = CScript([OP_0, hash160(key)])
- return script_to_p2sh_script(p2shscript, main)
+ return script_to_p2sh_script(p2shscript)
+
-def program_to_witness_script(version, program, main = False):
+def program_to_witness_script(version, program):
if isinstance(program, str):
program = bytes.fromhex(program)
assert 0 <= version <= 16
@@ -54,29 +70,34 @@ def program_to_witness_script(version, program, main = False):
assert version > 0 or len(program) in [20, 32]
return CScript([version, program])
-def script_to_p2wsh_script(script, main = False):
+
+def script_to_p2wsh_script(script):
script = check_script(script)
- return program_to_witness_script(0, sha256(script), main)
+ return program_to_witness_script(0, sha256(script))
+
-def key_to_p2wpkh_script(key, main = False):
+def key_to_p2wpkh_script(key):
key = check_key(key)
- return program_to_witness_script(0, hash160(key), main)
+ return program_to_witness_script(0, hash160(key))
+
-def script_to_p2sh_p2wsh_script(script, main = False):
+def script_to_p2sh_p2wsh_script(script):
script = check_script(script)
p2shscript = CScript([OP_0, sha256(script)])
- return script_to_p2sh_script(p2shscript, main)
+ return script_to_p2sh_script(p2shscript)
+
def check_key(key):
if isinstance(key, str):
- key = bytes.fromhex(key) # Assuming this is hex string
+ key = bytes.fromhex(key) # Assuming this is hex string
if isinstance(key, bytes) and (len(key) == 33 or len(key) == 65):
return key
assert False
+
def check_script(script):
if isinstance(script, str):
- script = bytes.fromhex(script) # Assuming this is hex string
+ script = bytes.fromhex(script) # Assuming this is hex string
if isinstance(script, bytes) or isinstance(script, CScript):
return script
assert False
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index f382e0fdb3..3aca93fab3 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -560,18 +560,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes[i].process.wait(timeout)
def connect_nodes(self, a, b):
- def connect_nodes_helper(from_connection, node_num):
- ip_port = "127.0.0.1:" + str(p2p_port(node_num))
- from_connection.addnode(ip_port, "onetry")
- # poll until version handshake complete to avoid race conditions
- # with transaction relaying
- # See comments in net_processing:
- # * Must have a version message before anything else
- # * Must have a verack message before anything else
- wait_until_helper(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
- wait_until_helper(lambda: all(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in from_connection.getpeerinfo()))
-
- connect_nodes_helper(self.nodes[a], b)
+ from_connection = self.nodes[a]
+ to_connection = self.nodes[b]
+ ip_port = "127.0.0.1:" + str(p2p_port(b))
+ from_connection.addnode(ip_port, "onetry")
+ # poll until version handshake complete to avoid race conditions
+ # with transaction relaying
+ # See comments in net_processing:
+ # * Must have a version message before anything else
+ # * Must have a verack message before anything else
+ wait_until_helper(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
+ wait_until_helper(lambda: all(peer['version'] != 0 for peer in to_connection.getpeerinfo()))
+ wait_until_helper(lambda: all(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in from_connection.getpeerinfo()))
+ wait_until_helper(lambda: all(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in to_connection.getpeerinfo()))
def disconnect_nodes(self, a, b):
def disconnect_nodes_helper(from_connection, node_num):
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index d66499dbcb..ea5c641b4a 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -560,17 +560,6 @@ def mine_large_block(test_framework, node, utxos=None):
test_framework.generate(node, 1)
-def generate_to_height(test_framework, node, target_height):
- """Generates blocks until a given target block height has been reached.
- To prevent timeouts, only up to 200 blocks are generated per RPC call.
- Can be used to activate certain soft-forks (e.g. CSV, CLTV)."""
- current_height = node.getblockcount()
- while current_height < target_height:
- nblocks = min(200, target_height - current_height)
- current_height += len(test_framework.generate(node, nblocks))
- assert_equal(node.getblockcount(), target_height)
-
-
def find_vout_for_address(node, txid, addr):
"""
Locate the vout index of the given transaction sending to the
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 2ffeb2d115..ef27cb3221 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -28,6 +28,7 @@ from test_framework.script import (
OP_NOP,
SIGHASH_ALL,
)
+from test_framework.script_util import key_to_p2wpkh_script
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
@@ -82,7 +83,7 @@ class MiniWallet:
def rescan_utxos(self):
"""Drop all utxos and rescan the utxo set"""
self._utxos = []
- res = self._test_node.scantxoutset(action="start", scanobjects=[f'raw({self._scriptPubKey.hex()})'])
+ res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
assert_equal(True, res['success'])
for utxo in res['unspents']:
self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount']})
@@ -110,12 +111,15 @@ class MiniWallet:
def generate(self, num_blocks):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
- blocks = self._test_node.generatetodescriptor(num_blocks, f'raw({self._scriptPubKey.hex()})')
+ blocks = self._test_node.generatetodescriptor(num_blocks, self.get_descriptor())
for b in blocks:
cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
return blocks
+ def get_descriptor(self):
+ return self._test_node.getdescriptorinfo(f'raw({self._scriptPubKey.hex()})')['descriptor']
+
def get_address(self):
return self._address
@@ -143,6 +147,25 @@ class MiniWallet:
self.sendrawtransaction(from_node=kwargs['from_node'], tx_hex=tx['hex'])
return tx
+ def send_to(self, *, from_node, scriptPubKey, amount, fee=1000):
+ """
+ Create and send a tx with an output to a given scriptPubKey/amount,
+ plus a change output to our internal address. To keep things simple, a
+ fixed fee given in Satoshi is used.
+
+ Note that this method fails if there is no single internal utxo
+ available that can cover the cost for the amount and the fixed fee
+ (the utxo with the largest value is taken).
+
+ Returns a tuple (txid, n) referring to the created external utxo outpoint.
+ """
+ tx = self.create_self_transfer(from_node=from_node, fee_rate=0, mempool_valid=False)['tx']
+ assert_greater_than_or_equal(tx.vout[0].nValue, amount + fee)
+ tx.vout[0].nValue -= (amount + fee) # change output -> MiniWallet
+ tx.vout.append(CTxOut(amount, scriptPubKey)) # arbitrary output -> to be returned
+ txid = self.sendrawtransaction(from_node=from_node, tx_hex=tx.serialize().hex())
+ return txid, 1
+
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0):
"""Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
self._utxos = sorted(self._utxos, key=lambda k: k['value'])
@@ -185,6 +208,14 @@ class MiniWallet:
return txid
+def random_p2wpkh():
+ """Generate a random P2WPKH scriptPubKey. Can be used when a random destination is needed,
+ but no compiled wallet is available (e.g. as replacement to the getnewaddress RPC)."""
+ key = ECKey()
+ key.generate()
+ return key_to_p2wpkh_script(key.get_pubkey().get_bytes())
+
+
def make_chain(node, address, privkeys, parent_txid, parent_value, n=0, parent_locking_script=None, fee=DEFAULT_FEE):
"""Build a transaction that spends parent_txid.vout[n] and produces one output with
amount = parent_value with a fee deducted.
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 3792d751de..2c2aaf6020 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -175,6 +175,7 @@ BASE_SCRIPTS = [
'rpc_rawtransaction.py --legacy-wallet',
'rpc_rawtransaction.py --descriptors',
'wallet_groups.py --legacy-wallet',
+ 'wallet_transactiontime_rescan.py',
'p2p_addrv2_relay.py',
'wallet_groups.py --descriptors',
'p2p_compactblocks_hb.py',
@@ -304,7 +305,6 @@ BASE_SCRIPTS = [
'feature_presegwit_node_upgrade.py',
'feature_settings.py',
'rpc_getdescriptorinfo.py',
- 'rpc_addresses_deprecation.py',
'rpc_help.py',
'feature_help.py',
'feature_shutdown.py',
@@ -401,8 +401,9 @@ def main():
for test in tests:
script = test.split("/")[-1]
script = script + ".py" if ".py" not in script else script
- if script in ALL_SCRIPTS:
- test_list.append(script)
+ matching_scripts = [s for s in ALL_SCRIPTS if s.startswith(script)]
+ if matching_scripts:
+ test_list.extend(matching_scripts)
else:
print("{}WARNING!{} Test '{}' not found in full test list.".format(BOLD[1], BOLD[0], test))
elif args.extended:
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 6372e1acd7..f57f2a44bd 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -13,6 +13,7 @@ from test_framework.util import (
assert_equal,
assert_fee_amount,
assert_raises_rpc_error,
+ find_vout_for_address,
)
from test_framework.wallet_util import test_address
@@ -121,13 +122,49 @@ class WalletTest(BitcoinTestFramework):
# Exercise locking of unspent outputs
unspent_0 = self.nodes[2].listunspent()[0]
unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]}
+ # Trying to unlock an output which isn't locked should error
assert_raises_rpc_error(-8, "Invalid parameter, expected locked output", self.nodes[2].lockunspent, True, [unspent_0])
+
+ # Locking an already-locked output should error
self.nodes[2].lockunspent(False, [unspent_0])
assert_raises_rpc_error(-8, "Invalid parameter, output already locked", self.nodes[2].lockunspent, False, [unspent_0])
+
+ # Restarting the node should clear the lock
+ self.restart_node(2)
+ self.nodes[2].lockunspent(False, [unspent_0])
+
+ # Unloading and reloating the wallet should clear the lock
+ assert_equal(self.nodes[0].listwallets(), [self.default_wallet_name])
+ self.nodes[2].unloadwallet(self.default_wallet_name)
+ self.nodes[2].loadwallet(self.default_wallet_name)
+ assert_equal(len(self.nodes[2].listlockunspent()), 0)
+
+ # Locking non-persistently, then re-locking persistently, is allowed
+ self.nodes[2].lockunspent(False, [unspent_0])
+ self.nodes[2].lockunspent(False, [unspent_0], True)
+
+ # Restarting the node with the lock written to the wallet should keep the lock
+ self.restart_node(2)
+ assert_raises_rpc_error(-8, "Invalid parameter, output already locked", self.nodes[2].lockunspent, False, [unspent_0])
+
+ # Unloading and reloading the wallet with a persistent lock should keep the lock
+ self.nodes[2].unloadwallet(self.default_wallet_name)
+ self.nodes[2].loadwallet(self.default_wallet_name)
+ assert_raises_rpc_error(-8, "Invalid parameter, output already locked", self.nodes[2].lockunspent, False, [unspent_0])
+
+ # Locked outputs should not be used, even if they are the only available funds
assert_raises_rpc_error(-6, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20)
assert_equal([unspent_0], self.nodes[2].listlockunspent())
+
+ # Unlocking should remove the persistent lock
self.nodes[2].lockunspent(True, [unspent_0])
+ self.restart_node(2)
assert_equal(len(self.nodes[2].listlockunspent()), 0)
+
+ # Reconnect node 2 after restarts
+ self.connect_nodes(1, 2)
+ self.connect_nodes(0, 2)
+
assert_raises_rpc_error(-8, "txid must be of length 64 (not 34, for '0000000000000000000000000000000000')",
self.nodes[2].lockunspent, False,
[{"txid": "0000000000000000000000000000000000", "vout": 0}])
@@ -427,6 +464,9 @@ class WalletTest(BitcoinTestFramework):
# 1. Send some coins to generate new UTXO
address_to_import = self.nodes[2].getnewaddress()
txid = self.nodes[0].sendtoaddress(address_to_import, 1)
+ self.sync_mempools(self.nodes[0:3])
+ vout = find_vout_for_address(self.nodes[2], txid, address_to_import)
+ self.nodes[2].lockunspent(False, [{"txid": txid, "vout": vout}])
self.generate(self.nodes[0], 1)
self.sync_all(self.nodes[0:3])
diff --git a/test/functional/wallet_transactiontime_rescan.py b/test/functional/wallet_transactiontime_rescan.py
new file mode 100755
index 0000000000..78859e6131
--- /dev/null
+++ b/test/functional/wallet_transactiontime_rescan.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018-2019 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 transaction time during old block rescanning
+"""
+
+import time
+
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal
+)
+
+
+class TransactionTimeRescanTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = False
+ self.num_nodes = 3
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ self.log.info('Prepare nodes and wallet')
+
+ minernode = self.nodes[0] # node used to mine BTC and create transactions
+ usernode = self.nodes[1] # user node with correct time
+ restorenode = self.nodes[2] # node used to restore user wallet and check time determination in ComputeSmartTime (wallet.cpp)
+
+ # time constant
+ cur_time = int(time.time())
+ ten_days = 10 * 24 * 60 * 60
+
+ # synchronize nodes and time
+ self.sync_all()
+ minernode.setmocktime(cur_time)
+ usernode.setmocktime(cur_time)
+ restorenode.setmocktime(cur_time)
+
+ # prepare miner wallet
+ minernode.createwallet(wallet_name='default')
+ miner_wallet = minernode.get_wallet_rpc('default')
+ m1 = miner_wallet.getnewaddress()
+
+ # prepare the user wallet with 3 watch only addresses
+ wo1 = usernode.getnewaddress()
+ wo2 = usernode.getnewaddress()
+ wo3 = usernode.getnewaddress()
+
+ usernode.createwallet(wallet_name='wo', disable_private_keys=True)
+ wo_wallet = usernode.get_wallet_rpc('wo')
+
+ wo_wallet.importaddress(wo1)
+ wo_wallet.importaddress(wo2)
+ wo_wallet.importaddress(wo3)
+
+ self.log.info('Start transactions')
+
+ # check blockcount
+ assert_equal(minernode.getblockcount(), 200)
+
+ # generate some btc to create transactions and check blockcount
+ initial_mine = COINBASE_MATURITY + 1
+ minernode.generatetoaddress(initial_mine, m1)
+ assert_equal(minernode.getblockcount(), initial_mine + 200)
+
+ # synchronize nodes and time
+ self.sync_all()
+ minernode.setmocktime(cur_time + ten_days)
+ usernode.setmocktime(cur_time + ten_days)
+ restorenode.setmocktime(cur_time + ten_days)
+ # send 10 btc to user's first watch-only address
+ self.log.info('Send 10 btc to user')
+ miner_wallet.sendtoaddress(wo1, 10)
+
+ # generate blocks and check blockcount
+ minernode.generatetoaddress(COINBASE_MATURITY, m1)
+ assert_equal(minernode.getblockcount(), initial_mine + 300)
+
+ # synchronize nodes and time
+ self.sync_all()
+ minernode.setmocktime(cur_time + ten_days + ten_days)
+ usernode.setmocktime(cur_time + ten_days + ten_days)
+ restorenode.setmocktime(cur_time + ten_days + ten_days)
+ # send 5 btc to our second watch-only address
+ self.log.info('Send 5 btc to user')
+ miner_wallet.sendtoaddress(wo2, 5)
+
+ # generate blocks and check blockcount
+ minernode.generatetoaddress(COINBASE_MATURITY, m1)
+ assert_equal(minernode.getblockcount(), initial_mine + 400)
+
+ # synchronize nodes and time
+ self.sync_all()
+ minernode.setmocktime(cur_time + ten_days + ten_days + ten_days)
+ usernode.setmocktime(cur_time + ten_days + ten_days + ten_days)
+ restorenode.setmocktime(cur_time + ten_days + ten_days + ten_days)
+ # send 1 btc to our third watch-only address
+ self.log.info('Send 1 btc to user')
+ miner_wallet.sendtoaddress(wo3, 1)
+
+ # generate more blocks and check blockcount
+ minernode.generatetoaddress(COINBASE_MATURITY, m1)
+ assert_equal(minernode.getblockcount(), initial_mine + 500)
+
+ self.log.info('Check user\'s final balance and transaction count')
+ assert_equal(wo_wallet.getbalance(), 16)
+ assert_equal(len(wo_wallet.listtransactions()), 3)
+
+ self.log.info('Check transaction times')
+ for tx in wo_wallet.listtransactions():
+ if tx['address'] == wo1:
+ assert_equal(tx['blocktime'], cur_time + ten_days)
+ assert_equal(tx['time'], cur_time + ten_days)
+ elif tx['address'] == wo2:
+ assert_equal(tx['blocktime'], cur_time + ten_days + ten_days)
+ assert_equal(tx['time'], cur_time + ten_days + ten_days)
+ elif tx['address'] == wo3:
+ assert_equal(tx['blocktime'], cur_time + ten_days + ten_days + ten_days)
+ assert_equal(tx['time'], cur_time + ten_days + ten_days + ten_days)
+
+ # restore user wallet without rescan
+ self.log.info('Restore user wallet on another node without rescan')
+ restorenode.createwallet(wallet_name='wo', disable_private_keys=True)
+ restorewo_wallet = restorenode.get_wallet_rpc('wo')
+
+ restorewo_wallet.importaddress(wo1, rescan=False)
+ restorewo_wallet.importaddress(wo2, rescan=False)
+ restorewo_wallet.importaddress(wo3, rescan=False)
+
+ # check user has 0 balance and no transactions
+ assert_equal(restorewo_wallet.getbalance(), 0)
+ assert_equal(len(restorewo_wallet.listtransactions()), 0)
+
+ # proceed to rescan, first with an incomplete one, then with a full rescan
+ self.log.info('Rescan last history part')
+ restorewo_wallet.rescanblockchain(initial_mine + 350)
+ self.log.info('Rescan all history')
+ restorewo_wallet.rescanblockchain()
+
+ self.log.info('Check user\'s final balance and transaction count after restoration')
+ assert_equal(restorewo_wallet.getbalance(), 16)
+ assert_equal(len(restorewo_wallet.listtransactions()), 3)
+
+ self.log.info('Check transaction times after restoration')
+ for tx in restorewo_wallet.listtransactions():
+ if tx['address'] == wo1:
+ assert_equal(tx['blocktime'], cur_time + ten_days)
+ assert_equal(tx['time'], cur_time + ten_days)
+ elif tx['address'] == wo2:
+ assert_equal(tx['blocktime'], cur_time + ten_days + ten_days)
+ assert_equal(tx['time'], cur_time + ten_days + ten_days)
+ elif tx['address'] == wo3:
+ assert_equal(tx['blocktime'], cur_time + ten_days + ten_days + ten_days)
+ assert_equal(tx['time'], cur_time + ten_days + ten_days + ten_days)
+
+
+if __name__ == '__main__':
+ TransactionTimeRescanTest().main()
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 3eb525a9bc..7f178d7d46 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -7,6 +7,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ find_vout_for_address
)
from test_framework.messages import (
COIN,
@@ -33,6 +34,13 @@ class TxnMallTest(BitcoinTestFramework):
super().setup_network()
self.disconnect_nodes(1, 2)
+ def spend_txid(self, txid, vout, outputs):
+ inputs = [{"txid": txid, "vout": vout}]
+ tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ tx = self.nodes[0].fundrawtransaction(tx)
+ tx = self.nodes[0].signrawtransactionwithwallet(tx['hex'])
+ return self.nodes[0].sendrawtransaction(tx['hex'])
+
def run_test(self):
if self.options.segwit:
output_type = "p2sh-segwit"
@@ -49,6 +57,7 @@ class TxnMallTest(BitcoinTestFramework):
node0_address1 = self.nodes[0].getnewaddress(address_type=output_type)
node0_txid1 = self.nodes[0].sendtoaddress(node0_address1, 1219)
node0_tx1 = self.nodes[0].gettransaction(node0_txid1)
+ self.nodes[0].lockunspent(False, [{"txid":node0_txid1, "vout": find_vout_for_address(self.nodes[0], node0_txid1, node0_address1)}])
node0_address2 = self.nodes[0].getnewaddress(address_type=output_type)
node0_txid2 = self.nodes[0].sendtoaddress(node0_address2, 29)
@@ -61,8 +70,8 @@ class TxnMallTest(BitcoinTestFramework):
node1_address = self.nodes[1].getnewaddress()
# Send tx1, and another transaction tx2 that won't be cloned
- txid1 = self.nodes[0].sendtoaddress(node1_address, 40)
- txid2 = self.nodes[0].sendtoaddress(node1_address, 20)
+ txid1 = self.spend_txid(node0_txid1, find_vout_for_address(self.nodes[0], node0_txid1, node0_address1), {node1_address: 40})
+ txid2 = self.spend_txid(node0_txid2, find_vout_for_address(self.nodes[0], node0_txid2, node0_address2), {node1_address: 20})
# Construct a clone of tx1, to be malleated
rawtx1 = self.nodes[0].getrawtransaction(txid1, 1)
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index bfa171d913..150e4083b9 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -9,6 +9,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
find_output,
+ find_vout_for_address
)
@@ -29,6 +30,13 @@ class TxnMallTest(BitcoinTestFramework):
super().setup_network()
self.disconnect_nodes(1, 2)
+ def spend_txid(self, txid, vout, outputs):
+ inputs = [{"txid": txid, "vout": vout}]
+ tx = self.nodes[0].createrawtransaction(inputs, outputs)
+ tx = self.nodes[0].fundrawtransaction(tx)
+ tx = self.nodes[0].signrawtransactionwithwallet(tx['hex'])
+ return self.nodes[0].sendrawtransaction(tx['hex'])
+
def run_test(self):
# All nodes should start with 1,250 BTC:
starting_balance = 1250
@@ -47,6 +55,7 @@ class TxnMallTest(BitcoinTestFramework):
node0_address_foo = self.nodes[0].getnewaddress()
fund_foo_txid = self.nodes[0].sendtoaddress(node0_address_foo, 1219)
fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
+ self.nodes[0].lockunspent(False, [{"txid":fund_foo_txid, "vout": find_vout_for_address(self.nodes[0], fund_foo_txid, node0_address_foo)}])
node0_address_bar = self.nodes[0].getnewaddress()
fund_bar_txid = self.nodes[0].sendtoaddress(node0_address_bar, 29)
@@ -77,8 +86,8 @@ class TxnMallTest(BitcoinTestFramework):
assert_equal(doublespend["complete"], True)
# Create two spends using 1 50 BTC coin each
- txid1 = self.nodes[0].sendtoaddress(node1_address, 40)
- txid2 = self.nodes[0].sendtoaddress(node1_address, 20)
+ txid1 = self.spend_txid(fund_foo_txid, find_vout_for_address(self.nodes[0], fund_foo_txid, node0_address_foo), {node1_address: 40})
+ txid2 = self.spend_txid(fund_bar_txid, find_vout_for_address(self.nodes[0], fund_bar_txid, node0_address_bar), {node1_address: 20})
# Have node0 mine a block:
if (self.options.mine_block):
diff --git a/test/lint/README.md b/test/lint/README.md
index 7e06308347..c4d76eac94 100644
--- a/test/lint/README.md
+++ b/test/lint/README.md
@@ -27,10 +27,10 @@ Usage: test/lint/git-subtree-check.sh [-r] DIR [COMMIT]
To do a full check with `-r`, make sure that you have fetched the upstream repository branch in which the subtree is
maintained:
* for `src/secp256k1`: https://github.com/bitcoin-core/secp256k1.git (branch master)
-* for `src/leveldb`: https://github.com/bitcoin-core/leveldb.git (branch bitcoin-fork)
-* for `src/univalue`: https://github.com/bitcoin-core/univalue.git (branch master)
+* for `src/leveldb`: https://github.com/bitcoin-core/leveldb-subtree.git (branch bitcoin-fork)
+* for `src/univalue`: https://github.com/bitcoin-core/univalue-subtree.git (branch master)
* for `src/crypto/ctaes`: https://github.com/bitcoin-core/ctaes.git (branch master)
-* for `src/crc32c`: https://github.com/google/crc32c.git (branch master)
+* for `src/crc32c`: https://github.com/bitcoin-core/crc32c-subtree.git (branch bitcoin-fork)
To do so, add the upstream repository as remote: