diff options
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): |