aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml5
-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/release-notes.md6
-rw-r--r--src/addrdb.cpp2
-rw-r--r--src/bech32.cpp34
-rw-r--r--src/bench/bench_bitcoin.cpp2
-rw-r--r--src/bitcoin-cli.cpp6
-rw-r--r--src/chainparams.cpp4
-rw-r--r--src/httpserver.cpp8
-rw-r--r--src/init.cpp52
-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/intro.cpp4
-rw-r--r--src/qt/psbtoperationsdialog.cpp7
-rw-r--r--src/rpc/blockchain.cpp4
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/script/sigcache.cpp2
-rw-r--r--src/test/fuzz/muhash.cpp73
-rw-r--r--src/test/fuzz/system.cpp2
-rw-r--r--src/test/getarg_tests.cpp18
-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/rpcdump.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp5
-rw-r--r--src/wallet/scriptpubkeyman.cpp6
-rw-r--r--src/wallet/spend.cpp9
-rw-r--r--src/wallet/test/coinselector_tests.cpp19
-rw-r--r--src/wallet/wallet.cpp2
-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_segwit.py55
-rwxr-xr-xtest/functional/mempool_persist.py2
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py78
-rwxr-xr-xtest/functional/rpc_psbt.py10
-rw-r--r--test/functional/test_framework/authproxy.py4
-rwxr-xr-xtest/functional/test_runner.py5
-rwxr-xr-xtest/functional/wallet_basic.py4
-rwxr-xr-xtest/functional/wallet_txn_clone.py13
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py13
51 files changed, 394 insertions, 210 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 703d28557e..b1d27540ed 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_asmap.py'
merge_script:
- git config --global user.email "ci@ci.ci"
- git config --global user.name "ci"
@@ -151,8 +150,8 @@ task:
# 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
- # 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
+ # TODO enable '--extended'.
+ - python test\functional\test_runner.py --nocleanup --ci --quiet --combinedlogslen=4000 --jobs=4 --timeout-factor=8 --failfast
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/release-notes.md b/doc/release-notes.md
index 0918adb8c2..c6b43379d3 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -86,6 +86,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
-------------------
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/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index 33a9ea80ed..0b43ea1fd5 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -107,7 +107,7 @@ int main(int argc, char** argv)
benchmark::Args args;
args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
args.is_list_only = argsman.GetBoolArg("-list", false);
- args.min_time = std::chrono::milliseconds(argsman.GetArg("-min_time", DEFAULT_MIN_TIME_MS));
+ 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);
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/chainparams.cpp b/src/chainparams.cpp
index fdee64c529..c1e59d36e0 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");
+
+ // TODO: remove hardcoded nodes 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");
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..25b9c6b9ec 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
@@ -915,8 +913,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 +928,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 +945,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 +990,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 +1099,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 +1206,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 +1216,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 +1323,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 +1341,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 +1497,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 +1514,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 +1707,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/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/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/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/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 08d21e0f71..3ee65922f9 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1503,7 +1503,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 +1650,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()));
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/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/system.cpp b/src/test/fuzz/system.cpp
index 0f53939eac..00403c1a32 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -97,7 +97,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);
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/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/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 c101f6560d..c430b1db5c 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -4420,7 +4420,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"
@@ -4467,6 +4467,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/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 a9610d05a4..7a7ff3ee33 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2735,7 +2735,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/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_segwit.py b/test/functional/feature_segwit.py
index 79254546f1..4054a9a903 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):
@@ -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)
@@ -232,11 +237,11 @@ 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)
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)
+ assert_equal(tmpl['transactions'][0]['sigops'], 8)
assert '!segwit' in tmpl['rules']
self.generate(self.nodes[0], 1) # Mine a block to clear the gbt cache
@@ -356,7 +361,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 +380,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 +399,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 +412,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 +451,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 +514,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 +524,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 +535,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 +602,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 +620,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 015876cbbf..71a132dca3 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -155,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)
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/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_runner.py b/test/functional/test_runner.py
index 3792d751de..b463f05869 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.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 5c464ae142..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
@@ -463,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_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):