mirror of https://github.com/bitcoin/bitcoin.git
Compare commits
51 Commits
eb74e70f86
...
51d4ef4ea1
Author | SHA1 | Date |
---|---|---|
|
51d4ef4ea1 | |
|
b510893d00 | |
|
ec5841888d | |
|
d735e2e9b3 | |
|
de1dc6b47b | |
|
919e6d01e9 | |
|
452ea59281 | |
|
a33bd767a3 | |
|
2578da69f4 | |
|
25dbe4bc86 | |
|
cfb0d74698 | |
|
86eaa4d6cd | |
|
007900ee9b | |
|
8e47ed6906 | |
|
3635d62f5a | |
|
2e09d66fbb | |
|
156927903d | |
|
e1a1b14c93 | |
|
1ed00a0d39 | |
|
c76de2eea1 | |
|
75353a0163 | |
|
87e7f37918 | |
|
2a4450ccbb | |
|
4268abae1a | |
|
1aaaaa078b | |
|
fadad7a494 | |
|
14ae71f323 | |
|
99bc552980 | |
|
576dd97cb9 | |
|
0f7d4ee4e8 | |
|
93a70a42d3 | |
|
6de8051263 | |
|
46135d90ea | |
|
771978952a | |
|
06df14ba75 | |
|
26e71c237d | |
|
bbe8e9063c | |
|
3a4d1a25cf | |
|
dda5228e02 | |
|
7b5261f7ef | |
|
7ae0497eef | |
|
94db966a3b | |
|
eca50854e1 | |
|
652424ad16 | |
|
417437eb01 | |
|
3cbbcb66ef | |
|
bddcadee82 | |
|
a5ead122fe | |
|
4577fb2b1e | |
|
a3986935f0 | |
|
5db8cd2d37 |
|
@ -105,7 +105,7 @@ jobs:
|
|||
name: ${{ matrix.job-name }}
|
||||
# Use any image to support the xcode-select below, but hardcode version to avoid silent upgrades (and breaks).
|
||||
# See: https://github.com/actions/runner-images#available-images.
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
|
||||
# When a contributor maintains a fork of the repo, any pull request they make
|
||||
# to their own fork, or to the main repository, will trigger two CI runs:
|
||||
|
@ -123,10 +123,10 @@ jobs:
|
|||
include:
|
||||
- job-type: standard
|
||||
file-env: './ci/test/00_setup_env_mac_native.sh'
|
||||
job-name: 'macOS 14 native, arm64, no depends, sqlite only, gui'
|
||||
job-name: 'macOS native, no depends, sqlite only, gui'
|
||||
- job-type: fuzz
|
||||
file-env: './ci/test/00_setup_env_mac_native_fuzz.sh'
|
||||
job-name: 'macOS 14 native, arm64, fuzz'
|
||||
job-name: 'macOS native, fuzz'
|
||||
|
||||
env:
|
||||
DANGER_RUN_CI_ON_HOST: 1
|
||||
|
@ -145,8 +145,8 @@ jobs:
|
|||
# Use the earliest Xcode supported by the version of macOS denoted in
|
||||
# doc/release-notes-empty-template.md and providing at least the
|
||||
# minimum clang version denoted in doc/dependencies.md.
|
||||
# See: https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes
|
||||
sudo xcode-select --switch /Applications/Xcode_15.0.app
|
||||
# See: https://developer.apple.com/documentation/xcode-release-notes/xcode-16-release-notes
|
||||
sudo xcode-select --switch /Applications/Xcode_16.0.app
|
||||
clang --version
|
||||
|
||||
- name: Install Homebrew packages
|
||||
|
@ -211,11 +211,16 @@ jobs:
|
|||
steps:
|
||||
- *CHECKOUT
|
||||
|
||||
- name: Configure Developer Command Prompt for Microsoft Visual C++
|
||||
# Using microsoft/setup-msbuild is not enough.
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x64
|
||||
- &SET_UP_VS
|
||||
name: Set up VS Developer Prompt
|
||||
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||
run: |
|
||||
$vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
$installationPath = & $vswherePath -latest -property installationPath
|
||||
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | foreach-object {
|
||||
$name, $value = $_ -split '=', 2
|
||||
echo "$name=$value" >> $env:GITHUB_ENV
|
||||
}
|
||||
|
||||
- name: Get tool information
|
||||
shell: pwsh
|
||||
|
@ -263,14 +268,26 @@ jobs:
|
|||
run: |
|
||||
cmake --build . -j $NUMBER_OF_PROCESSORS --config Release
|
||||
|
||||
- name: Get bitcoind manifest
|
||||
- name: Check executable manifests
|
||||
if: matrix.job-type == 'standard'
|
||||
working-directory: build
|
||||
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||
run: |
|
||||
mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -out:bitcoind.manifest
|
||||
cat bitcoind.manifest
|
||||
echo
|
||||
mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -validate_manifest
|
||||
mt.exe -nologo -inputresource:bin\Release\bitcoind.exe -out:bitcoind.manifest
|
||||
Get-Content bitcoind.manifest
|
||||
|
||||
Get-ChildItem -Filter "bin\Release\*.exe" | ForEach-Object {
|
||||
$exeName = $_.Name
|
||||
|
||||
# Skip as they currently do not have manifests
|
||||
if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe" -or $exeName -eq "test_bitcoin-qt.exe") {
|
||||
Write-Host "Skipping $exeName (no manifest present)"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Checking $exeName"
|
||||
& mt.exe -nologo -inputresource:$_.FullName -validate_manifest
|
||||
}
|
||||
|
||||
- name: Run test suite
|
||||
if: matrix.job-type == 'standard'
|
||||
|
@ -370,19 +387,26 @@ jobs:
|
|||
- name: Run bitcoind.exe
|
||||
run: ./bin/bitcoind.exe -version
|
||||
|
||||
- name: Find mt.exe tool
|
||||
shell: pwsh
|
||||
run: |
|
||||
$sdk_dir = (Get-ItemProperty 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots' -Name KitsRoot10).KitsRoot10
|
||||
$sdk_latest = (Get-ChildItem "$sdk_dir\bin" -Directory | Where-Object { $_.Name -match '^\d+\.\d+\.\d+\.\d+$' } | Sort-Object Name -Descending | Select-Object -First 1).Name
|
||||
"MT_EXE=${sdk_dir}bin\${sdk_latest}\x64\mt.exe" >> $env:GITHUB_ENV
|
||||
- *SET_UP_VS
|
||||
|
||||
- name: Get bitcoind manifest
|
||||
shell: pwsh
|
||||
- name: Check executable manifests
|
||||
shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
|
||||
run: |
|
||||
& $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
|
||||
mt.exe -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
|
||||
Get-Content bitcoind.manifest
|
||||
& $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -validate_manifest
|
||||
|
||||
Get-ChildItem -Filter "bin\*.exe" | ForEach-Object {
|
||||
$exeName = $_.Name
|
||||
|
||||
# Skip as they currently do not have manifests
|
||||
if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe") {
|
||||
Write-Host "Skipping $exeName (no manifest present)"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Checking $exeName"
|
||||
& mt.exe -nologo -inputresource:$_.FullName -validate_manifest
|
||||
}
|
||||
|
||||
- name: Run unit tests
|
||||
# Can't use ctest here like other jobs as we don't have a CMake build tree.
|
||||
|
|
|
@ -112,7 +112,6 @@ ELF_ALLOWED_LIBRARIES = {
|
|||
'libfontconfig.so.1', # font support
|
||||
'libfreetype.so.6', # font parsing
|
||||
'libdl.so.2', # programming interface to dynamic linker
|
||||
'libxcb-cursor.so.0',
|
||||
'libxcb-icccm.so.4',
|
||||
'libxcb-image.so.0',
|
||||
'libxcb-shm.so.0',
|
||||
|
@ -249,7 +248,7 @@ def check_MACHO_libraries(binary) -> bool:
|
|||
return ok
|
||||
|
||||
def check_MACHO_min_os(binary) -> bool:
|
||||
if binary.build_version.minos == [13,0,0]:
|
||||
if binary.build_version.minos == [14,0,0]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
@ -460,18 +460,18 @@ if config.translations_dir:
|
|||
sys.stderr.write(f"Error: Could not find translation dir \"{config.translations_dir[0]}\"\n")
|
||||
sys.exit(1)
|
||||
|
||||
print("+ Adding Qt translations +")
|
||||
print("+ Adding Qt translations +")
|
||||
|
||||
translations = Path(config.translations_dir[0])
|
||||
translations = Path(config.translations_dir[0])
|
||||
|
||||
regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)')
|
||||
regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)')
|
||||
|
||||
lang_files = [x for x in translations.iterdir() if regex.match(x.name)]
|
||||
lang_files = [x for x in translations.iterdir() if regex.match(x.name)]
|
||||
|
||||
for file in lang_files:
|
||||
if verbose:
|
||||
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
for file in lang_files:
|
||||
if verbose:
|
||||
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
|
||||
# ------------------------------------------------
|
||||
|
||||
|
|
|
@ -36,9 +36,8 @@ define fetch_file_inner
|
|||
endef
|
||||
|
||||
define fetch_file
|
||||
( test -f $$($(1)_source_dir)/$(4) || \
|
||||
( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \
|
||||
$(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5))))
|
||||
$(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5)))
|
||||
endef
|
||||
|
||||
# Shell script to create a source tarball in $(1)_source from local directory
|
||||
|
@ -109,7 +108,7 @@ $(1)_prefixbin:=$($($(1)_type)_prefix)/bin/
|
|||
$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources)
|
||||
|
||||
#stamps
|
||||
$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_file_name).hash
|
||||
$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_version)-$($(1)_sha256_hash).hash
|
||||
$(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted
|
||||
$(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed
|
||||
$(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned
|
||||
|
@ -247,7 +246,6 @@ endif
|
|||
$($(1)_fetched):
|
||||
mkdir -p $$(@D) $(SOURCES_PATH)
|
||||
rm -f $$@
|
||||
touch $$@
|
||||
cd $$(@D); $($(1)_fetch_cmds)
|
||||
cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);)
|
||||
touch $$@
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
OSX_MIN_VERSION=13.0
|
||||
OSX_MIN_VERSION=14.0
|
||||
OSX_SDK_VERSION=14.0
|
||||
XCODE_VERSION=15.0
|
||||
XCODE_BUILD_ID=15A240d
|
||||
|
|
|
@ -6,7 +6,7 @@ $(package)_sha256_hash=0e9c5446dc6f3beb8af6ebfcc9e27bcc6da6fe2860f7fc07b99144dfa
|
|||
$(package)_dependencies=libxcb libxcb_util_render libxcb_util_image
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts = --disable-static
|
||||
$(package)_config_opts = --disable-shared
|
||||
$(package)_config_opts += --disable-dependency-tracking --enable-option-checking
|
||||
endef
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package=qrencode
|
||||
$(package)_version=4.1.1
|
||||
$(package)_download_path=https://fukuchi.org/works/qrencode/
|
||||
$(package)_download_path=https://github.com/fukuchi/libqrencode/archive/refs/tags/
|
||||
$(package)_download_file=v$($(package)_version).tar.gz
|
||||
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=da448ed4f52aba6bcb0cd48cac0dd51b8692bccc4cd127431402fca6f8171e8e
|
||||
$(package)_sha256_hash=5385bc1b8c2f20f3b91d258bf8ccc8cf62023935df2d2676b5b67049f31a049c
|
||||
$(package)_patches=cmake_fixups.patch
|
||||
|
||||
define $(package)_set_vars
|
||||
|
|
|
@ -81,8 +81,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
|||
|
||||
sudo apt-get install qt6-base-dev qt6-tools-dev qt6-l10n-tools qt6-tools-dev-tools libgl-dev
|
||||
|
||||
For Qt 6.5 and later, the `libxcb-cursor0` package must be installed at runtime.
|
||||
|
||||
Additionally, to support Wayland protocol for modern desktop environments:
|
||||
|
||||
sudo apt install qt6-wayland
|
||||
|
@ -133,8 +131,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
|||
|
||||
sudo dnf install qt6-qtbase-devel qt6-qttools-devel
|
||||
|
||||
For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime.
|
||||
|
||||
Additionally, to support Wayland protocol for modern desktop environments:
|
||||
|
||||
sudo dnf install qt6-qtwayland
|
||||
|
@ -182,8 +178,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
|||
|
||||
apk add qt6-qtbase-dev qt6-qttools-dev
|
||||
|
||||
For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime.
|
||||
|
||||
The GUI will be able to encode addresses in QR codes unless this feature is explicitly disabled. To install libqrencode, run:
|
||||
|
||||
apk add libqrencode-dev
|
||||
|
|
|
@ -36,7 +36,7 @@ Compatibility
|
|||
==============
|
||||
|
||||
Bitcoin Core is supported and tested on operating systems using the
|
||||
Linux Kernel 3.17+, macOS 13+, and Windows 10+. Bitcoin
|
||||
Linux Kernel 3.17+, macOS 14+, and Windows 10+. Bitcoin
|
||||
Core should also work on most other Unix-like systems but is not as
|
||||
frequently tested on them. It is not recommended to use Bitcoin Core on
|
||||
unsupported systems.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<plist version="0.9">
|
||||
<dict>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13</string>
|
||||
<string>14</string>
|
||||
|
||||
<key>LSArchitecturePriority</key>
|
||||
<array>
|
||||
|
|
|
@ -24,7 +24,7 @@ static void VerifyScriptBench(benchmark::Bench& bench)
|
|||
{
|
||||
ECC_Context ecc_context{};
|
||||
|
||||
const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
const int witnessversion = 0;
|
||||
|
||||
// Key pair.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <chain.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/check.h>
|
||||
#include <util/time.h>
|
||||
|
||||
std::string CBlockFileInfo::ToString() const
|
||||
|
@ -158,18 +159,26 @@ int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& fr
|
|||
/** Find the last common ancestor two blocks have.
|
||||
* Both pa and pb must be non-nullptr. */
|
||||
const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex* pb) {
|
||||
// First rewind to the last common height (the forking point cannot be past one of the two).
|
||||
if (pa->nHeight > pb->nHeight) {
|
||||
pa = pa->GetAncestor(pb->nHeight);
|
||||
} else if (pb->nHeight > pa->nHeight) {
|
||||
pb = pb->GetAncestor(pa->nHeight);
|
||||
}
|
||||
|
||||
while (pa != pb && pa && pb) {
|
||||
while (pa != pb) {
|
||||
// Jump back until pa and pb have a common "skip" ancestor.
|
||||
while (pa->pskip != pb->pskip) {
|
||||
// This logic relies on the property that equal-height blocks have equal-height skip
|
||||
// pointers.
|
||||
Assume(pa->nHeight == pb->nHeight);
|
||||
Assume(pa->pskip->nHeight == pb->pskip->nHeight);
|
||||
pa = pa->pskip;
|
||||
pb = pb->pskip;
|
||||
}
|
||||
// At this point, pa and pb are different, but have equal pskip. The forking point lies in
|
||||
// between pa/pb on the one end, and pa->pskip/pb->pskip on the other end.
|
||||
pa = pa->pprev;
|
||||
pb = pb->pprev;
|
||||
}
|
||||
|
||||
// Eventually all chain branches meet at the genesis block.
|
||||
assert(pa == pb);
|
||||
return pa;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef BITCOIN_CONSENSUS_PARAMS_H
|
||||
#define BITCOIN_CONSENSUS_PARAMS_H
|
||||
|
||||
#include <script/verify_flags.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <array>
|
||||
|
@ -89,7 +90,7 @@ struct Params {
|
|||
* - buried in the chain, and
|
||||
* - fail if the default script verify flags are applied.
|
||||
*/
|
||||
std::map<uint256, uint32_t> script_flag_exceptions;
|
||||
std::map<uint256, script_verify_flags> script_flag_exceptions;
|
||||
/** Block height and hash at which BIP34 becomes active */
|
||||
int BIP34Height;
|
||||
uint256 BIP34Hash;
|
||||
|
|
|
@ -140,7 +140,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
|||
return nSigOps;
|
||||
}
|
||||
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags)
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags)
|
||||
{
|
||||
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define BITCOIN_CONSENSUS_TX_VERIFY_H
|
||||
|
||||
#include <consensus/amount.h>
|
||||
#include <script/verify_flags.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
@ -52,7 +53,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma
|
|||
* @param[in] flags Script verification flags
|
||||
* @return Total signature operation cost of tx
|
||||
*/
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, uint32_t flags);
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, script_verify_flags flags);
|
||||
|
||||
/**
|
||||
* Check if transaction is final and can be included in a block with the
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
#include <consensus/params.h>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
struct VBDeploymentInfo {
|
||||
/** Deployment name */
|
||||
|
|
120
src/net.cpp
120
src/net.cpp
|
@ -108,7 +108,7 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
|
|||
|
||||
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_NETWORKKEY = 0x0e8a2b136c592a7dULL; // SHA256("networkkey")[0:8]
|
||||
//
|
||||
// Global state variables
|
||||
//
|
||||
|
@ -331,42 +331,22 @@ bool IsLocal(const CService& addr)
|
|||
return mapLocalHost.count(addr) > 0;
|
||||
}
|
||||
|
||||
CNode* CConnman::FindNode(const CNetAddr& ip)
|
||||
bool CConnman::AlreadyConnectedToHost(const std::string& host) const
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
for (CNode* pnode : m_nodes) {
|
||||
if (static_cast<CNetAddr>(pnode->addr) == ip) {
|
||||
return pnode;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return std::ranges::any_of(m_nodes, [&host](CNode* node) { return node->m_addr_name == host; });
|
||||
}
|
||||
|
||||
CNode* CConnman::FindNode(const std::string& addrName)
|
||||
bool CConnman::AlreadyConnectedToAddressPort(const CService& addr_port) const
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
for (CNode* pnode : m_nodes) {
|
||||
if (pnode->m_addr_name == addrName) {
|
||||
return pnode;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return std::ranges::any_of(m_nodes, [&addr_port](CNode* node) { return node->addr == addr_port; });
|
||||
}
|
||||
|
||||
CNode* CConnman::FindNode(const CService& addr)
|
||||
bool CConnman::AlreadyConnectedToAddress(const CNetAddr& addr) const
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
for (CNode* pnode : m_nodes) {
|
||||
if (static_cast<CService>(pnode->addr) == addr) {
|
||||
return pnode;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
|
||||
{
|
||||
return FindNode(static_cast<CNetAddr>(addr));
|
||||
return std::ranges::any_of(m_nodes, [&addr](CNode* node) { return node->addr == addr; });
|
||||
}
|
||||
|
||||
bool CConnman::CheckIncomingNonce(uint64_t nonce)
|
||||
|
@ -393,7 +373,12 @@ static CService GetBindAddress(const Sock& sock)
|
|||
return addr_bind;
|
||||
}
|
||||
|
||||
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport)
|
||||
CNode* CConnman::ConnectNode(CAddress addrConnect,
|
||||
const char* pszDest,
|
||||
bool fCountFailure,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override)
|
||||
{
|
||||
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||
assert(conn_type != ConnectionType::INBOUND);
|
||||
|
@ -403,10 +388,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
return nullptr;
|
||||
|
||||
// Look for an existing connection
|
||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
||||
if (pnode)
|
||||
{
|
||||
LogPrintf("Failed to open new connection, already connected\n");
|
||||
if (AlreadyConnectedToAddressPort(addrConnect)) {
|
||||
LogInfo("Failed to open new connection to %s, already connected", addrConnect.ToStringAddrPort());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -436,9 +419,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
}
|
||||
// It is possible that we already have a connection to the IP/port pszDest resolved to.
|
||||
// In that case, drop the connection that was just created.
|
||||
LOCK(m_nodes_mutex);
|
||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
||||
if (pnode) {
|
||||
if (AlreadyConnectedToAddressPort(addrConnect)) {
|
||||
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -463,7 +444,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
|
||||
for (auto& target_addr: connect_to) {
|
||||
if (target_addr.IsValid()) {
|
||||
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
|
||||
bool use_proxy;
|
||||
if (proxy_override.has_value()) {
|
||||
use_proxy = true;
|
||||
proxy = proxy_override.value();
|
||||
} else {
|
||||
use_proxy = GetProxy(target_addr.GetNetwork(), proxy);
|
||||
}
|
||||
bool proxyConnectionFailed = false;
|
||||
|
||||
if (target_addr.IsI2P() && use_proxy) {
|
||||
|
@ -530,6 +517,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
if (!addr_bind.IsValid()) {
|
||||
addr_bind = GetBindAddress(*sock);
|
||||
}
|
||||
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
||||
.Write(target_addr.GetNetClass())
|
||||
.Write(addr_bind.GetAddrBytes())
|
||||
// For outbound connections, the port of the bound address is randomly
|
||||
// assigned by the OS and would therefore not be useful for seeding.
|
||||
.Write(0)
|
||||
.Finalize();
|
||||
CNode* pnode = new CNode(id,
|
||||
std::move(sock),
|
||||
target_addr,
|
||||
|
@ -539,6 +533,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
pszDest ? pszDest : "",
|
||||
conn_type,
|
||||
/*inbound_onion=*/false,
|
||||
network_id,
|
||||
CNodeOptions{
|
||||
.permission_flags = permission_flags,
|
||||
.i2p_sam_session = std::move(i2p_transient_session),
|
||||
|
@ -1832,6 +1827,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|||
ServiceFlags local_services = GetLocalServices();
|
||||
const bool use_v2transport(local_services & NODE_P2P_V2);
|
||||
|
||||
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
||||
.Write(inbound_onion ? NET_ONION : addr.GetNetClass())
|
||||
.Write(addr_bind.GetAddrBytes())
|
||||
.Write(addr_bind.GetPort()) // inbound connections use bind port
|
||||
.Finalize();
|
||||
CNode* pnode = new CNode(id,
|
||||
std::move(sock),
|
||||
CAddress{addr, NODE_NONE},
|
||||
|
@ -1841,6 +1841,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
inbound_onion,
|
||||
network_id,
|
||||
CNodeOptions{
|
||||
.permission_flags = permission_flags,
|
||||
.prefer_evict = discouraged,
|
||||
|
@ -2882,7 +2883,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
|||
const bool count_failures{((int)outbound_ipv46_peer_netgroups.size() + outbound_privacy_network_peers) >= std::min(m_max_automatic_connections - 1, 2)};
|
||||
// Use BIP324 transport when both us and them have NODE_V2_P2P set.
|
||||
const bool use_v2transport(addrConnect.nServices & GetLocalServices() & NODE_P2P_V2);
|
||||
OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*strDest=*/nullptr, conn_type, use_v2transport);
|
||||
OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*pszDest=*/nullptr, conn_type, use_v2transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2991,7 +2992,13 @@ void CConnman::ThreadOpenAddedConnections()
|
|||
}
|
||||
|
||||
// if successful, this moves the passed grant to the constructed node
|
||||
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char *pszDest, ConnectionType conn_type, bool use_v2transport)
|
||||
bool CConnman::OpenNetworkConnection(const CAddress& addrConnect,
|
||||
bool fCountFailure,
|
||||
CountingSemaphoreGrant<>&& grant_outbound,
|
||||
const char* pszDest,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override)
|
||||
{
|
||||
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||
assert(conn_type != ConnectionType::INBOUND);
|
||||
|
@ -3000,23 +3007,24 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
|||
// Initiate outbound network connection
|
||||
//
|
||||
if (m_interrupt_net->interrupted()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!fNetworkActive) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!pszDest) {
|
||||
bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
|
||||
if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
} else if (FindNode(std::string(pszDest)))
|
||||
return;
|
||||
} else if (AlreadyConnectedToHost(pszDest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport);
|
||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport, proxy_override);
|
||||
|
||||
if (!pnode)
|
||||
return;
|
||||
return false;
|
||||
pnode->grantOutbound = std::move(grant_outbound);
|
||||
|
||||
m_msgproc->InitializeNode(*pnode, m_local_services);
|
||||
|
@ -3034,6 +3042,8 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
|||
pnode->ConnectionTypeAsString().c_str(),
|
||||
pnode->ConnectedThroughNetwork(),
|
||||
GetNodeCount(ConnectionDirection::Out));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Mutex NetEventsInterface::g_msgproc_mutex;
|
||||
|
@ -3529,15 +3539,9 @@ std::vector<CAddress> CConnman::GetAddressesUnsafe(size_t max_addresses, size_t
|
|||
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
||||
{
|
||||
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
||||
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
|
||||
.Write(requestor.ConnectedThroughNetwork())
|
||||
.Write(local_socket_bytes)
|
||||
// For outbound connections, the port of the bound address is randomly
|
||||
// assigned by the OS and would therefore not be useful for seeding.
|
||||
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
|
||||
.Finalize();
|
||||
uint64_t network_id = requestor.m_network_key;
|
||||
const auto current_time = GetTime<std::chrono::microseconds>();
|
||||
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
|
||||
auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});
|
||||
CachedAddrResponse& cache_entry = r.first->second;
|
||||
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
||||
cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);
|
||||
|
@ -3651,9 +3655,11 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
|
|||
bool CConnman::DisconnectNode(const std::string& strNode)
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
if (CNode* pnode = FindNode(strNode)) {
|
||||
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), pnode->DisconnectMsg(fLogIPs));
|
||||
pnode->fDisconnect = true;
|
||||
auto it = std::ranges::find_if(m_nodes, [&strNode](CNode* node) { return node->m_addr_name == strNode; });
|
||||
if (it != m_nodes.end()) {
|
||||
CNode* node{*it};
|
||||
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), node->DisconnectMsg(fLogIPs));
|
||||
node->fDisconnect = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -3814,6 +3820,7 @@ CNode::CNode(NodeId idIn,
|
|||
const std::string& addrNameIn,
|
||||
ConnectionType conn_type_in,
|
||||
bool inbound_onion,
|
||||
uint64_t network_key,
|
||||
CNodeOptions&& node_opts)
|
||||
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
||||
m_permission_flags{node_opts.permission_flags},
|
||||
|
@ -3826,6 +3833,7 @@ CNode::CNode(NodeId idIn,
|
|||
m_inbound_onion{inbound_onion},
|
||||
m_prefer_evict{node_opts.prefer_evict},
|
||||
nKeyedNetGroup{nKeyedNetGroupIn},
|
||||
m_network_key{network_key},
|
||||
m_conn_type{conn_type_in},
|
||||
id{idIn},
|
||||
nLocalHostNonce{nLocalHostNonceIn},
|
||||
|
|
73
src/net.h
73
src/net.h
|
@ -738,6 +738,10 @@ public:
|
|||
std::atomic_bool fPauseRecv{false};
|
||||
std::atomic_bool fPauseSend{false};
|
||||
|
||||
/** Network key used to prevent fingerprinting our node across networks.
|
||||
* Influenced by the network and the bind address (+ bind port for inbounds) */
|
||||
const uint64_t m_network_key;
|
||||
|
||||
const ConnectionType m_conn_type;
|
||||
|
||||
/** Move all messages from the received queue to the processing queue. */
|
||||
|
@ -889,6 +893,7 @@ public:
|
|||
const std::string& addrNameIn,
|
||||
ConnectionType conn_type_in,
|
||||
bool inbound_onion,
|
||||
uint64_t network_key,
|
||||
CNodeOptions&& node_opts = {});
|
||||
CNode(const CNode&) = delete;
|
||||
CNode& operator=(const CNode&) = delete;
|
||||
|
@ -1143,7 +1148,28 @@ public:
|
|||
bool GetNetworkActive() const { return fNetworkActive; };
|
||||
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
||||
void SetNetworkActive(bool active);
|
||||
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char* strDest, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
/**
|
||||
* Open a new P2P connection and initialize it with the PeerManager at `m_msgproc`.
|
||||
* @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
|
||||
* @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
|
||||
* @param[in] grant_outbound Take ownership of this grant, to be released later when the connection is closed.
|
||||
* @param[in] pszDest Address to resolve and connect to.
|
||||
* @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
|
||||
* @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
|
||||
* @param[in] proxy_override Optional proxy to use and override normal proxy selection.
|
||||
* @retval true The connection was opened successfully.
|
||||
* @retval false The connection attempt failed.
|
||||
*/
|
||||
bool OpenNetworkConnection(const CAddress& addrConnect,
|
||||
bool fCountFailure,
|
||||
CountingSemaphoreGrant<>&& grant_outbound,
|
||||
const char* pszDest,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override = std::nullopt)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
bool CheckIncomingNonce(uint64_t nonce);
|
||||
void ASMapHealthCheck();
|
||||
|
||||
|
@ -1370,18 +1396,49 @@ private:
|
|||
|
||||
uint64_t CalculateKeyedNetGroup(const CNetAddr& ad) const;
|
||||
|
||||
CNode* FindNode(const CNetAddr& ip);
|
||||
CNode* FindNode(const std::string& addrName);
|
||||
CNode* FindNode(const CService& addr);
|
||||
/**
|
||||
* Determine whether we're already connected to a given "host:port".
|
||||
* Note that for inbound connections, the peer is likely using a random outbound
|
||||
* port on their side, so this will likely not match any inbound connections.
|
||||
* @param[in] host String of the form "host[:port]", e.g. "localhost" or "localhost:8333" or "1.2.3.4:8333".
|
||||
* @return true if connected to `host`.
|
||||
*/
|
||||
bool AlreadyConnectedToHost(const std::string& host) const;
|
||||
|
||||
/**
|
||||
* Determine whether we're already connected to a given address, in order to
|
||||
* avoid initiating duplicate connections.
|
||||
* Determine whether we're already connected to a given address:port.
|
||||
* Note that for inbound connections, the peer is likely using a random outbound
|
||||
* port on their side, so this will likely not match any inbound connections.
|
||||
* @param[in] addr_port Address and port to check.
|
||||
* @return true if connected to addr_port.
|
||||
*/
|
||||
bool AlreadyConnectedToAddress(const CAddress& addr);
|
||||
bool AlreadyConnectedToAddressPort(const CService& addr_port) const;
|
||||
|
||||
/**
|
||||
* Determine whether we're already connected to a given address.
|
||||
*/
|
||||
bool AlreadyConnectedToAddress(const CNetAddr& addr) const;
|
||||
|
||||
bool AttemptToEvictConnection();
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
/**
|
||||
* Open a new P2P connection.
|
||||
* @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
|
||||
* @param[in] pszDest Address to resolve and connect to.
|
||||
* @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
|
||||
* @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
|
||||
* @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
|
||||
* @param[in] proxy_override Optional proxy to use and override normal proxy selection.
|
||||
* @return Newly created CNode object or nullptr if the connection failed.
|
||||
*/
|
||||
CNode* ConnectNode(CAddress addrConnect,
|
||||
const char* pszDest,
|
||||
bool fCountFailure,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
|
||||
|
||||
void DeleteNode(CNode* pnode);
|
||||
|
|
|
@ -807,7 +807,7 @@ private:
|
|||
|
||||
uint32_t GetFetchFlags(const Peer& peer) const;
|
||||
|
||||
std::atomic<std::chrono::microseconds> m_next_inv_to_inbounds{0us};
|
||||
std::map<uint64_t, std::chrono::microseconds> m_next_inv_to_inbounds_per_network_key GUARDED_BY(g_msgproc_mutex);
|
||||
|
||||
/** Number of nodes with fSyncStarted. */
|
||||
int nSyncStarted GUARDED_BY(cs_main) = 0;
|
||||
|
@ -837,12 +837,14 @@ private:
|
|||
|
||||
/**
|
||||
* For sending `inv`s to inbound peers, we use a single (exponentially
|
||||
* distributed) timer for all peers. If we used a separate timer for each
|
||||
* distributed) timer for all peers with the same network key. If we used a separate timer for each
|
||||
* peer, a spy node could make multiple inbound connections to us to
|
||||
* accurately determine when we received the transaction (and potentially
|
||||
* determine the transaction's origin). */
|
||||
* accurately determine when we received a transaction (and potentially
|
||||
* determine the transaction's origin). Each network key has its own timer
|
||||
* to make fingerprinting harder. */
|
||||
std::chrono::microseconds NextInvToInbounds(std::chrono::microseconds now,
|
||||
std::chrono::seconds average_interval) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||
std::chrono::seconds average_interval,
|
||||
uint64_t network_key) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||
|
||||
|
||||
// All of the following cache a recent block, and are protected by m_most_recent_block_mutex
|
||||
|
@ -1143,15 +1145,15 @@ static bool CanServeWitnesses(const Peer& peer)
|
|||
}
|
||||
|
||||
std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::microseconds now,
|
||||
std::chrono::seconds average_interval)
|
||||
std::chrono::seconds average_interval,
|
||||
uint64_t network_key)
|
||||
{
|
||||
if (m_next_inv_to_inbounds.load() < now) {
|
||||
// If this function were called from multiple threads simultaneously
|
||||
// it would possible that both update the next send variable, and return a different result to their caller.
|
||||
// This is not possible in practice as only the net processing thread invokes this function.
|
||||
m_next_inv_to_inbounds = now + m_rng.rand_exp_duration(average_interval);
|
||||
auto [it, inserted] = m_next_inv_to_inbounds_per_network_key.try_emplace(network_key, 0us);
|
||||
auto& timer{it->second};
|
||||
if (timer < now) {
|
||||
timer = now + m_rng.rand_exp_duration(average_interval);
|
||||
}
|
||||
return m_next_inv_to_inbounds;
|
||||
return timer;
|
||||
}
|
||||
|
||||
bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
|
||||
|
@ -5715,7 +5717,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|||
if (tx_relay->m_next_inv_send_time < current_time) {
|
||||
fSendTrickle = true;
|
||||
if (pto->IsInboundConn()) {
|
||||
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL, pto->m_network_key);
|
||||
} else {
|
||||
tx_relay->m_next_inv_send_time = current_time + m_rng.rand_exp_duration(OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ static constexpr unsigned int MAX_DUST_OUTPUTS_PER_TX{1};
|
|||
* Note that this does not affect consensus validity; see GetBlockScriptFlags()
|
||||
* for that.
|
||||
*/
|
||||
static constexpr unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
||||
static constexpr script_verify_flags MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
||||
SCRIPT_VERIFY_DERSIG |
|
||||
SCRIPT_VERIFY_NULLDUMMY |
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
|
||||
|
@ -112,7 +112,7 @@ static constexpr unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS{SCRIPT_VERIFY_P2SH |
|
|||
* the additional (non-mandatory) rules here, to improve forwards and
|
||||
* backwards compatibility.
|
||||
*/
|
||||
static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERIFY_FLAGS |
|
||||
static constexpr script_verify_flags STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERIFY_FLAGS |
|
||||
SCRIPT_VERIFY_STRICTENC |
|
||||
SCRIPT_VERIFY_MINIMALDATA |
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
|
||||
|
@ -128,7 +128,7 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI
|
|||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE};
|
||||
|
||||
/** For convenience, standard but not mandatory verify flags. */
|
||||
static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
|
||||
static constexpr script_verify_flags STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS};
|
||||
|
||||
/** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */
|
||||
static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS{LOCKTIME_VERIFY_SEQUENCE};
|
||||
|
|
|
@ -1469,6 +1469,9 @@ RPCHelpMan getdeploymentinfo()
|
|||
RPCResult::Type::OBJ, "", "", {
|
||||
{RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
|
||||
{RPCResult::Type::NUM, "height", "requested block height (or tip)"},
|
||||
{RPCResult::Type::ARR, "script_flags", "script verify flags for the block", {
|
||||
{RPCResult::Type::STR, "flag", "a script verify flag"},
|
||||
}},
|
||||
{RPCResult::Type::OBJ_DYN, "deployments", "", {
|
||||
{RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
|
||||
}},
|
||||
|
@ -1495,6 +1498,12 @@ RPCHelpMan getdeploymentinfo()
|
|||
UniValue deploymentinfo(UniValue::VOBJ);
|
||||
deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString());
|
||||
deploymentinfo.pushKV("height", blockindex->nHeight);
|
||||
{
|
||||
const auto flagnames = GetScriptFlagNames(GetBlockScriptFlags(*blockindex, chainman));
|
||||
UniValue uv_flagnames(UniValue::VARR);
|
||||
uv_flagnames.push_backV(flagnames.begin(), flagnames.end());
|
||||
deploymentinfo.pushKV("script_flags", uv_flagnames);
|
||||
}
|
||||
deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, chainman));
|
||||
return deploymentinfo;
|
||||
},
|
||||
|
|
|
@ -130,7 +130,7 @@ static RPCHelpMan getpeerinfo()
|
|||
{
|
||||
{
|
||||
{RPCResult::Type::NUM, "id", "Peer index"},
|
||||
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
|
||||
{RPCResult::Type::STR, "addr", "(host:port) The IP address/hostname optionally followed by :port of the peer"},
|
||||
{RPCResult::Type::STR, "addrbind", /*optional=*/true, "(ip:port) Bind address of the connection to the peer"},
|
||||
{RPCResult::Type::STR, "addrlocal", /*optional=*/true, "(ip:port) Local address as reported by the peer"},
|
||||
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/*append_unroutable=*/true), ", ") + ")"},
|
||||
|
@ -322,7 +322,7 @@ static RPCHelpMan addnode()
|
|||
strprintf("Addnode connections are limited to %u at a time", MAX_ADDNODE_CONNECTIONS) +
|
||||
" and are counted separately from the -maxconnections limit.\n",
|
||||
{
|
||||
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the peer to connect to"},
|
||||
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address/hostname optionally followed by :port of the peer to connect to"},
|
||||
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
|
||||
{"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol (ignored for 'remove' command)"},
|
||||
},
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <crypto/sha256.h>
|
||||
#include <pubkey.h>
|
||||
#include <script/script.h>
|
||||
#include <tinyformat.h>
|
||||
#include <uint256.h>
|
||||
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
|
@ -197,7 +198,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror) {
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, script_verify_flags flags, ScriptError* serror) {
|
||||
// Empty signature. Not strictly DER encoded, but allowed to provide a
|
||||
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
|
||||
if (vchSig.size() == 0) {
|
||||
|
@ -214,7 +215,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
|
|||
return true;
|
||||
}
|
||||
|
||||
bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) {
|
||||
bool static CheckPubKeyEncoding(const valtype &vchPubKey, script_verify_flags flags, const SigVersion &sigversion, ScriptError* serror) {
|
||||
if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) {
|
||||
return set_error(serror, SCRIPT_ERR_PUBKEYTYPE);
|
||||
}
|
||||
|
@ -317,7 +318,7 @@ public:
|
|||
};
|
||||
}
|
||||
|
||||
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
||||
static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess)
|
||||
{
|
||||
assert(sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0);
|
||||
|
||||
|
@ -343,7 +344,7 @@ static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPu
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, ScriptExecutionData& execdata, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
{
|
||||
assert(sigversion == SigVersion::TAPSCRIPT);
|
||||
|
||||
|
@ -388,7 +389,7 @@ static bool EvalChecksigTapscript(const valtype& sig, const valtype& pubkey, Scr
|
|||
* A return value of false means the script fails entirely. When true is returned, the
|
||||
* success variable indicates whether the signature check itself succeeded.
|
||||
*/
|
||||
static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, ScriptExecutionData& execdata, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& success)
|
||||
{
|
||||
switch (sigversion) {
|
||||
case SigVersion::BASE:
|
||||
|
@ -403,7 +404,7 @@ static bool EvalChecksig(const valtype& sig, const valtype& pubkey, CScript::con
|
|||
assert(false);
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
{
|
||||
static const CScriptNum bnZero(0);
|
||||
static const CScriptNum bnOne(1);
|
||||
|
@ -1233,7 +1234,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
return set_success(serror);
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||
{
|
||||
ScriptExecutionData execdata;
|
||||
return EvalScript(stack, script, flags, checker, sigversion, execdata, serror);
|
||||
|
@ -1824,7 +1825,7 @@ bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSeq
|
|||
template class GenericTransactionSignatureChecker<CTransaction>;
|
||||
template class GenericTransactionSignatureChecker<CMutableTransaction>;
|
||||
|
||||
static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, const CScript& exec_script, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
static bool ExecuteWitnessScript(const std::span<const valtype>& stack_span, const CScript& exec_script, script_verify_flags flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptExecutionData& execdata, ScriptError* serror)
|
||||
{
|
||||
std::vector<valtype> stack{stack_span.begin(), stack_span.end()};
|
||||
|
||||
|
@ -1909,7 +1910,7 @@ static bool VerifyTaprootCommitment(const std::vector<unsigned char>& control, c
|
|||
return q.CheckTapTweak(p, merkle_root, control[0] & 1);
|
||||
}
|
||||
|
||||
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
|
||||
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror, bool is_p2sh)
|
||||
{
|
||||
CScript exec_script; //!< Actually executed script (last stack item in P2WSH; implied P2PKH script in P2WPKH; leaf script in P2TR)
|
||||
std::span stack{witness.stack};
|
||||
|
@ -1994,7 +1995,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
|
|||
// There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above.
|
||||
}
|
||||
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror)
|
||||
{
|
||||
static const CScriptWitness emptyWitness;
|
||||
if (witness == nullptr) {
|
||||
|
@ -2131,7 +2132,7 @@ size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& wi
|
|||
return 0;
|
||||
}
|
||||
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags)
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags)
|
||||
{
|
||||
static const CScriptWitness witnessEmpty;
|
||||
|
||||
|
@ -2161,3 +2162,48 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FLAG_NAME(flag) {std::string(#flag), SCRIPT_VERIFY_##flag}
|
||||
const std::map<std::string, script_verify_flag_name> g_verify_flag_names{
|
||||
FLAG_NAME(P2SH),
|
||||
FLAG_NAME(STRICTENC),
|
||||
FLAG_NAME(DERSIG),
|
||||
FLAG_NAME(LOW_S),
|
||||
FLAG_NAME(SIGPUSHONLY),
|
||||
FLAG_NAME(MINIMALDATA),
|
||||
FLAG_NAME(NULLDUMMY),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_NOPS),
|
||||
FLAG_NAME(CLEANSTACK),
|
||||
FLAG_NAME(MINIMALIF),
|
||||
FLAG_NAME(NULLFAIL),
|
||||
FLAG_NAME(CHECKLOCKTIMEVERIFY),
|
||||
FLAG_NAME(CHECKSEQUENCEVERIFY),
|
||||
FLAG_NAME(WITNESS),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM),
|
||||
FLAG_NAME(WITNESS_PUBKEYTYPE),
|
||||
FLAG_NAME(CONST_SCRIPTCODE),
|
||||
FLAG_NAME(TAPROOT),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_PUBKEYTYPE),
|
||||
FLAG_NAME(DISCOURAGE_OP_SUCCESS),
|
||||
FLAG_NAME(DISCOURAGE_UPGRADABLE_TAPROOT_VERSION),
|
||||
};
|
||||
#undef FLAG_NAME
|
||||
|
||||
std::vector<std::string> GetScriptFlagNames(script_verify_flags flags)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
if (flags == SCRIPT_VERIFY_NONE) {
|
||||
return res;
|
||||
}
|
||||
script_verify_flags leftover = flags;
|
||||
for (const auto& [name, flag] : g_verify_flag_names) {
|
||||
if ((flags & flag) != 0) {
|
||||
res.push_back(name);
|
||||
leftover &= ~flag;
|
||||
}
|
||||
}
|
||||
if (leftover != 0) {
|
||||
res.push_back(strprintf("0x%08x", leftover.as_int()));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <hash.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <script/script_error.h> // IWYU pragma: export
|
||||
#include <script/verify_flags.h> // IWYU pragma: export
|
||||
#include <span.h>
|
||||
#include <uint256.h>
|
||||
|
||||
|
@ -42,35 +43,36 @@ enum
|
|||
* All flags are intended to be soft forks: the set of acceptable scripts under
|
||||
* flags (A | B) is a subset of the acceptable scripts under flag (A).
|
||||
*/
|
||||
enum : uint32_t {
|
||||
SCRIPT_VERIFY_NONE = 0,
|
||||
|
||||
static constexpr script_verify_flags SCRIPT_VERIFY_NONE{0};
|
||||
|
||||
enum class script_verify_flag_name : uint8_t {
|
||||
// Evaluate P2SH subscripts (BIP16).
|
||||
SCRIPT_VERIFY_P2SH = (1U << 0),
|
||||
SCRIPT_VERIFY_P2SH,
|
||||
|
||||
// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
|
||||
// Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure.
|
||||
// (not used or intended as a consensus rule).
|
||||
SCRIPT_VERIFY_STRICTENC = (1U << 1),
|
||||
SCRIPT_VERIFY_STRICTENC,
|
||||
|
||||
// Passing a non-strict-DER signature to a checksig operation causes script failure (BIP62 rule 1)
|
||||
SCRIPT_VERIFY_DERSIG = (1U << 2),
|
||||
SCRIPT_VERIFY_DERSIG,
|
||||
|
||||
// Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
|
||||
// (BIP62 rule 5).
|
||||
SCRIPT_VERIFY_LOW_S = (1U << 3),
|
||||
SCRIPT_VERIFY_LOW_S,
|
||||
|
||||
// verify dummy stack item consumed by CHECKMULTISIG is of zero-length (BIP62 rule 7).
|
||||
SCRIPT_VERIFY_NULLDUMMY = (1U << 4),
|
||||
SCRIPT_VERIFY_NULLDUMMY,
|
||||
|
||||
// Using a non-push operator in the scriptSig causes script failure (BIP62 rule 2).
|
||||
SCRIPT_VERIFY_SIGPUSHONLY = (1U << 5),
|
||||
SCRIPT_VERIFY_SIGPUSHONLY,
|
||||
|
||||
// Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
|
||||
// pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
|
||||
// any other push causes the script to fail (BIP62 rule 3).
|
||||
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
|
||||
SCRIPT_VERIFY_MINIMALDATA = (1U << 6),
|
||||
SCRIPT_VERIFY_MINIMALDATA,
|
||||
|
||||
// Discourage use of NOPs reserved for upgrades (NOP1-10)
|
||||
//
|
||||
|
@ -82,7 +84,7 @@ enum : uint32_t {
|
|||
// executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
|
||||
// NOPs that have associated forks to give them new meaning (CLTV, CSV)
|
||||
// are not subject to this rule.
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
|
||||
|
||||
// Require that only a single stack element remains after evaluation. This changes the success criterion from
|
||||
// "At least one stack element must remain, and when interpreted as a boolean, it must be true" to
|
||||
|
@ -91,64 +93,72 @@ enum : uint32_t {
|
|||
// Note: CLEANSTACK should never be used without P2SH or WITNESS.
|
||||
// Note: WITNESS_V0 and TAPSCRIPT script execution have behavior similar to CLEANSTACK as part of their
|
||||
// consensus rules. It is automatic there and does not need this flag.
|
||||
SCRIPT_VERIFY_CLEANSTACK = (1U << 8),
|
||||
SCRIPT_VERIFY_CLEANSTACK,
|
||||
|
||||
// Verify CHECKLOCKTIMEVERIFY
|
||||
//
|
||||
// See BIP65 for details.
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9),
|
||||
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
|
||||
|
||||
// support CHECKSEQUENCEVERIFY opcode
|
||||
//
|
||||
// See BIP112 for details
|
||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10),
|
||||
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
|
||||
|
||||
// Support segregated witness
|
||||
//
|
||||
SCRIPT_VERIFY_WITNESS = (1U << 11),
|
||||
SCRIPT_VERIFY_WITNESS,
|
||||
|
||||
// Making v1-v16 witness program non-standard
|
||||
//
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
|
||||
|
||||
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
|
||||
//
|
||||
// Note: TAPSCRIPT script execution has behavior similar to MINIMALIF as part of its consensus
|
||||
// rules. It is automatic there and does not depend on this flag.
|
||||
SCRIPT_VERIFY_MINIMALIF = (1U << 13),
|
||||
SCRIPT_VERIFY_MINIMALIF,
|
||||
|
||||
// Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed
|
||||
//
|
||||
SCRIPT_VERIFY_NULLFAIL = (1U << 14),
|
||||
SCRIPT_VERIFY_NULLFAIL,
|
||||
|
||||
// Public keys in segregated witness scripts must be compressed
|
||||
//
|
||||
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
|
||||
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE,
|
||||
|
||||
// Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
|
||||
//
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
|
||||
SCRIPT_VERIFY_CONST_SCRIPTCODE,
|
||||
|
||||
// Taproot/Tapscript validation (BIPs 341 & 342)
|
||||
//
|
||||
SCRIPT_VERIFY_TAPROOT = (1U << 17),
|
||||
SCRIPT_VERIFY_TAPROOT,
|
||||
|
||||
// Making unknown Taproot leaf versions non-standard
|
||||
//
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1U << 18),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION,
|
||||
|
||||
// Making unknown OP_SUCCESS non-standard
|
||||
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1U << 19),
|
||||
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS,
|
||||
|
||||
// Making unknown public key versions (in BIP 342 scripts) non-standard
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20),
|
||||
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE,
|
||||
|
||||
// Constants to point to the highest flag in use. Add new flags above this line.
|
||||
//
|
||||
SCRIPT_VERIFY_END_MARKER
|
||||
};
|
||||
using enum script_verify_flag_name;
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||
static constexpr int MAX_SCRIPT_VERIFY_FLAGS_BITS = static_cast<int>(SCRIPT_VERIFY_END_MARKER);
|
||||
|
||||
// assert there is still a spare bit
|
||||
static_assert(0 < MAX_SCRIPT_VERIFY_FLAGS_BITS && MAX_SCRIPT_VERIFY_FLAGS_BITS <= 63);
|
||||
|
||||
static constexpr script_verify_flags::value_type MAX_SCRIPT_VERIFY_FLAGS = ((script_verify_flags::value_type{1} << MAX_SCRIPT_VERIFY_FLAGS_BITS) - 1);
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, script_verify_flags flags, ScriptError* serror);
|
||||
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
|
@ -363,12 +373,16 @@ uint256 ComputeTapbranchHash(std::span<const unsigned char> a, std::span<const u
|
|||
* Requires control block to have valid length (33 + k*32, with k in {0,1,..,128}). */
|
||||
uint256 ComputeTaprootMerkleRoot(std::span<const unsigned char> control, const uint256& tapleaf_hash);
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr);
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, script_verify_flags flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
|
||||
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, script_verify_flags flags);
|
||||
|
||||
int FindAndDelete(CScript& script, const CScript& b);
|
||||
|
||||
extern const std::map<std::string, script_verify_flag_name> g_verify_flag_names;
|
||||
|
||||
std::vector<std::string> GetScriptFlagNames(script_verify_flags flags);
|
||||
|
||||
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
||||
#define BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
enum class script_verify_flag_name : uint8_t;
|
||||
|
||||
class script_verify_flags
|
||||
{
|
||||
public:
|
||||
using value_type = uint64_t;
|
||||
|
||||
consteval script_verify_flags() = default;
|
||||
|
||||
// also allow construction with hard-coded 0 (but not other integers)
|
||||
consteval explicit(false) script_verify_flags(value_type f) : m_value{f} { if (f != 0) throw 0; }
|
||||
|
||||
// implicit construction from a hard-coded SCRIPT_VERIFY_* constant is also okay
|
||||
constexpr explicit(false) script_verify_flags(script_verify_flag_name f) : m_value{value_type{1} << static_cast<uint8_t>(f)} { }
|
||||
|
||||
// rule of 5
|
||||
constexpr script_verify_flags(const script_verify_flags&) = default;
|
||||
constexpr script_verify_flags(script_verify_flags&&) = default;
|
||||
constexpr script_verify_flags& operator=(const script_verify_flags&) = default;
|
||||
constexpr script_verify_flags& operator=(script_verify_flags&&) = default;
|
||||
constexpr ~script_verify_flags() = default;
|
||||
|
||||
// integer conversion needs to be very explicit
|
||||
static constexpr script_verify_flags from_int(value_type f) { script_verify_flags r; r.m_value = f; return r; }
|
||||
constexpr value_type as_int() const { return m_value; }
|
||||
|
||||
// bitwise operations
|
||||
constexpr script_verify_flags operator~() const { return from_int(~m_value); }
|
||||
friend constexpr script_verify_flags operator|(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value | b.m_value); }
|
||||
friend constexpr script_verify_flags operator&(script_verify_flags a, script_verify_flags b) { return from_int(a.m_value & b.m_value); }
|
||||
|
||||
// in-place bitwise operations
|
||||
constexpr script_verify_flags& operator|=(script_verify_flags vf) { m_value |= vf.m_value; return *this; }
|
||||
constexpr script_verify_flags& operator&=(script_verify_flags vf) { m_value &= vf.m_value; return *this; }
|
||||
|
||||
// tests
|
||||
constexpr explicit operator bool() const { return m_value != 0; }
|
||||
constexpr bool operator==(script_verify_flags other) const { return m_value == other.m_value; }
|
||||
|
||||
/** Compare two script_verify_flags. <, >, <=, and >= are auto-generated from this. */
|
||||
friend constexpr std::strong_ordering operator<=>(const script_verify_flags& a, const script_verify_flags& b) noexcept
|
||||
{
|
||||
return a.m_value <=> b.m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type m_value{0}; // default value is SCRIPT_VERIFY_NONE
|
||||
};
|
||||
|
||||
inline constexpr script_verify_flags operator~(script_verify_flag_name f)
|
||||
{
|
||||
return ~script_verify_flags{f};
|
||||
}
|
||||
|
||||
inline constexpr script_verify_flags operator|(script_verify_flag_name f1, script_verify_flag_name f2)
|
||||
{
|
||||
return script_verify_flags{f1} | f2;
|
||||
}
|
||||
|
||||
#endif // BITCOIN_SCRIPT_VERIFY_FLAGS_H
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
|
||||
|
||||
static constexpr unsigned int BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
|
||||
static constexpr script_verify_flags BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
|
||||
|
||||
static bool FetchAndClearCommitmentSection(const std::span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ add_executable(test_bitcoin
|
|||
bloom_tests.cpp
|
||||
bswap_tests.cpp
|
||||
caches_tests.cpp
|
||||
chain_tests.cpp
|
||||
chainstate_write_tests.cpp
|
||||
checkqueue_tests.cpp
|
||||
cluster_linearize_tests.cpp
|
||||
|
|
|
@ -459,10 +459,16 @@ BOOST_AUTO_TEST_CASE(getaddr_unfiltered)
|
|||
addrman->Attempt(addr3, /*fCountFailure=*/true, /*time=*/Now<NodeSeconds>() - 61s);
|
||||
}
|
||||
|
||||
// Set time more than 10 minutes in the future (flying DeLorean), so this
|
||||
// addr should be isTerrible = true
|
||||
CAddress addr4 = CAddress(ResolveService("250.252.2.4", 9997), NODE_NONE);
|
||||
addr4.nTime = Now<NodeSeconds>() + 11min;
|
||||
BOOST_CHECK(addrman->Add({addr4}, source));
|
||||
|
||||
// GetAddr filtered by quality (i.e. not IsTerrible) should only return addr1
|
||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 1U);
|
||||
// Unfiltered GetAddr should return all addrs
|
||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 3U);
|
||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 4U);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <chain.h>
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(chain_tests, BasicTestingSetup)
|
||||
|
||||
namespace {
|
||||
|
||||
const CBlockIndex* NaiveGetAncestor(const CBlockIndex* a, int height)
|
||||
{
|
||||
while (a->nHeight > height) {
|
||||
a = a->pprev;
|
||||
}
|
||||
BOOST_REQUIRE_EQUAL(a->nHeight, height);
|
||||
return a;
|
||||
}
|
||||
|
||||
const CBlockIndex* NaiveLastCommonAncestor(const CBlockIndex* a, const CBlockIndex* b)
|
||||
{
|
||||
while (a->nHeight > b->nHeight) {
|
||||
a = a->pprev;
|
||||
}
|
||||
while (b->nHeight > a->nHeight) {
|
||||
b = b->pprev;
|
||||
}
|
||||
while (a != b) {
|
||||
BOOST_REQUIRE_EQUAL(a->nHeight, b->nHeight);
|
||||
a = a->pprev;
|
||||
b = b->pprev;
|
||||
}
|
||||
BOOST_REQUIRE_EQUAL(a, b);
|
||||
return a;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(chain_test)
|
||||
{
|
||||
FastRandomContext ctx;
|
||||
std::vector<std::unique_ptr<CBlockIndex>> block_index;
|
||||
// Run 10 iterations of the whole test.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
block_index.clear();
|
||||
// Create genesis block.
|
||||
auto genesis = std::make_unique<CBlockIndex>();
|
||||
genesis->nHeight = 0;
|
||||
block_index.push_back(std::move(genesis));
|
||||
// Create 10000 more blocks.
|
||||
for (int b = 0; b < 10000; ++b) {
|
||||
auto new_index = std::make_unique<CBlockIndex>();
|
||||
// 95% of blocks build on top of the last block; the others fork off randomly.
|
||||
if (ctx.randrange(20) != 0) {
|
||||
new_index->pprev = block_index.back().get();
|
||||
} else {
|
||||
new_index->pprev = block_index[ctx.randrange(block_index.size())].get();
|
||||
}
|
||||
new_index->nHeight = new_index->pprev->nHeight + 1;
|
||||
new_index->BuildSkip();
|
||||
block_index.push_back(std::move(new_index));
|
||||
}
|
||||
// Run 10000 random GetAncestor queries.
|
||||
for (int q = 0; q < 10000; ++q) {
|
||||
const CBlockIndex* block = block_index[ctx.randrange(block_index.size())].get();
|
||||
unsigned height = ctx.randrange<unsigned>(block->nHeight + 1);
|
||||
const CBlockIndex* result = block->GetAncestor(height);
|
||||
BOOST_CHECK(result == NaiveGetAncestor(block, height));
|
||||
}
|
||||
// Run 10000 random LastCommonAncestor queries.
|
||||
for (int q = 0; q < 10000; ++q) {
|
||||
const CBlockIndex* block1 = block_index[ctx.randrange(block_index.size())].get();
|
||||
const CBlockIndex* block2 = block_index[ctx.randrange(block_index.size())].get();
|
||||
const CBlockIndex* result = LastCommonAncestor(block1, block2);
|
||||
BOOST_CHECK(result == NaiveLastCommonAncestor(block1, block2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -62,7 +62,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0};
|
||||
|
||||
connman.Handshake(
|
||||
/*node=*/dummyNode1,
|
||||
|
@ -128,7 +129,8 @@ void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager&
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
connType,
|
||||
/*inbound_onion=*/false});
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0});
|
||||
CNode &node = *vNodes.back();
|
||||
node.SetCommonVersion(PROTOCOL_VERSION);
|
||||
|
||||
|
@ -327,7 +329,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
|
||||
nodes[0]->fSuccessfullyConnected = true;
|
||||
|
@ -347,7 +350,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
|
||||
nodes[1]->fSuccessfullyConnected = true;
|
||||
|
@ -377,7 +381,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/2};
|
||||
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
|
||||
nodes[2]->fSuccessfullyConnected = true;
|
||||
|
@ -419,7 +424,8 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
|
||||
dummyNode.fSuccessfullyConnected = true;
|
||||
|
|
|
@ -284,7 +284,7 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend
|
|||
// consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed.
|
||||
return;
|
||||
}
|
||||
const auto flags{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
||||
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||
if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) {
|
||||
// Avoid:
|
||||
// script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed.
|
||||
|
|
|
@ -177,7 +177,7 @@ FUZZ_TARGET(connman, .init = initialize_connman)
|
|||
/*addrConnect=*/random_address,
|
||||
/*fCountFailure=*/fuzzed_data_provider.ConsumeBool(),
|
||||
/*grant_outbound=*/{},
|
||||
/*strDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
|
||||
/*pszDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
|
||||
/*conn_type=*/conn_type,
|
||||
/*use_v2transport=*/fuzzed_data_provider.ConsumeBool());
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
FUZZ_TARGET(eval_script)
|
||||
{
|
||||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
||||
const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
||||
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||
const std::vector<uint8_t> script_bytes = [&] {
|
||||
if (fuzzed_data_provider.remaining_bytes() != 0) {
|
||||
return fuzzed_data_provider.ConsumeRemainingBytes<uint8_t>();
|
||||
|
|
|
@ -75,7 +75,7 @@ auto& FuzzTargets()
|
|||
|
||||
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
|
||||
{
|
||||
const auto [it, ins]{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped after Apple-Clang-16 ? */ {std::move(target), std::move(opts)})};
|
||||
const auto [it, ins]{FuzzTargets().try_emplace(name, std::move(target), std::move(opts))};
|
||||
Assert(ins);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ void HeadersSyncSetup::ResetAndInitialize()
|
|||
|
||||
for (auto conn_type : conn_types) {
|
||||
CAddress addr{};
|
||||
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false));
|
||||
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false, 0));
|
||||
CNode& p2p_node = *m_connections.back();
|
||||
|
||||
connman.Handshake(
|
||||
|
|
|
@ -325,7 +325,7 @@ FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)
|
|||
return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{}));
|
||||
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
|
||||
/*bypass_limits=*/fuzzed_data_provider.ConsumeBool(), /*test_accept=*/!single_submit));
|
||||
/*bypass_limits=*/false, /*test_accept=*/!single_submit));
|
||||
|
||||
if (!single_submit && result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
|
||||
// We don't know anything about the validity since transactions were randomly generated, so
|
||||
|
|
|
@ -118,8 +118,8 @@ FUZZ_TARGET(script, .init = initialize_script)
|
|||
(void)FindAndDelete(script_mut, *other_script);
|
||||
}
|
||||
const std::vector<std::string> random_string_vector = ConsumeRandomLengthStringVector(fuzzed_data_provider);
|
||||
const uint32_t u32{fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
||||
const uint32_t flags{u32 | SCRIPT_VERIFY_P2SH};
|
||||
const auto flags_rand{fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>()};
|
||||
const auto flags = script_verify_flags::from_int(flags_rand) | SCRIPT_VERIFY_P2SH;
|
||||
{
|
||||
CScriptWitness wit;
|
||||
for (const auto& s : random_string_vector) {
|
||||
|
|
|
@ -90,22 +90,22 @@ CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
|
|||
return scriptwitness;
|
||||
}
|
||||
|
||||
const std::map<std::string, unsigned int> FLAG_NAMES = {
|
||||
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
|
||||
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
|
||||
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
|
||||
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
||||
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
||||
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
|
||||
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
|
||||
const std::map<std::string, script_verify_flag_name> FLAG_NAMES = {
|
||||
{std::string("P2SH"), SCRIPT_VERIFY_P2SH},
|
||||
{std::string("DERSIG"), SCRIPT_VERIFY_DERSIG},
|
||||
{std::string("NULLDUMMY"), SCRIPT_VERIFY_NULLDUMMY},
|
||||
{std::string("CHECKLOCKTIMEVERIFY"), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
||||
{std::string("CHECKSEQUENCEVERIFY"), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
||||
{std::string("WITNESS"), SCRIPT_VERIFY_WITNESS},
|
||||
{std::string("TAPROOT"), SCRIPT_VERIFY_TAPROOT},
|
||||
};
|
||||
|
||||
std::vector<unsigned int> AllFlags()
|
||||
std::vector<script_verify_flags> AllFlags()
|
||||
{
|
||||
std::vector<unsigned int> ret;
|
||||
std::vector<script_verify_flags> ret;
|
||||
|
||||
for (unsigned int i = 0; i < 128; ++i) {
|
||||
unsigned int flag = 0;
|
||||
script_verify_flags flag = 0;
|
||||
if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
|
||||
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
|
||||
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
|
@ -125,13 +125,13 @@ std::vector<unsigned int> AllFlags()
|
|||
return ret;
|
||||
}
|
||||
|
||||
const std::vector<unsigned int> ALL_FLAGS = AllFlags();
|
||||
const std::vector<script_verify_flags> ALL_FLAGS = AllFlags();
|
||||
|
||||
unsigned int ParseScriptFlags(const std::string& str)
|
||||
script_verify_flags ParseScriptFlags(const std::string& str)
|
||||
{
|
||||
if (str.empty()) return 0;
|
||||
|
||||
unsigned int flags = 0;
|
||||
script_verify_flags flags = 0;
|
||||
std::vector<std::string> words = SplitString(str, ',');
|
||||
|
||||
for (const std::string& word : words) {
|
||||
|
@ -153,7 +153,7 @@ void Test(const std::string& str)
|
|||
if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
|
||||
size_t idx = test["index"].getInt<int64_t>();
|
||||
if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
|
||||
unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
|
||||
script_verify_flags test_flags = ParseScriptFlags(test["flags"].get_str());
|
||||
bool final = test.exists("final") && test["final"].get_bool();
|
||||
|
||||
if (test.exists("success")) {
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
static DataStream& operator>>(DataStream& ds, script_verify_flags& f)
|
||||
{
|
||||
script_verify_flags::value_type n{0};
|
||||
ds >> n;
|
||||
f = script_verify_flags::from_int(n);
|
||||
assert(n == f.as_int());
|
||||
return ds;
|
||||
}
|
||||
|
||||
FUZZ_TARGET(script_flags)
|
||||
{
|
||||
if (buffer.size() > 100'000) return;
|
||||
|
@ -22,12 +31,14 @@ FUZZ_TARGET(script_flags)
|
|||
try {
|
||||
const CTransaction tx(deserialize, TX_WITH_WITNESS, ds);
|
||||
|
||||
unsigned int verify_flags;
|
||||
script_verify_flags verify_flags;
|
||||
ds >> verify_flags;
|
||||
|
||||
assert(verify_flags == script_verify_flags::from_int(verify_flags.as_int()));
|
||||
|
||||
if (!IsValidFlagCombination(verify_flags)) return;
|
||||
|
||||
unsigned int fuzzed_flags;
|
||||
script_verify_flags fuzzed_flags;
|
||||
ds >> fuzzed_flags;
|
||||
|
||||
std::vector<CTxOut> spent_outputs;
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
FUZZ_TARGET(signature_checker)
|
||||
{
|
||||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
||||
const unsigned int flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
||||
const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>());
|
||||
const SigVersion sig_version = fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0});
|
||||
const auto script_1{ConsumeScript(fuzzed_data_provider)};
|
||||
const auto script_2{ConsumeScript(fuzzed_data_provider)};
|
||||
|
|
|
@ -296,7 +296,6 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
|
|||
std::set<CTransactionRef> added;
|
||||
auto txr = std::make_shared<TransactionsDelta>(removed, added);
|
||||
node.validation_signals->RegisterSharedValidationInterface(txr);
|
||||
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
|
||||
|
||||
// Make sure ProcessNewPackage on one transaction works.
|
||||
// The result is not guaranteed to be the same as what is returned by ATMP.
|
||||
|
@ -311,7 +310,7 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
|
|||
it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID);
|
||||
}
|
||||
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), /*bypass_limits=*/false, /*test_accept=*/false));
|
||||
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||
node.validation_signals->SyncWithValidationInterfaceQueue();
|
||||
node.validation_signals->UnregisterSharedValidationInterface(txr);
|
||||
|
@ -394,6 +393,9 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
|
|||
|
||||
chainstate.SetMempool(&tx_pool);
|
||||
|
||||
// If we ever bypass limits, do not do TRUC invariants checks
|
||||
bool ever_bypassed_limits{false};
|
||||
|
||||
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
|
||||
{
|
||||
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
|
||||
|
@ -412,13 +414,17 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
|
|||
tx_pool.PrioritiseTransaction(txid, delta);
|
||||
}
|
||||
|
||||
const bool bypass_limits{fuzzed_data_provider.ConsumeBool()};
|
||||
ever_bypassed_limits |= bypass_limits;
|
||||
|
||||
const auto tx = MakeTransactionRef(mut_tx);
|
||||
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
|
||||
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||
if (accepted) {
|
||||
txids.push_back(tx->GetHash());
|
||||
CheckMempoolTRUCInvariants(tx_pool);
|
||||
if (!ever_bypassed_limits) {
|
||||
CheckMempoolTRUCInvariants(tx_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
Finish(fuzzed_data_provider, tx_pool, chainstate);
|
||||
|
|
|
@ -275,6 +275,8 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
|
|||
const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
|
||||
const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES);
|
||||
const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
|
||||
const uint64_t network_id = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
|
||||
if constexpr (ReturnUniquePtr) {
|
||||
return std::make_unique<CNode>(node_id,
|
||||
|
@ -286,6 +288,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
|
|||
addr_name,
|
||||
conn_type,
|
||||
inbound_onion,
|
||||
network_id,
|
||||
CNodeOptions{ .permission_flags = permission_flags });
|
||||
} else {
|
||||
return CNode{node_id,
|
||||
|
@ -297,6 +300,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
|
|||
addr_name,
|
||||
conn_type,
|
||||
inbound_onion,
|
||||
network_id,
|
||||
CNodeOptions{ .permission_flags = permission_flags }};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const
|
|||
|
||||
BOOST_AUTO_TEST_CASE(multisig_verify)
|
||||
{
|
||||
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
script_verify_flags flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
|
||||
ScriptError err;
|
||||
CKey key[4];
|
||||
|
|
|
@ -72,7 +72,8 @@ void AddPeer(NodeId& id, std::vector<CNode*>& nodes, PeerManager& peerman, Connm
|
|||
CAddress{},
|
||||
/*addrNameIn=*/"",
|
||||
conn_type,
|
||||
/*inbound_onion=*/inbound_onion});
|
||||
/*inbound_onion=*/inbound_onion,
|
||||
/*network_key=*/0});
|
||||
CNode& node = *nodes.back();
|
||||
node.SetCommonVersion(PROTOCOL_VERSION);
|
||||
|
||||
|
@ -151,15 +152,8 @@ BOOST_FIXTURE_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection,
|
|||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected");
|
||||
for (auto node : connman->TestNodes()) {
|
||||
BOOST_CHECK(connman->AlreadyConnectedPublic(node->addr));
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("\nCheck that peers with the same addresses as connected peers but different ports are detected as connected.");
|
||||
for (auto node : connman->TestNodes()) {
|
||||
uint16_t changed_port = node->addr.GetPort() + 1;
|
||||
CService address_with_changed_port{node->addr, changed_port};
|
||||
BOOST_CHECK(connman->AlreadyConnectedPublic(CAddress{address_with_changed_port, NODE_NONE}));
|
||||
for (const auto& node : connman->TestNodes()) {
|
||||
BOOST_CHECK(connman->AlreadyConnectedToAddressPublic(node->addr));
|
||||
}
|
||||
|
||||
// Clean up
|
||||
|
|
|
@ -67,7 +67,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0);
|
||||
BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
|
||||
BOOST_CHECK(pnode1->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
|
||||
|
@ -85,7 +86,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1);
|
||||
BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
|
||||
BOOST_CHECK(pnode2->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
|
||||
|
@ -103,7 +105,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/2);
|
||||
BOOST_CHECK(pnode3->IsFullOutboundConn() == true);
|
||||
BOOST_CHECK(pnode3->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode3->IsBlockOnlyConn() == false);
|
||||
|
@ -121,7 +124,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/true);
|
||||
/*inbound_onion=*/true,
|
||||
/*network_key=*/3);
|
||||
BOOST_CHECK(pnode4->IsFullOutboundConn() == false);
|
||||
BOOST_CHECK(pnode4->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode4->IsBlockOnlyConn() == false);
|
||||
|
@ -613,7 +617,8 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
|
|||
CAddress{},
|
||||
/*pszDest=*/std::string{},
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0);
|
||||
pnode->fSuccessfullyConnected.store(true);
|
||||
|
||||
// the peer claims to be reaching us via IPv6
|
||||
|
@ -667,7 +672,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
|
|||
/*addrBindIn=*/CService{},
|
||||
/*addrNameIn=*/std::string{},
|
||||
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0};
|
||||
peer_out.fSuccessfullyConnected = true;
|
||||
peer_out.SetAddrLocal(peer_us);
|
||||
|
||||
|
@ -688,7 +694,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
|
|||
/*addrBindIn=*/CService{},
|
||||
/*addrNameIn=*/std::string{},
|
||||
/*conn_type_in=*/ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
peer_in.fSuccessfullyConnected = true;
|
||||
peer_in.SetAddrLocal(peer_us);
|
||||
|
||||
|
@ -825,7 +832,8 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
|
|||
/*addrBindIn=*/CService{},
|
||||
/*addrNameIn=*/std::string{},
|
||||
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/2};
|
||||
|
||||
const uint64_t services{NODE_NETWORK | NODE_WITNESS};
|
||||
const int64_t time{0};
|
||||
|
@ -900,7 +908,8 @@ BOOST_AUTO_TEST_CASE(advertise_local_address)
|
|||
CAddress{},
|
||||
/*pszDest=*/std::string{},
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0);
|
||||
};
|
||||
g_reachable_nets.Add(NET_CJDNS);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include <univalue.h>
|
||||
|
||||
unsigned int ParseScriptFlags(std::string strFlags);
|
||||
script_verify_flags ParseScriptFlags(std::string strFlags);
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(script_assets_tests)
|
||||
|
||||
|
@ -71,12 +71,12 @@ static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
|
|||
return scriptwitness;
|
||||
}
|
||||
|
||||
static std::vector<unsigned int> AllConsensusFlags()
|
||||
static std::vector<script_verify_flags> AllConsensusFlags()
|
||||
{
|
||||
std::vector<unsigned int> ret;
|
||||
std::vector<script_verify_flags> ret;
|
||||
|
||||
for (unsigned int i = 0; i < 128; ++i) {
|
||||
unsigned int flag = 0;
|
||||
script_verify_flags flag = 0;
|
||||
if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
|
||||
if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
|
||||
if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
|
||||
|
@ -97,7 +97,7 @@ static std::vector<unsigned int> AllConsensusFlags()
|
|||
}
|
||||
|
||||
/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
|
||||
static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
|
||||
static const std::vector<script_verify_flags> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
|
||||
|
||||
static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
|
|||
const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
|
||||
BOOST_CHECK(prevouts.size() == mtx.vin.size());
|
||||
size_t idx = test["index"].getInt<int64_t>();
|
||||
uint32_t test_flags{ParseScriptFlags(test["flags"].get_str())};
|
||||
script_verify_flags test_flags{ParseScriptFlags(test["flags"].get_str())};
|
||||
bool fin = test.exists("final") && test["final"].get_bool();
|
||||
|
||||
if (test.exists("success")) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <core_io.h>
|
||||
#include <key.h>
|
||||
#include <rpc/util.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <script/script.h>
|
||||
#include <script/script_error.h>
|
||||
#include <script/sigcache.h>
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include <test/util/transaction_utils.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/string.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
|
@ -38,10 +40,9 @@
|
|||
|
||||
using namespace util::hex_literals;
|
||||
|
||||
static const unsigned int gFlags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
static const script_verify_flags gFlags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
|
||||
|
||||
unsigned int ParseScriptFlags(std::string strFlags);
|
||||
std::string FormatScriptFlags(unsigned int flags);
|
||||
script_verify_flags ParseScriptFlags(std::string strFlags);
|
||||
|
||||
struct ScriptErrorDesc
|
||||
{
|
||||
|
@ -95,6 +96,11 @@ static ScriptErrorDesc script_errors[]={
|
|||
{SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"},
|
||||
};
|
||||
|
||||
static std::string FormatScriptFlags(script_verify_flags flags)
|
||||
{
|
||||
return util::Join(GetScriptFlagNames(flags), ",");
|
||||
}
|
||||
|
||||
static std::string FormatScriptError(ScriptError_t err)
|
||||
{
|
||||
for (const auto& se : script_errors)
|
||||
|
@ -114,7 +120,7 @@ static ScriptError_t ParseScriptError(const std::string& name)
|
|||
}
|
||||
|
||||
struct ScriptTest : BasicTestingSetup {
|
||||
void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, uint32_t flags, const std::string& message, int scriptError, CAmount nValue = 0)
|
||||
void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, script_verify_flags flags, const std::string& message, int scriptError, CAmount nValue = 0)
|
||||
{
|
||||
bool expect = (scriptError == SCRIPT_ERR_OK);
|
||||
if (flags & SCRIPT_VERIFY_CLEANSTACK) {
|
||||
|
@ -128,13 +134,13 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
|
|||
BOOST_CHECK_MESSAGE(err == scriptError, FormatScriptError(err) + " where " + FormatScriptError((ScriptError_t)scriptError) + " expected: " + message);
|
||||
|
||||
// Verify that removing flags from a passing test or adding flags to a failing test does not change the result.
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint32_t extra_flags(m_rng.randbits(16));
|
||||
uint32_t combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)};
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
script_verify_flags extra_flags = script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS));
|
||||
script_verify_flags combined_flags{expect ? (flags & ~extra_flags) : (flags | extra_flags)};
|
||||
// Weed out some invalid flag combinations.
|
||||
if (combined_flags & SCRIPT_VERIFY_CLEANSTACK && ~combined_flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) continue;
|
||||
if (combined_flags & SCRIPT_VERIFY_WITNESS && ~combined_flags & SCRIPT_VERIFY_P2SH) continue;
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags));
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, combined_flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err) == expect, message + strprintf(" (with flags %x)", combined_flags.as_int()));
|
||||
}
|
||||
}
|
||||
}; // struct ScriptTest
|
||||
|
@ -226,7 +232,7 @@ private:
|
|||
bool havePush{false};
|
||||
std::vector<unsigned char> push;
|
||||
std::string comment;
|
||||
uint32_t flags;
|
||||
script_verify_flags flags;
|
||||
int scriptError{SCRIPT_ERR_OK};
|
||||
CAmount nValue;
|
||||
|
||||
|
@ -246,7 +252,7 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
TestBuilder(const CScript& script_, const std::string& comment_, uint32_t flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), comment(comment_), flags(flags_), nValue(nValue_)
|
||||
TestBuilder(const CScript& script_, const std::string& comment_, script_verify_flags flags_, bool P2SH = false, WitnessMode wm = WitnessMode::NONE, int witnessversion = 0, CAmount nValue_ = 0) : script(script_), comment(comment_), flags(flags_), nValue(nValue_)
|
||||
{
|
||||
CScript scriptPubKey = script;
|
||||
if (wm == WitnessMode::PKH) {
|
||||
|
@ -963,7 +969,7 @@ BOOST_AUTO_TEST_CASE(script_json_test)
|
|||
} else {
|
||||
scriptPubKey = ParseScript(scriptPubKeyString);
|
||||
}
|
||||
unsigned int scriptflags = ParseScriptFlags(test[pos++].get_str());
|
||||
script_verify_flags scriptflags = ParseScriptFlags(test[pos++].get_str());
|
||||
int scriptError = ParseScriptError(test[pos++].get_str());
|
||||
|
||||
DoTest(scriptPubKey, scriptSig, witness, scriptflags, strTest, scriptError, nValue);
|
||||
|
@ -1706,4 +1712,15 @@ BOOST_AUTO_TEST_CASE(compute_tapleaf)
|
|||
BOOST_CHECK_EQUAL(ComputeTapleafHash(0xc2, std::span(script)), tlc2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(formatscriptflags)
|
||||
{
|
||||
// quick check that FormatScriptFlags reports any unknown/unexpected bits
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH), "P2SH");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_TAPROOT), "P2SH,TAPROOT");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_P2SH | script_verify_flags::from_int(1u<<31)), "P2SH,0x80000000");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_TAPROOT | script_verify_flags::from_int(1u<<27)), "TAPROOT,0x08000000");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(SCRIPT_VERIFY_TAPROOT | script_verify_flags::from_int((1u<<28) | (1ull<<58))), "TAPROOT,0x400000010000000");
|
||||
BOOST_CHECK_EQUAL(FormatScriptFlags(script_verify_flags::from_int(1u<<26)), "0x04000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
|
|||
* Verifies script execution of the zeroth scriptPubKey of tx output and
|
||||
* zeroth scriptSig and witness of tx input.
|
||||
*/
|
||||
static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, uint32_t flags)
|
||||
static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, script_verify_flags flags)
|
||||
{
|
||||
ScriptError error;
|
||||
CTransaction inputi(input);
|
||||
|
@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
|
|||
CKey key = GenerateRandomKey();
|
||||
CPubKey pubkey = key.GetPubKey();
|
||||
// Default flags
|
||||
const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
|
||||
// Multisig script (legacy counting)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <policy/settings.h>
|
||||
#include <primitives/transaction_identifier.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <script/script.h>
|
||||
#include <script/script_error.h>
|
||||
#include <script/sigcache.h>
|
||||
|
@ -49,41 +50,21 @@ typedef std::vector<unsigned char> valtype;
|
|||
static CFeeRate g_dust{DUST_RELAY_TX_FEE};
|
||||
static bool g_bare_multi{DEFAULT_PERMIT_BAREMULTISIG};
|
||||
|
||||
static std::map<std::string, unsigned int> mapFlagNames = {
|
||||
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
|
||||
{std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
|
||||
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
|
||||
{std::string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S},
|
||||
{std::string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY},
|
||||
{std::string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA},
|
||||
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS},
|
||||
{std::string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK},
|
||||
{std::string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF},
|
||||
{std::string("NULLFAIL"), (unsigned int)SCRIPT_VERIFY_NULLFAIL},
|
||||
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
||||
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
||||
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
|
||||
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
|
||||
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
|
||||
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE},
|
||||
{std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
|
||||
{std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
|
||||
};
|
||||
static const std::map<std::string, script_verify_flag_name>& mapFlagNames = g_verify_flag_names;
|
||||
|
||||
unsigned int ParseScriptFlags(std::string strFlags)
|
||||
script_verify_flags ParseScriptFlags(std::string strFlags)
|
||||
{
|
||||
unsigned int flags = SCRIPT_VERIFY_NONE;
|
||||
script_verify_flags flags = SCRIPT_VERIFY_NONE;
|
||||
if (strFlags.empty() || strFlags == "NONE") return flags;
|
||||
|
||||
std::vector<std::string> words = SplitString(strFlags, ',');
|
||||
for (const std::string& word : words)
|
||||
{
|
||||
if (!mapFlagNames.count(word))
|
||||
if (!mapFlagNames.count(word)) {
|
||||
BOOST_ERROR("Bad test: unknown verification flag '" << word << "'");
|
||||
flags |= mapFlagNames[word];
|
||||
continue;
|
||||
}
|
||||
flags |= mapFlagNames.at(word);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
@ -91,34 +72,18 @@ unsigned int ParseScriptFlags(std::string strFlags)
|
|||
// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames.
|
||||
bool CheckMapFlagNames()
|
||||
{
|
||||
unsigned int standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
|
||||
script_verify_flags standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
|
||||
for (const auto& pair : mapFlagNames) {
|
||||
standard_flags_missing &= ~(pair.second);
|
||||
}
|
||||
return standard_flags_missing == 0;
|
||||
}
|
||||
|
||||
std::string FormatScriptFlags(unsigned int flags)
|
||||
{
|
||||
if (flags == SCRIPT_VERIFY_NONE) {
|
||||
return "";
|
||||
}
|
||||
std::string ret;
|
||||
std::map<std::string, unsigned int>::const_iterator it = mapFlagNames.begin();
|
||||
while (it != mapFlagNames.end()) {
|
||||
if (flags & it->second) {
|
||||
ret += it->first + ",";
|
||||
}
|
||||
it++;
|
||||
}
|
||||
return ret.substr(0, ret.size() - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the input scripts of a transaction are valid/invalid as expected.
|
||||
*/
|
||||
bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys,
|
||||
const std::map<COutPoint, int64_t>& map_prevout_values, unsigned int flags,
|
||||
const std::map<COutPoint, int64_t>& map_prevout_values, script_verify_flags flags,
|
||||
const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid)
|
||||
{
|
||||
bool tx_valid = true;
|
||||
|
@ -152,18 +117,18 @@ bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>&
|
|||
* CLEANSTACK must be used WITNESS and P2SH
|
||||
*/
|
||||
|
||||
unsigned int TrimFlags(unsigned int flags)
|
||||
script_verify_flags TrimFlags(script_verify_flags flags)
|
||||
{
|
||||
// WITNESS requires P2SH
|
||||
if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~(unsigned int)SCRIPT_VERIFY_WITNESS;
|
||||
if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~SCRIPT_VERIFY_WITNESS;
|
||||
|
||||
// CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH)
|
||||
if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int)SCRIPT_VERIFY_CLEANSTACK;
|
||||
if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~SCRIPT_VERIFY_CLEANSTACK;
|
||||
Assert(IsValidFlagCombination(flags));
|
||||
return flags;
|
||||
}
|
||||
|
||||
unsigned int FillFlags(unsigned int flags)
|
||||
script_verify_flags FillFlags(script_verify_flags flags)
|
||||
{
|
||||
// CLEANSTACK implies WITNESS
|
||||
if (flags & SCRIPT_VERIFY_CLEANSTACK) flags |= SCRIPT_VERIFY_WITNESS;
|
||||
|
@ -178,11 +143,11 @@ unsigned int FillFlags(unsigned int flags)
|
|||
// that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are
|
||||
// 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}.
|
||||
// Assumes that mapFlagNames contains all script verify flags.
|
||||
std::set<unsigned int> ExcludeIndividualFlags(unsigned int flags)
|
||||
std::set<script_verify_flags> ExcludeIndividualFlags(script_verify_flags flags)
|
||||
{
|
||||
std::set<unsigned int> flags_combos;
|
||||
std::set<script_verify_flags> flags_combos;
|
||||
for (const auto& pair : mapFlagNames) {
|
||||
const unsigned int flags_excluding_one = TrimFlags(flags & ~(pair.second));
|
||||
script_verify_flags flags_excluding_one = TrimFlags(flags & ~(pair.second));
|
||||
if (flags != flags_excluding_one) {
|
||||
flags_combos.insert(flags_excluding_one);
|
||||
}
|
||||
|
@ -247,7 +212,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||
BOOST_CHECK(state.IsValid());
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
script_verify_flags verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
|
||||
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
|
||||
if (~verify_flags != FillFlags(~verify_flags)) {
|
||||
|
@ -260,14 +225,14 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||
// Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction
|
||||
for (const auto& [name, flag] : mapFlagNames) {
|
||||
// Removing individual flags
|
||||
unsigned int flags = TrimFlags(~(verify_flags | flag));
|
||||
script_verify_flags flags = TrimFlags(~(verify_flags | flag));
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) {
|
||||
BOOST_ERROR("Tx unexpectedly failed with flag " << name << " unset: " << strTest);
|
||||
}
|
||||
// Removing random combinations of flags
|
||||
flags = TrimFlags(~(verify_flags | (unsigned int)m_rng.randbits(mapFlagNames.size())));
|
||||
flags = TrimFlags(~(verify_flags | script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS))));
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/true)) {
|
||||
BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags) << ": " << strTest);
|
||||
BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags.as_int()) << ": " << strTest);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,7 +302,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||
}
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
script_verify_flags verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
|
||||
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
|
||||
if (verify_flags != FillFlags(verify_flags)) {
|
||||
|
@ -350,13 +315,13 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||
|
||||
// Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction
|
||||
for (const auto& [name, flag] : mapFlagNames) {
|
||||
unsigned int flags = FillFlags(verify_flags | flag);
|
||||
script_verify_flags flags = FillFlags(verify_flags | flag);
|
||||
// Adding individual flags
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) {
|
||||
BOOST_ERROR("Tx unexpectedly passed with flag " << name << " set: " << strTest);
|
||||
}
|
||||
// Adding random combinations of flags
|
||||
flags = FillFlags(verify_flags | (unsigned int)m_rng.randbits(mapFlagNames.size()));
|
||||
flags = FillFlags(verify_flags | script_verify_flags::from_int(m_rng.randbits(MAX_SCRIPT_VERIFY_FLAGS_BITS)));
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /*expect_valid=*/false)) {
|
||||
BOOST_ERROR("Tx unexpectedly passed with random flags " << name << ": " << strTest);
|
||||
}
|
||||
|
@ -488,7 +453,7 @@ static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const
|
|||
assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack);
|
||||
}
|
||||
|
||||
static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, uint32_t flags, bool success)
|
||||
static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, script_verify_flags flags, bool success)
|
||||
{
|
||||
ScriptError error;
|
||||
CTransaction inputi(input);
|
||||
|
|
|
@ -21,7 +21,7 @@ struct Dersig100Setup : public TestChain100Setup {
|
|||
};
|
||||
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
@ -120,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
|
|||
// should fail.
|
||||
// Capture this interaction with the upgraded_nop argument: set it when evaluating
|
||||
// any script flag that is implemented as an upgraded NOP code.
|
||||
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
static void ValidateCheckInputsForAllFlags(const CTransaction &tx, script_verify_flags failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
PrecomputedTransactionData txdata;
|
||||
|
||||
|
@ -130,7 +130,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
|
|||
TxValidationState state;
|
||||
|
||||
// Randomly selects flag combinations
|
||||
uint32_t test_flags = (uint32_t) insecure_rand.randrange((SCRIPT_VERIFY_END_MARKER - 1) << 1);
|
||||
script_verify_flags test_flags = script_verify_flags::from_int(insecure_rand.randrange(MAX_SCRIPT_VERIFY_FLAGS));
|
||||
|
||||
// Filter out incompatible flag choices
|
||||
if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {
|
||||
|
|
|
@ -116,7 +116,7 @@ bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) co
|
|||
|
||||
CNode* ConnmanTestMsg::ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
|
||||
{
|
||||
CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true);
|
||||
CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true, /*proxy_override=*/std::nullopt);
|
||||
if (!node) return nullptr;
|
||||
node->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerman.InitializeNode(*node, ServiceFlags(NODE_NETWORK | NODE_WITNESS));
|
||||
|
|
|
@ -107,7 +107,7 @@ struct ConnmanTestMsg : public CConnman {
|
|||
bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) const;
|
||||
void FlushSendBuffer(CNode& node) const;
|
||||
|
||||
bool AlreadyConnectedPublic(const CAddress& addr) { return AlreadyConnectedToAddress(addr); };
|
||||
bool AlreadyConnectedToAddressPublic(const CNetAddr& addr) { return AlreadyConnectedToAddress(addr); };
|
||||
|
||||
CNode* ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <script/interpreter.h>
|
||||
#include <test/util/script.h>
|
||||
|
||||
bool IsValidFlagCombination(unsigned flags)
|
||||
bool IsValidFlagCombination(script_verify_flags flags)
|
||||
{
|
||||
if (flags & SCRIPT_VERIFY_CLEANSTACK && ~flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) return false;
|
||||
if (flags & SCRIPT_VERIFY_WITNESS && ~flags & SCRIPT_VERIFY_P2SH) return false;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <crypto/sha256.h>
|
||||
#include <script/script.h>
|
||||
#include <script/verify_flags.h>
|
||||
|
||||
static const std::vector<uint8_t> WITNESS_STACK_ELEM_OP_TRUE{uint8_t{OP_TRUE}};
|
||||
static const CScript P2WSH_OP_TRUE{
|
||||
|
@ -31,6 +32,6 @@ static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TRUE_STACK{{static_ca
|
|||
static const std::vector<std::vector<uint8_t>> P2WSH_EMPTY_TWO_STACK{{static_cast<uint8_t>(OP_2)}, {}};
|
||||
|
||||
/** Flags that are not forbidden by an assert in script validation */
|
||||
bool IsValidFlagCombination(unsigned flags);
|
||||
bool IsValidFlagCombination(script_verify_flags flags);
|
||||
|
||||
#endif // BITCOIN_TEST_UTIL_SCRIPT_H
|
||||
|
|
|
@ -137,7 +137,7 @@ const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locato
|
|||
}
|
||||
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks = nullptr)
|
||||
|
@ -260,9 +260,6 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
|
|||
return EvaluateSequenceLocks(index, {lock_points.height, lock_points.time});
|
||||
}
|
||||
|
||||
// Returns the script flags which should be checked for a given block
|
||||
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
|
||||
|
||||
static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
|
||||
{
|
||||
|
@ -396,7 +393,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
|
|||
* */
|
||||
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& view, const CTxMemPool& pool,
|
||||
unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
|
||||
script_verify_flags flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip,
|
||||
ValidationCache& validation_cache)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
|
||||
{
|
||||
|
@ -1042,26 +1039,28 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||
// Even though just checking direct mempool parents for inheritance would be sufficient, we
|
||||
// check using the full ancestor set here because it's more convenient to use what we have
|
||||
// already calculated.
|
||||
if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
|
||||
// Single transaction contexts only.
|
||||
if (args.m_allow_sibling_eviction && err->second != nullptr) {
|
||||
// We should only be considering where replacement is considered valid as well.
|
||||
Assume(args.m_allow_replacement);
|
||||
if (!args.m_bypass_limits) {
|
||||
if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
|
||||
// Single transaction contexts only.
|
||||
if (args.m_allow_sibling_eviction && err->second != nullptr) {
|
||||
// We should only be considering where replacement is considered valid as well.
|
||||
Assume(args.m_allow_replacement);
|
||||
|
||||
// Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
|
||||
// included in RBF checks.
|
||||
ws.m_conflicts.insert(err->second->GetHash());
|
||||
// Adding the sibling to m_iters_conflicting here means that it doesn't count towards
|
||||
// RBF Carve Out above. This is correct, since removing to-be-replaced transactions from
|
||||
// the descendant count is done separately in SingleTRUCChecks for TRUC transactions.
|
||||
ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value());
|
||||
ws.m_sibling_eviction = true;
|
||||
// The sibling will be treated as part of the to-be-replaced set in ReplacementChecks.
|
||||
// Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC
|
||||
// (which is normally done in PreChecks). However, the only way a TRUC transaction can
|
||||
// have a non-TRUC and non-BIP125 descendant is due to a reorg.
|
||||
} else {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first);
|
||||
// Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
|
||||
// included in RBF checks.
|
||||
ws.m_conflicts.insert(err->second->GetHash());
|
||||
// Adding the sibling to m_iters_conflicting here means that it doesn't count towards
|
||||
// RBF Carve Out above. This is correct, since removing to-be-replaced transactions from
|
||||
// the descendant count is done separately in SingleTRUCChecks for TRUC transactions.
|
||||
ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value());
|
||||
ws.m_sibling_eviction = true;
|
||||
// The sibling will be treated as part of the to-be-replaced set in ReplacementChecks.
|
||||
// Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC
|
||||
// (which is normally done in PreChecks). However, the only way a TRUC transaction can
|
||||
// have a non-TRUC and non-BIP125 descendant is due to a reorg.
|
||||
} else {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1247,7 +1246,7 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|||
const CTransaction& tx = *ws.m_ptx;
|
||||
TxValidationState& state = ws.m_state;
|
||||
|
||||
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
constexpr script_verify_flags scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
|
||||
// Check input scripts and signatures.
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
|
@ -1286,7 +1285,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|||
// There is a similar check in CreateNewBlock() to prevent creating
|
||||
// invalid blocks (using TestBlockValidity), however allowing such
|
||||
// transactions into the mempool can be exploited as a DoS attack.
|
||||
unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
|
||||
script_verify_flags currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
|
||||
if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
|
||||
ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
|
||||
LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
|
||||
|
@ -2094,7 +2093,7 @@ std::optional<std::pair<ScriptError, std::string>> CScriptCheck::operator()() {
|
|||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
|
||||
ScriptError error{SCRIPT_ERR_UNKNOWN_ERROR};
|
||||
if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) {
|
||||
if (VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, m_flags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *m_signature_cache, *txdata), &error)) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
auto debug_str = strprintf("input %i of %s (wtxid %s), spending %s:%i", nIn, ptxTo->GetHash().ToString(), ptxTo->GetWitnessHash().ToString(), ptxTo->vin[nIn].prevout.hash.ToString(), ptxTo->vin[nIn].prevout.n);
|
||||
|
@ -2138,7 +2137,7 @@ ValidationCache::ValidationCache(const size_t script_execution_cache_bytes, cons
|
|||
* Non-static (and redeclared) in src/test/txvalidationcache_tests.cpp
|
||||
*/
|
||||
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
|
||||
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
|
||||
const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,
|
||||
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
|
||||
ValidationCache& validation_cache,
|
||||
std::vector<CScriptCheck>* pvChecks)
|
||||
|
@ -2326,7 +2325,7 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn
|
|||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
||||
}
|
||||
|
||||
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman)
|
||||
script_verify_flags GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman)
|
||||
{
|
||||
const Consensus::Params& consensusparams = chainman.GetConsensus();
|
||||
|
||||
|
@ -2338,7 +2337,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
|
|||
// mainnet.
|
||||
// For simplicity, always leave P2SH+WITNESS+TAPROOT on except for the two
|
||||
// violating blocks.
|
||||
uint32_t flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT};
|
||||
script_verify_flags flags{SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT};
|
||||
const auto it{consensusparams.script_flag_exceptions.find(*Assert(block_index.phashBlock))};
|
||||
if (it != consensusparams.script_flag_exceptions.end()) {
|
||||
flags = it->second;
|
||||
|
@ -2552,7 +2551,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
}
|
||||
|
||||
// Get the script flags for this block
|
||||
unsigned int flags{GetBlockScriptFlags(*pindex, m_chainman)};
|
||||
script_verify_flags flags{GetBlockScriptFlags(*pindex, m_chainman)};
|
||||
|
||||
const auto time_2{SteadyClock::now()};
|
||||
m_chainman.time_forks += time_2 - time_1;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <script/script_error.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <script/verify_flags.h>
|
||||
#include <sync.h>
|
||||
#include <txdb.h>
|
||||
#include <txmempool.h>
|
||||
|
@ -336,14 +337,14 @@ private:
|
|||
CTxOut m_tx_out;
|
||||
const CTransaction *ptxTo;
|
||||
unsigned int nIn;
|
||||
unsigned int nFlags;
|
||||
script_verify_flags m_flags;
|
||||
bool cacheStore;
|
||||
PrecomputedTransactionData *txdata;
|
||||
SignatureCache* m_signature_cache;
|
||||
|
||||
public:
|
||||
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
|
||||
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { }
|
||||
CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, SignatureCache& signature_cache, unsigned int nInIn, script_verify_flags flags, bool cacheIn, PrecomputedTransactionData* txdataIn) :
|
||||
m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), m_flags(flags), cacheStore(cacheIn), txdata(txdataIn), m_signature_cache(&signature_cache) { }
|
||||
|
||||
CScriptCheck(const CScriptCheck&) = delete;
|
||||
CScriptCheck& operator=(const CScriptCheck&) = delete;
|
||||
|
@ -1365,4 +1366,7 @@ bool IsBIP30Repeat(const CBlockIndex& block_index);
|
|||
/** Identifies blocks which coinbase output was subsequently overwritten in the UTXO set (see BIP30) */
|
||||
bool IsBIP30Unspendable(const uint256& block_hash, int block_height);
|
||||
|
||||
// Returns the script flags which should be checked for a given block
|
||||
script_verify_flags GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
|
||||
|
||||
#endif // BITCOIN_VALIDATION_H
|
||||
|
|
|
@ -165,23 +165,36 @@ class MempoolTRUC(BitcoinTestFramework):
|
|||
def test_truc_reorg(self):
|
||||
node = self.nodes[0]
|
||||
self.log.info("Test that, during a reorg, TRUC rules are not enforced")
|
||||
tx_v2_block = self.wallet.send_self_transfer(from_node=node, version=2)
|
||||
tx_v3_block = self.wallet.send_self_transfer(from_node=node, version=3)
|
||||
tx_v3_block2 = self.wallet.send_self_transfer(from_node=node, version=3)
|
||||
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"]])
|
||||
self.check_mempool([])
|
||||
|
||||
# Testing 2<-3 versions allowed
|
||||
tx_v2_block = self.wallet.create_self_transfer(version=2)
|
||||
|
||||
# Testing 3<-2 versions allowed
|
||||
tx_v3_block = self.wallet.create_self_transfer(version=3)
|
||||
|
||||
# Testing overly-large child size
|
||||
tx_v3_block2 = self.wallet.create_self_transfer(version=3)
|
||||
|
||||
# Also create a linear chain of 3 TRUC transactions that will be directly mined, followed by one v2 in-mempool after block is made
|
||||
tx_chain_1 = self.wallet.create_self_transfer(version=3)
|
||||
tx_chain_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_1["new_utxo"], version=3)
|
||||
tx_chain_3 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_2["new_utxo"], version=3)
|
||||
|
||||
tx_to_mine = [tx_v3_block["hex"], tx_v2_block["hex"], tx_v3_block2["hex"], tx_chain_1["hex"], tx_chain_2["hex"], tx_chain_3["hex"]]
|
||||
block = self.generateblock(node, output="raw(42)", transactions=tx_to_mine)
|
||||
|
||||
block = self.generate(node, 1)
|
||||
self.check_mempool([])
|
||||
tx_v2_from_v3 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block["new_utxo"], version=2)
|
||||
tx_v3_from_v2 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v2_block["new_utxo"], version=3)
|
||||
tx_v3_child_large = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block2["new_utxo"], target_vsize=1250, version=3)
|
||||
assert_greater_than(node.getmempoolentry(tx_v3_child_large["txid"])["vsize"], TRUC_CHILD_MAX_VSIZE)
|
||||
self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]])
|
||||
node.invalidateblock(block[0])
|
||||
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]])
|
||||
# This is needed because generate() will create the exact same block again.
|
||||
node.reconsiderblock(block[0])
|
||||
tx_chain_4 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_chain_3["new_utxo"], version=2)
|
||||
self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_4["txid"]])
|
||||
|
||||
# Reorg should have all block transactions re-accepted, ignoring TRUC enforcement
|
||||
node.invalidateblock(block["hash"])
|
||||
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_1["txid"], tx_chain_2["txid"], tx_chain_3["txid"], tx_chain_4["txid"]])
|
||||
|
||||
@cleanup(extra_args=["-limitdescendantsize=10"])
|
||||
def test_nondefault_package_limits(self):
|
||||
|
|
|
@ -15,7 +15,7 @@ from test_framework.wallet import MiniWallet
|
|||
import time
|
||||
|
||||
class P2PNode(P2PDataStore):
|
||||
def on_inv(self, msg):
|
||||
def on_inv(self, message):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
def run_test(self):
|
||||
self.gen_node = self.nodes[0] # The block and tx generating node
|
||||
self.miniwallet = MiniWallet(self.gen_node)
|
||||
self.mocktime = int(time.time())
|
||||
|
||||
self.test_tx_in_block()
|
||||
self.test_notfound_on_replaced_tx()
|
||||
|
@ -33,20 +34,20 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
|
||||
def test_tx_in_block(self):
|
||||
self.log.info("Check that a transaction in the last block is uploaded (beneficial for compact block relay)")
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer = self.gen_node.add_p2p_connection(P2PNode())
|
||||
|
||||
self.log.debug("Generate transaction and block")
|
||||
inbound_peer.last_message.pop("inv", None)
|
||||
|
||||
self.gen_node.setmocktime(int(time.time())) # pause time based activities
|
||||
wtxid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
|
||||
rawmp = self.gen_node.getrawmempool(False, True)
|
||||
pi = self.gen_node.getpeerinfo()[0]
|
||||
assert_equal(rawmp["mempool_sequence"], 2) # our tx cause mempool activity
|
||||
assert_equal(pi["last_inv_sequence"], 1) # that is after the last inv
|
||||
assert_equal(pi["inv_to_send"], 1) # and our tx has been queued
|
||||
self.gen_node.setmocktime(0)
|
||||
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_until(lambda: "inv" in inbound_peer.last_message and inbound_peer.last_message.get("inv").inv[0].hash == int(wtxid, 16))
|
||||
|
||||
rawmp = self.gen_node.getrawmempool(False, True)
|
||||
|
@ -65,15 +66,20 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
|
||||
def test_notfound_on_replaced_tx(self):
|
||||
self.gen_node.disconnect_p2ps()
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer = self.gen_node.add_p2p_connection(P2PTxInvStore())
|
||||
|
||||
self.log.info("Transaction tx_a is broadcast")
|
||||
tx_a = self.miniwallet.send_self_transfer(from_node=self.gen_node)
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_for_broadcast(txns=[tx_a["wtxid"]])
|
||||
|
||||
tx_b = tx_a["tx"]
|
||||
tx_b.vout[0].nValue -= 9000
|
||||
self.gen_node.sendrawtransaction(tx_b.serialize().hex())
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_until(lambda: "tx" in inbound_peer.last_message and inbound_peer.last_message.get("tx").tx.wtxid_hex == tx_b.wtxid_hex)
|
||||
|
||||
self.log.info("Re-request of tx_a after replacement is answered with notfound")
|
||||
|
@ -96,28 +102,31 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
self.gen_node.disconnect_p2ps()
|
||||
inbound_peer = self.gen_node.add_p2p_connection(P2PNode()) # An "attacking" inbound peer
|
||||
|
||||
MAX_REPEATS = 100
|
||||
self.log.info("Running test up to {} times.".format(MAX_REPEATS))
|
||||
for i in range(MAX_REPEATS):
|
||||
self.log.info('Run repeat {}'.format(i + 1))
|
||||
txid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
|
||||
# Set a mock time so that time does not pass, and gen_node never announces the transaction
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
wtxid = int(self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"], 16)
|
||||
|
||||
want_tx = msg_getdata()
|
||||
want_tx.inv.append(CInv(t=MSG_TX, h=int(txid, 16)))
|
||||
with p2p_lock:
|
||||
inbound_peer.last_message.pop('notfound', None)
|
||||
inbound_peer.send_and_ping(want_tx)
|
||||
want_tx = msg_getdata()
|
||||
want_tx.inv.append(CInv(t=MSG_WTX, h=wtxid))
|
||||
with p2p_lock:
|
||||
inbound_peer.last_message.pop('notfound', None)
|
||||
inbound_peer.send_and_ping(want_tx)
|
||||
inbound_peer.wait_until(lambda: "notfound" in inbound_peer.last_message)
|
||||
with p2p_lock:
|
||||
assert_equal(inbound_peer.last_message.get("notfound").vec[0].hash, wtxid)
|
||||
inbound_peer.last_message.pop('notfound')
|
||||
|
||||
if inbound_peer.last_message.get('notfound'):
|
||||
self.log.debug('tx {} was not yet announced to us.'.format(txid))
|
||||
self.log.debug("node has responded with a notfound message. End test.")
|
||||
assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16))
|
||||
with p2p_lock:
|
||||
inbound_peer.last_message.pop('notfound')
|
||||
break
|
||||
else:
|
||||
self.log.debug('tx {} was already announced to us. Try test again.'.format(txid))
|
||||
assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv]
|
||||
# Move mocktime forward and wait for the announcement.
|
||||
inbound_peer.last_message.pop('inv', None)
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_for_inv([CInv(t=MSG_WTX, h=wtxid)], timeout=120)
|
||||
|
||||
# Send the getdata again, this time the node should send us a TX message.
|
||||
inbound_peer.last_message.pop('tx', None)
|
||||
inbound_peer.send_and_ping(want_tx)
|
||||
self.wait_until(lambda: "tx" in inbound_peer.last_message)
|
||||
assert_equal(wtxid, int(inbound_peer.last_message["tx"].tx.wtxid_hex, 16))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -213,6 +213,7 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
assert_equal(gdi_result, {
|
||||
"hash": blockhash,
|
||||
"height": height,
|
||||
"script_flags": ["CHECKLOCKTIMEVERIFY","CHECKSEQUENCEVERIFY","DERSIG","NULLDUMMY","P2SH","TAPROOT","WITNESS"],
|
||||
"deployments": {
|
||||
'bip34': {'type': 'buried', 'active': True, 'height': 2},
|
||||
'bip66': {'type': 'buried', 'active': True, 'height': 3},
|
||||
|
|
|
@ -474,6 +474,7 @@ def write_config(config_path, *, n, chain, extra_config="", disable_autoconnect=
|
|||
# min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + nBind = 151 + 8 + 3 = 162;
|
||||
# nMaxConnections = available_fds - min_required_fds = 256 - 161 = 94;
|
||||
f.write("maxconnections=94\n")
|
||||
f.write("par=" + str(min(2, os.cpu_count())) + "\n")
|
||||
f.write(extra_config)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue