aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml10
-rw-r--r--build_msvc/.gitignore1
-rw-r--r--build_msvc/README.md28
-rw-r--r--build_msvc/common.init.vcxproj9
-rw-r--r--build_msvc/vcpkg-packages.txt1
-rw-r--r--build_msvc/vcpkg.json19
-rw-r--r--contrib/linearize/example-linearize.cfg8
-rw-r--r--doc/release-notes-15367.md6
-rw-r--r--doc/release-notes-16378.md5
-rw-r--r--doc/release-notes.md17
-rw-r--r--src/bench/checkblock.cpp3
-rw-r--r--src/bitcoin-cli.cpp3
-rw-r--r--src/chainparams.cpp36
-rw-r--r--src/chainparams.h2
-rw-r--r--src/chainparamsbase.cpp4
-rw-r--r--src/crypto/common.h7
-rw-r--r--src/init.cpp43
-rw-r--r--src/interfaces/wallet.cpp3
-rw-r--r--src/net.cpp110
-rw-r--r--src/net.h53
-rw-r--r--src/net_processing.cpp133
-rw-r--r--src/net_processing.h3
-rw-r--r--src/netaddress.cpp306
-rw-r--r--src/netaddress.h169
-rw-r--r--src/policy/fees.cpp112
-rw-r--r--src/policy/fees.h4
-rw-r--r--src/primitives/transaction.h6
-rw-r--r--src/protocol.cpp33
-rw-r--r--src/protocol.h4
-rw-r--r--src/qt/paymentserver.cpp4
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/net.cpp10
-rw-r--r--src/rpc/rawtransaction_util.cpp8
-rw-r--r--src/script/interpreter.cpp2
-rw-r--r--src/script/script_error.cpp2
-rw-r--r--src/test/base32_tests.cpp3
-rw-r--r--src/test/fuzz/deserialize.cpp5
-rw-r--r--src/test/fuzz/p2p_transport_deserializer.cpp20
-rw-r--r--src/test/miner_tests.cpp13
-rw-r--r--src/test/net_tests.cpp298
-rw-r--r--src/test/pow_tests.cpp32
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/uint256_tests.cpp6
-rw-r--r--src/test/util/setup_common.h16
-rw-r--r--src/test/validation_tests.cpp66
-rw-r--r--src/test/versionbits_tests.cpp4
-rw-r--r--src/uint256.cpp5
-rw-r--r--src/uint256.h17
-rw-r--r--src/util/strencodings.cpp12
-rw-r--r--src/util/strencodings.h16
-rw-r--r--src/util/string.h13
-rw-r--r--src/validation.cpp10
-rw-r--r--src/version.h3
-rw-r--r--src/wallet/feebumper.cpp3
-rw-r--r--src/wallet/rpcwallet.cpp146
-rw-r--r--src/wallet/scriptpubkeyman.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp3
-rw-r--r--src/wallet/wallet.cpp12
-rw-r--r--src/wallet/wallet.h6
-rw-r--r--test/functional/README.md24
-rwxr-xr-xtest/functional/example_test.py12
-rwxr-xr-xtest/functional/feature_block.py6
-rwxr-xr-xtest/functional/feature_cltv.py14
-rwxr-xr-xtest/functional/feature_config_args.py7
-rwxr-xr-xtest/functional/feature_csv_activation.py4
-rwxr-xr-xtest/functional/feature_dersig.py14
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py8
-rwxr-xr-xtest/functional/feature_signet.py4
-rwxr-xr-xtest/functional/feature_versionbits_warning.py6
-rwxr-xr-xtest/functional/mempool_packages.py4
-rwxr-xr-xtest/functional/mining_basic.py6
-rwxr-xr-xtest/functional/p2p_blocksonly.py10
-rwxr-xr-xtest/functional/p2p_dos_header_tree.py16
-rwxr-xr-xtest/functional/p2p_filter.py2
-rwxr-xr-xtest/functional/p2p_getdata.py2
-rwxr-xr-xtest/functional/p2p_invalid_block.py14
-rwxr-xr-xtest/functional/p2p_invalid_locator.py14
-rwxr-xr-xtest/functional/p2p_invalid_messages.py24
-rwxr-xr-xtest/functional/p2p_invalid_tx.py12
-rwxr-xr-xtest/functional/p2p_segwit.py5
-rwxr-xr-xtest/functional/rpc_blockchain.py4
-rwxr-xr-xtest/functional/rpc_getpeerinfo_banscore_deprecation.py24
-rwxr-xr-xtest/functional/rpc_getpeerinfo_deprecation.py39
-rwxr-xr-xtest/functional/rpc_net.py6
-rw-r--r--test/functional/test_framework/address.py72
-rw-r--r--test/functional/test_framework/segwit_addr.py22
-rwxr-xr-xtest/functional/test_framework/test_node.py9
-rwxr-xr-xtest/functional/test_runner.py3
-rwxr-xr-xtest/functional/wallet_address_types.py13
-rwxr-xr-xtest/functional/wallet_basic.py12
-rwxr-xr-xtest/functional/wallet_multiwallet.py2
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py10
-rwxr-xr-xtest/functional/wallet_send.py31
-rwxr-xr-xtest/functional/wallet_txn_clone.py2
-rw-r--r--test/sanitizer_suppressions/ubsan1
95 files changed, 1665 insertions, 674 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 6722c1fbec..67739916f6 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -11,25 +11,19 @@ environment:
QT_DOWNLOAD_HASH: '9a8c6eb20967873785057fdcd329a657c7f922b0af08c5fde105cc597dd37e21'
QT_LOCAL_PATH: 'C:\Qt5.9.8_x64_static_vs2019'
VCPKG_INSTALL_PATH: 'C:\tools\vcpkg\installed'
- VCPKG_COMMIT_ID: 'f3f329a048eaff759c1992c458f2e12351486bc7'
+ VCPKG_COMMIT_ID: '13590753fec479c5b0a3d48dd553dde8d49615fc'
install:
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
# - cmd: pip install zmq
# Powershell block below is to install the c++ dependencies via vcpkg. The pseudo code is:
# a. Checkout the vcpkg source (including port files) for the specific checkout and build the vcpkg binary,
-# b. Install the missing packages.
+# b. Install the missing packages using the vcpkg manifest.
- ps: |
- $env:PACKAGES = Get-Content -Path build_msvc\vcpkg-packages.txt
- Write-Host "vcpkg installing packages: $env:PACKAGES"
cd c:\tools\vcpkg
$env:GIT_REDIRECT_STDERR = '2>&1' # git is writing non-errors to STDERR when doing git pull. Send to STDOUT instead.
git pull origin master > $null
git -c advice.detachedHead=false checkout $env:VCPKG_COMMIT_ID
.\bootstrap-vcpkg.bat > $null
- Add-Content "C:\tools\vcpkg\triplets\$env:PLATFORM-windows-static.cmake" "set(VCPKG_BUILD_TYPE release)"
- .\vcpkg install --triplet $env:PLATFORM-windows-static $env:PACKAGES.split() > $null
- Write-Host "vcpkg packages installed successfully."
- .\vcpkg integrate install
cd "$env:APPVEYOR_BUILD_FOLDER"
before_build:
# Powershell block below is to download and extract the Qt static libraries. The pseudo code is:
diff --git a/build_msvc/.gitignore b/build_msvc/.gitignore
index 3e71c7b8d3..ae8120fdf3 100644
--- a/build_msvc/.gitignore
+++ b/build_msvc/.gitignore
@@ -24,3 +24,4 @@ libtest_util/libtest_util.vcxproj
*/Win32
libbitcoin_qt/QtGeneratedFiles/*
test_bitcoin-qt/QtGeneratedFiles/*
+vcpkg_installed \ No newline at end of file
diff --git a/build_msvc/README.md b/build_msvc/README.md
index a489fb36b2..87ea556a23 100644
--- a/build_msvc/README.md
+++ b/build_msvc/README.md
@@ -3,7 +3,7 @@ Building Bitcoin Core with Visual Studio
Introduction
---------------------
-Solution and project files to build the Bitcoin Core applications `msbuild` or Visual Studio can be found in the build_msvc directory. The build has been tested with Visual Studio 2017 and 2019.
+Solution and project files to build the Bitcoin Core applications `msbuild` or Visual Studio can be found in the `build_msvc` directory. The build has been tested with Visual Studio 2017 and 2019.
Building with Visual Studio is an alternative to the Linux based [cross-compiler build](https://github.com/bitcoin/bitcoin/blob/master/doc/build-windows.md).
@@ -12,10 +12,9 @@ Quick Start
The minimal steps required to build Bitcoin Core with the msbuild toolchain are below. More detailed instructions are contained in the following sections.
```
-vcpkg install --triplet x64-windows-static berkeleydb boost-filesystem boost-multi-index boost-signals2 boost-test boost-thread libevent[thread] zeromq double-conversion
-vcpkg integrate install
-py -3 build_msvc\msvc-autogen.py
-msbuild /m build_msvc\bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
+cd build_msvc
+py -3 msvc-autogen.py
+msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
```
Dependencies
@@ -28,14 +27,7 @@ Options for installing the dependencies in a Visual Studio compatible manner are
- Download the source code, build each dependency, add the required include paths, link libraries and binary tools to the Visual Studio project files.
- Use [nuget](https://www.nuget.org/) packages with the understanding that any binary files have been compiled by an untrusted third party.
-The [external dependencies](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) required for building are:
-
-- Berkeley DB
-- Boost
-- DoubleConversion
-- libevent
-- Qt5
-- ZeroMQ
+The [external dependencies](https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md) required for building are listed in the `build_msvc/vcpkg.json` file. The `msbuild` project files are configured to automatically install the `vcpkg` dependencies.
Qt
---------------------
@@ -52,12 +44,6 @@ Building
The instructions below use `vcpkg` to install the dependencies.
- Install [`vcpkg`](https://github.com/Microsoft/vcpkg).
-- Install the required packages (replace x64 with x86 as required). The list of required packages can be found in the `build_msvc\vcpkg-packages.txt` file. The PowerShell command below will work if run from the repository root directory and `vcpkg` is in the path. Alternatively the contents of the packages text file can be pasted in place of the `Get-Content` cmdlet.
-
-```
-PS >.\vcpkg install --triplet x64-windows-static $(Get-Content -Path build_msvc\vcpkg-packages.txt).split()
-PS >.\vcpkg integrate install
-```
- Use Python to generate `*.vcxproj` from Makefile
@@ -65,7 +51,7 @@ PS >.\vcpkg integrate install
PS >py -3 msvc-autogen.py
```
-- An optional step is to adjust the settings in the build_msvc directory and the common.init.vcxproj file. This project file contains settings that are common to all projects such as the runtime library version and target Windows SDK version. The Qt directories can also be set.
+- An optional step is to adjust the settings in the `build_msvc` directory and the `common.init.vcxproj` file. This project file contains settings that are common to all projects such as the runtime library version and target Windows SDK version. The Qt directories can also be set.
- To build from the command line with the Visual Studio 2017 toolchain use:
@@ -79,7 +65,7 @@ msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /p:PlatformTools
msbuild /m bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build
```
-- Alternatively open the `build_msvc\bitcoin.sln` file in Visual Studio.
+- Alternatively open the `build_msvc/bitcoin.sln` file in Visual Studio.
AppVeyor
---------------------
diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj
index a080fd2aa4..ed227519ae 100644
--- a/build_msvc/common.init.vcxproj
+++ b/build_msvc/common.init.vcxproj
@@ -9,6 +9,15 @@
<UseNativeEnvironment>true</UseNativeEnvironment>
</PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>true</VcpkgEnabled>
+ <VcpkgEnableManifest>true</VcpkgEnableManifest>
+ <VcpkgManifestInstall>true</VcpkgManifestInstall>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ <VcpkgAutoLink>true</VcpkgAutoLink>
+ <VcpkgConfiguration>$(Configuration)</VcpkgConfiguration>
+ </PropertyGroup>
+
<PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' and !Exists('$(WindowsSdkDir)\DesignTime\CommonConfiguration\Neutral\Windows.props')">
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
<WindowsTargetPlatformVersion_10 Condition="'$(WindowsTargetPlatformVersion_10)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0@ProductVersion)</WindowsTargetPlatformVersion_10>
diff --git a/build_msvc/vcpkg-packages.txt b/build_msvc/vcpkg-packages.txt
deleted file mode 100644
index edce8576c3..0000000000
--- a/build_msvc/vcpkg-packages.txt
+++ /dev/null
@@ -1 +0,0 @@
-berkeleydb boost-filesystem boost-multi-index boost-process boost-signals2 boost-test boost-thread libevent[thread] zeromq double-conversion
diff --git a/build_msvc/vcpkg.json b/build_msvc/vcpkg.json
new file mode 100644
index 0000000000..5d5f94227f
--- /dev/null
+++ b/build_msvc/vcpkg.json
@@ -0,0 +1,19 @@
+{
+ "name": "bitcoin-core",
+ "version-string": "1",
+ "dependencies": [
+ "berkeleydb",
+ "boost-filesystem",
+ "boost-multi-index",
+ "boost-process",
+ "boost-signals2",
+ "boost-test",
+ "boost-thread",
+ "double-conversion",
+ {
+ "name": "libevent",
+ "features": ["thread"]
+ },
+ "zeromq"
+ ]
+} \ No newline at end of file
diff --git a/contrib/linearize/example-linearize.cfg b/contrib/linearize/example-linearize.cfg
index 5990b9307a..5f566261ca 100644
--- a/contrib/linearize/example-linearize.cfg
+++ b/contrib/linearize/example-linearize.cfg
@@ -13,6 +13,9 @@ port=8332
#regtest default
#port=18443
+#signet default
+#port=38332
+
# bootstrap.dat hashlist settings (linearize-hashes)
max_height=313000
@@ -33,6 +36,11 @@ input=/home/example/.bitcoin/blocks
#genesis=0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206
#input=/home/example/.bitcoin/regtest/blocks
+# signet
+#netmagic=0a03cf40
+#genesis=00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6
+#input=/home/example/.bitcoin/signet/blocks
+
# "output" option causes blockchain files to be written to the given location,
# with "output_file" ignored. If not used, "output_file" is used instead.
# output=/home/example/blockchain_directory
diff --git a/doc/release-notes-15367.md b/doc/release-notes-15367.md
new file mode 100644
index 0000000000..598e49dcae
--- /dev/null
+++ b/doc/release-notes-15367.md
@@ -0,0 +1,6 @@
+Configuration option changes
+----------------------------
+
+- The `startupnotify` option is used to specify a command to
+ execute when Bitcoin Core has finished with its startup
+ sequence. (#15367) \ No newline at end of file
diff --git a/doc/release-notes-16378.md b/doc/release-notes-16378.md
index b006ea1a56..958633e780 100644
--- a/doc/release-notes-16378.md
+++ b/doc/release-notes-16378.md
@@ -1,5 +1,6 @@
RPC
---
- A new `send` RPC with similar syntax to `walletcreatefundedpsbt`, including
- support for coin selection and a custom fee rate. Using the new `send` method
- is encouraged: `sendmany` and `sendtoaddress` may be deprecated in a future release.
+ support for coin selection and a custom fee rate. The `send` RPC is experimental
+ and may change in subsequent releases. Using it is encouraged once it's no
+ longer experimental: `sendmany` and `sendtoaddress` may be deprecated in a future release.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 1baff028f3..d5cc5e90f8 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -105,6 +105,17 @@ will trigger BIP 125 (replace-by-fee) opt-in. (#11413)
- The `testmempoolaccept` RPC returns `vsize` and a `fee` object with the `base` fee
if the transaction passes validation. (#19940)
+- The `getpeerinfo` RPC now returns a `connection_type` field. This indicates
+ the type of connection established with the peer. It will return one of six
+ options. For more information, see the `getpeerinfo` help documentation.
+ (#19725)
+
+- The `getpeerinfo` RPC no longer returns the `addnode` field by default. This
+ field will be fully removed in the next major release. It can be accessed
+ with the configuration option `-deprecatedrpc=getpeerinfo_addnode`. However,
+ it is recommended to instead use the `connection_type` field (it will return
+ `manual` when addnode is true). (#19725)
+
- The `walletcreatefundedpsbt` RPC call will now fail with
`Insufficient funds` when inputs are manually selected but are not enough to cover
the outputs and fee. Additional inputs can automatically be added through the
@@ -144,7 +155,8 @@ Updated settings
- Netmasks that contain 1-bits after 0-bits (the 1-bits are not contiguous on
the left side, e.g. 255.0.255.255) are no longer accepted. They are invalid
- according to RFC 4632.
+ according to RFC 4632. Netmasks are used in the `-rpcallowip` and `-whitelist`
+ configuration options and in the `setban` RPC. (#19628)
Changes to Wallet or GUI related settings can be found in the GUI or Wallet section below.
@@ -179,6 +191,9 @@ Wallet
introduced unbroadcast set. See the "P2P and network changes" section for
more information on the unbroadcast set. (#18038)
+- The `sendtoaddress` and `sendmany` RPCs accept an optional `verbose=True`
+ argument to also return the fee reason about the sent tx. (#19501)
+
- The wallet can create a transaction without change even when the keypool is
empty. Previously it failed. (#17219)
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index dc0aa4031c..a9f3f5f84d 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -34,7 +34,8 @@ static void DeserializeAndCheckBlockTest(benchmark::Bench& bench)
char a = '\0';
stream.write(&a, 1); // Prevent compaction
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ ArgsManager bench_args;
+ const auto chainParams = CreateChainParams(bench_args, CBaseChainParams::MAIN);
bench.unit("block").run([&] {
CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index e94d4dff49..aec4177b19 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -51,6 +51,7 @@ static void SetupCliArgs(ArgsManager& argsman)
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
+ const auto signetBaseParams = CreateBaseChainParams(CBaseChainParams::SIGNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -66,7 +67,7 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index d7f7888ef3..ef501e9de2 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -265,11 +265,21 @@ public:
vSeeds.clear();
if (!args.IsArgSet("-signetchallenge")) {
- LogPrintf("Using default signet network\n");
bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
vSeeds.emplace_back("178.128.221.177");
vSeeds.emplace_back("2a01:7c8:d005:390::5");
vSeeds.emplace_back("ntv3mtqw5wt63red.onion:38333");
+
+ consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000d145533ce");
+ consensus.defaultAssumeValid = uint256S("0x00000128807d9175c494e24d805fc7854f7d79aa965cbb128342ad8b70cecfa5"); // 5348
+ m_assumed_blockchain_size = 1;
+ m_assumed_chain_state_size = 0;
+ chainTxData = ChainTxData{
+ // Data from RPC: getchaintxstats 4096 00000128807d9175c494e24d805fc7854f7d79aa965cbb128342ad8b70cecfa5
+ /* nTime */ 1601382000,
+ /* nTxCount */ 5435,
+ /* dTxRate */ 0.001898346323372538,
+ };
} else {
const auto signet_challenge = args.GetArgs("-signetchallenge");
if (signet_challenge.size() != 1) {
@@ -277,6 +287,13 @@ public:
}
bin = ParseHex(signet_challenge[0]);
+ m_assumed_blockchain_size = 0;
+ m_assumed_chain_state_size = 0;
+ chainTxData = ChainTxData{
+ 0,
+ 0,
+ 0,
+ };
LogPrintf("Signet with challenge %s\n", signet_challenge[0]);
}
@@ -309,12 +326,9 @@ public:
h << consensus.signet_challenge;
uint256 hash = h.GetHash();
memcpy(pchMessageStart, hash.begin(), 4);
- LogPrintf("Signet derived magic (message start): %s\n", HexStr({pchMessageStart, pchMessageStart + 4}));
nDefaultPort = 38333;
nPruneAfterHeight = 1000;
- m_assumed_blockchain_size = 0;
- m_assumed_chain_state_size = 0;
genesis = CreateGenesisBlock(1598918400, 52613770, 0x1e0377ae, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
@@ -335,12 +349,6 @@ public:
fRequireStandard = true;
m_is_test_chain = true;
m_is_mockable_chain = false;
-
- chainTxData = ChainTxData{
- 0,
- 0,
- 0
- };
}
};
@@ -485,16 +493,16 @@ const CChainParams &Params() {
return *globalChainParams;
}
-std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain)
+std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const std::string& chain)
{
if (chain == CBaseChainParams::MAIN) {
return std::unique_ptr<CChainParams>(new CMainParams());
} else if (chain == CBaseChainParams::TESTNET) {
return std::unique_ptr<CChainParams>(new CTestNetParams());
} else if (chain == CBaseChainParams::SIGNET) {
- return std::unique_ptr<CChainParams>(new SigNetParams(gArgs));
+ return std::unique_ptr<CChainParams>(new SigNetParams(args));
} else if (chain == CBaseChainParams::REGTEST) {
- return std::unique_ptr<CChainParams>(new CRegTestParams(gArgs));
+ return std::unique_ptr<CChainParams>(new CRegTestParams(args));
}
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
@@ -502,5 +510,5 @@ std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain)
void SelectParams(const std::string& network)
{
SelectBaseParams(network);
- globalChainParams = CreateChainParams(network);
+ globalChainParams = CreateChainParams(gArgs, network);
}
diff --git a/src/chainparams.h b/src/chainparams.h
index 7aa999fb75..d8b25c7220 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -119,7 +119,7 @@ protected:
* @returns a CChainParams* of the chosen chain.
* @throws a std::runtime_error if the chain is not supported.
*/
-std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain);
+std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const std::string& chain);
/**
* Return the currently selected parameters. This won't change after app
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 034e897ca6..f19e72e9ab 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -18,13 +18,13 @@ const std::string CBaseChainParams::REGTEST = "regtest";
void SetupChainParamsBaseOptions(ArgsManager& argsman)
{
- argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-signet", "Use the signet chain. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
+ argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
}
diff --git a/src/crypto/common.h b/src/crypto/common.h
index 5b4932c992..c1acf8b22e 100644
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -53,6 +53,13 @@ void static inline WriteLE64(unsigned char* ptr, uint64_t x)
memcpy(ptr, (char*)&v, 8);
}
+uint16_t static inline ReadBE16(const unsigned char* ptr)
+{
+ uint16_t x;
+ memcpy((char*)&x, ptr, 2);
+ return be16toh(x);
+}
+
uint32_t static inline ReadBE32(const unsigned char* ptr)
{
uint32_t x;
diff --git a/src/init.cpp b/src/init.cpp
index 5ad807cbac..edf2a79998 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -37,6 +37,7 @@
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
+#include <protocol.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
@@ -378,10 +379,12 @@ void SetupServerArgs(NodeContext& node)
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
+ const auto signetBaseParams = CreateBaseChainParams(CBaseChainParams::SIGNET);
const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
- const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
- const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET);
- const auto regtestChainParams = CreateChainParams(CBaseChainParams::REGTEST);
+ const auto defaultChainParams = CreateChainParams(argsman, CBaseChainParams::MAIN);
+ const auto testnetChainParams = CreateChainParams(argsman, CBaseChainParams::TESTNET);
+ const auto signetChainParams = CreateChainParams(argsman, CBaseChainParams::SIGNET);
+ const auto regtestChainParams = CreateChainParams(argsman, CBaseChainParams::REGTEST);
// Hidden Options
std::vector<std::string> hidden_args = {
@@ -393,7 +396,7 @@ void SetupServerArgs(NodeContext& node)
#if HAVE_SYSTEM
argsman.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
- argsman.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s, signet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex(), signetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-blocksdir=<dir>", "Specify directory to hold blocks subdirectory for *.dat files (default: <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
argsman.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -411,7 +414,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-maxorphantx=<n>", strprintf("Keep at most <n> unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- argsman.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s, signet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex(), signetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-par=<n>", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)",
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -422,6 +425,9 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+#if HAVE_SYSTEM
+ argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+#endif
#ifndef WIN32
argsman.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
@@ -455,7 +461,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u signet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -565,7 +571,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
- argsman.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
+ argsman.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
@@ -674,6 +680,17 @@ static void CleanupBlockRevFiles()
}
}
+#if HAVE_SYSTEM
+static void StartupNotify(const ArgsManager& args)
+{
+ std::string cmd = args.GetArg("-startupnotify", "");
+ if (!cmd.empty()) {
+ std::thread t(runCommand, cmd);
+ t.detach(); // thread runs free
+ }
+}
+#endif
+
static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
{
const CChainParams& chainparams = Params();
@@ -950,6 +967,9 @@ bool AppInitParameterInteraction(const ArgsManager& args)
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
std::string network = args.GetChainName();
+ if (network == CBaseChainParams::SIGNET) {
+ LogPrintf("Signet derived magic (message start): %s\n", HexStr(chainparams.MessageStart()));
+ }
bilingual_str errors;
for (const auto& arg : args.GetUnsuitableSectionOnlyArgs()) {
errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, network, network);
@@ -1176,6 +1196,10 @@ bool AppInitParameterInteraction(const ArgsManager& args)
nMaxTipAge = args.GetArg("-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>."));
+ }
+
return true;
}
@@ -1618,7 +1642,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
bool failed_chainstate_init = false;
for (CChainState* chainstate : chainman.GetAll()) {
- LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
chainstate->InitCoinsDB(
/* cache_size_bytes */ nCoinDBCache,
/* in_memory */ false,
@@ -1973,5 +1996,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
banman->DumpBanlist();
}, DUMP_BANS_INTERVAL);
+#if HAVE_SYSTEM
+ StartupNotify(args);
+#endif
+
return true;
}
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 94c63da84e..f68016b557 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -231,8 +231,9 @@ public:
{
LOCK(m_wallet->cs_wallet);
CTransactionRef tx;
+ FeeCalculation fee_calc_out;
if (!m_wallet->CreateTransaction(recipients, tx, fee, change_pos,
- fail_reason, coin_control, sign)) {
+ fail_reason, coin_control, fee_calc_out, sign)) {
return {};
}
return tx;
diff --git a/src/net.cpp b/src/net.cpp
index e7d3a146ff..7dec4abfb9 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -10,7 +10,6 @@
#include <net.h>
#include <banman.h>
-#include <chainparams.h>
#include <clientversion.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
@@ -488,6 +487,26 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNet
}
}
+std::string CNode::ConnectionTypeAsString() const
+{
+ switch (m_conn_type) {
+ case ConnectionType::INBOUND:
+ return "inbound";
+ case ConnectionType::MANUAL:
+ return "manual";
+ case ConnectionType::FEELER:
+ return "feeler";
+ case ConnectionType::OUTBOUND_FULL_RELAY:
+ return "outbound-full-relay";
+ case ConnectionType::BLOCK_RELAY:
+ return "block-relay-only";
+ case ConnectionType::ADDR_FETCH:
+ return "addr-fetch";
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+}
+
std::string CNode::GetAddrName() const {
LOCK(cs_addrName);
return addrName;
@@ -582,9 +601,21 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";
+
+ stats.m_conn_type_string = ConnectionTypeAsString();
}
#undef X
+/**
+ * Receive bytes from the buffer and deserialize them into messages.
+ *
+ * @param[in] pch A pointer to the raw data
+ * @param[in] nBytes Size of the data
+ * @param[out] complete Set True if at least one message has been
+ * deserialized and is ready to be processed
+ * @return True if the peer should stay connected,
+ * False if the peer should be disconnected from.
+ */
bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)
{
complete = false;
@@ -595,25 +626,35 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
while (nBytes > 0) {
// absorb network data
int handled = m_deserializer->Read(pch, nBytes);
- if (handled < 0) return false;
+ if (handled < 0) {
+ // Serious header problem, disconnect from the peer.
+ return false;
+ }
pch += handled;
nBytes -= handled;
if (m_deserializer->Complete()) {
// decompose a transport agnostic CNetMessage from the deserializer
- CNetMessage msg = m_deserializer->GetMessage(Params().MessageStart(), time);
+ uint32_t out_err_raw_size{0};
+ Optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
+ if (!result) {
+ // Message deserialization failed. Drop the message but don't disconnect the peer.
+ // store the size of the corrupt message
+ mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER)->second += out_err_raw_size;
+ continue;
+ }
//store received bytes per message command
//to prevent a memory DOS, only allow valid commands
- mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(msg.m_command);
+ mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(result->m_command);
if (i == mapRecvBytesPerMsgCmd.end())
i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);
assert(i != mapRecvBytesPerMsgCmd.end());
- i->second += msg.m_raw_message_size;
+ i->second += result->m_raw_message_size;
// push the message to the process queue,
- vRecvMsg.push_back(std::move(msg));
+ vRecvMsg.push_back(std::move(*result));
complete = true;
}
@@ -640,11 +681,19 @@ int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes)
hdrbuf >> hdr;
}
catch (const std::exception&) {
+ LogPrint(BCLog::NET, "HEADER ERROR - UNABLE TO DESERIALIZE, peer=%d\n", m_node_id);
+ return -1;
+ }
+
+ // Check start string, network magic
+ if (memcmp(hdr.pchMessageStart, m_chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
+ LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESTART (%s, %u bytes), received %s, peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, HexStr(hdr.pchMessageStart), m_node_id);
return -1;
}
// reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
+ LogPrint(BCLog::NET, "HEADER ERROR - SIZE (%s, %u bytes), peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, m_node_id);
return -1;
}
@@ -679,36 +728,39 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
return data_hash;
}
-CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageStartChars& message_start, const std::chrono::microseconds time)
+Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
{
// decompose a single CNetMessage from the TransportDeserializer
- CNetMessage msg(std::move(vRecv));
+ Optional<CNetMessage> msg(std::move(vRecv));
- // store state about valid header, netmagic and checksum
- msg.m_valid_header = hdr.IsValid(message_start);
- msg.m_valid_netmagic = (memcmp(hdr.pchMessageStart, message_start, CMessageHeader::MESSAGE_START_SIZE) == 0);
- uint256 hash = GetMessageHash();
+ // store command string, time, and sizes
+ msg->m_command = hdr.GetCommand();
+ msg->m_time = time;
+ msg->m_message_size = hdr.nMessageSize;
+ msg->m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
- // store command string, payload size
- msg.m_command = hdr.GetCommand();
- msg.m_message_size = hdr.nMessageSize;
- msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
+ uint256 hash = GetMessageHash();
// We just received a message off the wire, harvest entropy from the time (and the message checksum)
RandAddEvent(ReadLE32(hash.begin()));
- msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0);
- if (!msg.m_valid_checksum) {
- LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n",
- SanitizeString(msg.m_command), msg.m_message_size,
+ // Check checksum and header command string
+ if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
+ LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
+ SanitizeString(msg->m_command), msg->m_message_size,
HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)),
- HexStr(hdr.pchChecksum));
- }
-
- // store receive time
- msg.m_time = time;
-
- // reset the network deserializer (prepare for the next message)
+ HexStr(hdr.pchChecksum),
+ m_node_id);
+ out_err_raw_size = msg->m_raw_message_size;
+ msg = nullopt;
+ } else if (!hdr.IsCommandValid()) {
+ LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
+ hdr.GetCommand(), msg->m_message_size, m_node_id);
+ out_err_raw_size = msg->m_raw_message_size;
+ msg = nullopt;
+ }
+
+ // Always reset the network deserializer (prepare for the next message)
Reset();
return msg;
}
@@ -1759,7 +1811,7 @@ void CConnman::SetTryNewOutboundPeer(bool flag)
// Return the number of peers we have over our outbound connection limit
// Exclude peers that are marked for disconnect, or are going to be
-// disconnected soon (eg one-shots and feelers)
+// disconnected soon (eg ADDR_FETCH and FEELER)
// Also exclude peers that haven't finished initial connection handshake yet
// (so that we don't decide we're over our desired connection limit, and then
// evict some peer that has finished the handshake)
@@ -2828,7 +2880,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}
- m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION));
+ m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
m_serializer = MakeUnique<V1TransportSerializer>(V1TransportSerializer());
}
diff --git a/src/net.h b/src/net.h
index 62167014ff..fb68827144 100644
--- a/src/net.h
+++ b/src/net.h
@@ -10,12 +10,14 @@
#include <addrman.h>
#include <amount.h>
#include <bloom.h>
+#include <chainparams.h>
#include <compat.h>
#include <crypto/siphash.h>
#include <hash.h>
#include <limitedmap.h>
-#include <netaddress.h>
#include <net_permissions.h>
+#include <netaddress.h>
+#include <optional.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
@@ -114,6 +116,14 @@ struct CSerializedNetMsg
std::string m_type;
};
+const std::vector<std::string> CONNECTION_TYPE_DOC{
+ "outbound-full-relay (default automatic connections)",
+ "block-relay-only (does not relay transactions or addresses)",
+ "inbound (initiated by the peer)",
+ "manual (added via addnode RPC or -addnode/-connect configuration options)",
+ "addr-fetch (short-lived automatic connection for soliciting addresses)",
+ "feeler (short-lived automatic connection for testing addresses)"};
+
/** Different types of connections to a peer. This enum encapsulates the
* information we have available at the time of opening or accepting the
* connection. Aside from INBOUND, all types are initiated by us. */
@@ -143,10 +153,19 @@ enum class ConnectionType {
MANUAL,
/**
- * Feeler connections are short lived connections used to increase the
- * number of connectable addresses in our AddrMan. Approximately every
- * FEELER_INTERVAL, we attempt to connect to a random address from the new
- * table. If successful, we add it to the tried table.
+ * Feeler connections are short-lived connections made to check that a node
+ * is alive. They can be useful for:
+ * - test-before-evict: if one of the peers is considered for eviction from
+ * our AddrMan because another peer is mapped to the same slot in the tried table,
+ * evict only if this longer-known peer is offline.
+ * - move node addresses from New to Tried table, so that we have more
+ * connectable addresses in our AddrMan.
+ * Note that in the literature ("Eclipse Attacks on Bitcoin’s Peer-to-Peer Network")
+ * only the latter feature is referred to as "feeler connections",
+ * although in our codebase feeler connections encompass test-before-evict as well.
+ * We make these connections approximately every FEELER_INTERVAL:
+ * first we resolve previously found collisions if they exist (test-before-evict),
+ * otherwise connect to a node from the new table.
*/
FEELER,
@@ -691,6 +710,7 @@ public:
// Bind address of our side of the connection
CAddress addrBind;
uint32_t m_mapped_as;
+ std::string m_conn_type_string;
};
@@ -703,11 +723,8 @@ class CNetMessage {
public:
CDataStream m_recv; //!< received message data
std::chrono::microseconds m_time{0}; //!< time of message receipt
- bool m_valid_netmagic = false;
- bool m_valid_header = false;
- bool m_valid_checksum = false;
- uint32_t m_message_size{0}; //!< size of the payload
- uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
+ uint32_t m_message_size{0}; //!< size of the payload
+ uint32_t m_raw_message_size{0}; //!< used wire size of the message (including header/checksum)
std::string m_command;
CNetMessage(CDataStream&& recv_in) : m_recv(std::move(recv_in)) {}
@@ -731,13 +748,15 @@ public:
// read and deserialize data
virtual int Read(const char *data, unsigned int bytes) = 0;
// decomposes a message from the context
- virtual CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) = 0;
+ virtual Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err) = 0;
virtual ~TransportDeserializer() {}
};
class V1TransportDeserializer final : public TransportDeserializer
{
private:
+ const CChainParams& m_chain_params;
+ const NodeId m_node_id; // Only for logging
mutable CHash256 hasher;
mutable uint256 data_hash;
bool in_data; // parsing header (false) or data (true)
@@ -763,8 +782,12 @@ private:
}
public:
-
- V1TransportDeserializer(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
+ V1TransportDeserializer(const CChainParams& chain_params, const NodeId node_id, int nTypeIn, int nVersionIn)
+ : m_chain_params(chain_params),
+ m_node_id(node_id),
+ hdrbuf(nTypeIn, nVersionIn),
+ vRecv(nTypeIn, nVersionIn)
+ {
Reset();
}
@@ -784,7 +807,7 @@ public:
if (ret < 0) Reset();
return ret;
}
- CNetMessage GetMessage(const CMessageHeader::MessageStartChars& message_start, std::chrono::microseconds time) override;
+ Optional<CNetMessage> GetMessage(std::chrono::microseconds time, uint32_t& out_err_raw_size) override;
};
/** The TransportSerializer prepares messages for the network transport
@@ -1144,6 +1167,8 @@ public:
std::string GetAddrName() const;
//! Sets the addrName only if it was not previously set
void MaybeSetAddrName(const std::string& addrNameIn);
+
+ std::string ConnectionTypeAsString() const;
};
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index f3f30285d8..d3a357fee2 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -153,8 +153,14 @@ struct COrphanTx {
int64_t nTimeExpire;
size_t list_pos;
};
+
+/** Guards orphan transactions and extra txs for compact blocks */
RecursiveMutex g_cs_orphans;
+/** Map from txid to orphan transaction record. Limited by
+ * -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
+/** Index from wtxid into the mapOrphanTransactions to lookup orphan
+ * transactions using their witness ids. */
std::map<uint256, std::map<uint256, COrphanTx>::iterator> g_orphans_by_wtxid GUARDED_BY(g_cs_orphans);
void EraseOrphansFor(NodeId peer);
@@ -258,12 +264,19 @@ namespace {
return &(*a) < &(*b);
}
};
- std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
- std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans); //! For random eviction
+ /** Index from the parents' COutPoint into the mapOrphanTransactions. Used
+ * to remove orphan transactions from the mapOrphanTransactions */
+ std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
+ /** Orphan transactions in vector for quick random eviction */
+ std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans);
- static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
+ /** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
+ * The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
+ * these are kept in a ring buffer */
static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
+ /** Offset into vExtraTxnForCompact to insert the next tx */
+ static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
} // namespace
namespace {
@@ -2021,13 +2034,20 @@ void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHe
return;
}
-void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn)
+/**
+ * Reconsider orphan transactions after a parent has been accepted to the mempool.
+ *
+ * @param[in/out] orphan_work_set The set of orphan transactions to reconsider. Generally only one
+ * orphan will be reconsidered on each call of this function. This set
+ * may be added to if accepting an orphan causes its children to be
+ * reconsidered.
+ */
+void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
{
AssertLockHeld(cs_main);
AssertLockHeld(g_cs_orphans);
- std::set<NodeId> setMisbehaving;
- bool done = false;
- while (!done && !orphan_work_set.empty()) {
+
+ while (!orphan_work_set.empty()) {
const uint256 orphanHash = *orphan_work_set.begin();
orphan_work_set.erase(orphan_work_set.begin());
@@ -2035,18 +2055,13 @@ void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<
if (orphan_it == mapOrphanTransactions.end()) continue;
const CTransactionRef porphanTx = orphan_it->second.tx;
- const CTransaction& orphanTx = *porphanTx;
- NodeId fromPeer = orphan_it->second.fromPeer;
- // Use a new TxValidationState because orphans come from different peers (and we call
- // MaybePunishNodeForTx based on the source peer from the orphan map, not based on the peer
- // that relayed the previous transaction).
- TxValidationState orphan_state;
-
- if (setMisbehaving.count(fromPeer)) continue;
- if (AcceptToMemoryPool(m_mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ TxValidationState state;
+ std::list<CTransactionRef> removed_txn;
+
+ if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
- for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
+ for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
for (const auto& elem : it_by_prev->second) {
@@ -2055,22 +2070,23 @@ void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<
}
}
EraseOrphanTx(orphanHash);
- done = true;
- } else if (orphan_state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
- if (orphan_state.IsInvalid()) {
- // Punish peer that gave us an invalid orphan tx
- if (MaybePunishNodeForTx(fromPeer, orphan_state)) {
- setMisbehaving.insert(fromPeer);
- }
+ for (const CTransactionRef& removedTx : removed_txn) {
+ AddToCompactExtraTransactions(removedTx);
+ }
+ break;
+ } else if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
+ if (state.IsInvalid()) {
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
orphanHash.ToString(),
- fromPeer,
- orphan_state.ToString());
+ orphan_it->second.fromPeer,
+ state.ToString());
+ // Maybe punish peer that gave us an invalid orphan tx
+ MaybePunishNodeForTx(orphan_it->second.fromPeer, state);
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
- if (orphan_state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
+ if (state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
// We can add the wtxid of this transaction to our reject filter.
// Do not add txids of witness transactions or witness-stripped
// transactions to the filter, as they can have been malleated;
@@ -2085,7 +2101,7 @@ void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<
// for concerns around weakening security of unupgraded nodes
// if we start doing this too early.
assert(recentRejects);
- recentRejects->insert(orphanTx.GetWitnessHash());
+ recentRejects->insert(porphanTx->GetWitnessHash());
// If the transaction failed for TX_INPUTS_NOT_STANDARD,
// then we know that the witness was irrelevant to the policy
// failure, since this check depends only on the txid
@@ -2094,17 +2110,17 @@ void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<
// processing of this transaction in the event that child
// transactions are later received (resulting in
// parent-fetching by txid via the orphan-handling logic).
- if (orphan_state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && orphanTx.GetWitnessHash() != orphanTx.GetHash()) {
+ if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && porphanTx->GetWitnessHash() != porphanTx->GetHash()) {
// We only add the txid if it differs from the wtxid, to
// avoid wasting entries in the rolling bloom filter.
- recentRejects->insert(orphanTx.GetHash());
+ recentRejects->insert(porphanTx->GetHash());
}
}
EraseOrphanTx(orphanHash);
- done = true;
+ break;
}
- m_mempool.check(&::ChainstateActive().CoinsTip());
}
+ m_mempool.check(&::ChainstateActive().CoinsTip());
}
/**
@@ -3017,8 +3033,12 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
tx.GetHash().ToString(),
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
+ for (const CTransactionRef& removedTx : lRemovedTxn) {
+ AddToCompactExtraTransactions(removedTx);
+ }
+
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(pfrom.orphan_work_set, lRemovedTxn);
+ ProcessOrphanTx(pfrom.orphan_work_set);
}
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
@@ -3119,9 +3139,6 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
}
}
- for (const CTransactionRef& removedTx : lRemovedTxn)
- AddToCompactExtraTransactions(removedTx);
-
// If a tx has been detected by recentRejects, we will have reached
// this point and the tx will have been ignored. Because we haven't run
// the tx through AcceptToMemoryPool, we won't have computed a DoS
@@ -3520,11 +3537,7 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
// Making nodes which are behind NAT and can only make outgoing connections ignore
// the getaddr message mitigates the attack.
if (!pfrom.IsInboundConn()) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
- return;
- }
- if (!pfrom.RelayAddrsWithConn()) {
- LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId());
+ LogPrint(BCLog::NET, "Ignoring \"getaddr\" from %s connection. peer=%d\n", pfrom.ConnectionTypeAsString(), pfrom.GetId());
return;
}
@@ -3819,26 +3832,14 @@ bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode)
bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
{
- //
- // Message format
- // (4) message start
- // (12) command
- // (4) size
- // (4) checksum
- // (x) data
- //
bool fMoreWork = false;
if (!pfrom->vRecvGetData.empty())
ProcessGetData(*pfrom, m_chainparams, m_connman, m_mempool, interruptMsgProc);
if (!pfrom->orphan_work_set.empty()) {
- std::list<CTransactionRef> removed_txn;
LOCK2(cs_main, g_cs_orphans);
- ProcessOrphanTx(pfrom->orphan_work_set, removed_txn);
- for (const CTransactionRef& removedTx : removed_txn) {
- AddToCompactExtraTransactions(removedTx);
- }
+ ProcessOrphanTx(pfrom->orphan_work_set);
}
if (pfrom->fDisconnect)
@@ -3867,35 +3868,13 @@ bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgP
CNetMessage& msg(msgs.front());
msg.SetVersion(pfrom->GetCommonVersion());
- // Check network magic
- if (!msg.m_valid_netmagic) {
- LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
- pfrom->fDisconnect = true;
- return false;
- }
-
- // Check header
- if (!msg.m_valid_header)
- {
- LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
- return fMoreWork;
- }
const std::string& msg_type = msg.m_command;
// Message size
unsigned int nMessageSize = msg.m_message_size;
- // Checksum
- CDataStream& vRecv = msg.m_recv;
- if (!msg.m_valid_checksum)
- {
- LogPrint(BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR peer=%d\n", __func__,
- SanitizeString(msg_type), nMessageSize, pfrom->GetId());
- return fMoreWork;
- }
-
try {
- ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, interruptMsgProc);
+ ProcessMessage(*pfrom, msg_type, msg.m_recv, msg.m_time, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
diff --git a/src/net_processing.h b/src/net_processing.h
index 3e748c0c5b..946a5f4715 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -121,8 +121,7 @@ private:
*/
bool MaybeDiscourageAndDisconnect(CNode& pnode);
- void ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn)
- EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
+ void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
/** Process a single headers message from a peer. */
void ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHeader>& headers, bool via_compact_block);
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index b50cf74069..08714dc2ec 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -5,18 +5,112 @@
#include <netaddress.h>
+#include <crypto/common.h>
+#include <crypto/sha3.h>
#include <hash.h>
+#include <prevector.h>
#include <tinyformat.h>
-#include <util/strencodings.h>
#include <util/asmap.h>
+#include <util/strencodings.h>
+#include <util/string.h>
#include <algorithm>
#include <array>
#include <cstdint>
+#include <ios>
#include <iterator>
#include <tuple>
constexpr size_t CNetAddr::V1_SERIALIZATION_SIZE;
+constexpr size_t CNetAddr::MAX_ADDRV2_SIZE;
+
+CNetAddr::BIP155Network CNetAddr::GetBIP155Network() const
+{
+ switch (m_net) {
+ case NET_IPV4:
+ return BIP155Network::IPV4;
+ case NET_IPV6:
+ return BIP155Network::IPV6;
+ case NET_ONION:
+ switch (m_addr.size()) {
+ case ADDR_TORV2_SIZE:
+ return BIP155Network::TORV2;
+ case ADDR_TORV3_SIZE:
+ return BIP155Network::TORV3;
+ default:
+ assert(false);
+ }
+ case NET_I2P:
+ return BIP155Network::I2P;
+ case NET_CJDNS:
+ return BIP155Network::CJDNS;
+ case NET_INTERNAL: // should have been handled before calling this function
+ case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
+ case NET_MAX: // m_net is never and should not be set to NET_MAX
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
+}
+
+bool CNetAddr::SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size)
+{
+ switch (possible_bip155_net) {
+ case BIP155Network::IPV4:
+ if (address_size == ADDR_IPV4_SIZE) {
+ m_net = NET_IPV4;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 IPv4 address with length %u (should be %u)", address_size,
+ ADDR_IPV4_SIZE));
+ case BIP155Network::IPV6:
+ if (address_size == ADDR_IPV6_SIZE) {
+ m_net = NET_IPV6;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 IPv6 address with length %u (should be %u)", address_size,
+ ADDR_IPV6_SIZE));
+ case BIP155Network::TORV2:
+ if (address_size == ADDR_TORV2_SIZE) {
+ m_net = NET_ONION;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 TORv2 address with length %u (should be %u)", address_size,
+ ADDR_TORV2_SIZE));
+ case BIP155Network::TORV3:
+ if (address_size == ADDR_TORV3_SIZE) {
+ m_net = NET_ONION;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 TORv3 address with length %u (should be %u)", address_size,
+ ADDR_TORV3_SIZE));
+ case BIP155Network::I2P:
+ if (address_size == ADDR_I2P_SIZE) {
+ m_net = NET_I2P;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 I2P address with length %u (should be %u)", address_size,
+ ADDR_I2P_SIZE));
+ case BIP155Network::CJDNS:
+ if (address_size == ADDR_CJDNS_SIZE) {
+ m_net = NET_CJDNS;
+ return true;
+ }
+ throw std::ios_base::failure(
+ strprintf("BIP155 CJDNS address with length %u (should be %u)", address_size,
+ ADDR_CJDNS_SIZE));
+ }
+
+ // Don't throw on addresses with unknown network ids (maybe from the future).
+ // Instead silently drop them and have the unserialization code consume
+ // subsequent ones which may be known to us.
+ return false;
+}
/**
* Construct an unspecified IPv6 network address (::/128).
@@ -36,7 +130,13 @@ void CNetAddr::SetIP(const CNetAddr& ipIn)
assert(ipIn.m_addr.size() == ADDR_IPV6_SIZE);
break;
case NET_ONION:
- assert(ipIn.m_addr.size() == ADDR_TORV2_SIZE);
+ assert(ipIn.m_addr.size() == ADDR_TORV2_SIZE || ipIn.m_addr.size() == ADDR_TORV3_SIZE);
+ break;
+ case NET_I2P:
+ assert(ipIn.m_addr.size() == ADDR_I2P_SIZE);
+ break;
+ case NET_CJDNS:
+ assert(ipIn.m_addr.size() == ADDR_CJDNS_SIZE);
break;
case NET_INTERNAL:
assert(ipIn.m_addr.size() == ADDR_INTERNAL_SIZE);
@@ -50,13 +150,6 @@ void CNetAddr::SetIP(const CNetAddr& ipIn)
m_addr = ipIn.m_addr;
}
-template <typename T1, size_t PREFIX_LEN>
-inline bool HasPrefix(const T1& obj, const std::array<uint8_t, PREFIX_LEN>& prefix)
-{
- return obj.size() >= PREFIX_LEN &&
- std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
-}
-
void CNetAddr::SetLegacyIPv6(Span<const uint8_t> ipv6)
{
assert(ipv6.size() == ADDR_IPV6_SIZE);
@@ -101,24 +194,80 @@ bool CNetAddr::SetInternal(const std::string &name)
return true;
}
+namespace torv3 {
+// https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt#n2135
+static constexpr size_t CHECKSUM_LEN = 2;
+static const unsigned char VERSION[] = {3};
+static constexpr size_t TOTAL_LEN = ADDR_TORV3_SIZE + CHECKSUM_LEN + sizeof(VERSION);
+
+static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKSUM_LEN])
+{
+ // TORv3 CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]
+ static const unsigned char prefix[] = ".onion checksum";
+ static constexpr size_t prefix_len = 15;
+
+ SHA3_256 hasher;
+
+ hasher.Write(MakeSpan(prefix).first(prefix_len));
+ hasher.Write(addr_pubkey);
+ hasher.Write(VERSION);
+
+ uint8_t checksum_full[SHA3_256::OUTPUT_SIZE];
+
+ hasher.Finalize(checksum_full);
+
+ memcpy(checksum, checksum_full, sizeof(checksum));
+}
+
+}; // namespace torv3
+
/**
- * Parse a TORv2 address and set this object to it.
+ * Parse a TOR address and set this object to it.
*
* @returns Whether or not the operation was successful.
*
* @see CNetAddr::IsTor()
*/
-bool CNetAddr::SetSpecial(const std::string &strName)
+bool CNetAddr::SetSpecial(const std::string& str)
{
- if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
- std::vector<unsigned char> vchAddr = DecodeBase32(strName.substr(0, strName.size() - 6).c_str());
- if (vchAddr.size() != ADDR_TORV2_SIZE) {
+ static const char* suffix{".onion"};
+ static constexpr size_t suffix_len{6};
+
+ if (!ValidAsCString(str) || str.size() <= suffix_len ||
+ str.substr(str.size() - suffix_len) != suffix) {
+ return false;
+ }
+
+ bool invalid;
+ const auto& input = DecodeBase32(str.substr(0, str.size() - suffix_len).c_str(), &invalid);
+
+ if (invalid) {
+ return false;
+ }
+
+ switch (input.size()) {
+ case ADDR_TORV2_SIZE:
+ m_net = NET_ONION;
+ m_addr.assign(input.begin(), input.end());
+ return true;
+ case torv3::TOTAL_LEN: {
+ Span<const uint8_t> input_pubkey{input.data(), ADDR_TORV3_SIZE};
+ Span<const uint8_t> input_checksum{input.data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN};
+ Span<const uint8_t> input_version{input.data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)};
+
+ uint8_t calculated_checksum[torv3::CHECKSUM_LEN];
+ torv3::Checksum(input_pubkey, calculated_checksum);
+
+ if (input_checksum != calculated_checksum || input_version != torv3::VERSION) {
return false;
}
+
m_net = NET_ONION;
- m_addr.assign(vchAddr.begin(), vchAddr.end());
+ m_addr.assign(input_pubkey.begin(), input_pubkey.end());
return true;
}
+ }
+
return false;
}
@@ -235,13 +384,21 @@ bool CNetAddr::IsHeNet() const
}
/**
- * @returns Whether or not this is a dummy address that maps an onion address
- * into IPv6.
- *
+ * Check whether this object represents a TOR address.
* @see CNetAddr::SetSpecial(const std::string &)
*/
bool CNetAddr::IsTor() const { return m_net == NET_ONION; }
+/**
+ * Check whether this object represents an I2P address.
+ */
+bool CNetAddr::IsI2P() const { return m_net == NET_I2P; }
+
+/**
+ * Check whether this object represents a CJDNS address.
+ */
+bool CNetAddr::IsCJDNS() const { return m_net == NET_CJDNS; }
+
bool CNetAddr::IsLocal() const
{
// IPv4 loopback (127.0.0.0/8 or 0.0.0.0/8)
@@ -328,28 +485,72 @@ enum Network CNetAddr::GetNetwork() const
return m_net;
}
+static std::string IPv6ToString(Span<const uint8_t> a)
+{
+ assert(a.size() == ADDR_IPV6_SIZE);
+ // clang-format off
+ return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
+ ReadBE16(&a[0]),
+ ReadBE16(&a[2]),
+ ReadBE16(&a[4]),
+ ReadBE16(&a[6]),
+ ReadBE16(&a[8]),
+ ReadBE16(&a[10]),
+ ReadBE16(&a[12]),
+ ReadBE16(&a[14]));
+ // clang-format on
+}
+
std::string CNetAddr::ToStringIP() const
{
- if (IsTor())
- return EncodeBase32(m_addr) + ".onion";
- if (IsInternal())
+ switch (m_net) {
+ case NET_IPV4:
+ case NET_IPV6: {
+ CService serv(*this, 0);
+ struct sockaddr_storage sockaddr;
+ socklen_t socklen = sizeof(sockaddr);
+ if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) {
+ char name[1025] = "";
+ if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name,
+ sizeof(name), nullptr, 0, NI_NUMERICHOST))
+ return std::string(name);
+ }
+ if (m_net == NET_IPV4) {
+ return strprintf("%u.%u.%u.%u", m_addr[0], m_addr[1], m_addr[2], m_addr[3]);
+ }
+ return IPv6ToString(m_addr);
+ }
+ case NET_ONION:
+ switch (m_addr.size()) {
+ case ADDR_TORV2_SIZE:
+ return EncodeBase32(m_addr) + ".onion";
+ case ADDR_TORV3_SIZE: {
+
+ uint8_t checksum[torv3::CHECKSUM_LEN];
+ torv3::Checksum(m_addr, checksum);
+
+ // TORv3 onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
+ prevector<torv3::TOTAL_LEN, uint8_t> address{m_addr.begin(), m_addr.end()};
+ address.insert(address.end(), checksum, checksum + torv3::CHECKSUM_LEN);
+ address.insert(address.end(), torv3::VERSION, torv3::VERSION + sizeof(torv3::VERSION));
+
+ return EncodeBase32(address) + ".onion";
+ }
+ default:
+ assert(false);
+ }
+ case NET_I2P:
+ return EncodeBase32(m_addr, false /* don't pad with = */) + ".b32.i2p";
+ case NET_CJDNS:
+ return IPv6ToString(m_addr);
+ case NET_INTERNAL:
return EncodeBase32(m_addr) + ".internal";
- CService serv(*this, 0);
- struct sockaddr_storage sockaddr;
- socklen_t socklen = sizeof(sockaddr);
- if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) {
- char name[1025] = "";
- if (!getnameinfo((const struct sockaddr*)&sockaddr, socklen, name, sizeof(name), nullptr, 0, NI_NUMERICHOST))
- return std::string(name);
- }
- if (IsIPv4())
- return strprintf("%u.%u.%u.%u", m_addr[0], m_addr[1], m_addr[2], m_addr[3]);
- assert(IsIPv6());
- return strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
- m_addr[0] << 8 | m_addr[1], m_addr[2] << 8 | m_addr[3],
- m_addr[4] << 8 | m_addr[5], m_addr[6] << 8 | m_addr[7],
- m_addr[8] << 8 | m_addr[9], m_addr[10] << 8 | m_addr[11],
- m_addr[12] << 8 | m_addr[13], m_addr[14] << 8 | m_addr[15]);
+ case NET_UNROUTABLE: // m_net is never and should not be set to NET_UNROUTABLE
+ case NET_MAX: // m_net is never and should not be set to NET_MAX
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ assert(false);
}
std::string CNetAddr::ToString() const
@@ -428,21 +629,22 @@ uint32_t CNetAddr::GetLinkedIPv4() const
assert(false);
}
-uint32_t CNetAddr::GetNetClass() const {
- uint32_t net_class = NET_IPV6;
- if (IsLocal()) {
- net_class = 255;
- }
+uint32_t CNetAddr::GetNetClass() const
+{
+ // Make sure that if we return NET_IPV6, then IsIPv6() is true. The callers expect that.
+
+ // Check for "internal" first because such addresses are also !IsRoutable()
+ // and we don't want to return NET_UNROUTABLE in that case.
if (IsInternal()) {
- net_class = NET_INTERNAL;
- } else if (!IsRoutable()) {
- net_class = NET_UNROUTABLE;
- } else if (HasLinkedIPv4()) {
- net_class = NET_IPV4;
- } else if (IsTor()) {
- net_class = NET_ONION;
+ return NET_INTERNAL;
}
- return net_class;
+ if (!IsRoutable()) {
+ return NET_UNROUTABLE;
+ }
+ if (HasLinkedIPv4()) {
+ return NET_IPV4;
+ }
+ return m_net;
}
uint32_t CNetAddr::GetMappedAS(const std::vector<bool> &asmap) const {
@@ -517,7 +719,7 @@ std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) co
vchRet.push_back((ipv4 >> 24) & 0xFF);
vchRet.push_back((ipv4 >> 16) & 0xFF);
return vchRet;
- } else if (IsTor()) {
+ } else if (IsTor() || IsI2P() || IsCJDNS()) {
nBits = 4;
} else if (IsHeNet()) {
// for he.net, use /36 groups
@@ -742,7 +944,7 @@ std::string CService::ToStringPort() const
std::string CService::ToStringIPPort() const
{
- if (IsIPv4() || IsTor() || IsInternal()) {
+ if (IsIPv4() || IsTor() || IsI2P() || IsInternal()) {
return ToStringIP() + ":" + ToStringPort();
} else {
return "[" + ToStringIP() + "]:" + ToStringPort();
diff --git a/src/netaddress.h b/src/netaddress.h
index d00f5a6f55..59f1b87ad3 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -13,13 +13,25 @@
#include <compat.h>
#include <prevector.h>
#include <serialize.h>
+#include <tinyformat.h>
+#include <util/strencodings.h>
+#include <util/string.h>
#include <array>
#include <cstdint>
+#include <ios>
#include <string>
#include <vector>
/**
+ * A flag that is ORed into the protocol version to designate that addresses
+ * should be serialized in (unserialized from) v2 format (BIP155).
+ * Make sure that this does not collide with any of the values in `version.h`
+ * or with `SERIALIZE_TRANSACTION_NO_WITNESS`.
+ */
+static const int ADDRV2_FORMAT = 0x20000000;
+
+/**
* A network type.
* @note An address may belong to more than one network, for example `10.0.0.1`
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`.
@@ -39,9 +51,15 @@ enum Network
/// IPv6
NET_IPV6,
- /// TORv2
+ /// TOR (v2 or v3)
NET_ONION,
+ /// I2P
+ NET_I2P,
+
+ /// CJDNS
+ NET_CJDNS,
+
/// A set of addresses that represent the hash of a string or FQDN. We use
/// them in CAddrMan to keep track of which DNS seeds were used.
NET_INTERNAL,
@@ -82,6 +100,16 @@ static constexpr size_t ADDR_IPV6_SIZE = 16;
/// Size of TORv2 address (in bytes).
static constexpr size_t ADDR_TORV2_SIZE = 10;
+/// Size of TORv3 address (in bytes). This is the length of just the address
+/// as used in BIP155, without the checksum and the version byte.
+static constexpr size_t ADDR_TORV3_SIZE = 32;
+
+/// Size of I2P address (in bytes).
+static constexpr size_t ADDR_I2P_SIZE = 32;
+
+/// Size of CJDNS address (in bytes).
+static constexpr size_t ADDR_CJDNS_SIZE = 16;
+
/// Size of "internal" (NET_INTERNAL) address (in bytes).
static constexpr size_t ADDR_INTERNAL_SIZE = 10;
@@ -139,6 +167,8 @@ class CNetAddr
bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
bool IsTor() const;
+ bool IsI2P() const;
+ bool IsCJDNS() const;
bool IsLocal() const;
bool IsRoutable() const;
bool IsInternal() const;
@@ -177,7 +207,11 @@ class CNetAddr
template <typename Stream>
void Serialize(Stream& s) const
{
- SerializeV1Stream(s);
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ SerializeV2Stream(s);
+ } else {
+ SerializeV1Stream(s);
+ }
}
/**
@@ -186,20 +220,58 @@ class CNetAddr
template <typename Stream>
void Unserialize(Stream& s)
{
- UnserializeV1Stream(s);
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ UnserializeV2Stream(s);
+ } else {
+ UnserializeV1Stream(s);
+ }
}
friend class CSubNet;
private:
/**
+ * BIP155 network ids recognized by this software.
+ */
+ enum BIP155Network : uint8_t {
+ IPV4 = 1,
+ IPV6 = 2,
+ TORV2 = 3,
+ TORV3 = 4,
+ I2P = 5,
+ CJDNS = 6,
+ };
+
+ /**
* Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
*/
static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
/**
+ * Maximum size of an address as defined in BIP155 (in bytes).
+ * This is only the size of the address, not the entire CNetAddr object
+ * when serialized.
+ */
+ static constexpr size_t MAX_ADDRV2_SIZE = 512;
+
+ /**
+ * Get the BIP155 network id of this address.
+ * Must not be called for IsInternal() objects.
+ * @returns BIP155 network id
+ */
+ BIP155Network GetBIP155Network() const;
+
+ /**
+ * Set `m_net` from the provided BIP155 network id and size after validation.
+ * @retval true the network was recognized, is valid and `m_net` was set
+ * @retval false not recognised (from future?) and should be silently ignored
+ * @throws std::ios_base::failure if the network is one of the BIP155 founding
+ * networks (id 1..6) with wrong address size.
+ */
+ bool SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size);
+
+ /**
* Serialize in pre-ADDRv2/BIP155 format to an array.
- * Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
*/
void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
{
@@ -217,6 +289,9 @@ class CNetAddr
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
case NET_ONION:
+ if (m_addr.size() == ADDR_TORV3_SIZE) {
+ break;
+ }
prefix_size = sizeof(TORV2_IN_IPV6_PREFIX);
assert(prefix_size + m_addr.size() == sizeof(arr));
memcpy(arr, TORV2_IN_IPV6_PREFIX.data(), prefix_size);
@@ -228,17 +303,21 @@ class CNetAddr
memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
return;
+ case NET_I2P:
+ break;
+ case NET_CJDNS:
+ break;
case NET_UNROUTABLE:
case NET_MAX:
assert(false);
} // no default case, so the compiler can warn about missing cases
- assert(false);
+ // Serialize TORv3, I2P and CJDNS as all-zeros.
+ memset(arr, 0x0, V1_SERIALIZATION_SIZE);
}
/**
* Serialize in pre-ADDRv2/BIP155 format to a stream.
- * Some addresses (e.g. TORv3) cannot be serialized in pre-BIP155 format.
*/
template <typename Stream>
void SerializeV1Stream(Stream& s) const
@@ -251,6 +330,25 @@ class CNetAddr
}
/**
+ * Serialize as ADDRv2 / BIP155.
+ */
+ template <typename Stream>
+ void SerializeV2Stream(Stream& s) const
+ {
+ if (IsInternal()) {
+ // Serialize NET_INTERNAL as embedded in IPv6. We need to
+ // serialize such addresses from addrman.
+ s << static_cast<uint8_t>(BIP155Network::IPV6);
+ s << COMPACTSIZE(ADDR_IPV6_SIZE);
+ SerializeV1Stream(s);
+ return;
+ }
+
+ s << static_cast<uint8_t>(GetBIP155Network());
+ s << m_addr;
+ }
+
+ /**
* Unserialize from a pre-ADDRv2/BIP155 format from an array.
*/
void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
@@ -272,6 +370,65 @@ class CNetAddr
UnserializeV1Array(serialized);
}
+
+ /**
+ * Unserialize from a ADDRv2 / BIP155 format.
+ */
+ template <typename Stream>
+ void UnserializeV2Stream(Stream& s)
+ {
+ uint8_t bip155_net;
+ s >> bip155_net;
+
+ size_t address_size;
+ s >> COMPACTSIZE(address_size);
+
+ if (address_size > MAX_ADDRV2_SIZE) {
+ throw std::ios_base::failure(strprintf(
+ "Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE));
+ }
+
+ scopeId = 0;
+
+ if (SetNetFromBIP155Network(bip155_net, address_size)) {
+ m_addr.resize(address_size);
+ s >> MakeSpan(m_addr);
+
+ if (m_net != NET_IPV6) {
+ return;
+ }
+
+ // Do some special checks on IPv6 addresses.
+
+ // Recognize NET_INTERNAL embedded in IPv6, such addresses are not
+ // gossiped but could be coming from addrman, when unserializing from
+ // disk.
+ if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
+ m_net = NET_INTERNAL;
+ memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
+ ADDR_INTERNAL_SIZE);
+ m_addr.resize(ADDR_INTERNAL_SIZE);
+ return;
+ }
+
+ if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) &&
+ !HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) {
+ return;
+ }
+
+ // IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
+ // encoding). Unserialize as !IsValid(), thus ignoring them.
+ } else {
+ // If we receive an unknown BIP155 network id (from the future?) then
+ // ignore the address - unserialize as !IsValid().
+ s.ignore(address_size);
+ }
+
+ // Mimic a default-constructed CNetAddr object which is !IsValid() and thus
+ // will not be gossiped, but continue reading next addresses from the stream.
+ m_net = NET_IPV6;
+ m_addr.assign(ADDR_IPV6_SIZE, 0x0);
+ }
};
class CSubNet
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 25458eead2..0f31093dbb 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -55,7 +55,7 @@ private:
// Sum the total feerate of all tx's in each bucket
// Track the historical moving average of this total over blocks
- std::vector<double> avg;
+ std::vector<double> m_feerate_avg;
// Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
// Combine the total value with the tx counts to calculate the avg feerate per bucket
@@ -114,12 +114,10 @@ public:
* @param confTarget target number of confirmations
* @param sufficientTxVal required average number of transactions per block in a bucket range
* @param minSuccess the success probability we require
- * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
- * return the highest feerate such that all lower values fail minSuccess
* @param nBlockHeight the current block height
*/
double EstimateMedianVal(int confTarget, double sufficientTxVal,
- double minSuccess, bool requireGreater, unsigned int nBlockHeight,
+ double minSuccess, unsigned int nBlockHeight,
EstimationResult *result = nullptr) const;
/** Return the max number of confirms we're tracking */
@@ -139,22 +137,18 @@ public:
TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
const std::map<double, unsigned int>& defaultBucketMap,
unsigned int maxPeriods, double _decay, unsigned int _scale)
- : buckets(defaultBuckets), bucketMap(defaultBucketMap)
+ : buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale)
{
- decay = _decay;
assert(_scale != 0 && "_scale must be non-zero");
- scale = _scale;
confAvg.resize(maxPeriods);
- for (unsigned int i = 0; i < maxPeriods; i++) {
- confAvg[i].resize(buckets.size());
- }
failAvg.resize(maxPeriods);
for (unsigned int i = 0; i < maxPeriods; i++) {
+ confAvg[i].resize(buckets.size());
failAvg[i].resize(buckets.size());
}
txCtAvg.resize(buckets.size());
- avg.resize(buckets.size());
+ m_feerate_avg.resize(buckets.size());
resizeInMemoryCounters(buckets.size());
}
@@ -172,68 +166,61 @@ void TxConfirmStats::resizeInMemoryCounters(size_t newbuckets) {
void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight)
{
for (unsigned int j = 0; j < buckets.size(); j++) {
- oldUnconfTxs[j] += unconfTxs[nBlockHeight%unconfTxs.size()][j];
+ oldUnconfTxs[j] += unconfTxs[nBlockHeight % unconfTxs.size()][j];
unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0;
}
}
-void TxConfirmStats::Record(int blocksToConfirm, double val)
+void TxConfirmStats::Record(int blocksToConfirm, double feerate)
{
// blocksToConfirm is 1-based
if (blocksToConfirm < 1)
return;
- int periodsToConfirm = (blocksToConfirm + scale - 1)/scale;
- unsigned int bucketindex = bucketMap.lower_bound(val)->second;
+ int periodsToConfirm = (blocksToConfirm + scale - 1) / scale;
+ unsigned int bucketindex = bucketMap.lower_bound(feerate)->second;
for (size_t i = periodsToConfirm; i <= confAvg.size(); i++) {
confAvg[i - 1][bucketindex]++;
}
txCtAvg[bucketindex]++;
- avg[bucketindex] += val;
+ m_feerate_avg[bucketindex] += feerate;
}
void TxConfirmStats::UpdateMovingAverages()
{
+ assert(confAvg.size() == failAvg.size());
for (unsigned int j = 0; j < buckets.size(); j++) {
- for (unsigned int i = 0; i < confAvg.size(); i++)
- confAvg[i][j] = confAvg[i][j] * decay;
- for (unsigned int i = 0; i < failAvg.size(); i++)
- failAvg[i][j] = failAvg[i][j] * decay;
- avg[j] = avg[j] * decay;
- txCtAvg[j] = txCtAvg[j] * decay;
+ for (unsigned int i = 0; i < confAvg.size(); i++) {
+ confAvg[i][j] *= decay;
+ failAvg[i][j] *= decay;
+ }
+ m_feerate_avg[j] *= decay;
+ txCtAvg[j] *= decay;
}
}
// returns -1 on error conditions
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
- double successBreakPoint, bool requireGreater,
- unsigned int nBlockHeight, EstimationResult *result) const
+ double successBreakPoint, unsigned int nBlockHeight,
+ EstimationResult *result) const
{
// Counters for a bucket (or range of buckets)
double nConf = 0; // Number of tx's confirmed within the confTarget
double totalNum = 0; // Total number of tx's that were ever confirmed
int extraNum = 0; // Number of tx's still in mempool for confTarget or longer
double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget
- int periodTarget = (confTarget + scale - 1)/scale;
-
- int maxbucketindex = buckets.size() - 1;
-
- // requireGreater means we are looking for the lowest feerate such that all higher
- // values pass, so we start at maxbucketindex (highest feerate) and look at successively
- // smaller buckets until we reach failure. Otherwise, we are looking for the highest
- // feerate such that all lower values fail, and we go in the opposite direction.
- unsigned int startbucket = requireGreater ? maxbucketindex : 0;
- int step = requireGreater ? -1 : 1;
+ const int periodTarget = (confTarget + scale - 1) / scale;
+ const int maxbucketindex = buckets.size() - 1;
// We'll combine buckets until we have enough samples.
// The near and far variables will define the range we've combined
// The best variables are the last range we saw which still had a high
// enough confirmation rate to count as success.
// The cur variables are the current range we're counting.
- unsigned int curNearBucket = startbucket;
- unsigned int bestNearBucket = startbucket;
- unsigned int curFarBucket = startbucket;
- unsigned int bestFarBucket = startbucket;
+ unsigned int curNearBucket = maxbucketindex;
+ unsigned int bestNearBucket = maxbucketindex;
+ unsigned int curFarBucket = maxbucketindex;
+ unsigned int bestFarBucket = maxbucketindex;
bool foundAnswer = false;
unsigned int bins = unconfTxs.size();
@@ -242,8 +229,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
EstimatorBucket passBucket;
EstimatorBucket failBucket;
- // Start counting from highest(default) or lowest feerate transactions
- for (int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) {
+ // Start counting from highest feerate transactions
+ for (int bucket = maxbucketindex; bucket >= 0; --bucket) {
if (newBucketRange) {
curNearBucket = bucket;
newBucketRange = false;
@@ -253,7 +240,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
totalNum += txCtAvg[bucket];
failNum += failAvg[periodTarget - 1][bucket];
for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++)
- extraNum += unconfTxs[(nBlockHeight - confct)%bins][bucket];
+ extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket];
extraNum += oldUnconfTxs[bucket];
// If we have enough transaction data points in this range of buckets,
// we can test for success
@@ -263,7 +250,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
double curPct = nConf / (totalNum + failNum + extraNum);
// Check to see if we are no longer getting confirmed at the success rate
- if ((requireGreater && curPct < successBreakPoint) || (!requireGreater && curPct > successBreakPoint)) {
+ if (curPct < successBreakPoint) {
if (passing == true) {
// First time we hit a failure record the failed bucket
unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
@@ -317,7 +304,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
if (txCtAvg[j] < txSum)
txSum -= txCtAvg[j];
else { // we're in the right bucket
- median = avg[j] / txCtAvg[j];
+ median = m_feerate_avg[j] / txCtAvg[j];
break;
}
}
@@ -338,13 +325,22 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
failBucket.leftMempool = failNum;
}
- LogPrint(BCLog::ESTIMATEFEE, "FeeEst: %d %s%.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
- confTarget, requireGreater ? ">" : "<", 100.0 * successBreakPoint, decay,
+ float passed_within_target_perc = 0.0;
+ float failed_within_target_perc = 0.0;
+ if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) {
+ passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool);
+ }
+ if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) {
+ failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool);
+ }
+
+ LogPrint(BCLog::ESTIMATEFEE, "FeeEst: %d > %.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
+ confTarget, 100.0 * successBreakPoint, decay,
median, passBucket.start, passBucket.end,
- 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool),
+ passed_within_target_perc,
passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool,
failBucket.start, failBucket.end,
- 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool),
+ failed_within_target_perc,
failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool);
@@ -361,7 +357,7 @@ void TxConfirmStats::Write(CAutoFile& fileout) const
{
fileout << decay;
fileout << scale;
- fileout << avg;
+ fileout << m_feerate_avg;
fileout << txCtAvg;
fileout << confAvg;
fileout << failAvg;
@@ -384,8 +380,8 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
}
- filein >> avg;
- if (avg.size() != numBuckets) {
+ filein >> m_feerate_avg;
+ if (m_feerate_avg.size() != numBuckets) {
throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
}
filein >> txCtAvg;
@@ -664,7 +660,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr
if (successThreshold > 1)
return CFeeRate(0);
- double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, true, nBestSeenHeight, result);
+ double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);
if (median < 0)
return CFeeRate(0);
@@ -725,26 +721,26 @@ double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, doubl
if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
// Find estimate from shortest time horizon possible
if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon
- estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, result);
+ estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);
}
else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon
- estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result);
+ estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
}
else { // long horizon
- estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result);
+ estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
}
if (checkShorterHorizon) {
EstimationResult tempResult;
// If a lower confTarget from a more recent horizon returns a lower answer use it.
if (confTarget > feeStats->GetMaxConfirms()) {
- double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, &tempResult);
+ double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);
if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
estimate = medMax;
if (result) *result = tempResult;
}
}
if (confTarget > shortStats->GetMaxConfirms()) {
- double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, &tempResult);
+ double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);
if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
estimate = shortMax;
if (result) *result = tempResult;
@@ -763,10 +759,10 @@ double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget,
double estimate = -1;
EstimationResult tempResult;
if (doubleTarget <= shortStats->GetMaxConfirms()) {
- estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, result);
+ estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result);
}
if (doubleTarget <= feeStats->GetMaxConfirms()) {
- double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, &tempResult);
+ double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult);
if (longEstimate > estimate) {
estimate = longEstimate;
if (result) *result = tempResult;
diff --git a/src/policy/fees.h b/src/policy/fees.h
index e79dbc9868..8ea8816dc3 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -138,9 +138,9 @@ private:
/** Decay of .962 is a half-life of 18 blocks or about 3 hours */
static constexpr double SHORT_DECAY = .962;
- /** Decay of .998 is a half-life of 144 blocks or about 1 day */
+ /** Decay of .9952 is a half-life of 144 blocks or about 1 day */
static constexpr double MED_DECAY = .9952;
- /** Decay of .9995 is a half-life of 1008 blocks or about 1 week */
+ /** Decay of .99931 is a half-life of 1008 blocks or about 1 week */
static constexpr double LONG_DECAY = .99931;
/** Require greater than 60% of X feerate transactions to be confirmed within Y/2 blocks*/
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 544bab6d9b..77cb1781a4 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -14,6 +14,12 @@
#include <tuple>
+/**
+ * A flag that is ORed into the protocol version to designate that a transaction
+ * should be (un)serialized without witness data.
+ * Make sure that this does not collide with any of the values in `version.h`
+ * or with `ADDRV2_FORMAT`.
+ */
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
/** An outpoint - a combination of a transaction hash and an index n into its vout */
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 48ca0c6df6..84b6e96aee 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -84,9 +84,9 @@ const static std::string allNetMessageTypes[] = {
};
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
-CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn)
+CMessageHeader::CMessageHeader()
{
- memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE);
+ memset(pchMessageStart, 0, MESSAGE_START_SIZE);
memset(pchCommand, 0, sizeof(pchCommand));
nMessageSize = -1;
memset(pchChecksum, 0, CHECKSUM_SIZE);
@@ -111,31 +111,20 @@ std::string CMessageHeader::GetCommand() const
return std::string(pchCommand, pchCommand + strnlen(pchCommand, COMMAND_SIZE));
}
-bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const
+bool CMessageHeader::IsCommandValid() const
{
- // Check start string
- if (memcmp(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE) != 0)
- return false;
-
// Check the command string for errors
- for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++)
- {
- if (*p1 == 0)
- {
+ for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; ++p1) {
+ if (*p1 == 0) {
// Must be all zeros after the first zero
- for (; p1 < pchCommand + COMMAND_SIZE; p1++)
- if (*p1 != 0)
+ for (; p1 < pchCommand + COMMAND_SIZE; ++p1) {
+ if (*p1 != 0) {
return false;
- }
- else if (*p1 < ' ' || *p1 > 0x7E)
+ }
+ }
+ } else if (*p1 < ' ' || *p1 > 0x7E) {
return false;
- }
-
- // Message size
- if (nMessageSize > MAX_SIZE)
- {
- LogPrintf("CMessageHeader::IsValid(): (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand(), nMessageSize);
- return false;
+ }
}
return true;
diff --git a/src/protocol.h b/src/protocol.h
index 7fb84cddf1..9a44a1626c 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -37,7 +37,7 @@ public:
static constexpr size_t HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE;
typedef unsigned char MessageStartChars[MESSAGE_START_SIZE];
- explicit CMessageHeader(const MessageStartChars& pchMessageStartIn);
+ explicit CMessageHeader();
/** Construct a P2P message header from message-start characters, a command and the size of the message.
* @note Passing in a `pszCommand` longer than COMMAND_SIZE will result in a run-time assertion error.
@@ -45,7 +45,7 @@ public:
CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn);
std::string GetCommand() const;
- bool IsValid(const MessageStartChars& messageStart) const;
+ bool IsCommandValid() const;
SERIALIZE_METHODS(CMessageHeader, obj) { READWRITE(obj.pchMessageStart, obj.pchCommand, obj.nMessageSize, obj.pchChecksum); }
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 8679ced685..6c2db52f63 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -94,12 +94,12 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
SendCoinsRecipient r;
if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
{
- auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
+ auto tempChainParams = CreateChainParams(gArgs, CBaseChainParams::MAIN);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
SelectParams(CBaseChainParams::MAIN);
} else {
- tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
+ tempChainParams = CreateChainParams(gArgs, CBaseChainParams::TESTNET);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
SelectParams(CBaseChainParams::TESTNET);
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 3c432464f2..88c8ebe1f6 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -41,6 +41,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendtoaddress", 5 , "replaceable" },
{ "sendtoaddress", 6 , "conf_target" },
{ "sendtoaddress", 8, "avoid_reuse" },
+ { "sendtoaddress", 9, "verbose"},
{ "settxfee", 0, "amount" },
{ "sethdseed", 0, "newkeypool" },
{ "getreceivedbyaddress", 1, "minconf" },
@@ -72,6 +73,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 4, "subtractfeefrom" },
{ "sendmany", 5 , "replaceable" },
{ "sendmany", 6 , "conf_target" },
+ { "sendmany", 8, "verbose" },
{ "deriveaddresses", 1, "range" },
{ "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 11a2d23d85..def21b119e 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -116,7 +116,9 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
{RPCResult::Type::STR, "subver", "The string version"},
{RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
- {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection"},
+ {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n"
+ "(DEPRECATED, returned only if the config option -deprecatedrpc=getpeerinfo_addnode is passed)"},
+ {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + "."},
{RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"},
{RPCResult::Type::NUM, "banscore", "The ban score (DEPRECATED, returned only if config option -deprecatedrpc=banscore is passed)"},
{RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"},
@@ -196,7 +198,10 @@ static RPCHelpMan getpeerinfo()
// their ver message.
obj.pushKV("subver", stats.cleanSubVer);
obj.pushKV("inbound", stats.fInbound);
- obj.pushKV("addnode", stats.m_manual_connection);
+ if (IsDeprecatedRPCEnabled("getpeerinfo_addnode")) {
+ // addnode is deprecated in v0.21 for removal in v0.22
+ obj.pushKV("addnode", stats.m_manual_connection);
+ }
obj.pushKV("startingheight", stats.nStartingHeight);
if (fStateStats) {
if (IsDeprecatedRPCEnabled("banscore")) {
@@ -232,6 +237,7 @@ static RPCHelpMan getpeerinfo()
recvPerMsgCmd.pushKV(i.first, i.second);
}
obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd);
+ obj.pushKV("connection_type", stats.m_conn_type_string);
ret.push_back(obj);
}
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index cfe4575090..8dfbead0e4 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -21,14 +21,16 @@
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf)
{
- if (outputs_in.isNull())
+ if (outputs_in.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
+ }
UniValue inputs;
- if (inputs_in.isNull())
+ if (inputs_in.isNull()) {
inputs = UniValue::VARR;
- else
+ } else {
inputs = inputs_in.get_array();
+ }
const bool outputs_is_obj = outputs_in.isObject();
UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 7b2457a5e3..50a6192476 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1375,7 +1375,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
if ((nHashType & 0x1f) == SIGHASH_SINGLE) {
if (nIn >= txTo.vout.size()) {
// nOut out of range
- return UINT256_ONE();
+ return uint256::ONE;
}
}
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index 69e14803f1..3b383393f9 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -76,7 +76,7 @@ std::string ScriptErrorString(const ScriptError serror)
case SCRIPT_ERR_PUBKEYTYPE:
return "Public key is neither compressed or uncompressed";
case SCRIPT_ERR_CLEANSTACK:
- return "Extra items left on stack after execution";
+ return "Stack size must be exactly one after execution";
case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH:
return "Witness program has incorrect length";
case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY:
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index eedab30576..d519eca859 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -13,10 +13,13 @@ BOOST_AUTO_TEST_CASE(base32_testvectors)
{
static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
static const std::string vstrOut[] = {"","my======","mzxq====","mzxw6===","mzxw6yq=","mzxw6ytb","mzxw6ytboi======"};
+ static const std::string vstrOutNoPadding[] = {"","my","mzxq","mzxw6","mzxw6yq","mzxw6ytb","mzxw6ytboi"};
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
{
std::string strEnc = EncodeBase32(vstrIn[i]);
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
+ strEnc = EncodeBase32(vstrIn[i], false);
+ BOOST_CHECK_EQUAL(strEnc, vstrOutNoPadding[i]);
std::string strDec = DecodeBase32(vstrOut[i]);
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
}
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 54793c890f..b799d3b43b 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -189,10 +189,9 @@ void test_one_input(const std::vector<uint8_t>& buffer)
DeserializeFromFuzzingInput(buffer, s);
AssertEqualAfterSerializeDeserialize(s);
#elif MESSAGEHEADER_DESERIALIZE
- const CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00};
- CMessageHeader mh(pchMessageStart);
+ CMessageHeader mh;
DeserializeFromFuzzingInput(buffer, mh);
- (void)mh.IsValid(pchMessageStart);
+ (void)mh.IsCommandValid();
#elif ADDRESS_DESERIALIZE
CAddress a;
DeserializeFromFuzzingInput(buffer, a);
diff --git a/src/test/fuzz/p2p_transport_deserializer.cpp b/src/test/fuzz/p2p_transport_deserializer.cpp
index 6fba2bfaba..7e216e16fe 100644
--- a/src/test/fuzz/p2p_transport_deserializer.cpp
+++ b/src/test/fuzz/p2p_transport_deserializer.cpp
@@ -19,7 +19,8 @@ void initialize()
void test_one_input(const std::vector<uint8_t>& buffer)
{
- V1TransportDeserializer deserializer{Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION};
+ // Construct deserializer, with a dummy NodeId
+ V1TransportDeserializer deserializer{Params(), (NodeId)0, SER_NETWORK, INIT_PROTO_VERSION};
const char* pch = (const char*)buffer.data();
size_t n_bytes = buffer.size();
while (n_bytes > 0) {
@@ -31,16 +32,13 @@ void test_one_input(const std::vector<uint8_t>& buffer)
n_bytes -= handled;
if (deserializer.Complete()) {
const std::chrono::microseconds m_time{std::numeric_limits<int64_t>::max()};
- const CNetMessage msg = deserializer.GetMessage(Params().MessageStart(), m_time);
- assert(msg.m_command.size() <= CMessageHeader::COMMAND_SIZE);
- assert(msg.m_raw_message_size <= buffer.size());
- assert(msg.m_raw_message_size == CMessageHeader::HEADER_SIZE + msg.m_message_size);
- assert(msg.m_time == m_time);
- if (msg.m_valid_header) {
- assert(msg.m_valid_netmagic);
- }
- if (!msg.m_valid_netmagic) {
- assert(!msg.m_valid_header);
+ uint32_t out_err_raw_size{0};
+ Optional<CNetMessage> result{deserializer.GetMessage(m_time, out_err_raw_size)};
+ if (result) {
+ assert(result->m_command.size() <= CMessageHeader::COMMAND_SIZE);
+ assert(result->m_raw_message_size <= buffer.size());
+ assert(result->m_raw_message_size == CMessageHeader::HEADER_SIZE + result->m_message_size);
+ assert(result->m_time == m_time);
}
}
}
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 62a0dc4241..3de79a9f45 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -36,17 +36,6 @@ struct MinerTestingSetup : public TestingSetup {
BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup)
-// BOOST_CHECK_EXCEPTION predicates to check the specific validation error
-class HasReason {
-public:
- explicit HasReason(const std::string& reason) : m_reason(reason) {}
- bool operator() (const std::runtime_error& e) const {
- return std::string(e.what()).find(m_reason) != std::string::npos;
- };
-private:
- const std::string m_reason;
-};
-
static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
@@ -209,7 +198,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
// Note that by default, these tests run with size accounting enabled.
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const CChainParams& chainparams = *chainParams;
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 85ebc89673..261396cd0c 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -10,6 +10,7 @@
#include <net.h>
#include <netbase.h>
#include <serialize.h>
+#include <span.h>
#include <streams.h>
#include <test/util/setup_common.h>
#include <util/memory.h>
@@ -20,6 +21,7 @@
#include <boost/test/unit_test.hpp>
+#include <ios>
#include <memory>
#include <string>
@@ -245,13 +247,38 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK_EQUAL(addr.ToString(), "1122:3344:5566:7788:9900:aabb:ccdd:eeff");
// TORv2
- addr.SetSpecial("6hzph5hv6337r6p2.onion");
+ BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
+ // TORv3
+ const char* torv3_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion";
+ BOOST_REQUIRE(addr.SetSpecial(torv3_addr));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsTor());
+
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
+
+ // TORv3, broken, with wrong checksum
+ BOOST_CHECK(!addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscsad.onion"));
+
+ // TORv3, broken, with wrong version
+ BOOST_CHECK(!addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscrye.onion"));
+
+ // TORv3, malicious
+ BOOST_CHECK(!addr.SetSpecial(std::string{
+ "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd\0wtf.onion", 66}));
+
+ // TOR, bogus length
+ BOOST_CHECK(!addr.SetSpecial(std::string{"mfrggzak.onion"}));
+
+ // TOR, invalid base32
+ BOOST_CHECK(!addr.SetSpecial(std::string{"mf*g zak.onion"}));
+
// Internal
addr.SetInternal("esffpp");
BOOST_REQUIRE(!addr.IsValid()); // "internal" is considered invalid
@@ -259,19 +286,286 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_CHECK(!addr.IsBindAny());
BOOST_CHECK_EQUAL(addr.ToString(), "esffpvrt3wpeaygy.internal");
+
+ // Totally bogus
+ BOOST_CHECK(!addr.SetSpecial("totally bogus"));
}
-BOOST_AUTO_TEST_CASE(cnetaddr_serialize)
+BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v1)
{
CNetAddr addr;
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "00000000000000000000000000000000");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1.2.3.4", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "00000000000000000000ffff01020304");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "1a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "fd87d87eeb43f1f2f3f4f5f6f7f8f9fa");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "00000000000000000000000000000000");
+ s.clear();
+
addr.SetInternal("a");
s << addr;
BOOST_CHECK_EQUAL(HexStr(s), "fd6b88c08724ca978112ca1bbdcafac2");
s.clear();
}
+BOOST_AUTO_TEST_CASE(cnetaddr_serialize_v2)
+{
+ CNetAddr addr;
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+ // Add ADDRV2_FORMAT to the version so that the CNetAddr
+ // serialize method produces an address in v2 format.
+ s.SetVersion(s.GetVersion() | ADDRV2_FORMAT);
+
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "021000000000000000000000000000000000");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1.2.3.4", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "010401020304");
+ s.clear();
+
+ BOOST_REQUIRE(LookupHost("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b", addr, false));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "030af1f2f3f4f5f6f7f8f9fa");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetSpecial("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "042053cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88");
+ s.clear();
+
+ BOOST_REQUIRE(addr.SetInternal("a"));
+ s << addr;
+ BOOST_CHECK_EQUAL(HexStr(s), "0210fd6b88c08724ca978112ca1bbdcafac2");
+ s.clear();
+}
+
+BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
+{
+ CNetAddr addr;
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+ // Add ADDRV2_FORMAT to the version so that the CNetAddr
+ // unserialize method expects an address in v2 format.
+ s.SetVersion(s.GetVersion() | ADDRV2_FORMAT);
+
+ // Valid IPv4.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "04" // address length
+ "01020304")); // address
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsIPv4());
+ BOOST_CHECK_EQUAL(addr.ToString(), "1.2.3.4");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid IPv4, valid length but address itself is shorter.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "04" // address length
+ "0102")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure, HasReason("end of data"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Invalid IPv4, with bogus length.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "05" // address length
+ "01020304")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 IPv4 address with length 5 (should be 4)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Invalid IPv4, with extreme length.
+ s << MakeSpan(ParseHex("01" // network type (IPv4)
+ "fd0102" // address length (513 as CompactSize)
+ "01020304")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("Address too long: 513 > 512"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Valid IPv6.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "10" // address length
+ "0102030405060708090a0b0c0d0e0f10")); // address
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsIPv6());
+ BOOST_CHECK_EQUAL(addr.ToString(), "102:304:506:708:90a:b0c:d0e:f10");
+ BOOST_REQUIRE(s.empty());
+
+ // Valid IPv6, contains embedded "internal".
+ s << MakeSpan(ParseHex(
+ "02" // network type (IPv6)
+ "10" // address length
+ "fd6b88c08724ca978112ca1bbdcafac2")); // address: 0xfd + sha256("bitcoin")[0:5] +
+ // sha256(name)[0:10]
+ s >> addr;
+ BOOST_CHECK(addr.IsInternal());
+ BOOST_CHECK_EQUAL(addr.ToString(), "zklycewkdo64v6wc.internal");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid IPv6, with bogus length.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "04" // address length
+ "00")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 IPv6 address with length 4 (should be 16)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Invalid IPv6, contains embedded IPv4.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "10" // address length
+ "00000000000000000000ffff01020304")); // address
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid IPv6, contains embedded TORv2.
+ s << MakeSpan(ParseHex("02" // network type (IPv6)
+ "10" // address length
+ "fd87d87eeb430102030405060708090a")); // address
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
+
+ // Valid TORv2.
+ s << MakeSpan(ParseHex("03" // network type (TORv2)
+ "0a" // address length
+ "f1f2f3f4f5f6f7f8f9fa")); // address
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsTor());
+ BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid TORv2, with bogus length.
+ s << MakeSpan(ParseHex("03" // network type (TORv2)
+ "07" // address length
+ "00")); // address
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 TORv2 address with length 7 (should be 10)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Valid TORv3.
+ s << MakeSpan(ParseHex("04" // network type (TORv3)
+ "20" // address length
+ "79bcc625184b05194975c28b66b66b04" // address
+ "69f7f6556fb1ac3189a79b40dda32f1f"
+ ));
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsTor());
+ BOOST_CHECK_EQUAL(addr.ToString(),
+ "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid TORv3, with bogus length.
+ s << MakeSpan(ParseHex("04" // network type (TORv3)
+ "00" // address length
+ "00" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 TORv3 address with length 0 (should be 32)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Valid I2P.
+ s << MakeSpan(ParseHex("05" // network type (I2P)
+ "20" // address length
+ "a2894dabaec08c0051a481a6dac88b64" // address
+ "f98232ae42d4b6fd2fa81952dfe36a87"));
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK_EQUAL(addr.ToString(),
+ "ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid I2P, with bogus length.
+ s << MakeSpan(ParseHex("05" // network type (I2P)
+ "03" // address length
+ "00" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 I2P address with length 3 (should be 32)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Valid CJDNS.
+ s << MakeSpan(ParseHex("06" // network type (CJDNS)
+ "10" // address length
+ "fc000001000200030004000500060007" // address
+ ));
+ s >> addr;
+ BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK_EQUAL(addr.ToString(), "fc00:1:2:3:4:5:6:7");
+ BOOST_REQUIRE(s.empty());
+
+ // Invalid CJDNS, with bogus length.
+ s << MakeSpan(ParseHex("06" // network type (CJDNS)
+ "01" // address length
+ "00" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("BIP155 CJDNS address with length 1 (should be 16)"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Unknown, with extreme length.
+ s << MakeSpan(ParseHex("aa" // network type (unknown)
+ "fe00000002" // address length (CompactSize's MAX_SIZE)
+ "01020304050607" // address
+ ));
+ BOOST_CHECK_EXCEPTION(s >> addr, std::ios_base::failure,
+ HasReason("Address too long: 33554432 > 512"));
+ BOOST_REQUIRE(!s.empty()); // The stream is not consumed on invalid input.
+ s.clear();
+
+ // Unknown, with reasonable length.
+ s << MakeSpan(ParseHex("aa" // network type (unknown)
+ "04" // address length
+ "01020304" // address
+ ));
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
+
+ // Unknown, with zero length.
+ s << MakeSpan(ParseHex("aa" // network type (unknown)
+ "00" // address length
+ "" // address
+ ));
+ s >> addr;
+ BOOST_CHECK(!addr.IsValid());
+ BOOST_REQUIRE(s.empty());
+}
+
// prior to PR #14728, this test triggers an undefined behavior
BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
{
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index ca49b89ad8..1d7f4861fb 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -14,7 +14,7 @@ BOOST_FIXTURE_TEST_SUITE(pow_tests, BasicTestingSetup)
/* Test calculation of next difficulty target with no constraints applying */
BOOST_AUTO_TEST_CASE(get_next_work)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1261130161; // Block #30240
CBlockIndex pindexLast;
pindexLast.nHeight = 32255;
@@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(get_next_work)
/* Test the constraint on the upper bound for next work */
BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1231006505; // Block #0
CBlockIndex pindexLast;
pindexLast.nHeight = 2015;
@@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
/* Test the constraint on the lower bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1279008237; // Block #66528
CBlockIndex pindexLast;
pindexLast.nHeight = 68543;
@@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
/* Test the constraint on the upper bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1263163443; // NOTE: Not an actual block time
CBlockIndex pindexLast;
pindexLast.nHeight = 46367;
@@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
nBits = UintToArith256(consensus.powLimit).GetCompact(true);
@@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits = ~0x00800000;
hash.SetHex("0x1");
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 nBits_arith = UintToArith256(consensus.powLimit);
@@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 hash_arith = UintToArith256(consensus.powLimit);
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 hash_arith{0};
@@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target)
BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
std::vector<CBlockIndex> blocks(10000);
for (int i = 0; i < 10000; i++) {
blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
@@ -135,9 +135,9 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
}
}
-void sanity_check_chainparams(std::string chainName)
+void sanity_check_chainparams(const ArgsManager& args, std::string chainName)
{
- const auto chainParams = CreateChainParams(chainName);
+ const auto chainParams = CreateChainParams(args, chainName);
const auto consensus = chainParams->GetConsensus();
// hash genesis is correct
@@ -164,22 +164,22 @@ void sanity_check_chainparams(std::string chainName)
BOOST_AUTO_TEST_CASE(ChainParams_MAIN_sanity)
{
- sanity_check_chainparams(CBaseChainParams::MAIN);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::MAIN);
}
BOOST_AUTO_TEST_CASE(ChainParams_REGTEST_sanity)
{
- sanity_check_chainparams(CBaseChainParams::REGTEST);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::REGTEST);
}
BOOST_AUTO_TEST_CASE(ChainParams_TESTNET_sanity)
{
- sanity_check_chainparams(CBaseChainParams::TESTNET);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::TESTNET);
}
BOOST_AUTO_TEST_CASE(ChainParams_SIGNET_sanity)
{
- sanity_check_chainparams(CBaseChainParams::SIGNET);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::SIGNET);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index c0bb92258b..bc862de78a 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -28,7 +28,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
{
if (nIn >= txTo.vin.size())
{
- return UINT256_ONE();
+ return uint256::ONE;
}
CMutableTransaction txTmp(txTo);
@@ -58,7 +58,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
unsigned int nOut = nIn;
if (nOut >= txTmp.vout.size())
{
- return UINT256_ONE();
+ return uint256::ONE;
}
txTmp.vout.resize(nOut+1);
for (unsigned int i = 0; i < nOut; i++)
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index c0ae2f8cf2..ae626d4613 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -278,4 +278,10 @@ BOOST_AUTO_TEST_CASE( operator_with_self )
BOOST_CHECK(v == UintToArith256(uint256S("0")));
}
+BOOST_AUTO_TEST_CASE( check_ONE )
+{
+ uint256 one = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
+ BOOST_CHECK_EQUAL(one, uint256::ONE);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 22f5d6d936..a09c8c122d 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -153,4 +153,20 @@ CBlock getBlock13b8a();
// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_*
std::ostream& operator<<(std::ostream& os, const uint256& num);
+/**
+ * BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
+ * Use as
+ * BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo"));
+ */
+class HasReason {
+public:
+ explicit HasReason(const std::string& reason) : m_reason(reason) {}
+ template <typename E>
+ bool operator() (const E& e) const {
+ return std::string(e.what()).find(m_reason) != std::string::npos;
+ };
+private:
+ const std::string m_reason;
+};
+
#endif
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 3b961db52d..a9ece50136 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -4,6 +4,7 @@
#include <chainparams.h>
#include <net.h>
+#include <signet.h>
#include <validation.h>
#include <test/util/setup_common.h>
@@ -39,7 +40,7 @@ static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval)
BOOST_AUTO_TEST_CASE(block_subsidy_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main
TestBlockSubsidyHalvings(150); // As in regtest
TestBlockSubsidyHalvings(1000); // Just another interval
@@ -47,7 +48,7 @@ BOOST_AUTO_TEST_CASE(block_subsidy_test)
BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
CAmount nSum = 0;
for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) {
CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus());
@@ -58,6 +59,67 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test)
BOOST_CHECK_EQUAL(nSum, CAmount{2099999997690000});
}
+BOOST_AUTO_TEST_CASE(signet_parse_tests)
+{
+ ArgsManager signet_argsman;
+ signet_argsman.ForceSetArg("-signetchallenge", "51"); // set challenge to OP_TRUE
+ const auto signet_params = CreateChainParams(signet_argsman, CBaseChainParams::SIGNET);
+ CBlock block;
+ BOOST_CHECK(signet_params->GetConsensus().signet_challenge == std::vector<uint8_t>{{OP_TRUE}});
+ CScript challenge{OP_TRUE};
+
+ // empty block is invalid
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no witness commitment
+ CMutableTransaction cb;
+ cb.vout.emplace_back(0, CScript{});
+ block.vtx.push_back(MakeTransactionRef(cb));
+ block.vtx.push_back(MakeTransactionRef(cb)); // Add dummy tx to excercise merkle root code
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no header is treated valid
+ std::vector<uint8_t> witness_commitment_section_141{0xaa, 0x21, 0xa9, 0xed};
+ for (int i = 0; i < 32; ++i) {
+ witness_commitment_section_141.push_back(0xff);
+ }
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no data after header, valid
+ std::vector<uint8_t> witness_commitment_section_325{0xec, 0xc7, 0xda, 0xa2};
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // Premature end of data, invalid
+ witness_commitment_section_325.push_back(0x01);
+ witness_commitment_section_325.push_back(0x51);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // has data, valid
+ witness_commitment_section_325.push_back(0x00);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // Extraneous data, invalid
+ witness_commitment_section_325.push_back(0x00);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+}
+
static bool ReturnFalse() { return false; }
static bool ReturnTrue() { return true; }
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 11c6bdad91..50444f7bbe 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
}
// Sanity checks of version bit deployments
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
uint32_t bitmask = VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(i));
@@ -250,7 +250,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
{
// Check that ComputeBlockVersion will set the appropriate bit correctly
// on mainnet.
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
// Use the TESTDUMMY deployment for testing purposes.
diff --git a/src/uint256.cpp b/src/uint256.cpp
index ee1b34eadd..d074df2f20 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -80,7 +80,4 @@ template std::string base_blob<256>::ToString() const;
template void base_blob<256>::SetHex(const char*);
template void base_blob<256>::SetHex(const std::string&);
-uint256& UINT256_ONE() {
- static uint256* one = new uint256(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
- return *one;
-}
+const uint256 uint256::ONE(1);
diff --git a/src/uint256.h b/src/uint256.h
index 8ab747ef49..c55cb31456 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -20,10 +20,11 @@ protected:
static constexpr int WIDTH = BITS / 8;
uint8_t m_data[WIDTH];
public:
- base_blob()
- {
- memset(m_data, 0, sizeof(m_data));
- }
+ /* construct 0 value by default */
+ constexpr base_blob() : m_data() {}
+
+ /* constructor for constants between 1 and 255 */
+ constexpr explicit base_blob(uint8_t v) : m_data{v} {}
explicit base_blob(const std::vector<unsigned char>& vch);
@@ -111,7 +112,7 @@ public:
*/
class uint160 : public base_blob<160> {
public:
- uint160() {}
+ constexpr uint160() {}
explicit uint160(const std::vector<unsigned char>& vch) : base_blob<160>(vch) {}
};
@@ -122,8 +123,10 @@ public:
*/
class uint256 : public base_blob<256> {
public:
- uint256() {}
+ constexpr uint256() {}
+ constexpr explicit uint256(uint8_t v) : base_blob<256>(v) {}
explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {}
+ static const uint256 ONE;
};
/* uint256 from const char *.
@@ -147,6 +150,4 @@ inline uint256 uint256S(const std::string& str)
return rv;
}
-uint256& UINT256_ONE();
-
#endif // BITCOIN_UINT256_H
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 079a4529a3..3236184b0b 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -201,20 +201,24 @@ std::string DecodeBase64(const std::string& str, bool* pf_invalid)
return std::string((const char*)vchRet.data(), vchRet.size());
}
-std::string EncodeBase32(Span<const unsigned char> input)
+std::string EncodeBase32(Span<const unsigned char> input, bool pad)
{
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
std::string str;
str.reserve(((input.size() + 4) / 5) * 8);
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
- while (str.size() % 8) str += '=';
+ if (pad) {
+ while (str.size() % 8) {
+ str += '=';
+ }
+ }
return str;
}
-std::string EncodeBase32(const std::string& str)
+std::string EncodeBase32(const std::string& str, bool pad)
{
- return EncodeBase32(MakeUCharSpan(str));
+ return EncodeBase32(MakeUCharSpan(str), pad);
}
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid)
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 1519214140..1a217dd12d 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -52,8 +52,20 @@ std::string EncodeBase64(Span<const unsigned char> input);
std::string EncodeBase64(const std::string& str);
std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr);
std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr);
-std::string EncodeBase32(Span<const unsigned char> input);
-std::string EncodeBase32(const std::string& str);
+
+/**
+ * Base32 encode.
+ * If `pad` is true, then the output will be padded with '=' so that its length
+ * is a multiple of 8.
+ */
+std::string EncodeBase32(Span<const unsigned char> input, bool pad = true);
+
+/**
+ * Base32 encode.
+ * If `pad` is true, then the output will be padded with '=' so that its length
+ * is a multiple of 8.
+ */
+std::string EncodeBase32(const std::string& str, bool pad = true);
void SplitHostPort(std::string in, int& portOut, std::string& hostOut);
int64_t atoi64(const std::string& str);
diff --git a/src/util/string.h b/src/util/string.h
index cdb41630c6..a0c87bd00c 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -7,6 +7,8 @@
#include <attributes.h>
+#include <algorithm>
+#include <array>
#include <cstring>
#include <locale>
#include <sstream>
@@ -74,4 +76,15 @@ std::string ToString(const T& t)
return oss.str();
}
+/**
+ * Check whether a container begins with the given prefix.
+ */
+template <typename T1, size_t PREFIX_LEN>
+NODISCARD inline bool HasPrefix(const T1& obj,
+ const std::array<uint8_t, PREFIX_LEN>& prefix)
+{
+ return obj.size() >= PREFIX_LEN &&
+ std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
+}
+
#endif // BITCOIN_UTIL_STRENCODINGS_H
diff --git a/src/validation.cpp b/src/validation.cpp
index d4463bf17b..e9c0607ced 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2449,9 +2449,9 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
}
bilingual_str warning_messages;
+ int num_unexpected_version = 0;
if (!::ChainstateActive().IsInitialBlockDownload())
{
- int nUpgraded = 0;
const CBlockIndex* pindex = pindexNew;
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
@@ -2470,11 +2470,9 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
{
int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus());
if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0)
- ++nUpgraded;
+ ++num_unexpected_version;
pindex = pindex->pprev;
}
- if (nUpgraded > 0)
- AppendWarning(warning_messages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
@@ -2482,6 +2480,10 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
+
+ if (num_unexpected_version > 0) {
+ LogPrint(BCLog::VALIDATION, "%d of last 100 blocks have unexpected version\n", num_unexpected_version);
+ }
}
/** Disconnect m_chain's tip.
diff --git a/src/version.h b/src/version.h
index b5f379e1b8..019c3a3ae7 100644
--- a/src/version.h
+++ b/src/version.h
@@ -38,4 +38,7 @@ static const int INVALID_CB_NO_BAN_VERSION = 70015;
//! "wtxidrelay" command for wtxid-based relay starts with this version
static const int WTXID_RELAY_VERSION = 70016;
+// Make sure that none of the values above collide with
+// `SERIALIZE_TRANSACTION_NO_WITNESS` or `ADDRV2_FORMAT`.
+
#endif // BITCOIN_VERSION_H
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index cacf306891..6cbad14de8 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -219,7 +219,8 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
CAmount fee_ret;
int change_pos_in_out = -1; // No requested location for change
bilingual_str fail_reason;
- if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
+ FeeCalculation fee_calc_out;
+ if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
return Result::WALLET_ERROR;
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2217bc2875..10af5c36c8 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -397,7 +397,7 @@ void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_f
}
}
-UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value)
+UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
{
EnsureWalletIsUnlocked(pwallet);
@@ -409,11 +409,18 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
int nChangePosRet = -1;
bilingual_str error;
CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
+ FeeCalculation fee_calc_out;
+ bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
+ if (verbose) {
+ UniValue entry(UniValue::VOBJ);
+ entry.pushKV("txid", tx->GetHash().GetHex());
+ entry.pushKV("fee_reason", StringForFeeReason(fee_calc_out.reason));
+ return entry;
+ }
return tx->GetHash().GetHex();
}
@@ -426,21 +433,31 @@ static RPCHelpMan sendtoaddress()
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to send to."},
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
- " This is not part of the transaction, just kept in your wallet."},
+ "This is not part of the transaction, just kept in your wallet."},
{"comment_to", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment to store the name of the person or organization\n"
- " to which you're sending the transaction. This is not part of the \n"
- " transaction, just kept in your wallet."},
+ "to which you're sending the transaction. This is not part of the \n"
+ "transaction, just kept in your wallet."},
{"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
- " The recipient will receive less bitcoins than you enter in the amount field."},
+ "The recipient will receive less bitcoins than you enter in the amount field."},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
- " dirty if they have previously been used in a transaction."},
+ "dirty if they have previously been used in a transaction."},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If true, return extra information about the transaction."},
},
- RPCResult{
- RPCResult::Type::STR_HEX, "txid", "The transaction id."
+ {
+ RPCResult{"if verbose is not set or set to false",
+ RPCResult::Type::STR_HEX, "txid", "The transaction id."
+ },
+ RPCResult{"if verbose is set to true",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ },
+ },
},
RPCExamples{
HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1")
@@ -497,8 +514,9 @@ static RPCHelpMan sendtoaddress()
std::vector<CRecipient> recipients;
ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
+ bool verbose = request.params[9].isNull() ? false: request.params[9].get_bool();
- return SendMoney(pwallet, coin_control, recipients, mapValue);
+ return SendMoney(pwallet, coin_control, recipients, mapValue, verbose);
},
};
}
@@ -842,9 +860,9 @@ static RPCHelpMan sendmany()
{"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
{"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The addresses.\n"
- " The fee will be equally deducted from the amount of each selected address.\n"
- " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no addresses are specified here, the sender pays the fee.",
+ "The fee will be equally deducted from the amount of each selected address.\n"
+ "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ "If no addresses are specified here, the sender pays the fee.",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Subtract fee from this address"},
},
@@ -853,11 +871,22 @@ static RPCHelpMan sendmany()
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false", "If true, return extra infomration about the transaction."},
+ },
+ {
+ RPCResult{"if verbose is not set or set to false",
+ RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
+ "the number of addresses."
+ },
+ RPCResult{"if verbose is set to true",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
+ "the number of addresses."},
+ {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ },
+ },
},
- RPCResult{
- RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
- "the number of addresses."
- },
RPCExamples{
"\nSend two amounts to two different addresses:\n"
+ HelpExampleCli("sendmany", "\"\" \"{\\\"" + EXAMPLE_ADDRESS[0] + "\\\":0.01,\\\"" + EXAMPLE_ADDRESS[1] + "\\\":0.02}\"") +
@@ -902,12 +931,14 @@ static RPCHelpMan sendmany()
std::vector<CRecipient> recipients;
ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
+ bool verbose = request.params[8].isNull() ? false : request.params[8].get_bool();
- return SendMoney(pwallet, coin_control, recipients, std::move(mapValue));
+ return SendMoney(pwallet, coin_control, recipients, std::move(mapValue), verbose);
},
};
}
+
static RPCHelpMan addmultisigaddress()
{
return RPCHelpMan{"addmultisigaddress",
@@ -1482,7 +1513,7 @@ static RPCHelpMan listsinceblock()
{"target_confirmations", RPCArg::Type::NUM, /* default */ "1", "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
{"include_removed", RPCArg::Type::BOOL, /* default */ "true", "Show transactions that were removed due to a reorg in the \"removed\" array\n"
- " (not guaranteed to work on pruned nodes)"},
+ "(not guaranteed to work on pruned nodes)"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -2801,7 +2832,7 @@ static RPCHelpMan listunspent()
},
},
{"include_unsafe", RPCArg::Type::BOOL, /* default */ "true", "Include outputs that are not safe to spend\n"
- " See description of \"safe\" attribute below."},
+ "See description of \"safe\" attribute below."},
{"query_options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "JSON with query options",
{
{"minimumAmount", RPCArg::Type::AMOUNT, /* default */ "0", "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
@@ -3041,7 +3072,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
{"lockUnspents", UniValueType(UniValue::VBOOL)},
{"lock_unspents", UniValueType(UniValue::VBOOL)},
{"locktime", UniValueType(UniValue::VNUM)},
- {"feeRate", UniValueType()}, // will be checked below,
+ {"feeRate", UniValueType()}, // will be checked below
{"psbt", UniValueType(UniValue::VBOOL)},
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
@@ -3165,15 +3196,15 @@ static RPCHelpMan fundrawtransaction()
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "The integers.\n"
- " The fee will be equally deducted from the amount of each specified output.\n"
- " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no outputs are specified here, the sender pays the fee.",
+ "The fee will be equally deducted from the amount of each specified output.\n"
+ "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ "If no outputs are specified here, the sender pays the fee.",
{
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees"},
+ "Allows this transaction to be replaced by a transaction with higher fees"},
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
@@ -3353,15 +3384,15 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
{
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
{"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'conf_target'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
- " Specify a fee rate instead of relying on the built-in fee estimator.\n"
+ "Specify a fee rate instead of relying on the built-in fee estimator.\n"
"Must be at least 0.0001 " + CURRENCY_UNIT + " per kB higher than the current transaction fee rate.\n"},
{"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n"
- " marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
- " be left unchanged from the original. If false, any input sequence numbers in the\n"
- " original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
- " so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
- " still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
- " are replaceable)."},
+ "marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
+ "be left unchanged from the original. If false, any input sequence numbers in the\n"
+ "original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
+ "so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
+ "still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
+ "are replaceable)."},
{"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
},
@@ -3743,7 +3774,7 @@ RPCHelpMan getaddressinfo()
{RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program."},
{RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program."},
{RPCResult::Type::STR, "script", /* optional */ true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
- " types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
+ "types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
"witness_v0_scripthash, witness_unknown."},
{RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."},
{RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
@@ -3959,9 +3990,10 @@ static RPCHelpMan listlabels()
static RPCHelpMan send()
{
return RPCHelpMan{"send",
+ "\nEXPERIMENTAL warning: this call may be changed in future releases.\n"
"\nSend a transaction.\n",
{
- {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For convenience, a dictionary, which holds the key-value pairs directly, is also accepted.",
{
@@ -3993,7 +4025,7 @@ static RPCHelpMan send()
{"include_watching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only.\n"
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
- {"inputs", RPCArg::Type::ARR, /* default */ "empty array", "Specify inputs instead of adding them automatically. A json array of json objects",
+ {"inputs", RPCArg::Type::ARR, /* default */ "empty array", "Specify inputs instead of adding them automatically. A JSON array of JSON objects",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
@@ -4003,7 +4035,7 @@ static RPCHelpMan send()
{"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
{"lock_unspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"psbt", RPCArg::Type::BOOL, /* default */ "automatic", "Always return a PSBT, implies add_to_wallet=false."},
- {"subtract_fee_from_outputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
+ {"subtract_fee_from_outputs", RPCArg::Type::ARR, /* default */ "empty array", "A JSON array of integers.\n"
"The fee will be equally deducted from the amount of each specified output.\n"
"Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
"If no outputs are specified here, the sender pays the fee.",
@@ -4012,7 +4044,7 @@ static RPCHelpMan send()
},
},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees"},
+ "Allows this transaction to be replaced by a transaction with higher fees"},
},
"options"},
},
@@ -4027,8 +4059,8 @@ static RPCHelpMan send()
},
RPCExamples{""
"\nSend with a fee rate of 1 satoshi per byte\n"
- + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 sat/b\n" +
- "\nCreate a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n")
+ + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 sat/b\n") +
+ "\nCreate a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n"
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 economical '{\"add_to_wallet\": false, \"inputs\": [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\", \"vout\":1}]}'")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
@@ -4079,7 +4111,7 @@ static RPCHelpMan send()
int change_position;
bool rbf = pwallet->m_signal_rbf;
if (options.exists("replaceable")) {
- rbf = options["add_to_wallet"].get_bool();
+ rbf = options["replaceable"].get_bool();
}
CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"], rbf);
CCoinControl coin_control;
@@ -4096,7 +4128,7 @@ static RPCHelpMan send()
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
- // Fill transaction with out data and sign
+ // Fill transaction with our data and sign
bool complete = true;
const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
if (err != TransactionError::OK) {
@@ -4108,13 +4140,11 @@ static RPCHelpMan send()
UniValue result(UniValue::VOBJ);
- // Serialize the PSBT
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << psbtx;
- const std::string result_str = EncodeBase64(ssTx.str());
-
if (psbt_opt_in || !complete || !add_to_wallet) {
- result.pushKV("psbt", result_str);
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ result.pushKV("psbt", EncodeBase64(ssTx.str()));
}
if (complete) {
@@ -4144,11 +4174,11 @@ static RPCHelpMan sethdseed()
HELP_REQUIRING_PASSPHRASE,
{
{"newkeypool", RPCArg::Type::BOOL, /* default */ "true", "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
- " If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
- " If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
- " keypool will be used until it has been depleted."},
+ "If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
+ "If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
+ "keypool will be used until it has been depleted."},
{"seed", RPCArg::Type::STR, /* default */ "random seed", "The WIF private key to use as the new HD seed.\n"
- " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
+ "The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
@@ -4293,7 +4323,7 @@ static RPCHelpMan walletcreatefundedpsbt()
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n"
"That is, each address can only appear once and there can only be one 'data' object.\n"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
- " accepted as second parameter.",
+ "accepted as second parameter.",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
@@ -4318,15 +4348,15 @@ static RPCHelpMan walletcreatefundedpsbt()
{"lockUnspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
{"feeRate", RPCArg::Type::AMOUNT, /* default */ "not set: makes wallet determine the fee", "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
{"subtractFeeFromOutputs", RPCArg::Type::ARR, /* default */ "empty array", "The outputs to subtract the fee from.\n"
- " The fee will be equally deducted from the amount of each specified output.\n"
- " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
- " If no outputs are specified here, the sender pays the fee.",
+ "The fee will be equally deducted from the amount of each specified output.\n"
+ "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ "If no outputs are specified here, the sender pays the fee.",
{
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
- " Allows this transaction to be replaced by a transaction with higher fees"},
+ "Allows this transaction to be replaced by a transaction with higher fees"},
{"conf_target", RPCArg::Type::NUM, /* default */ "fall back to wallet's confirmation target (txconfirmtarget)", "Confirmation target (in blocks)"},
{"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" \"" + FeeModes("\"\n\"") + "\""},
@@ -4501,8 +4531,8 @@ static const CRPCCommand commands[] =
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
{ "wallet", "send", &send, {"outputs","conf_target","estimate_mode","options"} },
- { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
- { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse"} },
+ { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode","verbose"} },
+ { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse","verbose"} },
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
{ "wallet", "setlabel", &setlabel, {"address","label"} },
{ "wallet", "settxfee", &settxfee, {"amount"} },
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 51715462c5..435716e56a 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -655,7 +655,7 @@ std::unique_ptr<CKeyMetadata> LegacyScriptPubKeyMan::GetMetadata(const CTxDestin
uint256 LegacyScriptPubKeyMan::GetID() const
{
- return UINT256_ONE();
+ return uint256::ONE;
}
/**
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 4393bb7701..c42114c394 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -524,8 +524,9 @@ public:
int changePos = -1;
bilingual_str error;
CCoinControl dummy;
+ FeeCalculation fee_calc_out;
{
- BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy));
+ BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
}
wallet->CommitTransaction(tx, {}, {});
CMutableTransaction blocktx;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 66857dbb39..4d8c0b175b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -324,8 +324,6 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin
return wallet;
}
-const uint256 CWalletTx::ABANDON_HASH(UINT256_ONE());
-
/** @defgroup mapWallet
*
* @{
@@ -2631,7 +2629,8 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
LOCK(cs_wallet);
CTransactionRef tx_new;
- if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, false)) {
+ FeeCalculation fee_calc_out;
+ if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
return false;
}
@@ -2755,6 +2754,7 @@ bool CWallet::CreateTransactionInternal(
int& nChangePosInOut,
bilingual_str& error,
const CCoinControl& coin_control,
+ FeeCalculation& fee_calc_out,
bool sign)
{
CAmount nValue = 0;
@@ -3098,6 +3098,7 @@ bool CWallet::CreateTransactionInternal(
// Before we return success, we assume any change key will be used to prevent
// accidental re-use.
reservedest.KeepDestination();
+ fee_calc_out = feeCalc;
WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
@@ -3117,11 +3118,12 @@ bool CWallet::CreateTransaction(
int& nChangePosInOut,
bilingual_str& error,
const CCoinControl& coin_control,
+ FeeCalculation& fee_calc_out,
bool sign)
{
int nChangePosIn = nChangePosInOut;
CTransactionRef tx2 = tx;
- bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, sign);
+ bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
// try with avoidpartialspends unless it's enabled already
if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
CCoinControl tmp_cc = coin_control;
@@ -3129,7 +3131,7 @@ bool CWallet::CreateTransaction(
CAmount nFeeRet2;
int nChangePosInOut2 = nChangePosIn;
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
- if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, sign)) {
+ if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
// if fee of this alternative one is within the range of the max fee, we use this one
const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee;
WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index f15712dd0e..fb08cb4085 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -275,7 +275,7 @@ private:
/** Constant used in hashBlock to indicate tx has been abandoned, only used at
* serialization/deserialization to avoid ambiguity with conflicted.
*/
- static const uint256 ABANDON_HASH;
+ static constexpr const uint256& ABANDON_HASH = uint256::ONE;
public:
/**
@@ -723,7 +723,7 @@ private:
// ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure
std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers;
- bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, bool sign);
+ bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign);
public:
/*
@@ -974,7 +974,7 @@ public:
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
- bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, bool sign = true);
+ bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
/**
* Submit the transaction to the node's mempool and then relay to peers.
* Should be called after CreateTransaction unless you want to abort
diff --git a/test/functional/README.md b/test/functional/README.md
index 0d85a74074..82b30fed51 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -87,7 +87,9 @@ P2P messages. These can be found in the following source files:
#### Using the P2P interface
-- [messages.py](test_framework/messages.py) contains all the definitions for objects that pass
+- `P2P`s can be used to test specific P2P protocol behavior.
+[p2p.py](test_framework/p2p.py) contains test framework p2p objects and
+[messages.py](test_framework/messages.py) contains all the definitions for objects passed
over the network (`CBlock`, `CTransaction`, etc, along with the network-level
wrappers for them, `msg_block`, `msg_tx`, etc).
@@ -100,8 +102,22 @@ contains the higher level logic for processing P2P payloads and connecting to
the Bitcoin Core node application logic. For custom behaviour, subclass the
P2PInterface object and override the callback methods.
-- Can be used to write tests where specific P2P protocol behavior is tested.
-Examples tests are [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
+`P2PConnection`s can be used as such:
+
+```python
+p2p_conn = node.add_p2p_connection(P2PInterface())
+p2p_conn.send_and_ping(msg)
+```
+
+They can also be referenced by indexing into a `TestNode`'s `p2ps` list, which
+contains the list of test framework `p2p` objects connected to itself
+(it does not include any `TestNode`s):
+
+```python
+node.p2ps[0].sync_with_ping()
+```
+
+More examples can be found in [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
[p2p_compactblocks.py](p2p_compactblocks.py).
#### Prototyping tests
@@ -157,7 +173,7 @@ way is the use the `profile_with_perf` context manager, e.g.
with node.profile_with_perf("send-big-msgs"):
# Perform activity on the node you're interested in profiling, e.g.:
for _ in range(10000):
- node.p2p.send_message(some_large_message)
+ node.p2ps[0].send_message(some_large_message)
```
To see useful textual output, run
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 083deb6460..3b9bd3048f 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -136,7 +136,7 @@ class ExampleTest(BitcoinTestFramework):
"""Main test logic"""
# Create P2P connections will wait for a verack to make sure the connection is fully up
- self.nodes[0].add_p2p_connection(BaseNode())
+ peer_messaging = self.nodes[0].add_p2p_connection(BaseNode())
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -173,7 +173,7 @@ class ExampleTest(BitcoinTestFramework):
block.solve()
block_message = msg_block(block)
# Send message is used to send a P2P message to the node over our P2PInterface
- self.nodes[0].p2p.send_message(block_message)
+ peer_messaging.send_message(block_message)
self.tip = block.sha256
blocks.append(self.tip)
self.block_time += 1
@@ -191,25 +191,25 @@ class ExampleTest(BitcoinTestFramework):
self.log.info("Add P2P connection to node2")
self.nodes[0].disconnect_p2ps()
- self.nodes[2].add_p2p_connection(BaseNode())
+ peer_receiving = self.nodes[2].add_p2p_connection(BaseNode())
self.log.info("Test that node2 propagates all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
getdata_request.inv.append(CInv(MSG_BLOCK, block))
- self.nodes[2].p2p.send_message(getdata_request)
+ peer_receiving.send_message(getdata_request)
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
# P2PInterface objects.
- self.nodes[2].p2p.wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5)
+ peer_receiving.wait_until(lambda: sorted(blocks) == sorted(list(peer_receiving.block_receive_map.keys())), timeout=5)
self.log.info("Check that each block was received only once")
# The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving
# messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
# and synchronization issues. Note p2p.wait_until() acquires this global lock internally when testing the predicate.
with p2p_lock:
- for block in self.nodes[2].p2p.block_receive_map.values():
+ for block in peer_receiving.block_receive_map.values():
assert_equal(block, 1)
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index efafcfaec3..19753d73ef 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -1386,14 +1386,14 @@ class FullBlockTest(BitcoinTestFramework):
"""Add a P2P connection to the node.
Helper to connect and wait for version handshake."""
- self.nodes[0].add_p2p_connection(P2PDataStore())
+ self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
# We need to wait for the initial getheaders from the peer before we
# start populating our blockstore. If we don't, then we may run ahead
# to the next subtest before we receive the getheaders. We'd then send
# an INV for the next block and receive two getheaders - one for the
# IBD and one for the INV. We'd respond to both and could get
# unexpectedly disconnected if the DoS score for that error is 50.
- self.nodes[0].p2p.wait_for_getheaders(timeout=timeout)
+ self.helper_peer.wait_for_getheaders(timeout=timeout)
def reconnect_p2p(self, timeout=60):
"""Tear down and bootstrap the P2P connection to the node.
@@ -1407,7 +1407,7 @@ class FullBlockTest(BitcoinTestFramework):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
+ self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
if reconnect:
self.reconnect_p2p(timeout=timeout)
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 2919b0ea0b..aad255c4a9 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -75,7 +75,7 @@ class BIP65Test(BitcoinTestFramework):
)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.test_cltv_info(is_active=False)
@@ -99,7 +99,7 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -111,9 +111,9 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000003)'.format(block.hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
block.nVersion = 4
@@ -136,9 +136,9 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Negative locktime)'.format(block.vtx[-1].hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
@@ -150,7 +150,7 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_cltv_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 34e856c1ba..b4355a6587 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -78,6 +78,12 @@ class ConfArgsTest(BitcoinTestFramework):
with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
+ def test_invalid_command_line_options(self):
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg='Error: No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>.',
+ extra_args=['-proxy'],
+ )
+
def test_log_buffer(self):
with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0\n']):
self.start_node(0, extra_args=['-noconnect=0'])
@@ -146,6 +152,7 @@ class ConfArgsTest(BitcoinTestFramework):
self.test_networkactive()
self.test_config_file_parser()
+ self.test_invalid_command_line_options()
# Remove the -datadir argument so it doesn't override the config file
self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")]
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 38e95f00e9..39e8bca751 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -182,10 +182,10 @@ class BIP68_112_113Test(BitcoinTestFramework):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
+ self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PDataStore())
+ self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
self.log.info("Generate blocks in the past for coinbase outputs.")
long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index f263c93c8a..3f7efdbded 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -59,7 +59,7 @@ class BIP66Test(BitcoinTestFramework):
)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.test_dersig_info(is_active=False)
@@ -84,7 +84,7 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -97,9 +97,9 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000002)'.format(block.hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
@@ -123,9 +123,9 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)'.format(block.vtx[-1].hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0)
@@ -134,7 +134,7 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_dersig_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index e5c62d1ea7..d0a94658ff 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -145,16 +145,16 @@ class MaxUploadTest(BitcoinTestFramework):
self.restart_node(0, ["-whitelist=download@127.0.0.1", "-maxuploadtarget=1"])
# Reconnect to self.nodes[0]
- self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer = self.nodes[0].add_p2p_connection(TestP2PConn())
#retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(MSG_BLOCK, big_new_block)]
for i in range(20):
- self.nodes[0].p2p.send_and_ping(getdata_request)
- assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1)
+ peer.send_and_ping(getdata_request)
+ assert_equal(peer.block_receive_map[big_new_block], i+1)
getdata_request.inv = [CInv(MSG_BLOCK, big_old_block)]
- self.nodes[0].p2p.send_and_ping(getdata_request)
+ peer.send_and_ping(getdata_request)
self.log.info("Peer still connected after trying to download old block (download permission)")
peer_info = self.nodes[0].getpeerinfo()
diff --git a/test/functional/feature_signet.py b/test/functional/feature_signet.py
index a0e7f3ee6e..96c581dede 100755
--- a/test/functional/feature_signet.py
+++ b/test/functional/feature_signet.py
@@ -65,6 +65,10 @@ class SignetBasicTest(BitcoinTestFramework):
assert_equal(self.nodes[4].submitblock(signet_blocks[0]), 'bad-signet-blksig')
+ self.log.info("test that signet logs the network magic on node start")
+ with self.nodes[0].assert_debug_log(["Signet derived magic (message start)"]):
+ self.restart_node(0)
+
if __name__ == '__main__':
SignetBasicTest().main()
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index e1016e1581..2e4f4796b0 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -61,7 +61,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
- node.add_p2p_connection(P2PInterface())
+ peer = node.add_p2p_connection(P2PInterface())
node_deterministic_address = node.get_deterministic_priv_key().address
# Mine one period worth of blocks
@@ -69,7 +69,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
self.log.info("Check that there is no warning if previous VB_BLOCKS have <VB_THRESHOLD blocks with unknown versionbits version.")
# Build one period of blocks with < VB_THRESHOLD blocks signaling some unknown bit
- self.send_blocks_with_version(node.p2p, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
+ self.send_blocks_with_version(peer, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD + 1, node_deterministic_address)
# Check that we're not getting any versionbit-related errors in get*info()
@@ -77,7 +77,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
assert not VB_PATTERN.match(node.getnetworkinfo()["warnings"])
# Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit
- self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION)
+ self.send_blocks_with_version(peer, VB_THRESHOLD, VB_UNKNOWN_VERSION)
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD, node_deterministic_address)
self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.")
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index e74ef8cf16..d7cb7db9f8 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -58,7 +58,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
def run_test(self):
# Mine some blocks and have them mature.
- self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
+ peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
self.nodes[0].generate(101)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
@@ -80,7 +80,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
- self.nodes[0].p2p.wait_for_broadcast(witness_chain)
+ peer_inv_store.wait_for_broadcast(witness_chain)
# Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
# count and fees should look correct
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index b13740750f..1b2c7644bd 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -234,9 +234,9 @@ class MiningTest(BitcoinTestFramework):
assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
# Should ask for the block from a p2p node, if they announce the header as well:
- node.add_p2p_connection(P2PDataStore())
- node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders
- node.p2p.send_blocks_and_test(blocks=[block], node=node)
+ peer = node.add_p2p_connection(P2PDataStore())
+ peer.wait_for_getheaders(timeout=5) # Drop the first getheaders
+ peer.send_blocks_and_test(blocks=[block], node=node)
# Must be active now:
assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips()
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 3ead31c9ea..646baa1550 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -17,7 +17,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
self.extra_args = [["-blocksonly"]]
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ block_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info('Check that txs from p2p are rejected and result in disconnect')
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
@@ -41,20 +41,20 @@ class P2PBlocksOnly(BitcoinTestFramework):
)['hex']
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
- self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.nodes[0].p2p.wait_for_disconnect()
+ block_relay_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
+ block_relay_peer.wait_for_disconnect()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
# Remove the disconnected peer and add a new one.
del self.nodes[0].p2ps[0]
- self.nodes[0].add_p2p_connection(P2PInterface())
+ tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer=1'.format(txid)]):
self.nodes[0].sendrawtransaction(sigtx)
- self.nodes[0].p2p.wait_for_tx(txid)
+ tx_relay_peer.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
self.log.info('Check that txs from peers with relay-permission are not rejected and relayed to others')
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
index 7dd8c3146b..2349afa1ee 100755
--- a/test/functional/p2p_dos_header_tree.py
+++ b/test/functional/p2p_dos_header_tree.py
@@ -46,8 +46,8 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.headers_fork = [FromHex(CBlockHeader(), h) for h in self.headers_fork]
self.log.info("Feed all non-fork headers, including and up to the first checkpoint")
- self.nodes[0].add_p2p_connection(P2PInterface())
- self.nodes[0].p2p.send_and_ping(msg_headers(self.headers))
+ peer_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_checkpoint.send_and_ping(msg_headers(self.headers))
assert {
'height': 546,
'hash': '000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70',
@@ -57,14 +57,14 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.log.info("Feed all fork headers (fails due to checkpoint)")
with self.nodes[0].assert_debug_log(['bad-fork-prior-to-checkpoint']):
- self.nodes[0].p2p.send_message(msg_headers(self.headers_fork))
- self.nodes[0].p2p.wait_for_disconnect()
+ peer_checkpoint.send_message(msg_headers(self.headers_fork))
+ peer_checkpoint.wait_for_disconnect()
self.log.info("Feed all fork headers (succeeds without checkpoint)")
# On node 0 it succeeds because checkpoints are disabled
self.restart_node(0, extra_args=['-nocheckpoints'])
- self.nodes[0].add_p2p_connection(P2PInterface())
- self.nodes[0].p2p.send_and_ping(msg_headers(self.headers_fork))
+ peer_no_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_no_checkpoint.send_and_ping(msg_headers(self.headers_fork))
assert {
"height": 2,
"hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
@@ -73,8 +73,8 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
} in self.nodes[0].getchaintips()
# On node 1 it succeeds because no checkpoint has been reached yet by a chain tip
- self.nodes[1].add_p2p_connection(P2PInterface())
- self.nodes[1].p2p.send_and_ping(msg_headers(self.headers_fork))
+ peer_before_checkpoint = self.nodes[1].add_p2p_connection(P2PInterface())
+ peer_before_checkpoint.send_and_ping(msg_headers(self.headers_fork))
assert {
"height": 2,
"hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 613d96eaad..642a217047 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -131,7 +131,7 @@ class FilterTest(BitcoinTestFramework):
self.log.debug("Send a mempool msg after connecting and check that the tx is received")
self.nodes[0].add_p2p_connection(filter_peer)
filter_peer.send_and_ping(filter_peer.watch_filter_init)
- self.nodes[0].p2p.send_message(msg_mempool())
+ filter_peer.send_message(msg_mempool())
filter_peer.wait_for_tx(txid)
def test_frelay_false(self, filter_peer):
diff --git a/test/functional/p2p_getdata.py b/test/functional/p2p_getdata.py
index 51921a8ab5..89d68d5ba0 100755
--- a/test/functional/p2p_getdata.py
+++ b/test/functional/p2p_getdata.py
@@ -42,7 +42,7 @@ class GetdataTest(BitcoinTestFramework):
good_getdata = msg_getdata()
good_getdata.inv.append(CInv(t=2, h=best_block))
p2p_block_store.send_and_ping(good_getdata)
- p2p_block_store.wait_until(lambda: self.nodes[0].p2ps[0].blocks[best_block] == 1)
+ p2p_block_store.wait_until(lambda: p2p_block_store.blocks[best_block] == 1)
if __name__ == '__main__':
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index b2c3c5d45f..483f25f48c 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -27,7 +27,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
def run_test(self):
# Add p2p connection to node0
node = self.nodes[0] # convenience reference to the node
- node.add_p2p_connection(P2PDataStore())
+ peer = node.add_p2p_connection(P2PDataStore())
best_block = node.getblock(node.getbestblockhash())
tip = int(node.getbestblockhash(), 16)
@@ -42,7 +42,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
# Save the coinbase for later
block1 = block
tip = block.sha256
- node.p2p.send_blocks_and_test([block1], node, success=True)
+ peer.send_blocks_and_test([block1], node, success=True)
self.log.info("Mature the block.")
node.generatetoaddress(100, node.get_deterministic_priv_key().address)
@@ -80,7 +80,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
assert_equal(orig_hash, block2.rehash())
assert block2_orig.vtx != block2.vtx
- node.p2p.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
+ peer.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
# Check transactions for duplicate inputs (CVE-2018-17144)
self.log.info("Test duplicate input block.")
@@ -91,7 +91,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root()
block2_dup.rehash()
block2_dup.solve()
- node.p2p.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ peer.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
self.log.info("Test very broken block.")
@@ -104,14 +104,14 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block3.rehash()
block3.solve()
- node.p2p.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
+ peer.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
# Complete testing of CVE-2012-2459 by sending the original block.
# It should be accepted even though it has the same hash as the mutated one.
self.log.info("Test accepting original block after rejecting its mutated version.")
- node.p2p.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
+ peer.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
# Update tip info
height += 1
@@ -131,7 +131,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block4.rehash()
block4.solve()
self.log.info("Test inflation by duplicating input")
- node.p2p.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ peer.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
if __name__ == '__main__':
InvalidBlockRequestTest().main()
diff --git a/test/functional/p2p_invalid_locator.py b/test/functional/p2p_invalid_locator.py
index 24328c2919..e4fc9fd178 100755
--- a/test/functional/p2p_invalid_locator.py
+++ b/test/functional/p2p_invalid_locator.py
@@ -23,20 +23,20 @@ class InvalidLocatorTest(BitcoinTestFramework):
block_count = node.getblockcount()
for msg in [msg_getheaders(), msg_getblocks()]:
self.log.info('Wait for disconnect when sending {} hashes in locator'.format(MAX_LOCATOR_SZ + 1))
- node.add_p2p_connection(P2PInterface())
+ exceed_max_peer = node.add_p2p_connection(P2PInterface())
msg.locator.vHave = [int(node.getblockhash(i - 1), 16) for i in range(block_count, block_count - (MAX_LOCATOR_SZ + 1), -1)]
- node.p2p.send_message(msg)
- node.p2p.wait_for_disconnect()
+ exceed_max_peer.send_message(msg)
+ exceed_max_peer.wait_for_disconnect()
node.disconnect_p2ps()
self.log.info('Wait for response when sending {} hashes in locator'.format(MAX_LOCATOR_SZ))
- node.add_p2p_connection(P2PInterface())
+ within_max_peer = node.add_p2p_connection(P2PInterface())
msg.locator.vHave = [int(node.getblockhash(i - 1), 16) for i in range(block_count, block_count - (MAX_LOCATOR_SZ), -1)]
- node.p2p.send_message(msg)
+ within_max_peer.send_message(msg)
if type(msg) == msg_getheaders:
- node.p2p.wait_for_header(node.getbestblockhash())
+ within_max_peer.wait_for_header(node.getbestblockhash())
else:
- node.p2p.wait_for_block(int(node.getbestblockhash(), 16))
+ within_max_peer.wait_for_block(int(node.getbestblockhash(), 16))
if __name__ == '__main__':
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index fe57057a83..fbe58c5e2f 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -22,12 +22,11 @@ from test_framework.p2p import (
P2PInterface,
)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
-)
+from test_framework.util import assert_equal
VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5 # Account for the 5-byte length prefix
+
class msg_unrecognized:
"""Nonsensical message. Modeled after similar types in test_framework.messages."""
@@ -64,13 +63,13 @@ class InvalidMessagesTest(BitcoinTestFramework):
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
# Create valid message
msg = conn.build_message(msg_ping(nonce=12345))
- cut_pos = 12 # Chosen at an arbitrary position within the header
+ cut_pos = 12 # Chosen at an arbitrary position within the header
# Send message in two pieces
- before = int(self.nodes[0].getnettotals()['totalbytesrecv'])
+ before = self.nodes[0].getnettotals()['totalbytesrecv']
conn.send_raw_message(msg[:cut_pos])
# Wait until node has processed the first half of the message
- self.wait_until(lambda: int(self.nodes[0].getnettotals()['totalbytesrecv']) != before)
- middle = int(self.nodes[0].getnettotals()['totalbytesrecv'])
+ self.wait_until(lambda: self.nodes[0].getnettotals()['totalbytesrecv'] != before)
+ middle = self.nodes[0].getnettotals()['totalbytesrecv']
# If this assert fails, we've hit an unlikely race
# where the test framework sent a message in between the two halves
assert_equal(middle, before + cut_pos)
@@ -81,7 +80,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_magic_bytes(self):
self.log.info("Test message with invalid magic bytes disconnects peer")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: INVALID MESSAGESTART badmsg']):
+ with self.nodes[0].assert_debug_log(['HEADER ERROR - MESSAGESTART (badmsg, 2 bytes), received ffffffff']):
msg = conn.build_message(msg_unrecognized(str_data="d"))
# modify magic bytes
msg = b'\xff' * 4 + msg[4:]
@@ -100,12 +99,14 @@ class InvalidMessagesTest(BitcoinTestFramework):
msg = msg[:cut_len] + b'\xff' * 4 + msg[cut_len + 4:]
conn.send_raw_message(msg)
conn.sync_with_ping(timeout=1)
+ # Check that traffic is accounted for (24 bytes header + 2 bytes payload)
+ assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
self.nodes[0].disconnect_p2ps()
def test_size(self):
self.log.info("Test message with oversized payload disconnects peer")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- with self.nodes[0].assert_debug_log(['']):
+ with self.nodes[0].assert_debug_log(['HEADER ERROR - SIZE (badmsg, 4000001 bytes)']):
msg = msg_unrecognized(str_data="d" * (VALID_DATA_LIMIT + 1))
msg = conn.build_message(msg)
conn.send_raw_message(msg)
@@ -115,14 +116,15 @@ class InvalidMessagesTest(BitcoinTestFramework):
def test_msgtype(self):
self.log.info("Test message with invalid message type logs an error")
conn = self.nodes[0].add_p2p_connection(P2PDataStore())
- with self.nodes[0].assert_debug_log(['PROCESSMESSAGE: ERRORS IN HEADER']):
+ with self.nodes[0].assert_debug_log(['HEADER ERROR - COMMAND']):
msg = msg_unrecognized(str_data="d")
- msg.msgtype = b'\xff' * 12
msg = conn.build_message(msg)
# Modify msgtype
msg = msg[:7] + b'\x00' + msg[7 + 1:]
conn.send_raw_message(msg)
conn.sync_with_ping(timeout=1)
+ # Check that traffic is accounted for (24 bytes header + 2 bytes payload)
+ assert_equal(self.nodes[0].getpeerinfo()[0]['bytesrecv_per_msg']['*other*'], 26)
self.nodes[0].disconnect_p2ps()
def test_oversized_msg(self, msg, size):
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index a0ef6c9d6e..489d903c21 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -61,7 +61,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
# Save the coinbase for later
block1 = block
tip = block.sha256
- node.p2p.send_blocks_and_test([block], node, success=True)
+ node.p2ps[0].send_blocks_and_test([block], node, success=True)
self.log.info("Mature the block.")
self.nodes[0].generatetoaddress(100, self.nodes[0].get_deterministic_priv_key().address)
@@ -72,7 +72,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__)
template = BadTxTemplate(spend_block=block1)
tx = template.get_tx()
- node.p2p.send_txs_and_test(
+ node.p2ps[0].send_txs_and_test(
[tx], node, success=False,
expect_disconnect=template.expect_disconnect,
reject_reason=template.reject_reason,
@@ -121,7 +121,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info('Send the orphans ... ')
# Send valid orphan txs from p2ps[0]
- node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
+ node.p2ps[0].send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
# Send invalid tx from p2ps[1]
node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False)
@@ -130,7 +130,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info('Send the withhold tx ... ')
with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):
- node.p2p.send_txs_and_test([tx_withhold], node, success=True)
+ node.p2ps[0].send_txs_and_test([tx_withhold], node, success=True)
# Transactions that should end up in the mempool
expected_mempool = {
@@ -155,14 +155,14 @@ class InvalidTxRequestTest(BitcoinTestFramework):
orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']):
- node.p2p.send_txs_and_test(orphan_tx_pool, node, success=False)
+ node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)
rejected_parent = CTransaction()
rejected_parent.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0)))
rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
rejected_parent.rehash()
with node.assert_debug_log(['not keeping orphan with rejected parents {}'.format(rejected_parent.hash)]):
- node.p2p.send_txs_and_test([rejected_parent], node, success=False)
+ node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)
if __name__ == '__main__':
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index dfbf5d52f4..d79ed449e5 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -89,7 +89,6 @@ from test_framework.util import (
# The versionbit bit used to signal activation of SegWit
VB_WITNESS_BIT = 1
-VB_PERIOD = 144
VB_TOP_BITS = 0x20000000
MAX_SIGOP_COST = 80000
@@ -2097,14 +2096,14 @@ class SegWitTest(BitcoinTestFramework):
tx = FromHex(CTransaction(), raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
with self.nodes[0].assert_debug_log(['Superfluous witness record']):
- self.nodes[0].p2p.send_and_ping(msg_bogus_tx(tx))
+ self.test_node.send_and_ping(msg_bogus_tx(tx))
raw = self.nodes[0].signrawtransactionwithwallet(raw)
assert raw['complete']
raw = raw['hex']
tx = FromHex(CTransaction(), raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
- self.nodes[0].p2p.send_and_ping(msg_bogus_tx(tx))
+ self.test_node.send_and_ping(msg_bogus_tx(tx))
@subtest # type: ignore
def test_wtxid_relay(self):
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index c005584485..35cea85c07 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -317,7 +317,7 @@ class BlockchainTest(BitcoinTestFramework):
def _test_waitforblockheight(self):
self.log.info("Test waitforblockheight")
node = self.nodes[0]
- node.add_p2p_connection(P2PInterface())
+ peer = node.add_p2p_connection(P2PInterface())
current_height = node.getblock(node.getbestblockhash())['height']
@@ -334,7 +334,7 @@ class BlockchainTest(BitcoinTestFramework):
def solve_and_send_block(prevhash, height, time):
b = create_block(prevhash, create_coinbase(height), time)
b.solve()
- node.p2p.send_and_ping(msg_block(b))
+ peer.send_and_ping(msg_block(b))
return b
b21f = solve_and_send_block(int(b20hash, 16), 21, b20['time'] + 1)
diff --git a/test/functional/rpc_getpeerinfo_banscore_deprecation.py b/test/functional/rpc_getpeerinfo_banscore_deprecation.py
deleted file mode 100755
index b830248e1e..0000000000
--- a/test/functional/rpc_getpeerinfo_banscore_deprecation.py
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2020 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test deprecation of getpeerinfo RPC banscore field."""
-
-from test_framework.test_framework import BitcoinTestFramework
-
-
-class GetpeerinfoBanscoreDeprecationTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 2
- self.extra_args = [[], ["-deprecatedrpc=banscore"]]
-
- def run_test(self):
- self.log.info("Test getpeerinfo by default no longer returns a banscore field")
- assert "banscore" not in self.nodes[0].getpeerinfo()[0].keys()
-
- self.log.info("Test getpeerinfo returns banscore with -deprecatedrpc=banscore")
- assert "banscore" in self.nodes[1].getpeerinfo()[0].keys()
-
-
-if __name__ == "__main__":
- GetpeerinfoBanscoreDeprecationTest().main()
diff --git a/test/functional/rpc_getpeerinfo_deprecation.py b/test/functional/rpc_getpeerinfo_deprecation.py
new file mode 100755
index 0000000000..287c40ae3e
--- /dev/null
+++ b/test/functional/rpc_getpeerinfo_deprecation.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test deprecation of getpeerinfo RPC fields."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import connect_nodes
+
+
+class GetpeerinfoDeprecationTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.extra_args = [[], ["-deprecatedrpc=banscore"]]
+
+ def run_test(self):
+ self.test_banscore_deprecation()
+ self.test_addnode_deprecation()
+
+ def test_banscore_deprecation(self):
+ self.log.info("Test getpeerinfo by default no longer returns a banscore field")
+ assert "banscore" not in self.nodes[0].getpeerinfo()[0].keys()
+
+ self.log.info("Test getpeerinfo returns banscore with -deprecatedrpc=banscore")
+ assert "banscore" in self.nodes[1].getpeerinfo()[0].keys()
+
+ def test_addnode_deprecation(self):
+ self.restart_node(1, ["-deprecatedrpc=getpeerinfo_addnode"])
+ connect_nodes(self.nodes[0], 1)
+
+ self.log.info("Test getpeerinfo by default no longer returns an addnode field")
+ assert "addnode" not in self.nodes[0].getpeerinfo()[0].keys()
+
+ self.log.info("Test getpeerinfo returns addnode with -deprecatedrpc=addnode")
+ assert "addnode" in self.nodes[1].getpeerinfo()[0].keys()
+
+
+if __name__ == "__main__":
+ GetpeerinfoDeprecationTest().main()
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index bc0e5b458e..b8a04f494d 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -175,6 +175,12 @@ class NetTest(BitcoinTestFramework):
for info in peer_info:
assert_net_servicesnames(int(info[0]["services"], 0x10), info[0]["servicesnames"])
+ assert_equal(peer_info[0][0]['connection_type'], 'inbound')
+ assert_equal(peer_info[0][1]['connection_type'], 'manual')
+
+ assert_equal(peer_info[1][0]['connection_type'], 'manual')
+ assert_equal(peer_info[1][1]['connection_type'], 'inbound')
+
def test_service_flags(self):
self.log.info("Test service flags")
self.nodes[0].add_p2p_connection(P2PInterface(), services=(1 << 4) | (1 << 63))
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 9506b63f82..360962b8da 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -2,17 +2,17 @@
# Copyright (c) 2016-2020 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Encode and decode BASE58, P2PKH and P2SH addresses."""
+"""Encode and decode Bitcoin addresses.
+
+- base58 P2PKH and P2SH addresses.
+- bech32 segwit v0 P2WPKH and P2WSH addresses."""
import enum
import unittest
from .script import hash256, hash160, sha256, CScript, OP_0
-from .util import hex_str_to_bytes
-
-from . import segwit_addr
-
-from test_framework.util import assert_equal
+from .segwit_addr import encode_segwit_address
+from .util import assert_equal, hex_str_to_bytes
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR = 'addr(bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj)#juyq9d97'
@@ -35,7 +35,7 @@ def byte_to_base58(b, version):
str = chr(version).encode('latin-1').hex() + str
checksum = hash256(hex_str_to_bytes(str)).hex()
str += checksum[:8]
- value = int('0x'+str,0)
+ value = int('0x' + str, 0)
while value > 0:
result = chars[value % 58] + result
value //= 58
@@ -45,7 +45,10 @@ def byte_to_base58(b, version):
return result
-def base58_to_byte(s, verify_checksum=True):
+def base58_to_byte(s):
+ """Converts a base58-encoded string to its data and version.
+
+ Throws if the base58 checksum is invalid."""
if not s:
return b''
n = 0
@@ -65,66 +68,67 @@ def base58_to_byte(s, verify_checksum=True):
else:
break
res = b'\x00' * pad + res
- if verify_checksum:
- assert_equal(hash256(res[:-4])[:4], res[-4:])
+
+ # Assert if the checksum is invalid
+ assert_equal(hash256(res[:-4])[:4], res[-4:])
return res[1:-4], int(res[0])
-def keyhash_to_p2pkh(hash, main = False):
+def keyhash_to_p2pkh(hash, main=False):
assert len(hash) == 20
version = 0 if main else 111
return byte_to_base58(hash, version)
-def scripthash_to_p2sh(hash, main = False):
+def scripthash_to_p2sh(hash, main=False):
assert len(hash) == 20
version = 5 if main else 196
return byte_to_base58(hash, version)
-def key_to_p2pkh(key, main = False):
+def key_to_p2pkh(key, main=False):
key = check_key(key)
return keyhash_to_p2pkh(hash160(key), main)
-def script_to_p2sh(script, main = False):
+def script_to_p2sh(script, main=False):
script = check_script(script)
return scripthash_to_p2sh(hash160(script), main)
-def key_to_p2sh_p2wpkh(key, main = False):
+def key_to_p2sh_p2wpkh(key, main=False):
key = check_key(key)
p2shscript = CScript([OP_0, hash160(key)])
return script_to_p2sh(p2shscript, main)
-def program_to_witness(version, program, main = False):
+def program_to_witness(version, program, main=False):
if (type(program) is str):
program = hex_str_to_bytes(program)
assert 0 <= version <= 16
assert 2 <= len(program) <= 40
assert version > 0 or len(program) in [20, 32]
- return segwit_addr.encode("bc" if main else "bcrt", version, program)
+ return encode_segwit_address("bc" if main else "bcrt", version, program)
-def script_to_p2wsh(script, main = False):
+def script_to_p2wsh(script, main=False):
script = check_script(script)
return program_to_witness(0, sha256(script), main)
-def key_to_p2wpkh(key, main = False):
+def key_to_p2wpkh(key, main=False):
key = check_key(key)
return program_to_witness(0, hash160(key), main)
-def script_to_p2sh_p2wsh(script, main = False):
+def script_to_p2sh_p2wsh(script, main=False):
script = check_script(script)
p2shscript = CScript([OP_0, sha256(script)])
return script_to_p2sh(p2shscript, main)
def check_key(key):
if (type(key) is str):
- key = hex_str_to_bytes(key) # Assuming this is hex string
+ key = hex_str_to_bytes(key) # Assuming this is hex string
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
return key
assert False
def check_script(script):
if (type(script) is str):
- script = hex_str_to_bytes(script) # Assuming this is hex string
+ script = hex_str_to_bytes(script) # Assuming this is hex string
if (type(script) is bytes or type(script) is CScript):
return script
assert False
@@ -135,15 +139,15 @@ class TestFrameworkScript(unittest.TestCase):
def check_base58(data, version):
self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))
- check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 111)
- check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 111)
- check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
- check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 0)
- check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 0)
- check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
- check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
+ check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 111)
+ check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 111)
+ check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
+ check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 0)
+ check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 0)
+ check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 0)
+ check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
+ check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
+ check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
diff --git a/test/functional/test_framework/segwit_addr.py b/test/functional/test_framework/segwit_addr.py
index 02368e938f..00c0d8a919 100644
--- a/test/functional/test_framework/segwit_addr.py
+++ b/test/functional/test_framework/segwit_addr.py
@@ -3,7 +3,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Reference implementation for Bech32 and segwit addresses."""
-
+import unittest
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
@@ -84,7 +84,7 @@ def convertbits(data, frombits, tobits, pad=True):
return ret
-def decode(hrp, addr):
+def decode_segwit_address(hrp, addr):
"""Decode a segwit address."""
hrpgot, data = bech32_decode(addr)
if hrpgot != hrp:
@@ -99,9 +99,23 @@ def decode(hrp, addr):
return (data[0], decoded)
-def encode(hrp, witver, witprog):
+def encode_segwit_address(hrp, witver, witprog):
"""Encode a segwit address."""
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
- if decode(hrp, ret) == (None, None):
+ if decode_segwit_address(hrp, ret) == (None, None):
return None
return ret
+
+class TestFrameworkScript(unittest.TestCase):
+ def test_segwit_encode_decode(self):
+ def test_python_bech32(addr):
+ hrp = addr[:4]
+ self.assertEqual(hrp, "bcrt")
+ (witver, witprog) = decode_segwit_address(hrp, addr)
+ self.assertEqual(encode_segwit_address(hrp, witver, witprog), addr)
+
+ # P2WPKH
+ test_python_bech32('bcrt1qthmht0k2qnh3wy7336z05lu2km7emzfpm3wg46')
+ # P2WSH
+ test_python_bech32('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj')
+ test_python_bech32('bcrt1qft5p2uhsdcdc3l2ua4ap5qqfg4pjaqlp250x7us7a8qqhrxrxfsqseac85')
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index d034986821..046efe730e 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -542,15 +542,6 @@ class TestNode():
return p2p_conn
- @property
- def p2p(self):
- """Return the first p2p connection
-
- Convenience property - most tests only use a single p2p connection to each
- node, so this saves having to write node.p2ps[0] many times."""
- assert self.p2ps, self._node_msg("No p2p connection")
- return self.p2ps[0]
-
def num_test_p2p_connections(self):
"""Return number of test framework p2p connections to the node."""
return len([peer for peer in self.getpeerinfo() if peer['subver'] == MY_SUBVERSION])
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 6c3d50df93..cb86305cc6 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -71,6 +71,7 @@ TEST_FRAMEWORK_MODULES = [
"blocktools",
"muhash",
"script",
+ "segwit_addr",
"util",
]
@@ -251,7 +252,7 @@ BASE_SCRIPTS = [
'feature_config_args.py',
'feature_settings.py',
'rpc_getdescriptorinfo.py',
- 'rpc_getpeerinfo_banscore_deprecation.py',
+ 'rpc_getpeerinfo_deprecation.py',
'rpc_help.py',
'feature_help.py',
'feature_shutdown.py',
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index 68e22b7e86..bba0b8974d 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -64,10 +64,6 @@ from test_framework.util import (
assert_raises_rpc_error,
connect_nodes,
)
-from test_framework.segwit_addr import (
- encode,
- decode,
-)
class AddressTypeTest(BitcoinTestFramework):
def set_test_params(self):
@@ -101,13 +97,6 @@ class AddressTypeTest(BitcoinTestFramework):
"""Return a list of balances."""
return [self.nodes[i].getbalances()['mine'][key] for i in range(4)]
- # Quick test of python bech32 implementation
- def test_python_bech32(self, addr):
- hrp = addr[:4]
- assert_equal(hrp, "bcrt")
- (witver, witprog) = decode(hrp, addr)
- assert_equal(encode(hrp, witver, witprog), addr)
-
def test_address(self, node, address, multisig, typ):
"""Run sanity checks on an address."""
info = self.nodes[node].getaddressinfo(address)
@@ -132,7 +121,6 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 40)
assert 'pubkey' in info
- self.test_python_bech32(info["address"])
elif typ == 'legacy':
# P2SH-multisig
assert info['isscript']
@@ -158,7 +146,6 @@ class AddressTypeTest(BitcoinTestFramework):
assert_equal(info['witness_version'], 0)
assert_equal(len(info['witness_program']), 64)
assert 'pubkeys' in info
- self.test_python_bech32(info["address"])
else:
# Unknown type
assert False
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index bb208341a0..689a0fa4df 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -661,6 +661,18 @@ class WalletTest(BitcoinTestFramework):
assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
assert_equal(tx[verbose_field], self.nodes[0].decoderawtransaction(tx["hex"]))
+ self.log.info("Test send* RPCs with verbose=True")
+ address = self.nodes[0].getnewaddress("test")
+ txid_feeReason_one = self.nodes[2].sendtoaddress(address=address, amount=5, verbose=True)
+ assert_equal(txid_feeReason_one["fee_reason"], "Fallback fee")
+ txid_feeReason_two = self.nodes[2].sendmany(dummy='', amounts={address: 5}, verbose=True)
+ assert_equal(txid_feeReason_two["fee_reason"], "Fallback fee")
+ self.log.info("Test send* RPCs with verbose=False")
+ txid_feeReason_three = self.nodes[2].sendtoaddress(address=address, amount=5, verbose=False)
+ assert_equal(self.nodes[2].gettransaction(txid_feeReason_three)['txid'], txid_feeReason_three)
+ txid_feeReason_four = self.nodes[2].sendmany(dummy='', amounts={address: 5}, verbose=False)
+ assert_equal(self.nodes[2].gettransaction(txid_feeReason_four)['txid'], txid_feeReason_four)
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index aaf050ebf7..f0be271c66 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -21,8 +21,6 @@ from test_framework.util import (
get_rpc_proxy,
)
-FEATURE_LATEST = 169900
-
got_loading_error = False
def test_load_unload(node, name):
global got_loading_error
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 0327c9e070..d3c03c4764 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -21,7 +21,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0] # alias
- node.add_p2p_connection(P2PTxInvStore())
+ peer_first = node.add_p2p_connection(P2PTxInvStore())
self.log.info("Create a new transaction and wait until it's broadcast")
txid = node.sendtoaddress(node.getnewaddress(), 1)
@@ -33,10 +33,10 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
time.sleep(1.1)
# Can take a few seconds due to transaction trickling
- node.p2p.wait_for_broadcast([txid])
+ peer_first.wait_for_broadcast([txid])
# Add a second peer since txs aren't rebroadcast to the same peer (see filterInventoryKnown)
- node.add_p2p_connection(P2PTxInvStore())
+ peer_second = node.add_p2p_connection(P2PTxInvStore())
self.log.info("Create a block")
# Create and submit a block without the transaction.
@@ -58,13 +58,13 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
two_min = 2 * 60
node.setmocktime(now + twelve_hrs - two_min)
time.sleep(2) # ensure enough time has passed for rebroadcast attempt to occur
- assert_equal(int(txid, 16) in node.p2ps[1].get_invs(), False)
+ assert_equal(int(txid, 16) in peer_second.get_invs(), False)
self.log.info("Bump time & check that transaction is rebroadcast")
# Transaction should be rebroadcast approximately 24 hours in the future,
# but can range from 12-36. So bump 36 hours to be sure.
node.setmocktime(now + 36 * 60 * 60)
- node.p2p.wait_for_broadcast([txid])
+ peer_second.wait_for_broadcast([txid])
if __name__ == '__main__':
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index b64d2030a4..4fdfad01c3 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -29,9 +29,9 @@ class WalletSendTest(BitcoinTestFramework):
def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
arg_conf_target=None, arg_estimate_mode=None,
- conf_target=None, estimate_mode=None, add_to_wallet=None,psbt=None,
- inputs=None,add_inputs=None,change_address=None,change_position=None,change_type=None,
- include_watching=None,locktime=None,lock_unspents=None,replaceable=None,subtract_fee_from_outputs=None,
+ conf_target=None, estimate_mode=None, add_to_wallet=None, psbt=None,
+ inputs=None, add_inputs=None, change_address=None, change_position=None, change_type=None,
+ include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
expect_error=None):
assert (amount is None) != (data is None)
@@ -92,13 +92,13 @@ class WalletSendTest(BitcoinTestFramework):
res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
else:
try:
- assert_raises_rpc_error(expect_error[0],expect_error[1],from_wallet.send,
- outputs=outputs,conf_target=arg_conf_target,estimate_mode=arg_estimate_mode,options=options)
+ assert_raises_rpc_error(expect_error[0], expect_error[1], from_wallet.send,
+ outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
except AssertionError:
# Provide debug info if the test fails
self.log.error("Unexpected successful result:")
self.log.error(options)
- res = from_wallet.send(outputs=outputs,conf_target=arg_conf_target,estimate_mode=arg_estimate_mode,options=options)
+ res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
self.log.error(res)
if "txid" in res and add_to_wallet:
self.log.error("Transaction details:")
@@ -131,7 +131,7 @@ class WalletSendTest(BitcoinTestFramework):
assert tx
assert_equal(tx["bip125-replaceable"], "yes" if replaceable else "no")
# Ensure transaction exists in the mempool:
- tx = from_wallet.getrawtransaction(res["txid"],True)
+ tx = from_wallet.getrawtransaction(res["txid"], True)
assert tx
if amount:
if subtract_fee_from_outputs:
@@ -164,7 +164,7 @@ class WalletSendTest(BitcoinTestFramework):
self.nodes[1].createwallet(wallet_name="w2")
w2 = self.nodes[1].get_wallet_rpc("w2")
# w3 is a watch-only wallet, based on w2
- self.nodes[1].createwallet(wallet_name="w3",disable_private_keys=True)
+ self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)
w3 = self.nodes[1].get_wallet_rpc("w3")
for _ in range(3):
a2_receive = w2.getnewaddress()
@@ -188,7 +188,7 @@ class WalletSendTest(BitcoinTestFramework):
self.sync_blocks()
# w4 has private keys enabled, but only contains watch-only keys (from w2)
- self.nodes[1].createwallet(wallet_name="w4",disable_private_keys=False)
+ self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
w4 = self.nodes[1].get_wallet_rpc("w4")
for _ in range(3):
a2_receive = w2.getnewaddress()
@@ -253,7 +253,7 @@ class WalletSendTest(BitcoinTestFramework):
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=-1, estimate_mode="sat/b",
expect_error=(-3, "Amount out of range"))
# Fee rate of 0.1 satoshi per byte should throw an error
- # TODO: error should say 1.000 sat/b
+ # TODO: error should use sat/b
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b",
expect_error=(-4, "Fee rate (0.00000100 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)"))
@@ -325,11 +325,16 @@ class WalletSendTest(BitcoinTestFramework):
locked_coins = w0.listlockunspent()
assert_equal(len(locked_coins), 1)
# Locked coins are automatically unlocked when manually selected
- self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1],add_to_wallet=False)
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False)
+ assert res["complete"]
self.log.info("Replaceable...")
- self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, replaceable=True)
- self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, replaceable=False)
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True, replaceable=True)
+ assert res["complete"]
+ assert_equal(self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"], "yes")
+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=True, replaceable=False)
+ assert res["complete"]
+ assert_equal(self.nodes[0].gettransaction(res["txid"])["bip125-replaceable"], "no")
self.log.info("Subtract fee from output")
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 5e1a804d33..33a2a9411e 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -25,7 +25,7 @@ class TxnMallTest(BitcoinTestFramework):
parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true",
help="Test double-spend of 1-confirmed transaction")
parser.add_argument("--segwit", dest="segwit", default=False, action="store_true",
- help="Test behaviour with SegWit txn (which should fail")
+ help="Test behaviour with SegWit txn (which should fail)")
def setup_network(self):
# Start with split network:
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index b3d9b9e6ec..75257d886b 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -1,6 +1,5 @@
# -fsanitize=undefined suppressions
# =================================
-float-divide-by-zero:policy/fees.cpp
float-divide-by-zero:validation.cpp
float-divide-by-zero:wallet/wallet.cpp