Merge ElementsProject/elements#1029: Elements 0.21: backports from Core 0.21.0

3168f2ce7d configure: bump version to 0.21.0rc1 (Andrew Poelstra)
b221f4e624 doc: update manpages for 0.21.0 (Andrew Poelstra)
ebf6a248b8 fuzz: cleanups for versionbits fuzzer (Anthony Towns)
e98b981378 test: make sure non-IP peers get discouraged and disconnected (Vasil Dimov)
3f396ba229 test: also check disconnect in denialofservice_tests/peer_discouragement (Vasil Dimov)
79f48d69ef test: use pointers in denialofservice_tests/peer_discouragement (Vasil Dimov)
00fe59fd43 Rename CoinSelectionParams::effective_fee to m_effective_feerate (Andrew Chow)
f897fd9575 wallet: Move discard feerate fetching to CreateTransaction (Andrew Chow)
300372f8bf wallet: Move long term feerate setting to CreateTransaction (Andrew Chow)
1f58b91c48 wallet: Replace nFeeRateNeeded with effective_fee (Andrew Chow)
12a25655e8 wallet: Use existing feerate instead of getting a new one (Andrew Chow)
78cd67d4de Test that signrawtx works when a signed CSV and CLTV inputs are present (Andrew Chow)
5f714365cb Introduce DeferringSignatureChecker and inherit with SignatureExtractor (Andrew Chow)
ad985d5c23 net: add ifaddrs.h include (fanquake)
33eaeada30 build: check if -lsocket is required with *ifaddrs (fanquake)
f8237b8d50 rand: only try and use freeifaddrs if available (fanquake)
d135a860cf doc: Remove outdated comment (Hennadii Stepanov)
d92fb9693f fuzz: Bump FuzzedDataProvider.h (MarcoFalke)
9256eacadf fuzz: Bump FuzzedDataProvider.h (MarcoFalke)
07745976c7 fuzz: Update FuzzedDataProvider.h from upstream (LLVM) (practicalswift)
cdb30c3787 doc: add signet to doc/bitcoin-conf.md (Jon Atack)
4940b460c4 doc: add signet to share/examples/bitcoin.conf (Jon Atack)
969357988d Update vcpkg checkout commit. (Aaron Clauson)
337297d605 GUI: Write PSBTs to file with binary mode (Andrew Chow)
c4494ede6b tests: Test that a fully signed tx given to signrawtx is unchanged (Andrew Chow)
08298c34d8 test: disallow sendtoaddress/sendmany when private keys disabled (Jon Atack)
a143b7b87d Disallow sendtoaddress and sendmany when private keys disabled (Andrew Chow)
7233f69fc4 util: Disallow negative mocktime (MarcoFalke)
f0dec76142 net: Avoid UBSan warning in ProcessMessage(...) (practicalswift)
16e2673e24 fix the unreachable code at feature_taproot (Bruno Garcia)
9e6c08d514 qt: Use "fusion" style on macOS Big Sur with old Qt (Hennadii Stepanov)
b514a872c3 raise helpMessageDialog (randymcmillan)
640b94fe9a Fix MSVC build after gui#176 (Hennadii Stepanov)
ce108b0061 qt: Stop the effect of hidden widgets on the size of QStackedWidget (Hennadii Stepanov)
2ccf3823cf qt: Fix TxViewDelegate layout (Hennadii Stepanov)
5823cc3b43 qt: Add TransactionOverviewWidget class (Hennadii Stepanov)
c89caab283 qt: Use layout manager for Create Wallet dialog (Hennadii Stepanov)
62c12bde50 Bugfix: GUI: Restore SendConfirmationDialog button default to "Yes" (Luke Dashjr)
de31f16d60 test: add test for banning of non-IP addresses (Vasil Dimov)
0682f35ee4 net: allow CSubNet of non-IP networks (Vasil Dimov)
81ea0ed4a6 tests: Add fuzzing harness for versionbits (Anthony Towns)
b8d678c9c9 tests: check never active versionbits (Anthony Towns)
c3bd45e5dd tests: more helpful errors for failing versionbits tests (Anthony Towns)
010ba4e6e6 doc: Archive release notes, Add template for minor release (MarcoFalke)
c38b555adc doc: Move 0.21.0 release notes from wiki (Wladimir J. van der Laan)
554445bc59 Revert "Add patch to make codesign_allocate compatible with Apple's" (Pieter Wuille)
33d10b1f00 [doc] Add permissions to the getpeerinfo help. (Amiti Uttarwar)
4bca4c28c2 rpc: Add missing description of vout in getrawtransaction help text (Ben Carman)
492ed0947f qt: Align layout of checkboxes (Hennadii Stepanov)
4b1e64ac2f Add patch to make codesign_allocate compatible with Apple's (Pieter Wuille)
8db26435a0 doc: update -externalip documentation in tor.md (Jon Atack)
9a18ee73d1 doc: add tor.md section on how to get tor info via bitcoind (Jon Atack)
ba3306519f doc: update tor.md address examples from onion v2 to v3 (Jon Atack)
6299124c9c doc: warn that incoming conns are unlikely when not using default ports (Adam Jonas)
24e35438d1 doc: Add warnings for http interfaces limitations (Fabian Jahr)
a9bb81316d Removed redundant git pull from appveyor config. (Aaron Clauson)
3acfb477e3 Adjusted msvc compiler and linker settings to remove optimisations that are causing sporadic ABI issues on Visual Studio updates. (Aaron Clauson)
5e935dd6fe This change to the appveyor CI config for msvc builds reverses a change introduced in #19960. It re-applies a setting to inform vcpkg to only build release vesions of the dependencies rather than the default of debug and release. (Aaron Clauson)
14d7d20c68 Move signet onion seed from v2 to v3 (Sjors Provoost)
09ca7bbf4e doc: Update wallet database installation guide for macOS (Hennadii Stepanov)
fdd69bf902 build: Use Homebrew's sqlite package if it is available (Hennadii Stepanov)
92b1189afc build, refactor: Check that Homebrew's qt5 package is actually installed (Hennadii Stepanov)
29b0796eb9 build: Check that Homebrew's berkeley-db4 package is actually installed (Hennadii Stepanov)
b52bac2c39 Don't set BDB flags when configuring without (Jonas Schnelli)
e45e7c15e7 Add regression test for incorrect decoding (Pieter Wuille)
3bfce85eaf Improve heuristic hex transaction decoding (Pieter Wuille)
e48a9793b2 test: add coverage for passing fee rate as a string (Jon Atack)
b4eb953986 wallet, bugfix: allow send to take string fee rate values (Jon Atack)
f15c72cbd2 Send and require SENDADDRV2 before VERACK (Pieter Wuille)
c117c6c125 Don't send 'sendaddrv2' to pre-70016 software (Pieter Wuille)
a97c53172a Use the correct incremental fee constant in bumpfee help (Jon Atack)
d635fc743a Update feeRate (BTC/kvB) to fee_rate (sat/vB) in wallet_bumpfee (Jon Atack)
765c2b4d17 Allow zero-fee fundrawtxn and walletcreatefundedpsbt calls (Jon Atack)
2ca3943fdf build: Avoid secp256k1.h include from system (Niklas Gögge)
df1bd79314 wallet: fix and improve upgradewallet error responses (Jon Atack)
77627d8efd wallet: fix and improve upgradewallet result responses (Jon Atack)
92251542e5 Don't upgrade to HD split if it is already supported (Andrew Chow)
9c3df80c40 wallet: refactor GetClosestWalletFeature() (Jon Atack)
ae26ff27a9 wallet: Do not treat default constructed types as None-type (MarcoFalke)
2fe52979ea refactor: Change pointer to reference because it can not be null (MarcoFalke)
029b6799d1 test: Fix intermittent issue in mempool_compatibility (MarcoFalke)
ae8a5a5baa Fix QPainter non-determinism on macOS (Andrew Chow)
4f36e4cfc6 RPC/Wallet: unloadwallet: Clarify docs/error when both the RPC endpoint and wallet_name parameter specify a wallet (Luke Dashjr)
5412789739 tests: shrink feature_taproot transfer of funds tx (Anthony Towns)

Pull request description:

  This is a cherry-pick of all the commits from Bitcoin Core 0.21.1 that are not on master, **except** those related to bech32m and Speedy Trial.

  **TODO** (will do each in a separate additional commit, no need to delay review)
  * [ ] regenerate manpages
  * [ ] bump version in configure.ac

ACKs for top commit:
  gwillen:
    utACK 3168f2ce7d

Tree-SHA512: e154a205cd4a7640d8c6b0c9527c0b2d1d307c32b3407b0c66e0ea20687391a4649f30f61eadae7e325adc44009055d97834c9641aeeca1c0b56207a684c0178
This commit is contained in:
Andrew Poelstra 2021-09-22 20:00:45 +00:00
commit d04207ff22
No known key found for this signature in database
GPG key ID: C588D63CE41B97C1
76 changed files with 4055 additions and 1369 deletions

View file

@ -1,29 +1,30 @@
version: '{branch}.{build}'
skip_tags: true
image: Previous Visual Studio 2019
image: Visual Studio 2019
configuration: Release
platform: x64
clone_depth: 5
environment:
PATH: 'C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%'
PYTHONUTF8: 1
QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/v1.6/Qt5.9.8_x64_static_vs2019.zip'
QT_DOWNLOAD_HASH: '9a8c6eb20967873785057fdcd329a657c7f922b0af08c5fde105cc597dd37e21'
QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/qt598x64_vs2019_v1681/qt598_x64_vs2019_1681.zip'
QT_DOWNLOAD_HASH: '00cf7327818c07d74e0b1a4464ffe987c2728b00d49d4bf333065892af0515c3'
QT_LOCAL_PATH: 'C:\Qt5.9.8_x64_static_vs2019'
VCPKG_INSTALL_PATH: 'C:\tools\vcpkg\installed'
VCPKG_COMMIT_ID: '40230b8e3f6368dcb398d649331be878ca1e9007'
VCPKG_TAG: '75522bb1f2e7d863078bcd06322348f053a9e33f'
install:
# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes.
# - cmd: pip install zmq
# Powershell block below is to install the c++ dependencies via vcpkg. The pseudo code is:
# The powershell block below is to set up vcpkg to install the c++ dependencies. The pseudo code is:
# a. Checkout the vcpkg source (including port files) for the specific checkout and build the vcpkg binary,
# b. Install the missing packages using the vcpkg manifest.
# b. Append a setting to the vcpkg cmake config file to only do release builds of dependencies (skipping deubg builds saves ~5 mins).
# Note originally this block also installed the dependencies using 'vcpkg install'. Dependencies are now installed
# as part of the msbuild command using vcpkg mainfests.
- ps: |
cd c:\tools\vcpkg
$env:GIT_REDIRECT_STDERR = '2>&1' # git is writing non-errors to STDERR when doing git pull. Send to STDOUT instead.
git pull origin master > $null
git -c advice.detachedHead=false checkout $env:VCPKG_COMMIT_ID
git -c advice.detachedHead=false checkout $env:VCPKG_TAG
.\bootstrap-vcpkg.bat > $null
Add-Content "C:\tools\vcpkg\triplets\$env:PLATFORM-windows-static.cmake" "set(VCPKG_BUILD_TYPE release)"
cd "$env:APPVEYOR_BUILD_FOLDER"
before_build:
# Powershell block below is to download and extract the Qt static libraries. The pseudo code is:

36
build-aux/m4/l_socket.m4 Normal file
View file

@ -0,0 +1,36 @@
# Illumos/SmartOS requires linking with -lsocket if
# using getifaddrs & freeifaddrs
m4_define([_CHECK_SOCKET_testbody], [[
#include <sys/types.h>
#include <ifaddrs.h>
int main() {
struct ifaddrs *ifaddr;
getifaddrs(&ifaddr);
freeifaddrs(ifaddr);
}
]])
AC_DEFUN([CHECK_SOCKET], [
AC_LANG_PUSH(C++)
AC_MSG_CHECKING([whether ifaddrs funcs can be used without link library])
AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_SOCKET_testbody])],[
AC_MSG_RESULT([yes])
],[
AC_MSG_RESULT([no])
LIBS="$LIBS -lsocket"
AC_MSG_CHECKING([whether getifaddrs needs -lsocket])
AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_SOCKET_testbody])],[
AC_MSG_RESULT([yes])
],[
AC_MSG_RESULT([no])
AC_MSG_FAILURE([cannot figure out how to use getifaddrs])
])
])
AC_LANG_POP
])

View file

@ -56,7 +56,7 @@
</ClCompile>
<Link>
<AdditionalDependencies>$(QtReleaseLibraries);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/ignore:4206</AdditionalOptions>
<AdditionalOptions>/ignore:4206 /LTCG:OFF</AdditionalOptions>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories>

View file

@ -4,8 +4,6 @@
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
<VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
<UseNativeEnvironment>true</UseNativeEnvironment>
</PropertyGroup>
@ -16,6 +14,8 @@
<VcpkgUseStatic>true</VcpkgUseStatic>
<VcpkgAutoLink>true</VcpkgAutoLink>
<VcpkgConfiguration>$(Configuration)</VcpkgConfiguration>
<VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows-static</VcpkgTriplet>
<VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows-static</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Condition="'$(WindowsTargetPlatformVersion)'=='' and !Exists('$(WindowsSdkDir)\DesignTime\CommonConfiguration\Neutral\Windows.props')">
@ -45,66 +45,46 @@
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<LinkIncremental>false</LinkIncremental>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<GenerateManifest>No</GenerateManifest>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<LinkIncremental>true</LinkIncremental>
<WholeProgramOptimization>false</WholeProgramOptimization>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<LinkIncremental>false</LinkIncremental>
<WholeProgramOptimization>true</WholeProgramOptimization>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<Optimization>Disabled</Optimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<DebugInformationFormat>None</DebugInformationFormat>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>false</EnableCOMDATFolding>
<OptimizeReferences>false</OptimizeReferences>
<AdditionalOptions>/LTCG:OFF</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<Optimization>Disabled</Optimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -124,7 +104,6 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Iphlpapi.lib;ws2_32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>

View file

@ -4,7 +4,7 @@
<Target Name="CopyBuildArtifacts" Condition="'$(ConfigurationType)' != 'StaticLibrary'">
<ItemGroup>
<BuildArtifacts Include="$(OutDir)$(TargetName)$(TargetExt)"></BuildArtifacts>
<BuildArtifacts Include="$(OutDir)$(TargetName).pdb"></BuildArtifacts>
<BuildArtifacts Include="$(OutDir)$(TargetName).pdb" Condition="Exists('$(OutDir)$(TargetName).pdb')"></BuildArtifacts>
</ItemGroup>
<Copy SourceFiles="@(BuildArtifacts)" SkipUnchangedFiles="true" DestinationFolder="..\..\src\" Condition="'$(OutDir)' != ''"></Copy>
</Target>

View file

@ -104,6 +104,7 @@
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactiondesc.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactiondescdialog.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactionfilterproxy.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactionoverviewwidget.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactionrecord.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactiontablemodel.cpp" />
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_transactionview.cpp" />

View file

@ -73,7 +73,7 @@
</ClCompile>
<Link>
<AdditionalDependencies>$(QtLibraryDir)\Qt5Test.lib;$(QtReleaseLibraries);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/ignore:4206</AdditionalOptions>
<AdditionalOptions>/ignore:4206 /LTCG:OFF</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
@ -83,7 +83,7 @@
</ClCompile>
<Link>
<AdditionalDependencies>$(QtDebugLibraries);%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/ignore:4206</AdditionalOptions>
<AdditionalOptions>/ignore:4206</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View file

@ -1,9 +1,9 @@
AC_PREREQ([2.69])
define(_CLIENT_VERSION_MAJOR, 0)
define(_CLIENT_VERSION_MINOR, 20)
define(_CLIENT_VERSION_REVISION, 99)
define(_CLIENT_VERSION_MINOR, 21)
define(_CLIENT_VERSION_REVISION, 0)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_RC, 0)
define(_CLIENT_VERSION_RC, 1)
define(_CLIENT_VERSION_IS_RELEASE, false)
define(_COPYRIGHT_YEAR, 2021)
define(_COPYRIGHT_HOLDERS,[The %s developers])
@ -656,16 +656,19 @@ case $host in
dnl It's safe to add these paths even if the functionality is disabled by
dnl the user (--without-wallet or --without-gui for example).
bdb_prefix=$($BREW --prefix berkeley-db4 2>/dev/null)
qt5_prefix=$($BREW --prefix qt5 2>/dev/null)
if test x$bdb_prefix != x && test "x$BDB_CFLAGS" = "x" && test "x$BDB_LIBS" = "x"; then
if test "x$use_bdb" != xno && $BREW list --versions berkeley-db4 >/dev/null && test "x$BDB_CFLAGS" = "x" && test "x$BDB_LIBS" = "x"; then
bdb_prefix=$($BREW --prefix berkeley-db4 2>/dev/null)
dnl This must precede the call to BITCOIN_FIND_BDB48 below.
BDB_CFLAGS="-I$bdb_prefix/include"
BDB_LIBS="-L$bdb_prefix/lib -ldb_cxx-4.8"
fi
if test x$qt5_prefix != x; then
PKG_CONFIG_PATH="$qt5_prefix/lib/pkgconfig:$PKG_CONFIG_PATH"
export PKG_CONFIG_PATH
if test "x$use_sqlite" != xno && $BREW list --versions sqlite3 >/dev/null; then
export PKG_CONFIG_PATH="$($BREW --prefix sqlite3 2>/dev/null)/lib/pkgconfig:$PKG_CONFIG_PATH"
fi
if $BREW list --versions qt5 >/dev/null; then
export PKG_CONFIG_PATH="$($BREW --prefix qt5 2>/dev/null)/lib/pkgconfig:$PKG_CONFIG_PATH"
fi
dnl On some versions of osx stack check is turned on by default and is broken
AX_CHECK_COMPILE_FLAG([-fno-stack-check],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fno-stack-check"])
@ -875,7 +878,7 @@ fi
AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h])
AC_CHECK_DECLS([getifaddrs, freeifaddrs],,,
AC_CHECK_DECLS([getifaddrs, freeifaddrs],[CHECK_SOCKET],,
[#include <sys/types.h>
#include <ifaddrs.h>]
)

View file

@ -12,6 +12,7 @@ $(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch
$(package)_patches+= fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch
$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
$(package)_patches+= freetype_back_compat.patch drop_lrelease_dependency.patch fix_powerpc_libpng.patch
$(package)_patches+= fix_qpainter_non_determinism.patch
# Update OSX_QT_TRANSLATIONS when this is updated
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
@ -195,6 +196,7 @@ endef
define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/freetype_back_compat.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_powerpc_libpng.patch && \
patch -p1 -i $($(package)_patch_dir)/fix_qpainter_non_determinism.patch &&\
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
patch -p1 -i $($(package)_patch_dir)/drop_lrelease_dependency.patch && \
patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch &&\

View file

@ -0,0 +1,63 @@
commit 2a8f7dc6ddfc414a66491522501c1574a1343ee1
Author: Andrew Chow <achow101-github@achow101.com>
Date: Sat Nov 21 01:11:04 2020 -0500
build: Fix determinism issue when building with Clang 8
When building Qt with LLVM/Clang 8 under -O3 (the default), we run into
a determinism issue in `qt_interset_spans`. The issue has been fixed for
LLVM/Clang 9, see
https://github.com/llvm/llvm-project/commit/db101864bdc938deb1d63fe4f7da761bd38e5cae
and https://reviews.llvm.org/D64601, however this fix was not backported
to 8.x. Once LLVM/Clang 9 is used, this patch can be dropped.
The particular issue appears to be an optimization done by -O3 which
adds a temporary variable for `spans->y` in `qt_intersect_spans`. When
it does this, sometimes it chooses to use a 32-bit movs instruction
(movswl), and other times it chooses a 64-bit movs instruction (movswq).
By patching `qt_intersect_spans` to always make a temporary variable for
`spans->y`, we are able to sidestep this problem.
diff --git a/qtbase/src/gui/painting/qpaintengine_raster.cpp b/qtbase/src/gui/painting/qpaintengine_raster.cpp
index 92ab6e8375..f018009e0b 100644
--- a/qtbase/src/gui/painting/qpaintengine_raster.cpp
+++ b/qtbase/src/gui/painting/qpaintengine_raster.cpp
@@ -3971,22 +3971,23 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
const QSpan *clipEnd = clip->m_spans + clip->count;
while (available && spans < end ) {
+ const short spans_y = spans->y;
if (clipSpans >= clipEnd) {
spans = end;
break;
}
- if (clipSpans->y > spans->y) {
+ if (clipSpans->y > spans_y) {
++spans;
continue;
}
- if (spans->y != clipSpans->y) {
- if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
- clipSpans = clip->m_clipLines[spans->y].spans;
+ if (spans_y != clipSpans->y) {
+ if (spans_y < clip->count && clip->m_clipLines[spans_y].spans)
+ clipSpans = clip->m_clipLines[spans_y].spans;
else
++clipSpans;
continue;
}
- Q_ASSERT(spans->y == clipSpans->y);
+ Q_ASSERT(spans_y == clipSpans->y);
int sx1 = spans->x;
int sx2 = sx1 + spans->len;
@@ -4005,7 +4006,7 @@ static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
if (len) {
out->x = qMax(sx1, cx1);
out->len = qMin(sx2, cx2) - out->x;
- out->y = spans->y;
+ out->y = spans_y;
out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
++out;
--available;

View file

@ -127,3 +127,14 @@ However, the wallet may not be up-to-date with the current state of the mempool
or the state of the mempool by an RPC that returned before this RPC. For
example, a wallet transaction that was BIP-125-replaced in the mempool prior to
this RPC may not yet be reflected as such in this RPC response.
## Limitations
There is a known issue in the JSON-RPC interface that can cause a node to crash if
too many http connections are being opened at the same time because the system runs
out of available file descriptors. To prevent this from happening you might
want to increase the number of maximum allowed file descriptors in your system
and try to prevent opening too many connections to your JSON-RPC interface at the
same time if this is under your control. It is hard to give general advice
since this depends on your system but if you make several hundred requests at
once you are definitely at risk of encountering this issue.

View file

@ -12,6 +12,18 @@ REST Interface consistency guarantees
The [same guarantees as for the RPC Interface](/doc/JSON-RPC-interface.md#rpc-consistency-guarantees)
apply.
Limitations
-----------
There is a known issue in the REST interface that can cause a node to crash if
too many http connections are being opened at the same time because the system runs
out of available file descriptors. To prevent this from happening you might
want to increase the number of maximum allowed file descriptors in your system
and try to prevent opening too many connections to your rest interface at the
same time if this is under your control. It is hard to give general advice
since this depends on your system but if you make several hundred requests at
once you are definitely at risk of encountering this issue.
Supported API
-------------

View file

@ -27,7 +27,7 @@ Comments may appear in two ways:
### Network specific options
Network specific options can be:
- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`) or `[regtest]`;
- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`), `[signet]` or `[regtest]`;
- prefixed with a chain name; e.g., `regtest.maxmempool=100`.
Network specific options take precedence over non-network specific options.

View file

@ -19,7 +19,7 @@ Then install [Homebrew](https://brew.sh).
## Dependencies
```shell
brew install automake berkeley-db4 libtool boost miniupnpc pkg-config python qt libevent qrencode sqlite
brew install automake libtool boost miniupnpc pkg-config python qt libevent qrencode
```
If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting).
@ -29,8 +29,22 @@ If you want to build the disk image with `make deploy` (.dmg / optional), you ne
brew install librsvg libicns imagemagick
Berkeley DB
-----------
The wallet support requires one or both of the dependencies ([*SQLite*](#sqlite) and [*Berkeley DB*](#berkeley-db)) in the sections below.
To build Bitcoin Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode).
#### SQLite
Usually, macOS installation already has a suitable SQLite installation.
Also, the Homebrew package could be installed:
```shell
brew install sqlite
```
In that case the Homebrew package will prevail.
#### Berkeley DB
It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
you can use [this](/contrib/install_db4.sh) script to install it
like so:
@ -41,7 +55,11 @@ like so:
from the root of the repository.
**Note**: You only need Berkeley DB if the wallet is enabled (see [*Disable-wallet mode*](/doc/build-osx.md#disable-wallet-mode)).
Also, the Homebrew package could be installed:
```shell
brew install berkeley-db4
```
## Build Bitcoin Core
@ -72,14 +90,14 @@ from the root of the repository.
make deploy
```
## `disable-wallet` mode
## Disable-wallet mode
When the intention is to run only a P2P node without a wallet, Bitcoin Core may be
compiled in `disable-wallet` mode with:
compiled in disable-wallet mode with:
```shell
./configure --disable-wallet
```
In this case there is no dependency on Berkeley DB 4.8 and SQLite.
In this case there is no dependency on [*Berkeley DB*](#berkeley-db) and [*SQLite*](#sqlite).
Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call.

View file

@ -103,7 +103,7 @@ Build using:
cd depends
make HOST=x86_64-w64-mingw32
cd ..
./autogen.sh # not required when building from tarball
./autogen.sh
CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure --prefix=/
make
sudo bash -c "echo 1 > /proc/sys/fs/binfmt_misc/status" # Enable WSL support for Win32 applications.

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.10.
.TH ELEMENTS-CLI "1" "May 2019" "elements-cli v0.17.0.1" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.3.
.TH ELEMENTS-CLI "1" "September 2021" "elements-cli elements-0.21.0_rc1" "User Commands"
.SH NAME
elements-cli \- manual page for elements-cli v0.17.0.1
elements-cli \- manual page for elements-cli elements-0.21.0_rc1
.SH SYNOPSIS
.B elements-cli
[\fI\,options\/\fR] \fI\,<command> \/\fR[\fI\,params\/\fR] \fI\,Send command to Elements Core\/\fR
@ -15,7 +15,7 @@ elements-cli \- manual page for elements-cli v0.17.0.1
.B elements-cli
[\fI\,options\/\fR] \fI\,help <command> Get help for a command\/\fR
.SH DESCRIPTION
Elements Core RPC client version v0.17.0.1\-dirty
Elements Core RPC client version elements\-0.21.0_rc1
.SH OPTIONS
.HP
\-?
@ -31,6 +31,15 @@ location. (default: elements.conf)
.IP
Specify data directory
.HP
\fB\-generate\fR
.IP
Generate blocks immediately, equivalent to RPC generatenewaddress
followed by RPC generatetoaddress. Optional positional integer
arguments are number of blocks to generate (default: 1) and
maximum iterations to try (default: 1000000), equivalent to RPC
generatetoaddress nblocks and maxtries arguments. Example:
bitcoin\-cli \fB\-generate\fR 4 1000
.HP
\fB\-getinfo\fR
.IP
Get general information from the remote server. Note that unlike
@ -43,6 +52,12 @@ be as of a different block from the chain state reported)
.IP
Pass named instead of positional arguments (default: false)
.HP
\fB\-netinfo\fR
.IP
Get network peer connection information from the remote server. An
optional integer argument from 0 to 4 can be passed for different
peers listings (default: 0).
.HP
\fB\-rpcclienttimeout=\fR<n>
.IP
Timeout in seconds during HTTP requests, or 0 for no timeout. (default:
@ -63,8 +78,8 @@ Password for JSON\-RPC connections
.HP
\fB\-rpcport=\fR<port>
.IP
Connect to JSON\-RPC on <port> (default: 8332, testnet: 18332, regtest:
18443)
Connect to JSON\-RPC on <port> (default: 7041, testnet: 18332, signet:
38332, regtest: 18443)
.HP
\fB\-rpcuser=\fR<user>
.IP
@ -77,7 +92,7 @@ Wait for RPC server to start
\fB\-rpcwallet=\fR<walletname>
.IP
Send RPC for non\-default wallet on RPC server (needs to exactly match
corresponding \fB\-wallet\fR option passed to the daemon). This changes
corresponding \fB\-wallet\fR option passed to elementsd). This changes
the RPC endpoint used, e.g.
http://127.0.0.1:8332/wallet/<walletname>
.HP
@ -92,18 +107,28 @@ is used for the RPC password.
.IP
Read RPC password from standard input as a single line. When combined
with \fB\-stdin\fR, the first line from standard input is used for the
RPC password.
RPC password. When combined with \fB\-stdinwalletpassphrase\fR,
\fB\-stdinrpcpass\fR consumes the first line, and \fB\-stdinwalletpassphrase\fR
consumes the second.
.HP
\fB\-stdinwalletpassphrase\fR
.IP
Read wallet passphrase from standard input as a single line. When
combined with \fB\-stdin\fR, the first line from standard input is used
for the wallet passphrase.
.HP
\fB\-version\fR
.IP
Print version and exit
.PP
Debugging/Testing options:
.PP
Chain selection options:
.HP
\fB\-chain=\fR<chain>
.IP
Use the chain <chain> (default: main). Reserved values: main, test,
regtest
Use the chain <chain> (default: liquidv1). Reserved values: main, test,
signet, regtest, liquidv1, liquidv1test
.HP
\fB\-con_blockheightinheader\fR
.IP
@ -136,7 +161,8 @@ signatures are necessary to solve it.
.HP
\fB\-fedpegscript\fR
.IP
The script for the federated peg.
The script for the federated peg enforce from genesis block. This script
may stop being enforced once dynamic federations activates.
.HP
\fB\-parentgenesisblockhash\fR
.IP
@ -147,9 +173,27 @@ The genesis blockhash of the parent chain.
Signed blockchain enumberance. Only active when `\-con_signed_blocks` set
to true.
.HP
\fB\-signet\fR
.IP
Use the signet chain. Equivalent to \fB\-chain\fR=\fI\,signet\/\fR. Note that the network
is defined by the \fB\-signetchallenge\fR parameter
.HP
\fB\-signetchallenge\fR
.IP
Blocks must satisfy the given script to be considered valid (only for
signet networks; defaults to the global default signet test
network challenge)
.HP
\fB\-signetseednode\fR
.IP
Specify a seed node for the signet network, in the hostname[:port]
format, e.g. sig.net:1234 (may be used multiple times to specify
multiple seed nodes; defaults to the global default signet test
network seed node(s))
.HP
\fB\-testnet\fR
.IP
Use the test chain
Use the test chain. Equivalent to \fB\-chain\fR=\fI\,test\/\fR.
.PP
Elements Options:
.HP
@ -158,7 +202,7 @@ Elements Options:
Defines the amount of block subsidy to start with, at genesis block, in
satoshis.
.HP
\fB\-con_connect_coinbase\fR
\fB\-con_connect_genesis_outputs\fR
.IP
Connect outputs in genesis block to utxo database.
.HP
@ -167,6 +211,18 @@ Connect outputs in genesis block to utxo database.
Starting height for CSV deployment. (default: \fB\-1\fR, which means ACTIVE
from genesis)
.HP
\fB\-con_dyna_deploy_signal\fR
.IP
Whether to signal for the Dynamic Federations deployment (default:
false).
.HP
\fB\-con_dyna_deploy_start\fR
.IP
Starting height for Dynamic Federations deployment. Once active,
signblockscript becomes a BIP141 WSH scriptPubKey of the original
signblockscript. All other dynamic parameters stay
constant.(default: \fB\-1\fR, which means ACTIVE from genesis)
.HP
\fB\-con_elementsmode\fR
.TP
Use Elements\-like instead of Core\-like witness encoding.
@ -179,27 +235,45 @@ required for CA/CT. (default: true)
All non\-zero valued coinbase outputs must go to this scriptPubKey, if
set.
.HP
\fB\-con_taproot_signal_start\fR
.IP
Whether, and at what blockheight, to start signalling for Taproot
activation (default: false) (regtest, Liquid testnet, or custom
only).
.HP
\fB\-dynamic_epoch_length\fR
.IP
Per\-chain parameter that sets how many blocks dynamic federation voting
and enforcement are in effect for.
.HP
\fB\-enforce_pak\fR
.IP
Causes standardness checks to enforce Pegout Authorization Key(PAK)
validation, and miner to include PAK commitments when configured.
Can not be set when acceptnonstdtx is set to true.
validation before dynamic federations, and consensus enforcement
after.
.HP
\fB\-multi_data_permitted\fR
.IP
Allow relay of multiple OP_RETURN outputs. (default: true)
Allow relay of multiple OP_RETURN outputs. (default: \fB\-enforce_pak\fR)
.HP
\fB\-pak\fR
.IP
Entries in the PAK list. Order of entries matter.
.SH COPYRIGHT
Copyright (C) 2009-2019 The Elements Project developers
Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Elements Core useful. Visit
<https://bitcoincore.org> for further information about the software.
The source code is available from <https://github.com/bitcoin/bitcoin>.
This is experimental software.
Distributed under the MIT software license, see the accompanying file COPYING
or <https://opensource.org/licenses/MIT>
Sets the 'first extension space' field to the pak entries ala
pre\-dynamic federations. Only used for testing in custom chains.
.HP
\fB\-total_valid_epochs\fR
.IP
Per\-chain parameter that sets how long a particular fedpegscript is in
effect for.
.SH "SEE ALSO"
The full documentation for
.B elements-cli
is maintained as a Texinfo manual. If the
.B info
and
.B elements-cli
programs are properly installed at your site, the command
.IP
.B info elements-cli
.PP
should give you access to the complete manual.

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.10.
.TH ELEMENTS-TX "1" "May 2019" "elements-tx v0.17.0.1" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.3.
.TH ELEMENTS-TX "1" "September 2021" "elements-tx elements-0.21.0_rc1" "User Commands"
.SH NAME
elements-tx \- manual page for elements-tx v0.17.0.1
elements-tx \- manual page for elements-tx elements-0.21.0_rc1
.SH SYNOPSIS
.B elements-tx
[\fI\,options\/\fR] \fI\,<hex-tx> \/\fR[\fI\,commands\/\fR] \fI\,Update hex-encoded bitcoin transaction\/\fR
@ -9,12 +9,12 @@ elements-tx \- manual page for elements-tx v0.17.0.1
.B elements-tx
[\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR] \fI\,Create hex-encoded bitcoin transaction\/\fR
.SH DESCRIPTION
Elements Core elements\-tx utility version v0.17.0.1\-dirty
Elements Core elements\-tx utility version elements\-0.21.0_rc1
.SH OPTIONS
.HP
\-?
.IP
This help message
Print this help message and exit
.HP
\fB\-create\fR
.IP
@ -28,12 +28,14 @@ Select JSON output
.IP
Output only the hex\-encoded transaction id of the resultant transaction.
.PP
Debugging/Testing options:
.PP
Chain selection options:
.HP
\fB\-chain=\fR<chain>
.IP
Use the chain <chain> (default: main). Reserved values: main, test,
regtest
Use the chain <chain> (default: liquidv1). Reserved values: main, test,
signet, regtest, liquidv1, liquidv1test
.HP
\fB\-con_blockheightinheader\fR
.IP
@ -66,7 +68,8 @@ signatures are necessary to solve it.
.HP
\fB\-fedpegscript\fR
.IP
The script for the federated peg.
The script for the federated peg enforce from genesis block. This script
may stop being enforced once dynamic federations activates.
.HP
\fB\-parentgenesisblockhash\fR
.IP
@ -77,17 +80,36 @@ The genesis blockhash of the parent chain.
Signed blockchain enumberance. Only active when `\-con_signed_blocks` set
to true.
.HP
\fB\-signet\fR
.IP
Use the signet chain. Equivalent to \fB\-chain\fR=\fI\,signet\/\fR. Note that the network
is defined by the \fB\-signetchallenge\fR parameter
.HP
\fB\-signetchallenge\fR
.IP
Blocks must satisfy the given script to be considered valid (only for
signet networks; defaults to the global default signet test
network challenge)
.HP
\fB\-signetseednode\fR
.IP
Specify a seed node for the signet network, in the hostname[:port]
format, e.g. sig.net:1234 (may be used multiple times to specify
multiple seed nodes; defaults to the global default signet test
network seed node(s))
.HP
\fB\-testnet\fR
.IP
Use the test chain
Use the test chain. Equivalent to \fB\-chain\fR=\fI\,test\/\fR.
.PP
Elements Options:
.HP
\fB\-con_blocksubsidy\fR
.IP
Defines the amount of block subsidy to start with, at genesis block.
Defines the amount of block subsidy to start with, at genesis block, in
satoshis.
.HP
\fB\-con_connect_coinbase\fR
\fB\-con_connect_genesis_outputs\fR
.IP
Connect outputs in genesis block to utxo database.
.HP
@ -96,6 +118,18 @@ Connect outputs in genesis block to utxo database.
Starting height for CSV deployment. (default: \fB\-1\fR, which means ACTIVE
from genesis)
.HP
\fB\-con_dyna_deploy_signal\fR
.IP
Whether to signal for the Dynamic Federations deployment (default:
false).
.HP
\fB\-con_dyna_deploy_start\fR
.IP
Starting height for Dynamic Federations deployment. Once active,
signblockscript becomes a BIP141 WSH scriptPubKey of the original
signblockscript. All other dynamic parameters stay
constant.(default: \fB\-1\fR, which means ACTIVE from genesis)
.HP
\fB\-con_elementsmode\fR
.TP
Use Elements\-like instead of Core\-like witness encoding.
@ -108,19 +142,36 @@ required for CA/CT. (default: true)
All non\-zero valued coinbase outputs must go to this scriptPubKey, if
set.
.HP
\fB\-con_taproot_signal_start\fR
.IP
Whether, and at what blockheight, to start signalling for Taproot
activation (default: false) (regtest, Liquid testnet, or custom
only).
.HP
\fB\-dynamic_epoch_length\fR
.IP
Per\-chain parameter that sets how many blocks dynamic federation voting
and enforcement are in effect for.
.HP
\fB\-enforce_pak\fR
.IP
Causes standardness checks to enforce Pegout Authorization Key(PAK)
validation, and miner to include PAK commitments when configured.
Can not be set when acceptnonstdtx is set to true.
validation before dynamic federations, and consensus enforcement
after.
.HP
\fB\-multi_data_permitted\fR
.IP
Allow relay of multiple OP_RETURN outputs. (default: true)
Allow relay of multiple OP_RETURN outputs. (default: \fB\-enforce_pak\fR)
.HP
\fB\-pak\fR
.IP
Entries in the PAK list. Order of entries matter.
Sets the 'first extension space' field to the pak entries ala
pre\-dynamic federations. Only used for testing in custom chains.
.HP
\fB\-total_valid_epochs\fR
.IP
Per\-chain parameter that sets how long a particular fedpegscript is in
effect for.
.PP
Commands:
.IP
@ -180,7 +231,7 @@ sign=SIGHASH\-FLAGS
.IP
Add zero or more signatures to transaction. This command requires JSON
registers:prevtxs=JSON object, privatekeys=JSON object. See
signrawtransaction docs for format of sighash flags, JSON
signrawtransactionwithkey docs for format of sighash flags, JSON
objects.
.PP
Register Commands:
@ -197,15 +248,15 @@ Load JSON file FILENAME into register NAME
set=NAME:JSON\-STRING
.IP
Set register NAME to given JSON\-STRING
.SH COPYRIGHT
Copyright (C) 2009-2019 The Elements Project developers
Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Elements Core useful. Visit
<https://bitcoincore.org> for further information about the software.
The source code is available from <https://github.com/bitcoin/bitcoin>.
This is experimental software.
Distributed under the MIT software license, see the accompanying file COPYING
or <https://opensource.org/licenses/MIT>
.SH "SEE ALSO"
The full documentation for
.B elements-tx
is maintained as a Texinfo manual. If the
.B info
and
.B elements-tx
programs are properly installed at your site, the command
.IP
.B info elements-tx
.PP
should give you access to the complete manual.

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
.TH ELEMENTS-WALLET "1" "February 2019" "elements-wallet v0.17.99.0" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.3.
.TH ELEMENTS-WALLET "1" "September 2021" "elements-wallet elements-0.21.0_rc1" "User Commands"
.SH NAME
elements-wallet \- manual page for elements-wallet v0.17.99.0
elements-wallet \- manual page for elements-wallet elements-0.21.0_rc1
.SH DESCRIPTION
Elements Core elements\-wallet version v0.17.99.0
Elements Core elements\-wallet version elements\-0.21.0_rc1
.PP
wallet\-tool is an offline tool for creating and interacting with Elements Core wallet files.
By default wallet\-tool will act on wallets in the default mainnet wallet directory in the datadir.
elements\-wallet is an offline tool for creating and interacting with Elements Core wallet files.
By default elements\-wallet will act on wallets in the default mainnet wallet directory in the datadir.
To change the target wallet, use the \fB\-datadir\fR, \fB\-wallet\fR and \fB\-testnet\fR/\-regtest arguments.
.SS "Usage:"
.IP
@ -34,13 +34,150 @@ Output debugging information (default: 0).
\fB\-printtoconsole\fR
.IP
Send trace/debug info to console (default: 1 when no \fB\-debug\fR is true, 0
otherwise.
otherwise).
.PP
Chain selection options:
.HP
\fB\-chain=\fR<chain>
.IP
Use the chain <chain> (default: liquidv1). Reserved values: main, test,
signet, regtest, liquidv1, liquidv1test
.HP
\fB\-con_blockheightinheader\fR
.IP
Whether the chain includes the block height directly in the header, for
easier validation of block height in low\-resource environments.
(default: true)
.HP
\fB\-con_has_parent_chain\fR
.IP
Whether or not there is a parent chain.
.HP
\fB\-con_max_block_sig_size\fR
.IP
Max allowed witness data for the signed block header.
.HP
\fB\-con_parent_chain_signblockscript\fR
.IP
Whether parent chain uses pow or signed blocks. If the parent chain uses
signed blocks, the challenge (scriptPubKey) script. If not, an
empty string. (default: empty script [ie parent uses pow])
.HP
\fB\-con_parentpowlimit\fR
.IP
The proof\-of\-work limit value for the parent chain.
.HP
\fB\-con_signed_blocks\fR
.IP
Signed blockchain. Uses input of `\-signblockscript` to define what
signatures are necessary to solve it.
.HP
\fB\-fedpegscript\fR
.IP
The script for the federated peg enforce from genesis block. This script
may stop being enforced once dynamic federations activates.
.HP
\fB\-parentgenesisblockhash\fR
.IP
The genesis blockhash of the parent chain.
.HP
\fB\-signblockscript\fR
.IP
Signed blockchain enumberance. Only active when `\-con_signed_blocks` set
to true.
.HP
\fB\-signet\fR
.IP
Use the signet chain. Equivalent to \fB\-chain\fR=\fI\,signet\/\fR. Note that the network
is defined by the \fB\-signetchallenge\fR parameter
.HP
\fB\-signetchallenge\fR
.IP
Blocks must satisfy the given script to be considered valid (only for
signet networks; defaults to the global default signet test
network challenge)
.HP
\fB\-signetseednode\fR
.IP
Specify a seed node for the signet network, in the hostname[:port]
format, e.g. sig.net:1234 (may be used multiple times to specify
multiple seed nodes; defaults to the global default signet test
network seed node(s))
.HP
\fB\-testnet\fR
.IP
Use the test chain
Use the test chain. Equivalent to \fB\-chain\fR=\fI\,test\/\fR.
.PP
Elements Options:
.HP
\fB\-con_blocksubsidy\fR
.IP
Defines the amount of block subsidy to start with, at genesis block, in
satoshis.
.HP
\fB\-con_connect_genesis_outputs\fR
.IP
Connect outputs in genesis block to utxo database.
.HP
\fB\-con_csv_deploy_start\fR
.IP
Starting height for CSV deployment. (default: \fB\-1\fR, which means ACTIVE
from genesis)
.HP
\fB\-con_dyna_deploy_signal\fR
.IP
Whether to signal for the Dynamic Federations deployment (default:
false).
.HP
\fB\-con_dyna_deploy_start\fR
.IP
Starting height for Dynamic Federations deployment. Once active,
signblockscript becomes a BIP141 WSH scriptPubKey of the original
signblockscript. All other dynamic parameters stay
constant.(default: \fB\-1\fR, which means ACTIVE from genesis)
.HP
\fB\-con_elementsmode\fR
.TP
Use Elements\-like instead of Core\-like witness encoding.
This is
.IP
required for CA/CT. (default: true)
.HP
\fB\-con_mandatorycoinbase\fR
.IP
All non\-zero valued coinbase outputs must go to this scriptPubKey, if
set.
.HP
\fB\-con_taproot_signal_start\fR
.IP
Whether, and at what blockheight, to start signalling for Taproot
activation (default: false) (regtest, Liquid testnet, or custom
only).
.HP
\fB\-dynamic_epoch_length\fR
.IP
Per\-chain parameter that sets how many blocks dynamic federation voting
and enforcement are in effect for.
.HP
\fB\-enforce_pak\fR
.IP
Causes standardness checks to enforce Pegout Authorization Key(PAK)
validation before dynamic federations, and consensus enforcement
after.
.HP
\fB\-multi_data_permitted\fR
.IP
Allow relay of multiple OP_RETURN outputs. (default: \fB\-enforce_pak\fR)
.HP
\fB\-pak\fR
.IP
Sets the 'first extension space' field to the pak entries ala
pre\-dynamic federations. Only used for testing in custom chains.
.HP
\fB\-total_valid_epochs\fR
.IP
Per\-chain parameter that sets how long a particular fedpegscript is in
effect for.
.PP
Commands:
.IP
@ -51,14 +188,20 @@ Create new wallet file
info
.IP
Get wallet info
.SH COPYRIGHT
Copyright (C) 2009-2020 The Elements Project developers
Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Bitcoin Core useful. Visit
<https://elementscore.org> for further information about the software.
The source code is available from <https://github.com/elements/elements>.
This is experimental software.
Distributed under the MIT software license, see the accompanying file COPYING
or <https://opensource.org/licenses/MIT>
.IP
salvage
.IP
Attempt to recover private keys from a corrupt wallet. Warning:
\&'salvage' is experimental.
.SH "SEE ALSO"
The full documentation for
.B elements-wallet
is maintained as a Texinfo manual. If the
.B info
and
.B elements-wallet
programs are properly installed at your site, the command
.IP
.B info elements-wallet
.PP
should give you access to the complete manual.

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
.TH ELEMENTSD "1" "February 2019" "elementsd v0.17.99.0" "User Commands"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.3.
.TH ELEMENTSD "1" "September 2021" "elementsd elements-0.21.0_rc1" "User Commands"
.SH NAME
elementsd \- manual page for elementsd v0.17.99.0
elementsd \- manual page for elementsd elements-0.21.0_rc1
.SH SYNOPSIS
.B elementsd
[\fI\,options\/\fR] \fI\,Start Elements Core Daemon\/\fR
[\fI\,options\/\fR] \fI\,Start Elements Core\/\fR
.SH DESCRIPTION
Elements Core Daemon version v0.17.99.0
Elements Core version elements\-0.21.0_rc1
.SH OPTIONS
.HP
\-?
@ -23,9 +23,17 @@ long fork (%s in cmd is replaced by message)
If this block is in the chain assume that it and its ancestors are valid
and potentially skip their script verification (0 to verify all,
default:
0000000000000000002e63058c023a9a1de233554f28c7b21380b6c9003f36a8,
0000000000000000000000000000000000000000000000000000000000000000,
testnet:
0000000000000037a8cd3e06cd5edbfe9dd1dbcc5dacab279376ef7cfc2b4c75)
000000000000006433d1efec504c53ca332b64963c425395515b01977bd7b3b0,
signet:
0000002a1de0f46379358c1fd09906f7ac59adf3712323ed90eb59e4c183c020)
.HP
\fB\-blockfilterindex=\fR<type>
.IP
Maintain an index of compact filters by block (default: 0, values:
basic). If <type> is not supplied or if <type> = 1, indexes for
all known types are enabled.
.HP
\fB\-blocknotify=\fR<cmd>
.IP
@ -39,12 +47,20 @@ Extra transactions to keep in memory for compact block reconstructions
.HP
\fB\-blocksdir=\fR<dir>
.IP
Specify blocks directory (default: <datadir>/blocks)
Specify directory to hold blocks subdirectory for *.dat files (default:
<datadir>)
.HP
\fB\-blocksonly\fR
.IP
Whether to reject transactions from network peers. Automatic broadcast
and rebroadcast of any transactions from inbound peers is
disabled, unless the peer has the 'forcerelay' permission. RPC
transactions are not affected. (default: 0)
.HP
\fB\-conf=\fR<file>
.IP
Specify configuration file. Relative paths will be prefixed by datadir
location. (default: elements.conf)
Specify path to read\-only configuration file. Relative paths will be
prefixed by datadir location. (default: elements.conf)
.HP
\fB\-daemon\fR
.IP
@ -56,7 +72,9 @@ Specify data directory
.HP
\fB\-dbcache=\fR<n>
.IP
Set database cache size in MiB (4 to 16384, default: 450)
Maximum database cache size <n> MiB (4 to 16384, default: 450). In
addition, unused mempool memory is shared for this cache (see
\fB\-maxmempool\fR).
.HP
\fB\-debuglogfile=\fR<file>
.IP
@ -71,7 +89,7 @@ Specify additional configuration file, relative to the \fB\-datadir\fR path
.HP
\fB\-loadblock=\fR<file>
.IP
Imports blocks from external blk000??.dat file on startup
Imports blocks from external file on startup
.HP
\fB\-maxmempool=\fR<n>
.IP
@ -88,7 +106,7 @@ Do not keep transactions in the mempool longer than <n> hours (default:
.HP
\fB\-par=\fR<n>
.IP
Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 =
Set the number of script verification threads (\fB\-64\fR to 15, 0 = auto, <0 =
leave that many cores free, default: 0)
.HP
\fB\-persistmempool\fR
@ -122,6 +140,18 @@ Rebuild chain state from the currently indexed blocks. When in pruning
mode or if blocks on disk might be corrupted, use full \fB\-reindex\fR
instead.
.HP
\fB\-settings=\fR<file>
.IP
Specify path to dynamic settings data file. Can be disabled with
\fB\-nosettings\fR. File is written at runtime and not meant to be
edited by users (use elements.conf instead for custom settings).
Relative paths will be prefixed by datadir location. (default:
settings.json)
.HP
\fB\-startupnotify=\fR<cmd>
.IP
Execute command on startup.
.HP
\fB\-sysperms\fR
.IP
Create new files with system default permissions, instead of umask 077
@ -144,19 +174,24 @@ Add a node to connect to and attempt to keep the connection open (see
the `addnode` RPC command help for more info). This option can be
specified multiple times to add multiple nodes.
.HP
\fB\-banscore=\fR<n>
\fB\-asmap=\fR<file>
.IP
Threshold for disconnecting misbehaving peers (default: 100)
Specify asn mapping used for bucketing of the peers (default:
ip_asn.map). Relative paths will be prefixed by the net\-specific
datadir location.
.HP
\fB\-bantime=\fR<n>
.IP
Number of seconds to keep misbehaving peers from reconnecting (default:
Default duration (in seconds) of manually configured bans (default:
86400)
.HP
\fB\-bind=\fR<addr>
\fB\-bind=\fR<addr>[:<port>][=onion]
.IP
Bind to given address and always listen on it. Use [host]:port notation
for IPv6
Bind to given address and always listen on it (default: 0.0.0.0). Use
[host]:port notation for IPv6. Append =onion to tag any incoming
connections to that address and port as incoming Tor connections
(default: 127.0.0.1:37041=onion, testnet: 127.0.0.1:18334=onion,
signet: 127.0.0.1:38334=onion, regtest: 127.0.0.1:18445=onion)
.HP
\fB\-connect=\fR<ip>
.IP
@ -193,7 +228,7 @@ Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\
.HP
\fB\-listenonion\fR
.IP
Automatically create Tor hidden service (default: 1)
Automatically create Tor onion service (default: 1)
.HP
\fB\-maxconnections=\fR<n>
.IP
@ -215,12 +250,18 @@ amount. (default: 4200 seconds)
.HP
\fB\-maxuploadtarget=\fR<n>
.IP
Tries to keep outbound traffic under the given target (in MiB per 24h),
0 = no limit (default: 0)
Tries to keep outbound traffic under the given target (in MiB per 24h).
Limit does not apply to peers with 'download' permission. 0 = no
limit (default: 0)
.HP
\fB\-networkactive\fR
.IP
Enable all P2P network activity (default: 1). Can be changed by the
setnetworkactive RPC command
.HP
\fB\-onion=\fR<ip:port>
.IP
Use separate SOCKS5 proxy to reach peers via Tor hidden services, set
Use separate SOCKS5 proxy to reach peers via Tor onion services, set
\fB\-noonion\fR to disable (default: \fB\-proxy\fR)
.HP
\fB\-onlynet=\fR<net>
@ -230,10 +271,14 @@ onion). Incoming connections are not affected by this option.
This option can be specified multiple times to allow multiple
networks.
.HP
\fB\-peerblockfilters\fR
.IP
Serve compact block filters to peers per BIP 157 (default: 0)
.HP
\fB\-peerbloomfilters\fR
.IP
Support filtering of blocks and transaction with bloom filters (default:
1)
0)
.HP
\fB\-permitbaremultisig\fR
.IP
@ -241,8 +286,9 @@ Relay non\-P2SH multisig (default: 1)
.HP
\fB\-port=\fR<port>
.IP
Listen for connections on <port> (default: 8333, testnet: 18333,
regtest: 18444)
Listen for connections on <port>. Nodes not using the default ports
(default: 7042, testnet: 18333, signet: 38333, regtest: 18444)
are unlikely to get incoming connections.
.HP
\fB\-proxy=\fR<ip:port>
.IP
@ -273,29 +319,35 @@ Tor control port to use if onion listening enabled (default:
.IP
Tor control port password (default: empty)
.HP
\fB\-upnp\fR
\fB\-whitebind=\fR<[permissions@]addr>
.IP
Use UPnP to map the listening port (default: 0)
Bind to the given address and add permission flags to the peers
connecting to it. Use [host]:port notation for IPv6. Allowed
permissions: bloomfilter (allow requesting BIP37 filtered blocks
and transactions), noban (do not ban for misbehavior; implies
download), forcerelay (relay transactions that are already in the
mempool; implies relay), relay (relay even in \fB\-blocksonly\fR mode,
and unlimited transaction announcements), mempool (allow
requesting BIP35 mempool contents), download (allow getheaders
during IBD, no disconnect after maxuploadtarget limit), addr
(responses to GETADDR avoid hitting the cache and contain random
records with the most up\-to\-date info). Specify multiple
permissions separated by commas (default:
download,noban,mempool,relay). Can be specified multiple times.
.HP
\fB\-whitebind=\fR<addr>
\fB\-whitelist=\fR<[permissions@]IP address or network>
.IP
Bind to given address and whitelist peers connecting to it. Use
[host]:port notation for IPv6
.HP
\fB\-whitelist=\fR<IP address or network>
.IP
Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or
CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple
times. Whitelisted peers cannot be DoS banned and their
transactions are always relayed, even if they are already in the
mempool, useful e.g. for a gateway
Add permission flags to the peers connecting from the given IP address
(e.g. 1.2.3.4) or CIDR\-notated network (e.g. 1.2.3.0/24). Uses
the same permissions as \fB\-whitebind\fR. Can be specified multiple
times.
.PP
Wallet options:
.HP
\fB\-addresstype\fR
.IP
What type of addresses to use ("legacy", "p2sh\-segwit", or "bech32",
default: "p2sh\-segwit")
default: "bech32")
.HP
\fB\-avoidpartialspends\fR
.IP
@ -303,7 +355,8 @@ Group outputs by address, selecting all or none, instead of selecting on
a per\-output basis. Privacy is improved as an address is only
used once (unless someone sends to it after spending from it),
but may result in slightly higher fees as suboptimal coin
selection may result due to the added limitation (default: 0)
selection may result due to the added limitation (default: 0
(always enabled for wallets with "avoid_reuse" enabled))
.HP
\fB\-changetype\fR
.IP
@ -327,16 +380,25 @@ limited by the fee estimate for the longest target
\fB\-fallbackfee=\fR<amt>
.IP
A fee rate (in BTC/kB) that will be used when fee estimation has
insufficient data (default: 0.0002)
insufficient data. 0 to entirely disable the fallbackfee feature.
(default: 0.00)
.HP
\fB\-keypool=\fR<n>
.IP
Set key pool size to <n> (default: 1000)
Set key pool size to <n> (default: 1000). Warning: Smaller sizes may
increase the risk of losing funds when restoring from an old
backup, if none of the addresses in the original keypool have
been used.
.HP
\fB\-maxapsfee=\fR<n>
.IP
Spend up to this amount in additional (absolute) fees (in BTC) if it
allows the use of partial spend avoidance (default: 0.00)
.HP
\fB\-mintxfee=\fR<amt>
.IP
Fees (in BTC/kB) smaller than this are considered zero fee for
transaction creation (default: 0.00001)
transaction creation (default: 0.000001)
.HP
\fB\-paytxfee=\fR<amt>
.IP
@ -346,10 +408,6 @@ Fee (in BTC/kB) to add to transactions you send (default: 0.00)
.IP
Rescan the block chain for missing wallet transactions on startup
.HP
\fB\-salvagewallet\fR
.IP
Attempt to recover private keys from a corrupt wallet on startup
.HP
\fB\-spendzeroconfchange\fR
.IP
Spend unconfirmed change when sending transactions (default: 1)
@ -359,18 +417,15 @@ Spend unconfirmed change when sending transactions (default: 1)
If paytxfee is not set, include enough fee so transactions begin
confirmation on average within n blocks (default: 6)
.HP
\fB\-upgradewallet\fR
.IP
Upgrade wallet to latest format on startup
.HP
\fB\-wallet=\fR<path>
.IP
Specify wallet database path. Can be specified multiple times to load
multiple wallets. Path is interpreted relative to <walletdir> if
it is not absolute, and will be created if it does not exist (as
a directory containing a wallet.dat file and log files). For
backwards compatibility this will also accept names of existing
data files in <walletdir>.)
Specify wallet path to load at startup. Can be used multiple times to
load multiple wallets. Path is to a directory containing wallet
data and log files. If the path is not absolute, it is
interpreted relative to <walletdir>. This only loads existing
wallets and does not create new ones. For backwards compatibility
this also accepts names of existing top\-level data files in
<walletdir>.
.HP
\fB\-walletbroadcast\fR
.IP
@ -383,18 +438,15 @@ exists, otherwise <datadir>)
.HP
\fB\-walletnotify=\fR<cmd>
.IP
Execute command when a wallet transaction changes (%s in cmd is replaced
by TxID)
Execute command when a wallet transaction changes. %s in cmd is replaced
by TxID and %w is replaced by wallet name. %w is not currently
implemented on windows. On systems where %w is supported, it
should NOT be quoted because this would break shell escaping used
to invoke the command.
.HP
\fB\-walletrbf\fR
.IP
Send transactions with full\-RBF opt\-in enabled (RPC only, default: 0)
.HP
\fB\-zapwallettxes=\fR<mode>
.IP
Delete all wallet transactions and only recover those parts of the
blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g.
payment request information, 2 = drop tx meta data)
Send transactions with full\-RBF opt\-in enabled (RPC only, default: 1)
.PP
ZeroMQ notification options:
.HP
@ -402,17 +454,43 @@ ZeroMQ notification options:
.IP
Enable publish hash block in <address>
.HP
\fB\-zmqpubhashblockhwm=\fR<n>
.IP
Set publish hash block outbound message high water mark (default: 1000)
.HP
\fB\-zmqpubhashtx=\fR<address>
.IP
Enable publish hash transaction in <address>
.HP
\fB\-zmqpubhashtxhwm=\fR<n>
.IP
Set publish hash transaction outbound message high water mark (default:
1000)
.HP
\fB\-zmqpubrawblock=\fR<address>
.IP
Enable publish raw block in <address>
.HP
\fB\-zmqpubrawblockhwm=\fR<n>
.IP
Set publish raw block outbound message high water mark (default: 1000)
.HP
\fB\-zmqpubrawtx=\fR<address>
.IP
Enable publish raw transaction in <address>
.HP
\fB\-zmqpubrawtxhwm=\fR<n>
.IP
Set publish raw transaction outbound message high water mark (default:
1000)
.HP
\fB\-zmqpubsequence=\fR<address>
.IP
Enable publish hash block and tx sequence in <address>
.HP
\fB\-zmqpubsequencehwm=\fR<n>
.IP
Set publish hash sequence message high water mark (default: 1000)
.PP
Debugging/Testing options:
.HP
@ -421,9 +499,9 @@ Debugging/Testing options:
Output debugging information (default: \fB\-nodebug\fR, supplying <category> is
optional). If <category> is not supplied or if <category> = 1,
output all debugging information. <category> can be: net, tor,
mempool, http, bench, zmq, db, rpc, estimatefee, addrman,
mempool, http, bench, zmq, walletdb, rpc, estimatefee, addrman,
selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej,
libevent, coindb, qt, leveldb.
libevent, coindb, qt, leveldb, validation.
.HP
\fB\-debugexclude=\fR<category>
.IP
@ -439,15 +517,19 @@ Print help message with debugging options and exit
.IP
Include IP addresses in debug output (default: 0)
.HP
\fB\-logthreadnames\fR
.IP
Prepend debug output with name of the originating thread (only available
on platforms supporting thread_local) (default: 0)
.HP
\fB\-logtimestamps\fR
.IP
Prepend debug output with timestamp (default: 1)
.HP
\fB\-maxtxfee=\fR<amt>
.IP
Maximum total fees (in BTC) to use in a single wallet transaction or raw
transaction; setting this too low may abort large transactions
(default: 0.10)
Maximum total fees (in BTC) to use in a single wallet transaction;
setting this too low may abort large transactions (default: 0.10)
.HP
\fB\-printtoconsole\fR
.IP
@ -464,9 +546,158 @@ Append comment to the user agent string
.PP
Chain selection options:
.HP
\fB\-bech32_hrp\fR
.IP
The human\-readable part of the chain's bech32 encoding. (default: ex)
.HP
\fB\-blech32_hrp\fR
.IP
The human\-readable part of the chain's blech32 encoding. Used in
confidential addresses.(default: lq)
.HP
\fB\-chain=\fR<chain>
.IP
Use the chain <chain> (default: liquidv1). Reserved values: main, test,
signet, regtest, liquidv1, liquidv1test
.HP
\fB\-con_blockheightinheader\fR
.IP
Whether the chain includes the block height directly in the header, for
easier validation of block height in low\-resource environments.
(default: true)
.HP
\fB\-con_has_parent_chain\fR
.IP
Whether or not there is a parent chain.
.HP
\fB\-con_max_block_sig_size\fR
.IP
Max allowed witness data for the signed block header.
.HP
\fB\-con_parent_chain_signblockscript\fR
.IP
Whether parent chain uses pow or signed blocks. If the parent chain uses
signed blocks, the challenge (scriptPubKey) script. If not, an
empty string. (default: empty script [ie parent uses pow])
.HP
\fB\-con_parent_pegged_asset=\fR<hex>
.IP
Asset ID (hex) for pegged asset for when parent chain has CA. (default:
0x00)
.HP
\fB\-con_parentpowlimit\fR
.IP
The proof\-of\-work limit value for the parent chain.
.HP
\fB\-con_signed_blocks\fR
.IP
Signed blockchain. Uses input of `\-signblockscript` to define what
signatures are necessary to solve it.
.HP
\fB\-ct_bits\fR
.IP
The default number of hiding bits in a rangeproof. Will be exceeded to
cover amounts exceeding the maximum hiding value. (default: 52)
.HP
\fB\-ct_exponent\fR
.IP
The hiding exponent. (default: 0)
.HP
\fB\-extprvkeyprefix\fR
.IP
The 4\-byte prefix, in hex, of the chain's base58 extended private key
encoding. (default: 0488ade4)
.HP
\fB\-extpubkeyprefix\fR
.IP
The 4\-byte prefix, in hex, of the chain's base58 extended public key
encoding. (default: 0488b21e)
.HP
\fB\-fedpegscript\fR
.IP
The script for the federated peg enforce from genesis block. This script
may stop being enforced once dynamic federations activates.
.HP
\fB\-feeasset=\fR<hex>
.IP
Asset ID (hex) for mempool/relay fees (default:
6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d)
.HP
\fB\-initialreissuancetokens=\fR<n>
.IP
The amount of reissuance tokens created in the genesis block. (default:
0)
.HP
\fB\-parent_bech32_hrp\fR
.IP
The human\-readable part of the parent chain's bech32 encoding. (default:
bc)
.HP
\fB\-parent_blech32_hrp\fR
.IP
The human\-readable part of the parent chain's blech32 encoding.
(default: bc)
.HP
\fB\-parentgenesisblockhash\fR
.IP
The genesis blockhash of the parent chain.
.HP
\fB\-parentpubkeyprefix\fR
.IP
The byte prefix, in decimal, of the parent chain's base58 pubkey
address. (default: 111)
.HP
\fB\-parentscriptprefix\fR
.IP
The byte prefix, in decimal, of the parent chain's base58 script
address. (default: 196)
.HP
\fB\-pubkeyprefix\fR
.IP
The byte prefix, in decimal, of the chain's base58 pubkey address.
(default: 57)
.HP
\fB\-scriptprefix\fR
.IP
The byte prefix, in decimal, of the chain's base58 script address.
(default: 39)
.HP
\fB\-secretprefix\fR
.IP
The byte prefix, in decimal, of the chain's base58 secret key encoding.
(default: 128)
.HP
\fB\-signblockscript\fR
.IP
Signed blockchain enumberance. Only active when `\-con_signed_blocks` set
to true.
.HP
\fB\-signet\fR
.IP
Use the signet chain. Equivalent to \fB\-chain\fR=\fI\,signet\/\fR. Note that the network
is defined by the \fB\-signetchallenge\fR parameter
.HP
\fB\-signetchallenge\fR
.IP
Blocks must satisfy the given script to be considered valid (only for
signet networks; defaults to the global default signet test
network challenge)
.HP
\fB\-signetseednode\fR
.IP
Specify a seed node for the signet network, in the hostname[:port]
format, e.g. sig.net:1234 (may be used multiple times to specify
multiple seed nodes; defaults to the global default signet test
network seed node(s))
.HP
\fB\-subsidyasset=\fR<hex>
.IP
Asset ID (hex) for the block subsidy (default:
6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d)
.HP
\fB\-testnet\fR
.IP
Use the test chain
Use the test chain. Equivalent to \fB\-chain\fR=\fI\,test\/\fR.
.PP
Node relay options:
.HP
@ -487,17 +718,19 @@ Maximum size of data in data carrier transactions we relay and mine
\fB\-minrelaytxfee=\fR<amt>
.IP
Fees (in BTC/kB) smaller than this are considered zero fee for relaying,
mining and transaction creation (default: 0.00001)
mining and transaction creation (default: 0.000001)
.HP
\fB\-whitelistforcerelay\fR
.IP
Force relay of transactions from whitelisted peers even if they violate
local relay policy (default: 0)
Add 'forcerelay' permission to whitelisted inbound peers with default
permissions. This will relay transactions even if the
transactions were already in the mempool. (default: 0)
.HP
\fB\-whitelistrelay\fR
.IP
Accept relayed transactions received from whitelisted peers even when
not relaying transactions (default: 1)
Add 'relay' permission to whitelisted inbound peers with default
permissions. This will accept relayed transactions even when not
relaying transactions (default: 1)
.PP
Block creation options:
.HP
@ -508,7 +741,7 @@ Set maximum BIP141 block weight (default: 3996000)
\fB\-blockmintxfee=\fR<amt>
.IP
Set lowest fee rate (in BTC/kB) for transactions to be included in block
creation. (default: 0.00001)
creation. (default: 0.000001)
.PP
RPC server options:
.HP
@ -552,8 +785,8 @@ Password for JSON\-RPC connections
.HP
\fB\-rpcport=\fR<port>
.IP
Listen for JSON\-RPC connections on <port> (default: 8332, testnet:
18332, regtest: 18443)
Listen for JSON\-RPC connections on <port> (default: 7041, testnet:
18332, signet: 38332, regtest: 18443)
.HP
\fB\-rpcserialversion\fR
.IP
@ -568,16 +801,169 @@ Set the number of threads to service RPC calls (default: 4)
.IP
Username for JSON\-RPC connections
.HP
\fB\-rpcwhitelist=\fR<whitelist>
.IP
Set a whitelist to filter incoming RPC calls for a specific user. The
field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc
2>,...,<rpc n>. If multiple whitelists are set for a given user,
they are set\-intersected. See \fB\-rpcwhitelistdefault\fR documentation
for information on default whitelist behavior.
.HP
\fB\-rpcwhitelistdefault\fR
.IP
Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault
is set to 0, if any \fB\-rpcwhitelist\fR is set, the rpc server acts as
if all rpc users are subject to empty\-unless\-otherwise\-specified
whitelists. If rpcwhitelistdefault is set to 1 and no
\fB\-rpcwhitelist\fR is set, rpc server acts as if all rpc users are
subject to empty whitelists.
.HP
\fB\-server\fR
.IP
Accept command line and JSON\-RPC commands
.SH COPYRIGHT
Copyright (C) 2009-2019 The Bitcoin Core developers
Please contribute if you find Bitcoin Core useful. Visit
<https://bitcoincore.org> for further information about the software.
The source code is available from <https://github.com/bitcoin/bitcoin>.
This is experimental software.
Distributed under the MIT software license, see the accompanying file COPYING
or <https://opensource.org/licenses/MIT>
.PP
Elements Options:
.HP
\fB\-assetdir\fR
.IP
Entries of pet names of assets, in this format:asset=<hex>:<label>.
There can be any number of entries.
.HP
\fB\-blindedaddresses\fR
.IP
Give blind addresses by default via getnewaddress and
getrawchangeaddress. (default: \fB\-con_elementsmode\fR value)
.HP
\fB\-blindedprefix\fR
.IP
The byte prefix, in decimal, of blinded addresses. (default: 4)
.HP
\fB\-con_blocksubsidy\fR
.IP
Defines the amount of block subsidy to start with, at genesis block, in
satoshis.
.HP
\fB\-con_connect_genesis_outputs\fR
.IP
Connect outputs in genesis block to utxo database.
.HP
\fB\-con_csv_deploy_start\fR
.IP
Starting height for CSV deployment. (default: \fB\-1\fR, which means ACTIVE
from genesis)
.HP
\fB\-con_dyna_deploy_signal\fR
.IP
Whether to signal for the Dynamic Federations deployment (default:
false).
.HP
\fB\-con_dyna_deploy_start\fR
.IP
Starting height for Dynamic Federations deployment. Once active,
signblockscript becomes a BIP141 WSH scriptPubKey of the original
signblockscript. All other dynamic parameters stay
constant.(default: \fB\-1\fR, which means ACTIVE from genesis)
.HP
\fB\-con_elementsmode\fR
.TP
Use Elements\-like instead of Core\-like witness encoding.
This is
.IP
required for CA/CT. (default: true)
.HP
\fB\-con_mandatorycoinbase\fR
.IP
All non\-zero valued coinbase outputs must go to this scriptPubKey, if
set.
.HP
\fB\-con_taproot_signal_start\fR
.IP
Whether, and at what blockheight, to start signalling for Taproot
activation (default: false) (regtest, Liquid testnet, or custom
only).
.HP
\fB\-defaultpeggedassetname\fR
.IP
Default name of the pegged asset. (default: bitcoin)
.HP
\fB\-dynamic_epoch_length\fR
.IP
Per\-chain parameter that sets how many blocks dynamic federation voting
and enforcement are in effect for.
.HP
\fB\-enforce_pak\fR
.IP
Causes standardness checks to enforce Pegout Authorization Key(PAK)
validation before dynamic federations, and consensus enforcement
after.
.HP
\fB\-mainchainrpccookiefile=\fR<file>
.IP
The bitcoind cookie auth path which the daemon will use to connect to
the trusted mainchain daemon to validate peg\-ins. (default:
`<datadir>/regtest/.cookie`)
.HP
\fB\-mainchainrpchost=\fR<host>
.IP
The address which the daemon will try to connect to the trusted
mainchain daemon to validate peg\-ins, if enabled. (default:
127.0.0.1)
.HP
\fB\-mainchainrpcpassword=\fR<pwd>
.IP
The rpc password which the daemon will use to connect to the trusted
mainchain daemon to validate peg\-ins, if enabled. (default:
cookie auth)
.HP
\fB\-mainchainrpcport=\fR<n>
.IP
The port which the daemon will try to connect to the trusted mainchain
daemon to validate peg\-ins, if enabled. (default: 8332)
.HP
\fB\-mainchainrpctimeout=\fR<n>
.IP
Timeout in seconds during mainchain RPC requests, or 0 for no timeout.
(default: 900)
.HP
\fB\-mainchainrpcuser=\fR<user>
.IP
The rpc username that the daemon will use to connect to the trusted
mainchain daemon to validate peg\-ins, if enabled. (default:
cookie auth)
.HP
\fB\-multi_data_permitted\fR
.IP
Allow relay of multiple OP_RETURN outputs. (default: \fB\-enforce_pak\fR)
.HP
\fB\-pak\fR
.IP
Sets the 'first extension space' field to the pak entries ala
pre\-dynamic federations. Only used for testing in custom chains.
.HP
\fB\-peginconfirmationdepth=\fR<n>
.IP
Pegin claims must be this deep to be considered valid. (default: 8)
.HP
\fB\-total_valid_epochs\fR
.IP
Per\-chain parameter that sets how long a particular fedpegscript is in
effect for.
.HP
\fB\-validatepegin\fR
.IP
Validate peg\-in claims. An RPC connection will be attempted to the
trusted mainchain daemon using the `mainchain*` settings below.
All functionaries must run this enabled. (default: true if chain
has federated peg)
.SH "SEE ALSO"
The full documentation for
.B elementsd
is maintained as a Texinfo manual. If the
.B info
and
.B elementsd
programs are properly installed at your site, the command
.IP
.B info elementsd
.PP
should give you access to the complete manual.

View file

@ -1,25 +1,11 @@
*After branching off for a major version release of Bitcoin Core, use this
template to create the initial release notes draft.*
0.21.1 Release Notes
====================
*The release notes draft is a temporary file that can be added to by anyone. See
[/doc/developer-notes.md#release-notes](/doc/developer-notes.md#release-notes)
for the process.*
Bitcoin Core version 0.21.1 is now available from:
*Create the draft, named* "*version* Release Notes Draft"
*(e.g. "0.20.0 Release Notes Draft"), as a collaborative wiki in:*
<https://bitcoincore.org/bin/bitcoin-core-0.21.1/>
https://github.com/bitcoin-core/bitcoin-devwiki/wiki/
*Before the final release, move the notes back to this git repository.*
*version* Release Notes Draft
===============================
Bitcoin Core version *version* is now available from:
<https://bitcoincore.org/bin/bitcoin-core-*version*/>
This release includes new features, various bug fixes and performance
This minor release includes various bug fixes and performance
improvements, as well as updated translations.
Please report bugs using the issue tracker at GitHub:
@ -45,11 +31,6 @@ wallet versions of Bitcoin Core are generally supported.
Compatibility
==============
During this release cycle, work has been done to ensure that the codebase is fully
compatible with C++17. The intention is to begin using C++17 features starting
with the 0.22.0 release. This means that a compiler that supports C++17 will be
required to compile 0.22.0.
Bitcoin Core is supported and extensively tested on operating systems
using the Linux kernel, macOS 10.14+, and Windows 7 and newer. Bitcoin
Core should also work on most other Unix-like systems but is not as
@ -60,421 +41,16 @@ From Bitcoin Core 0.22.0 onwards, macOS versions earlier than 10.14 are no
longer supported. Additionally, Bitcoin Core does not yet change appearance
when macOS "dark mode" is activated.
The node's known peers are persisted to disk in a file called `peers.dat`. The
format of this file has been changed in a backwards-incompatible way in order to
accommodate the storage of Tor v3 and other BIP155 addresses. This means that if
the file is modified by 0.21.0 or newer then older versions will not be able to
read it. Those old versions, in the event of a downgrade, will log an error
message "Incorrect keysize in addrman deserialization" and will continue normal
operation as if the file was missing, creating a new empty one. (#19954)
Notable changes
===============
P2P and network changes
-----------------------
- The mempool now tracks whether transactions submitted via the wallet or RPCs
have been successfully broadcast. Every 10-15 minutes, the node will try to
announce unbroadcast transactions until a peer requests it via a `getdata`
message or the transaction is removed from the mempool for other reasons.
The node will not track the broadcast status of transactions submitted to the
node using P2P relay. This version reduces the initial broadcast guarantees
for wallet transactions submitted via P2P to a node running the wallet. (#18038)
- The size of the set of transactions that peers have announced and we consider
for requests has been reduced from 100000 to 5000 (per peer), and further
announcements will be ignored when that limit is reached. If you need to dump
(very) large batches of transactions, exceptions can be made for trusted
peers using the "relay" network permission. For localhost for example it can
be enabled using the command line option `-whitelist=relay@127.0.0.1`.
(#19988)
- The Tor onion service that is automatically created by setting the
`-listenonion` configuration parameter will now be created as a Tor v3 service
instead of Tor v2. The private key that was used for Tor v2 (if any) will be
left untouched in the `onion_private_key` file in the data directory (see
`-datadir`) and can be removed if not needed. Bitcoin Core will no longer
attempt to read it. The private key for the Tor v3 service will be saved in a
file named `onion_v3_private_key`. To use the deprecated Tor v2 service (not
recommended), then `onion_private_key` can be copied over
`onion_v3_private_key`, e.g.
`cp -f onion_private_key onion_v3_private_key`. (#19954)
Updated RPCs
------------
- The `getpeerinfo` RPC now has additional `last_block` and `last_transaction`
fields that return the UNIX epoch time of the last block and the last valid
transaction received from each peer. (#19731)
- `getnetworkinfo` now returns two new fields, `connections_in` and
`connections_out`, that provide the number of inbound and outbound peer
connections. These new fields are in addition to the existing `connections`
field, which returns the total number of peer connections. (#19405)
- Exposed transaction version numbers are now treated as unsigned 32-bit
integers instead of signed 32-bit integers. This matches their treatment in
consensus logic. Versions greater than 2 continue to be non-standard
(matching previous behavior of smaller than 1 or greater than 2 being
non-standard). Note that this includes the joinpsbt command, which combines
partially-signed transactions by selecting the highest version number.
(#16525)
- `getmempoolinfo` now returns an additional `unbroadcastcount` field. The
mempool tracks locally submitted transactions until their initial broadcast
is acknowledged by a peer. This field returns the count of transactions
waiting for acknowledgement.
- Mempool RPCs such as `getmempoolentry` and `getrawmempool` with
`verbose=true` now return an additional `unbroadcast` field. This indicates
whether initial broadcast of the transaction has been acknowledged by a
peer. `getmempoolancestors` and `getmempooldescendants` are also updated.
- The `bumpfee`, `fundrawtransaction`, `sendmany`, `sendtoaddress`, and `walletcreatefundedpsbt`
RPC commands have been updated to include two new fee estimation methods "BTC/kB" and "sat/B".
The target is the fee expressed explicitly in the given form. Note that use of this feature
will trigger BIP 125 (replace-by-fee) opt-in. (#11413)
- In addition, the `estimate_mode` parameter is now case insensitive for all of
the above RPC commands. (#11413)
- The `bumpfee` command now uses `conf_target` rather than `confTarget` in the
options. (#11413)
- The `getpeerinfo` RPC no longer returns the `banscore` field unless the configuration
option `-deprecatedrpc=banscore` is used. The `banscore` field will be fully
removed in the next major release. (#19469)
- The `testmempoolaccept` RPC returns `vsize` and a `fees` object with the `base` fee
if the transaction would pass validation. (#19940)
- The `getpeerinfo` RPC now returns a `connection_type` field. This indicates
the type of connection established with the peer. It will return one of six
options. For more information, see the `getpeerinfo` help documentation.
(#19725)
- The `getpeerinfo` RPC no longer returns the `addnode` field by default. This
field will be fully removed in the next major release. It can be accessed
with the configuration option `-deprecatedrpc=getpeerinfo_addnode`. However,
it is recommended to instead use the `connection_type` field (it will return
`manual` when addnode is true). (#19725)
- The `walletcreatefundedpsbt` RPC call will now fail with
`Insufficient funds` when inputs are manually selected but are not enough to cover
the outputs and fee. Additional inputs can automatically be added through the
new `add_inputs` option. (#16377)
- The `fundrawtransaction` RPC now supports `add_inputs` option that when `false`
prevents adding more inputs if necessary and consequently the RPC fails.
Changes to Wallet or GUI related RPCs can be found in the GUI or Wallet section below.
New RPCs
--------
- The `getindexinfo` RPC returns the actively running indices of the node,
including their current sync status and height. It also accepts an `index_name`
to specify returning only the status of that index. (#19550)
Build System
------------
Updated settings
----------------
- The same ZeroMQ notification (e.g. `-zmqpubhashtx=address`) can now be
specified multiple times to publish the same notification to different ZeroMQ
sockets. (#18309)
- The `-banscore` configuration option, which modified the default threshold for
disconnecting and discouraging misbehaving peers, has been removed as part of
changes in 0.20.1 and in this release to the handling of misbehaving peers.
Refer to "Changes regarding misbehaving peers" in the 0.20.1 release notes for
details. (#19464)
- The `-debug=db` logging category, which was deprecated in 0.20 and replaced by
`-debug=walletdb` to distinguish it from `coindb`, has been removed. (#19202)
- A `download` permission has been extracted from the `noban` permission. For
compatibility, `noban` implies the `download` permission, but this may change
in future releases. Refer to the help of the affected settings `-whitebind`
and `-whitelist` for more details. (#19191)
- Netmasks that contain 1-bits after 0-bits (the 1-bits are not contiguous on
the left side, e.g. 255.0.255.255) are no longer accepted. They are invalid
according to RFC 4632. Netmasks are used in the `-rpcallowip` and `-whitelist`
configuration options and in the `setban` RPC. (#19628)
Changes to Wallet or GUI related settings can be found in the GUI or Wallet section below.
Tools and Utilities
-------------------
- The `connections` field of `bitcoin-cli -getinfo` is expanded to return a JSON
object with `in`, `out` and `total` numbers of peer connections. It previously
returned a single integer value for the total number of peer connections. (#19405)
- A new `bitcoin-cli -generate` command, equivalent to RPC `generatenewaddress`
followed by `generatetoaddress`, can generate blocks for command line testing
purposes. This is a client-side version of the
former `generate` RPC. See the help for details. (#19133)
- The `bitcoin-cli -getinfo` command now displays the wallet name and balance for
each of the loaded wallets when more than one is loaded (e.g. in multiwallet
mode) and a wallet is not specified with `-rpcwallet`. (#18594)
New settings
------------
- The `startupnotify` option is used to specify a command to
execute when Bitcoin Core has finished with its startup
sequence. (#15367)
Wallet
------
- Backwards compatibility has been dropped for two `getaddressinfo` RPC
deprecations, as notified in the 0.20 release notes. The deprecated `label`
field has been removed as well as the deprecated `labels` behavior of
returning a JSON object containing `name` and `purpose` key-value pairs. Since
0.20, the `labels` field returns a JSON array of label names. (#19200)
- To improve wallet privacy, the frequency of wallet rebroadcast attempts is
reduced from approximately once every 15 minutes to once every 12-36 hours.
To maintain a similar level of guarantee for initial broadcast of wallet
transactions, the mempool tracks these transactions as a part of the newly
introduced unbroadcast set. See the "P2P and network changes" section for
more information on the unbroadcast set. (#18038)
- The `sendtoaddress` and `sendmany` RPCs accept an optional `verbose=True`
argument to also return the fee reason about the sent tx. (#19501)
- The wallet can create a transaction without change even when the keypool is
empty. Previously it failed. (#17219)
- The `-salvagewallet` startup option has been removed. A new `salvage` command
has been added to the `bitcoin-wallet` tool which performs the salvage
operations that `-salvagewallet` did. (#18918)
- A new configuration flag `-maxapsfee` has been added, which sets the max
allowed avoid partial spends (APS) fee. It defaults to 0 (i.e. fee is the
same with and without APS). Setting it to -1 will disable APS, unless
`-avoidpartialspends` is set. (#14582)
- The wallet will now avoid partial spends (APS) by default, if this does not
result in a difference in fees compared to the non-APS variant. The allowed
fee threshold can be adjusted using the new `-maxapsfee` configuration
option. (#14582)
- The `createwallet`, `loadwallet`, and `unloadwallet` RPCs now accept
`load_on_startup` options to modify the settings list. Unless these options
are explicitly set to true or false, the list is not modified, so the RPC
methods remain backwards compatible. (#15937)
- A new `send` RPC with similar syntax to `walletcreatefundedpsbt`, including
support for coin selection and a custom fee rate. The `send` RPC is
experimental and may change in subsequent releases. Using it is encouraged
once it's no longer experimental: `sendmany` and `sendtoaddress` may be
deprecated in a future release. (#16378)
- `fundrawtransaction` and `walletcreatefundedpsbt` when used with the
`lockUnspents` argument now lock manually selected coins, in addition to
automatically selected coins. Note that locked coins are never used in
automatic coin selection, but can still be manually selected. (#18244)
- The `-zapwallettxes` startup option has been removed and its functionality
removed from the wallet. This option was originally intended to allow for
the fee bumping of transactions that did not signal RBF. This functionality
has been superseded with the abandon transaction feature. (#19671)
- The error code when no wallet is loaded, but a wallet RPC is called, has been
changed from `-32601` (method not found) to `-18` (wallet not found).
(#20101)
### Automatic wallet creation removed
Bitcoin Core will no longer automatically create new wallets on startup. It will
load existing wallets specified by `-wallet` options on the command line or in
`bitcoin.conf` or `settings.json` files. And by default it will also load a
top-level unnamed ("") wallet. However, if specified wallets don't exist,
Bitcoin Core will now just log warnings instead of creating new wallets with
new keys and addresses like previous releases did.
New wallets can be created through the GUI (which has a more prominent create
wallet option), through the `bitcoin-cli createwallet` or `bitcoin-wallet
create` commands, or the `createwallet` RPC. (#15454)
### Experimental Descriptor Wallets
Please note that Descriptor Wallets are still experimental and not all expected functionality
is available. Additionally there may be some bugs and current functions may change in the future.
Bugs and missing functionality can be reported to the [issue tracker](https://github.com/bitcoin/bitcoin/issues).
0.21 introduces a new type of wallet - Descriptor Wallets. Descriptor Wallets store
scriptPubKey information using descriptors. This is in contrast to the Legacy Wallet
structure where keys are used to generate scriptPubKeys and addresses. Because of this
shift to being script based instead of key based, many of the confusing things that Legacy
Wallets do are not possible with Descriptor Wallets. Descriptor Wallets use a definition
of "mine" for scripts which is simpler and more intuitive than that used by Legacy Wallets.
Descriptor Wallets also uses different semantics for watch-only things and imports.
As Descriptor Wallets are a new type of wallet, their introduction does not affect existing wallets.
Users who already have a Bitcoin Core wallet can continue to use it as they did before without
any change in behavior. Newly created Legacy Wallets (which is the default type of wallet) will
behave as they did in previous versions of Bitcoin Core.
The differences between Descriptor Wallets and Legacy Wallets are largely limited to non user facing
things. They are intended to behave similarly except for the import/export and watchonly functionality
as described below.
#### Creating Descriptor Wallets
Descriptor Wallets are not created by default. They must be explicitly created using the
`createwallet` RPC or via the GUI. A `descriptors` option has been added to `createwallet`.
Setting `descriptors` to `true` will create a Descriptor Wallet instead of a Legacy Wallet.
In the GUI, a checkbox has been added to the Create Wallet Dialog to indicate that a
Descriptor Wallet should be created.
Without those options being set, a Legacy Wallet will be created instead. Additionally the
Default Wallet created upon first startup of Bitcoin Core will be a Legacy Wallet.
#### `IsMine` Semantics
`IsMine` refers to the function used to determine whether a script belongs to the wallet.
This is used to determine whether an output belongs to the wallet. `IsMine` in Legacy Wallets
returns true if the wallet would be able to sign an input that spends an output with that script.
Since keys can be involved in a variety of different scripts, this definition for `IsMine` can
lead to many unexpected scripts being considered part of the wallet.
With Descriptor Wallets, descriptors explicitly specify the set of scripts that are owned by
the wallet. Since descriptors are deterministic and easily enumerable, users will know exactly
what scripts the wallet will consider to belong to it. Additionally the implementation of `IsMine`
in Descriptor Wallets is far simpler than for Legacy Wallets. Notably, in Legacy Wallets, `IsMine`
allowed for users to take one type of address (e.g. P2PKH), mutate it into another address type
(e.g. P2WPKH), and the wallet would still detect outputs sending to the new address type
even without that address being requested from the wallet. Descriptor Wallets does not
allow for this and will only watch for the addresses that were explicitly requested from the wallet.
These changes to `IsMine` will make it easier to reason about what scripts the wallet will
actually be watching for in outputs. However for the vast majority of users, this change is
largely transparent and will not have noticeable effect.
#### Imports and Exports
In Legacy Wallets, raw scripts and keys could be imported to the wallet. Those imported scripts
and keys are treated separately from the keys generated by the wallet. This complicates the `IsMine`
logic as it has to distinguish between spendable and watchonly.
Descriptor Wallets handle importing scripts and keys differently. Only complete descriptors can be
imported. These descriptors are then added to the wallet as if it were a descriptor generated by
the wallet itself. This simplifies the `IsMine` logic so that it no longer has to distinguish
between spendable and watchonly. As such, the watchonly model for Descriptor Wallets is also
different and described in more detail in the next section.
To import into a Descriptor Wallet, a new `importdescriptors` RPC has been added that uses a syntax
similar to that of `importmulti`.
As Legacy Wallets and Descriptor Wallets use different mechanisms for storing and importing scripts and keys
the existing import RPCs have been disabled for descriptor wallets.
New export RPCs for Descriptor Wallets have not yet been added.
The following RPCs are disabled for Descriptor Wallets:
* importprivkey
* importpubkey
* importaddress
* importwallet
* dumpprivkey
* dumpwallet
* importmulti
* addmultisigaddress
* sethdseed
#### Watchonly Wallets
A Legacy Wallet contains both private keys and scripts that were being watched.
Those watched scripts would not contribute to your normal balance. In order to see the watchonly
balance and to use watchonly things in transactions, an `include_watchonly` option was added
to many RPCs that would allow users to do that. However it is easy to forget to include this option.
Descriptor Wallets move to a per-wallet watchonly model. Instead an entire wallet is considered to be
watchonly depending on whether it was created with private keys disabled. This eliminates the need
to distinguish between things that are watchonly and things that are not within a wallet itself.
This change does have a caveat. If a Descriptor Wallet with private keys *enabled* has
a multiple key descriptor without all of the private keys (e.g. `multi(...)` with only one private key),
then the wallet will fail to sign and broadcast transactions. Such wallets would need to use the PSBT
workflow but the typical GUI Send, `sendtoaddress`, etc. workflows would still be available, just
non-functional.
This issue is worsened if the wallet contains both single key (e.g. `wpkh(...)`) descriptors and such
multiple key descriptors as some transactions could be signed and broadast and others not. This is
due to some transactions containing only single key inputs, while others would contain both single
key and multiple key inputs, depending on which are available and how the coin selection algorithm
selects inputs. However this is not considered to be a supported use case; multisigs
should be in their own wallets which do not already have descriptors. Although users cannot export
descriptors with private keys for now as explained earlier.
#### BIP 44/49/84 Support
The change to using descriptors changes the default derivation paths used by Bitcoin Core
to adhere to BIP 44/49/84. Descriptors with different derivation paths can be imported without
issue.
### Wallet RPC changes
- The `upgradewallet` RPC replaces the `-upgradewallet` command line option.
(#15761)
- The `settxfee` RPC will fail if the fee was set higher than the `-maxtxfee`
command line setting. The wallet will already fail to create transactions
with fees higher than `-maxtxfee`. (#18467)
GUI changes
-----------
- Wallets created or loaded in the GUI will now be automatically loaded on
startup, so they don't need to be manually reloaded next time Bitcoin Core is
started. The list of wallets to load on startup is stored in
`\<datadir\>/settings.json` and augments any command line or `bitcoin.conf`
`-wallet=` settings that specify more wallets to load. Wallets that are
unloaded in the GUI get removed from the settings list so they won't load
again automatically next startup. (#19754)
- The GUI Peers window no longer displays a "Ban Score" field. This is part of
changes in 0.20.1 and in this release to the handling of misbehaving
peers. Refer to "Changes regarding misbehaving peers" in the 0.20.1 release
notes for details. (#19512)
Low-level changes
=================
RPC
---
- To make RPC `sendtoaddress` more consistent with `sendmany` the following error
`sendtoaddress` codes were changed from `-4` to `-6`:
- Insufficient funds
- Fee estimation failed
- Transaction has too long of a mempool chain
- The `sendrawtransaction` error code for exceeding `maxfeerate` has been changed from
`-26` to `-25`. The error string has been changed from "absurdly-high-fee" to
"Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)." The
`testmempoolaccept` RPC returns `max-fee-exceeded` rather than `absurdly-high-fee`
as the `reject-reason`. (#19339)
0.21.1 change log
=================
- To make wallet and rawtransaction RPCs more consistent, the error message for
exceeding maximum feerate has been changed to "Fee exceeds maximum configured by user
(e.g. -maxtxfee, maxfeerate)." (#19339)
Tests
-----
- The BIP 325 default signet can be enabled by the `-chain=signet` or `-signet`
setting. The settings `-signetchallenge` and `-signetseednode` allow
enabling a custom signet.
Credits
=======

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,16 @@ It is possible to run Bitcoin Core as a Tor onion service, and connect to such s
The following directions assume you have a Tor proxy running on port 9050. Many distributions default to having a SOCKS proxy listening on port 9050, but others may not. In particular, the Tor Browser Bundle defaults to listening on port 9150. See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort) for how to properly
configure Tor.
## How to see information about your Tor configuration via Bitcoin Core
There are several ways to see your local onion address in Bitcoin Core:
- in the debug log (grep for "tor:" or "AddLocal")
- in the output of RPC `getnetworkinfo` in the "localaddresses" section
- in the output of the CLI `-netinfo` peer connections dashboard
You may set the `-debug=tor` config logging option to have additional
information in the debug log about your Tor configuration.
## 1. Run Bitcoin Core behind a Tor proxy
@ -52,14 +62,19 @@ The directory can be different of course, but virtual port numbers should be equ
your bitcoind's P2P listen port (8333 by default), and target addresses and ports
should be equal to binding address and port for inbound Tor connections (127.0.0.1:8334 by default).
-externalip=X You can tell bitcoin about its publicly reachable address using
this option, and this can be a .onion address. Given the above
configuration, you can find your .onion address in
-externalip=X You can tell bitcoin about its publicly reachable addresses using
this option, and this can be an onion address. Given the above
configuration, you can find your onion address in
/var/lib/tor/bitcoin-service/hostname. For connections
coming from unroutable addresses (such as 127.0.0.1, where the
Tor proxy typically runs), .onion addresses are given
Tor proxy typically runs), onion addresses are given
preference for your node to advertise itself with.
You can set multiple local addresses with -externalip. The
one that will be rumoured to a particular peer is the most
compatible one and also using heuristics, e.g. the address
with the most incoming connections, etc.
-listen You'll need to enable listening for incoming connections, as this
is off by default behind a proxy.
@ -72,7 +87,7 @@ should be equal to binding address and port for inbound Tor connections (127.0.0
In a typical situation, where you're only reachable via Tor, this should suffice:
./bitcoind -proxy=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -listen
./bitcoind -proxy=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -listen
(obviously, replace the .onion address with your own). It should be noted that you still
listen on all devices and another node could establish a clearnet connection, when knowing
@ -90,7 +105,7 @@ and open port 8333 on your firewall (or use -upnp).
If you only want to use Tor to reach .onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use:
./bitcoind -onion=127.0.0.1:9050 -externalip=57qr3yd1nyntf5k.onion -discover
./bitcoind -onion=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -discover
## 3. Automatically listen on Tor

View file

@ -4,13 +4,16 @@
# Network-related settings:
# Note that if you use testnet or regtest, particularly with the options
# Note that if you use testnet, signet or regtest, particularly with the options
# addnode, connect, port, bind, rpcport, rpcbind or wallet, you will also
# want to read "[Sections]" further down.
# Run on the test network instead of the real bitcoin network.
# Run on the testnet network
#testnet=0
# Run on a signet network
#signet=0
# Run a regression test network
#regtest=0
@ -57,7 +60,7 @@
# Listening mode, enabled by default except when 'connect' is being used
#listen=1
# Port on which to listen for connections (default: 8333, testnet: 18333, regtest: 18444)
# Port on which to listen for connections (default: 8333, testnet: 18333, signet: 38333, regtest: 18444)
#port=
# Maximum number of inbound+outbound connections.
@ -155,7 +158,7 @@
#minimizetotray=1
# [Sections]
# Most options apply to mainnet, testnet and regtest.
# Most options apply to mainnet, testnet, signet and regtest.
# If you want to confine an option to just one network, you should add it in the
# relevant section below.
# EXCEPTIONS: The options addnode, connect, port, bind, rpcport, rpcbind and wallet
@ -167,5 +170,8 @@
# Options only for testnet
[test]
# Options only for signet
[signet]
# Options only for regtest
[regtest]

View file

@ -20,9 +20,8 @@ else
LIBUNIVALUE = $(UNIVALUE_LIBS)
endif
BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/secp256k1/include $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
LIBBITCOIN_SERVER=libbitcoin_server.a

View file

@ -77,6 +77,7 @@ QT_MOC_CPP = \
qt/moc_transactiondesc.cpp \
qt/moc_transactiondescdialog.cpp \
qt/moc_transactionfilterproxy.cpp \
qt/moc_transactionoverviewwidget.cpp \
qt/moc_transactiontablemodel.cpp \
qt/moc_transactionview.cpp \
qt/moc_utilitydialog.cpp \
@ -150,6 +151,7 @@ BITCOIN_QT_H = \
qt/transactiondesc.h \
qt/transactiondescdialog.h \
qt/transactionfilterproxy.h \
qt/transactionoverviewwidget.h \
qt/transactionrecord.h \
qt/transactiontablemodel.h \
qt/transactionview.h \

View file

@ -158,7 +158,8 @@ FUZZ_TARGETS = \
test/fuzz/txundo_deserialize \
test/fuzz/uint160_deserialize \
test/fuzz/uint256_deserialize \
test/fuzz/witness_program
test/fuzz/witness_program \
test/fuzz/versionbits
if ENABLE_FUZZ
noinst_PROGRAMS += $(FUZZ_TARGETS:=)
@ -1274,6 +1275,12 @@ test_fuzz_witness_program_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_witness_program_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_witness_program_SOURCES = test/fuzz/witness_program.cpp
test_fuzz_versionbits_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_versionbits_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_versionbits_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_versionbits_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_versionbits_SOURCES = test/fuzz/versionbits.cpp
endif # ENABLE_FUZZ
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)

View file

@ -52,7 +52,10 @@ static void CoinSelection(benchmark::Bench& bench)
}
const CoinEligibilityFilter filter_standard(1, 6, 0);
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
const CoinSelectionParams coin_selection_params(/* use_bnb= */ true, /* change_output_size= */ 34,
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
/* tx_no_inputs_size= */ 0);
bench.run([&] {
std::set<CInputCoin> setCoinsRet;
CAmountMap mapValueRet;

View file

@ -332,7 +332,7 @@ public:
bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
vSeeds.emplace_back("178.128.221.177");
vSeeds.emplace_back("2a01:7c8:d005:390::5");
vSeeds.emplace_back("ntv3mtqw5wt63red.onion:38333");
vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333");
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000000019fd16269a");
consensus.defaultAssumeValid = uint256S("0x0000002a1de0f46379358c1fd09906f7ac59adf3712323ed90eb59e4c183c020"); // 9434

View file

@ -119,31 +119,72 @@ static bool CheckTxScriptsSanity(const CMutableTransaction& tx)
static bool DecodeTx(CMutableTransaction& tx, const std::vector<unsigned char>& tx_data, bool try_no_witness, bool try_witness)
{
// General strategy:
// - Decode both with extended serialization (which interprets the 0x0001 tag as a marker for
// the presense of witnesses) and with legacy serialization (which interprets the tag as a
// 0-input 1-output incomplete transaction).
// - Restricted by try_no_witness (which disables legacy if false) and try_witness (which
// disables extended if false).
// - Ignore serializations that do not fully consume the hex string.
// - If neither succeeds, fail.
// - If only one succeeds, return that one.
// - If both decode attempts succeed:
// - If only one passes the CheckTxScriptsSanity check, return that one.
// - If neither or both pass CheckTxScriptsSanity, return the extended one.
CMutableTransaction tx_extended, tx_legacy;
bool ok_extended = false, ok_legacy = false;
// Try decoding with extended serialization support, and remember if the result successfully
// consumes the entire input.
if (try_witness) {
CDataStream ssData(tx_data, SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> tx;
// If transaction looks sane, we don't try other mode even if requested
if (ssData.empty() && (!try_no_witness || CheckTxScriptsSanity(tx))) {
return true;
}
ssData >> tx_extended;
if (ssData.empty()) ok_extended = true;
} catch (const std::exception&) {
// Fall through.
}
}
// Optimization: if extended decoding succeeded and the result passes CheckTxScriptsSanity,
// don't bother decoding the other way.
if (ok_extended && CheckTxScriptsSanity(tx_extended)) {
tx = std::move(tx_extended);
return true;
}
// Try decoding with legacy serialization, and remember if the result successfully consumes the entire input.
if (try_no_witness) {
CDataStream ssData(tx_data, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
try {
ssData >> tx;
if (ssData.empty()) {
return true;
}
ssData >> tx_legacy;
if (ssData.empty()) ok_legacy = true;
} catch (const std::exception&) {
// Fall through.
}
}
// If legacy decoding succeeded and passes CheckTxScriptsSanity, that's our answer, as we know
// at this point that extended decoding either failed or doesn't pass the sanity check.
if (ok_legacy && CheckTxScriptsSanity(tx_legacy)) {
tx = std::move(tx_legacy);
return true;
}
// If extended decoding succeeded, and neither decoding passes sanity, return the extended one.
if (ok_extended) {
tx = std::move(tx_extended);
return true;
}
// If legacy decoding succeeded and extended didn't, return the legacy one.
if (ok_legacy) {
tx = std::move(tx_legacy);
return true;
}
// If none succeeded, we failed.
return false;
}

View file

@ -472,7 +472,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u signet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port>. Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections.", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);

View file

@ -28,6 +28,10 @@
#include <fcntl.h>
#endif
#if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
#include <ifaddrs.h>
#endif
#ifdef USE_POLL
#include <poll.h>
#endif
@ -2050,7 +2054,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
continue;
}
// do not allow non-default ports, unless after 50 invalid addresses selected already
// Do not allow non-default ports, unless after 50 invalid
// addresses selected already. This is to prevent malicious peers
// from advertising themselves as a service on another host and
// port, causing a DoS attack as nodes around the network attempt
// to connect to it fruitlessly.
if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50)
continue;

View file

@ -2305,6 +2305,9 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
bool fRelay = true;
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
if (nTime < 0) {
nTime = 0;
}
nServices = ServiceFlags(nServiceInt);
if (!pfrom.IsInboundConn())
{
@ -2364,10 +2367,16 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::WTXIDRELAY));
}
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
// Signal ADDRv2 support (BIP155).
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));
if (greatest_common_version >= 70016) {
// BIP155 defines addrv2 and sendaddrv2 for all protocol versions, but some
// implementations reject messages they don't know. As a courtesy, don't send
// it to nodes with a version before 70016, as no software is known to support
// BIP155 that doesn't announce at least that protocol version number.
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));
}
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
pfrom.nServices = nServices;
pfrom.SetAddrLocal(addrMe);
@ -2540,6 +2549,17 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
return;
}
if (msg_type == NetMsgType::SENDADDRV2) {
if (pfrom.fSuccessfullyConnected) {
// Disconnect peers that send SENDADDRV2 message after VERACK; this
// must be negotiated between VERSION and VERACK.
pfrom.fDisconnect = true;
return;
}
pfrom.m_wants_addrv2 = true;
return;
}
if (!pfrom.fSuccessfullyConnected) {
LogPrint(BCLog::NET, "Unsupported message \"%s\" prior to verack from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
return;
@ -2607,11 +2627,6 @@ void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDat
return;
}
if (msg_type == NetMsgType::SENDADDRV2) {
pfrom.m_wants_addrv2 = true;
return;
}
if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
State(pfrom.GetId())->fPreferHeaders = true;

View file

@ -1063,15 +1063,24 @@ CSubNet::CSubNet(const CNetAddr& addr, const CNetAddr& mask) : CSubNet()
CSubNet::CSubNet(const CNetAddr& addr) : CSubNet()
{
valid = addr.IsIPv4() || addr.IsIPv6();
if (!valid) {
switch (addr.m_net) {
case NET_IPV4:
case NET_IPV6:
valid = true;
assert(addr.m_addr.size() <= sizeof(netmask));
memset(netmask, 0xFF, addr.m_addr.size());
break;
case NET_ONION:
case NET_I2P:
case NET_CJDNS:
valid = true;
break;
case NET_INTERNAL:
case NET_UNROUTABLE:
case NET_MAX:
return;
}
assert(addr.m_addr.size() <= sizeof(netmask));
memset(netmask, 0xFF, addr.m_addr.size());
network = addr;
}
@ -1083,6 +1092,21 @@ bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid() || network.m_net != addr.m_net)
return false;
switch (network.m_net) {
case NET_IPV4:
case NET_IPV6:
break;
case NET_ONION:
case NET_I2P:
case NET_CJDNS:
case NET_INTERNAL:
return addr == network;
case NET_UNROUTABLE:
case NET_MAX:
return false;
}
assert(network.m_addr.size() == addr.m_addr.size());
for (size_t x = 0; x < addr.m_addr.size(); ++x) {
if ((addr.m_addr[x] & netmask[x]) != network.m_addr[x]) {
@ -1094,18 +1118,35 @@ bool CSubNet::Match(const CNetAddr &addr) const
std::string CSubNet::ToString() const
{
assert(network.m_addr.size() <= sizeof(netmask));
std::string suffix;
uint8_t cidr = 0;
switch (network.m_net) {
case NET_IPV4:
case NET_IPV6: {
assert(network.m_addr.size() <= sizeof(netmask));
for (size_t i = 0; i < network.m_addr.size(); ++i) {
if (netmask[i] == 0x00) {
break;
uint8_t cidr = 0;
for (size_t i = 0; i < network.m_addr.size(); ++i) {
if (netmask[i] == 0x00) {
break;
}
cidr += NetmaskBits(netmask[i]);
}
cidr += NetmaskBits(netmask[i]);
suffix = strprintf("/%u", cidr);
break;
}
case NET_ONION:
case NET_I2P:
case NET_CJDNS:
case NET_INTERNAL:
case NET_UNROUTABLE:
case NET_MAX:
break;
}
return network.ToString() + strprintf("/%u", cidr);
return network.ToString() + suffix;
}
bool CSubNet::IsValid() const
@ -1115,7 +1156,19 @@ bool CSubNet::IsValid() const
bool CSubNet::SanityCheck() const
{
if (!(network.IsIPv4() || network.IsIPv6())) return false;
switch (network.m_net) {
case NET_IPV4:
case NET_IPV6:
break;
case NET_ONION:
case NET_I2P:
case NET_CJDNS:
return true;
case NET_INTERNAL:
case NET_UNROUTABLE:
case NET_MAX:
return false;
}
for (size_t x = 0; x < network.m_addr.size(); ++x) {
if (network.m_addr[x] & ~netmask[x]) return false;

View file

@ -462,11 +462,33 @@ class CSubNet
bool SanityCheck() const;
public:
/**
* Construct an invalid subnet (empty, `Match()` always returns false).
*/
CSubNet();
/**
* Construct from a given network start and number of bits (CIDR mask).
* @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
* created.
* @param[in] mask CIDR mask, must be in [0, 32] for IPv4 addresses and in [0, 128] for
* IPv6 addresses. Otherwise an invalid subnet is created.
*/
CSubNet(const CNetAddr& addr, uint8_t mask);
/**
* Construct from a given network start and mask.
* @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
* created.
* @param[in] mask Network mask, must be of the same type as `addr` and not contain 0-bits
* followed by 1-bits. Otherwise an invalid subnet is created.
*/
CSubNet(const CNetAddr& addr, const CNetAddr& mask);
//constructor for single ip subnet (<ipv4>/32 or <ipv6>/128)
/**
* Construct a single-host subnet.
* @param[in] addr The sole address to be contained in the subnet, can also be non-IPv[46].
*/
explicit CSubNet(const CNetAddr& addr);
bool Match(const CNetAddr &addr) const;

View file

@ -50,6 +50,7 @@
#include <QThread>
#include <QTimer>
#include <QTranslator>
#include <QtGlobal>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@ -443,6 +444,13 @@ int GuiMain(int argc, char* argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#if (QT_VERSION <= QT_VERSION_CHECK(5, 9, 8)) && defined(Q_OS_MACOS)
const auto os_name = QSysInfo::prettyProductName();
if (os_name.startsWith("macOS 11") || os_name.startsWith("macOS 10.16")) {
QApplication::setStyle("fusion");
}
#endif
BitcoinApplication app;
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these

View file

@ -839,7 +839,7 @@ void BitcoinGUI::showDebugWindowActivateConsole()
void BitcoinGUI::showHelpMessageClicked()
{
helpMessageDialog->show();
GUIUtil::bringToFront(helpMessageDialog);
}
#ifdef ENABLE_WALLET

View file

@ -7,140 +7,135 @@
<x>0</x>
<y>0</y>
<width>364</width>
<height>213</height>
<height>249</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Wallet</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>10</x>
<y>170</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLineEdit" name="wallet_name_line_edit">
<property name="geometry">
<rect>
<x>120</x>
<y>20</y>
<width>231</width>
<height>24</height>
</rect>
</property>
<property name="placeholderText">
<string>Wallet</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>101</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Wallet Name</string>
</property>
</widget>
<widget class="QCheckBox" name="encrypt_wallet_checkbox">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>171</width>
<height>22</height>
</rect>
</property>
<property name="toolTip">
<string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string>
</property>
<property name="text">
<string>Encrypt Wallet</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
<widget class="QLabel" name="advanced_options_label">
<property name="geometry">
<rect>
<x>20</x>
<y>90</y>
<width>130</width>
<height>21</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font-weight:bold;</string>
</property>
<property name="text">
<string>Advanced options</string>
</property>
</widget>
<widget class="QCheckBox" name="disable_privkeys_checkbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>20</x>
<y>115</y>
<width>171</width>
<height>22</height>
</rect>
</property>
<property name="toolTip">
<string>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</string>
</property>
<property name="text">
<string>Disable Private Keys</string>
</property>
</widget>
<widget class="QCheckBox" name="blank_wallet_checkbox">
<property name="geometry">
<rect>
<x>20</x>
<y>135</y>
<width>220</width>
<height>22</height>
</rect>
</property>
<property name="toolTip">
<string>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</string>
</property>
<property name="text">
<string>Make Blank Wallet</string>
</property>
</widget>
<widget class="QCheckBox" name="descriptor_checkbox">
<property name="geometry">
<rect>
<x>20</x>
<y>155</y>
<width>171</width>
<height>22</height>
</rect>
</property>
<property name="toolTip">
<string>Use descriptors for scriptPubKey management</string>
</property>
<property name="text">
<string>Descriptor Wallet</string>
</property>
</widget>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="wallet_name_label">
<property name="text">
<string>Wallet Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="wallet_name_line_edit">
<property name="minimumSize">
<size>
<width>262</width>
<height>0</height>
</size>
</property>
<property name="placeholderText">
<string>Wallet</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="encrypt_wallet_checkbox">
<property name="toolTip">
<string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string>
</property>
<property name="text">
<string>Encrypt Wallet</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Advanced Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_groupbox">
<item>
<widget class="QCheckBox" name="disable_privkeys_checkbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</string>
</property>
<property name="text">
<string>Disable Private Keys</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="blank_wallet_checkbox">
<property name="toolTip">
<string>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</string>
</property>
<property name="text">
<string>Make Blank Wallet</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="descriptor_checkbox">
<property name="toolTip">
<string>Use descriptors for scriptPubKey management</string>
</property>
<property name="text">
<string>Descriptor Wallet</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>wallet_name_line_edit</tabstop>

View file

@ -610,7 +610,7 @@
</layout>
</item>
<item>
<widget class="QListView" name="listTransactions">
<widget class="TransactionOverviewWidget" name="listTransactions">
<property name="styleSheet">
<string notr="true">QListView { background: transparent; }</string>
</property>
@ -623,9 +623,15 @@
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
@ -639,6 +645,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>TransactionOverviewWidget</class>
<extends>QListView</extends>
<header>qt/transactionoverviewwidget.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../bitcoin.qrc"/>
</resources>

View file

@ -12,6 +12,7 @@
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/transactionfilterproxy.h>
#include <qt/transactionoverviewwidget.h>
#include <qt/transactiontablemodel.h>
#include <qt/walletmodel.h>
@ -22,6 +23,9 @@
#include <QPainter>
#include <QStatusTipEvent>
#include <algorithm>
#include <map>
#define DECORATION_SIZE 54
#define NUM_ITEMS 5
@ -35,7 +39,7 @@ public:
QAbstractItemDelegate(parent), unit(BitcoinUnits::BTC),
platformStyle(_platformStyle)
{
connect(this, &TxViewDelegate::width_changed, this, &TxViewDelegate::sizeHintChanged);
}
inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
@ -68,13 +72,15 @@ public:
painter->setPen(foreground);
QRect boundingRect;
painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address, &boundingRect);
painter->drawText(addressRect, Qt::AlignLeft | Qt::AlignVCenter, address, &boundingRect);
int address_rect_min_width = boundingRect.width();
if (index.data(TransactionTableModel::WatchonlyRole).toBool())
{
QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole));
QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight);
iconWatchonly.paint(painter, watchonlyRect);
address_rect_min_width += 5 + watchonlyRect.width();
}
if(amount < 0)
@ -91,23 +97,41 @@ public:
}
painter->setPen(foreground);
QString amountText = index.sibling(index.row(), TransactionTableModel::Amount).data(Qt::DisplayRole).toString();
painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText);
QRect amount_bounding_rect;
painter->drawText(amountRect, Qt::AlignRight | Qt::AlignVCenter, amountText, &amount_bounding_rect);
painter->setPen(option.palette.color(QPalette::Text));
painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::dateTimeStr(date));
QRect date_bounding_rect;
painter->drawText(amountRect, Qt::AlignLeft | Qt::AlignVCenter, GUIUtil::dateTimeStr(date), &date_bounding_rect);
const int minimum_width = std::max(address_rect_min_width, amount_bounding_rect.width() + date_bounding_rect.width());
const auto search = m_minimum_width.find(index.row());
if (search == m_minimum_width.end() || search->second != minimum_width) {
m_minimum_width[index.row()] = minimum_width;
Q_EMIT width_changed(index);
}
painter->restore();
}
inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
return QSize(DECORATION_SIZE, DECORATION_SIZE);
const auto search = m_minimum_width.find(index.row());
const int minimum_text_width = search == m_minimum_width.end() ? 0 : search->second;
return {DECORATION_SIZE + 8 + minimum_text_width, DECORATION_SIZE};
}
int unit;
const PlatformStyle *platformStyle;
Q_SIGNALS:
//! An intermediate signal for emitting from the `paint() const` member function.
void width_changed(const QModelIndex& index) const;
private:
const PlatformStyle* platformStyle;
mutable std::map<int, int> m_minimum_width;
};
#include <qt/overviewpage.moc>
OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) :
@ -133,7 +157,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false);
connect(ui->listTransactions, &QListView::clicked, this, &OverviewPage::handleTransactionClicked);
connect(ui->listTransactions, &TransactionOverviewWidget::clicked, this, &OverviewPage::handleTransactionClicked);
// start with displaying the "out of sync" warnings
showOutOfSyncWarning(true);

View file

@ -145,7 +145,7 @@ void PSBTOperationsDialog::saveTransaction() {
if (filename.isEmpty()) {
return;
}
std::ofstream out(filename.toLocal8Bit().data());
std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
out << ssTx.str();
out.close();
showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);

View file

@ -435,7 +435,7 @@ void SendCoinsDialog::on_sendButton_clicked()
if (filename.isEmpty()) {
return;
}
std::ofstream out(filename.toLocal8Bit().data());
std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
out << ssTx.str();
out.close();
Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);
@ -981,6 +981,9 @@ SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QStri
setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
setDefaultButton(QMessageBox::Cancel);
yesButton = button(QMessageBox::Yes);
if (confirmButtonText.isEmpty()) {
confirmButtonText = yesButton->text();
}
updateYesButton();
connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
}

View file

@ -115,7 +115,7 @@ class SendConfirmationDialog : public QMessageBox
Q_OBJECT
public:
SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "Send", QWidget* parent = nullptr);
SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "", QWidget* parent = nullptr);
int exec() override;
private Q_SLOTS:

View file

@ -0,0 +1,41 @@
// Copyright (c) 2021 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_QT_TRANSACTIONOVERVIEWWIDGET_H
#define BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H
#include <qt/transactiontablemodel.h>
#include <QListView>
#include <QSize>
#include <QSizePolicy>
QT_BEGIN_NAMESPACE
class QShowEvent;
class QWidget;
QT_END_NAMESPACE
class TransactionOverviewWidget : public QListView
{
Q_OBJECT
public:
explicit TransactionOverviewWidget(QWidget* parent = nullptr) : QListView(parent) {}
QSize sizeHint() const override
{
return {sizeHintForColumn(TransactionTableModel::ToAddress), QListView::sizeHint().height()};
}
protected:
void showEvent(QShowEvent* event) override
{
Q_UNUSED(event);
QSizePolicy sp = sizePolicy();
sp.setHorizontalPolicy(QSizePolicy::Minimum);
setSizePolicy(sp);
}
};
#endif // BITCOIN_QT_TRANSACTIONOVERVIEWWIDGET_H

View file

@ -106,9 +106,24 @@ void WalletFrame::setCurrentWallet(WalletModel* wallet_model)
{
if (mapWalletViews.count(wallet_model) == 0) return;
// Stop the effect of hidden widgets on the size hint of the shown one in QStackedWidget.
WalletView* view_about_to_hide = currentWalletView();
if (view_about_to_hide) {
QSizePolicy sp = view_about_to_hide->sizePolicy();
sp.setHorizontalPolicy(QSizePolicy::Ignored);
view_about_to_hide->setSizePolicy(sp);
}
WalletView *walletView = mapWalletViews.value(wallet_model);
walletStack->setCurrentWidget(walletView);
assert(walletView);
// Set or restore the default QSizePolicy which could be set to QSizePolicy::Ignored previously.
QSizePolicy sp = walletView->sizePolicy();
sp.setHorizontalPolicy(QSizePolicy::Preferred);
walletView->setSizePolicy(sp);
walletView->updateGeometry();
walletStack->setCurrentWidget(walletView);
walletView->updateEncryptionStatus();
}

View file

@ -38,7 +38,7 @@
#include <sys/utsname.h>
#include <unistd.h>
#endif
#if HAVE_DECL_GETIFADDRS
#if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
#include <ifaddrs.h>
#endif
#if HAVE_SYSCTL
@ -361,7 +361,7 @@ void RandAddStaticEnv(CSHA512& hasher)
hasher.Write((const unsigned char*)hname, strnlen(hname, 256));
}
#if HAVE_DECL_GETIFADDRS
#if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
// Network interfaces
struct ifaddrs *ifad = NULL;
getifaddrs(&ifad);

View file

@ -401,13 +401,13 @@ static RPCHelpMan signmessagewithprivkey()
static RPCHelpMan setmocktime()
{
return RPCHelpMan{"setmocktime",
"\nSet the local time to given timestamp (-regtest only)\n",
{
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
" Pass 0 to go back to using the system time."},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{""},
"\nSet the local time to given timestamp (-regtest only)\n",
{
{"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
"Pass 0 to go back to using the system time."},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
if (!Params().IsMockableChain()) {
@ -422,7 +422,10 @@ static RPCHelpMan setmocktime()
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VNUM});
int64_t time = request.params[0].get_int64();
const int64_t time{request.params[0].get_int64()};
if (time < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime can not be negative: %s.", time));
}
SetMockTime(time);
if (request.context.Has<NodeContext>()) {
for (const auto& chain_client : request.context.Get<NodeContext>().chain_clients) {

View file

@ -141,6 +141,10 @@ static RPCHelpMan getpeerinfo()
}},
{RPCResult::Type::BOOL, "whitelisted", /* optional */ true, "Whether the peer is whitelisted with default permissions\n"
"(DEPRECATED, returned only if config option -deprecatedrpc=whitelisted is passed)"},
{RPCResult::Type::ARR, "permissions", "Any special permissions that have been granted to this peer",
{
{RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"},
}},
{RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"},
{RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "",
{

View file

@ -121,7 +121,7 @@ static RPCHelpMan getrawtransaction()
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id"},
{RPCResult::Type::STR, "vout", ""},
{RPCResult::Type::NUM, "vout", "The output number"},
{RPCResult::Type::OBJ, "scriptSig", "The script",
{
{RPCResult::Type::STR, "asm", "asm"},

View file

@ -339,6 +339,34 @@ public:
using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>;
class DeferringSignatureChecker : public BaseSignatureChecker
{
protected:
BaseSignatureChecker& m_checker;
public:
DeferringSignatureChecker(BaseSignatureChecker& checker) : m_checker(checker) {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override
{
return m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion, flags);
}
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
{
return m_checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror);
}
bool CheckLockTime(const CScriptNum& nLockTime) const override
{
return m_checker.CheckLockTime(nLockTime);
}
bool CheckSequence(const CScriptNum& nSequence) const override
{
return m_checker.CheckSequence(nSequence);
}
};
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);

View file

@ -266,17 +266,17 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
}
namespace {
class SignatureExtractorChecker final : public BaseSignatureChecker
class SignatureExtractorChecker final : public DeferringSignatureChecker
{
private:
SignatureData& sigdata;
BaseSignatureChecker& checker;
public:
SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : DeferringSignatureChecker(checker), sigdata(sigdata) {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override
{
if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion, flags)) {
if (m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion, flags)) {
CPubKey pubkey(vchPubKey);
sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
return true;

View file

@ -22,6 +22,7 @@
#include <test/util/setup_common.h>
#include <array>
#include <stdint.h>
#include <boost/test/unit_test.hpp>
@ -221,46 +222,89 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
{
const CChainParams& chainparams = Params();
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337);
auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
banman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::INBOUND);
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.fSuccessfullyConnected = true;
peerLogic->Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
}
BOOST_CHECK(banman->IsDiscouraged(addr1));
BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001|0x0000ff00))); // Different IP, not discouraged
CNetAddr tor_netaddr;
BOOST_REQUIRE(
tor_netaddr.SetSpecial("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"));
const CService tor_service(tor_netaddr, Params().GetDefaultPort());
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", ConnectionType::INBOUND);
dummyNode2.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode2);
dummyNode2.fSuccessfullyConnected = true;
peerLogic->Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
const std::array<CAddress, 3> addr{CAddress{ip(0xa0b0c001), NODE_NONE},
CAddress{ip(0xa0b0c002), NODE_NONE},
CAddress{tor_service, NODE_NONE}};
const CNetAddr other_addr{ip(0xa0b0ff01)}; // Not any of addr[].
std::array<CNode*, 3> nodes;
banman->ClearBanned();
nodes[0] = new CNode{id++, NODE_NETWORK, 0, INVALID_SOCKET, addr[0], 0, 0, CAddress(), "", ConnectionType::INBOUND};
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[0]);
nodes[0]->fSuccessfullyConnected = true;
connman->AddNode(*nodes[0]);
peerLogic->Misbehaving(nodes[0]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
LOCK(nodes[0]->cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(nodes[0]));
}
BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not discouraged yet...
BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
peerLogic->Misbehaving(dummyNode2.GetId(), 1, /* message */ ""); // 2 reaches discouragement threshold
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(!banman->IsDiscouraged(other_addr)); // Different address, not discouraged
nodes[1] = new CNode{id++, NODE_NETWORK, 0, INVALID_SOCKET, addr[1], 1, 1, CAddress(), "", ConnectionType::INBOUND};
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[1]);
nodes[1]->fSuccessfullyConnected = true;
connman->AddNode(*nodes[1]);
peerLogic->Misbehaving(nodes[1]->GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
LOCK(nodes[1]->cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
}
BOOST_CHECK(banman->IsDiscouraged(addr1)); // Expect both 1 and 2
BOOST_CHECK(banman->IsDiscouraged(addr2)); // to be discouraged now
// [0] is still discouraged/disconnected.
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
// [1] is not discouraged/disconnected yet.
BOOST_CHECK(!banman->IsDiscouraged(addr[1]));
BOOST_CHECK(!nodes[1]->fDisconnect);
peerLogic->Misbehaving(nodes[1]->GetId(), 1, /* message */ ""); // [1] reaches discouragement threshold
{
LOCK(nodes[1]->cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(nodes[1]));
}
// Expect both [0] and [1] to be discouraged/disconnected now.
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
BOOST_CHECK(nodes[1]->fDisconnect);
// Make sure non-IP peers are discouraged and disconnected properly.
nodes[2] = new CNode{id++, NODE_NETWORK, 0, INVALID_SOCKET, addr[2], 1, 1, CAddress(), "",
ConnectionType::OUTBOUND_FULL_RELAY};
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(nodes[2]);
nodes[2]->fSuccessfullyConnected = true;
connman->AddNode(*nodes[2]);
peerLogic->Misbehaving(nodes[2]->GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
{
LOCK(nodes[2]->cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(nodes[2]));
}
BOOST_CHECK(banman->IsDiscouraged(addr[0]));
BOOST_CHECK(banman->IsDiscouraged(addr[1]));
BOOST_CHECK(banman->IsDiscouraged(addr[2]));
BOOST_CHECK(nodes[0]->fDisconnect);
BOOST_CHECK(nodes[1]->fDisconnect);
BOOST_CHECK(nodes[2]->fDisconnect);
bool dummy;
peerLogic->FinalizeNode(dummyNode1, dummy);
peerLogic->FinalizeNode(dummyNode2, dummy);
for (CNode* node : nodes) {
peerLogic->FinalizeNode(*node, dummy);
}
connman->ClearNodes();
}
BOOST_AUTO_TEST_CASE(DoS_bantime)

View file

@ -15,11 +15,13 @@
#include <limits>
#include <algorithm>
#include <array>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <limits>
#include <string>
#include <type_traits>
#include <utility>
@ -35,208 +37,49 @@ class FuzzedDataProvider {
: data_ptr_(data), remaining_bytes_(size) {}
~FuzzedDataProvider() = default;
// Returns a std::vector containing |num_bytes| of input data. If fewer than
// |num_bytes| of data remain, returns a shorter std::vector containing all
// of the data that's left. Can be used with any byte sized type, such as
// char, unsigned char, uint8_t, etc.
template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes) {
num_bytes = std::min(num_bytes, remaining_bytes_);
return ConsumeBytes<T>(num_bytes, num_bytes);
}
// See the implementation below (after the class definition) for more verbose
// comments for each of the methods.
// Similar to |ConsumeBytes|, but also appends the terminator value at the end
// of the resulting vector. Useful, when a mutable null-terminated C-string is
// needed, for example. But that is a rare case. Better avoid it, if possible,
// and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods.
// Methods returning std::vector of bytes. These are the most popular choice
// when splitting fuzzing input into pieces, as every piece is put into a
// separate buffer (i.e. ASan would catch any under-/overflow) and the memory
// will be released automatically.
template <typename T> std::vector<T> ConsumeBytes(size_t num_bytes);
template <typename T>
std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes,
T terminator = 0) {
num_bytes = std::min(num_bytes, remaining_bytes_);
std::vector<T> result = ConsumeBytes<T>(num_bytes + 1, num_bytes);
result.back() = terminator;
return result;
}
std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, T terminator = 0);
template <typename T> std::vector<T> ConsumeRemainingBytes();
// Returns a std::string containing |num_bytes| of input data. Using this and
// |.c_str()| on the resulting string is the best way to get an immutable
// null-terminated C string. If fewer than |num_bytes| of data remain, returns
// a shorter std::string containing all of the data that's left.
std::string ConsumeBytesAsString(size_t num_bytes) {
static_assert(sizeof(std::string::value_type) == sizeof(uint8_t),
"ConsumeBytesAsString cannot convert the data to a string.");
// Methods returning strings. Use only when you need a std::string or a null
// terminated C-string. Otherwise, prefer the methods returning std::vector.
std::string ConsumeBytesAsString(size_t num_bytes);
std::string ConsumeRandomLengthString(size_t max_length);
std::string ConsumeRandomLengthString();
std::string ConsumeRemainingBytesAsString();
num_bytes = std::min(num_bytes, remaining_bytes_);
std::string result(
reinterpret_cast<const std::string::value_type *>(data_ptr_),
num_bytes);
Advance(num_bytes);
return result;
}
// Methods returning integer values.
template <typename T> T ConsumeIntegral();
template <typename T> T ConsumeIntegralInRange(T min, T max);
// Returns a number in the range [min, max] by consuming bytes from the
// input data. The value might not be uniformly distributed in the given
// range. If there's no input data left, always returns |min|. |min| must
// be less than or equal to |max|.
template <typename T> T ConsumeIntegralInRange(T min, T max) {
static_assert(std::is_integral<T>::value, "An integral type is required.");
static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type.");
// Methods returning floating point values.
template <typename T> T ConsumeFloatingPoint();
template <typename T> T ConsumeFloatingPointInRange(T min, T max);
if (min > max)
abort();
// 0 <= return value <= 1.
template <typename T> T ConsumeProbability();
// Use the biggest type possible to hold the range and the result.
uint64_t range = static_cast<uint64_t>(max) - min;
uint64_t result = 0;
size_t offset = 0;
bool ConsumeBool();
while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 &&
remaining_bytes_ != 0) {
// Pull bytes off the end of the seed data. Experimentally, this seems to
// allow the fuzzer to more easily explore the input space. This makes
// sense, since it works by modifying inputs that caused new code to run,
// and this data is often used to encode length of data read by
// |ConsumeBytes|. Separating out read lengths makes it easier modify the
// contents of the data that is actually read.
--remaining_bytes_;
result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_];
offset += CHAR_BIT;
}
// Returns a value chosen from the given enum.
template <typename T> T ConsumeEnum();
// Avoid division by 0, in case |range + 1| results in overflow.
if (range != std::numeric_limits<decltype(range)>::max())
result = result % (range + 1);
return static_cast<T>(min + result);
}
// Returns a std::string of length from 0 to |max_length|. When it runs out of
// input data, returns what remains of the input. Designed to be more stable
// with respect to a fuzzer inserting characters than just picking a random
// length and then consuming that many bytes with |ConsumeBytes|.
std::string ConsumeRandomLengthString(size_t max_length) {
// Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\"
// followed by anything else to the end of the string. As a result of this
// logic, a fuzzer can insert characters into the string, and the string
// will be lengthened to include those new characters, resulting in a more
// stable fuzzer than picking the length of a string independently from
// picking its contents.
std::string result;
// Reserve the anticipated capaticity to prevent several reallocations.
result.reserve(std::min(max_length, remaining_bytes_));
for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) {
char next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
Advance(1);
if (next == '\\' && remaining_bytes_ != 0) {
next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
Advance(1);
if (next != '\\')
break;
}
result += next;
}
result.shrink_to_fit();
return result;
}
// Returns a std::vector containing all remaining bytes of the input data.
template <typename T> std::vector<T> ConsumeRemainingBytes() {
return ConsumeBytes<T>(remaining_bytes_);
}
// Returns a std::string containing all remaining bytes of the input data.
// Prefer using |ConsumeRemainingBytes| unless you actually need a std::string
// object.
std::string ConsumeRemainingBytesAsString() {
return ConsumeBytesAsString(remaining_bytes_);
}
// Returns a number in the range [Type's min, Type's max]. The value might
// not be uniformly distributed in the given range. If there's no input data
// left, always returns |min|.
template <typename T> T ConsumeIntegral() {
return ConsumeIntegralInRange(std::numeric_limits<T>::min(),
std::numeric_limits<T>::max());
}
// Reads one byte and returns a bool, or false when no data remains.
bool ConsumeBool() { return 1 & ConsumeIntegral<uint8_t>(); }
// Returns a copy of the value selected from the given fixed-size |array|.
// Returns a value from the given array.
template <typename T, size_t size> T PickValueInArray(const T (&array)[size]);
template <typename T, size_t size>
T PickValueInArray(const T (&array)[size]) {
static_assert(size > 0, "The array must be non empty.");
return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
}
T PickValueInArray(const std::array<T, size> &array);
template <typename T> T PickValueInArray(std::initializer_list<const T> list);
template <typename T>
T PickValueInArray(std::initializer_list<const T> list) {
// TODO(Dor1s): switch to static_assert once C++14 is allowed.
if (!list.size())
abort();
return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1));
}
// Returns an enum value. The enum must start at 0 and be contiguous. It must
// also contain |kMaxValue| aliased to its largest (inclusive) value. Such as:
// enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue };
template <typename T> T ConsumeEnum() {
static_assert(std::is_enum<T>::value, "|T| must be an enum type.");
return static_cast<T>(ConsumeIntegralInRange<uint32_t>(
0, static_cast<uint32_t>(T::kMaxValue)));
}
// Returns a floating point number in the range [0.0, 1.0]. If there's no
// input data left, always returns 0.
template <typename T> T ConsumeProbability() {
static_assert(std::is_floating_point<T>::value,
"A floating point type is required.");
// Use different integral types for different floating point types in order
// to provide better density of the resulting values.
using IntegralType =
typename std::conditional<(sizeof(T) <= sizeof(uint32_t)), uint32_t,
uint64_t>::type;
T result = static_cast<T>(ConsumeIntegral<IntegralType>());
result /= static_cast<T>(std::numeric_limits<IntegralType>::max());
return result;
}
// Returns a floating point value in the range [Type's lowest, Type's max] by
// consuming bytes from the input data. If there's no input data left, always
// returns approximately 0.
template <typename T> T ConsumeFloatingPoint() {
return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(),
std::numeric_limits<T>::max());
}
// Returns a floating point value in the given range by consuming bytes from
// the input data. If there's no input data left, returns |min|. Note that
// |min| must be less than or equal to |max|.
template <typename T> T ConsumeFloatingPointInRange(T min, T max) {
if (min > max)
abort();
T range = .0;
T result = min;
constexpr T zero(.0);
if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) {
// The diff |max - min| would overflow the given floating point type. Use
// the half of the diff as the range and consume a bool to decide whether
// the result is in the first of the second part of the diff.
range = (max / 2.0) - (min / 2.0);
if (ConsumeBool()) {
result += range;
}
} else {
range = max - min;
}
return result + range * ConsumeProbability<T>();
}
// Writes data to the given destination and returns number of bytes written.
size_t ConsumeData(void *destination, size_t num_bytes);
// Reports the remaining bytes available for fuzzed input.
size_t remaining_bytes() { return remaining_bytes_; }
@ -245,62 +88,311 @@ class FuzzedDataProvider {
FuzzedDataProvider(const FuzzedDataProvider &) = delete;
FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete;
void Advance(size_t num_bytes) {
if (num_bytes > remaining_bytes_)
abort();
void CopyAndAdvance(void *destination, size_t num_bytes);
data_ptr_ += num_bytes;
remaining_bytes_ -= num_bytes;
}
void Advance(size_t num_bytes);
template <typename T>
std::vector<T> ConsumeBytes(size_t size, size_t num_bytes_to_consume) {
static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type.");
std::vector<T> ConsumeBytes(size_t size, size_t num_bytes);
// The point of using the size-based constructor below is to increase the
// odds of having a vector object with capacity being equal to the length.
// That part is always implementation specific, but at least both libc++ and
// libstdc++ allocate the requested number of bytes in that constructor,
// which seems to be a natural choice for other implementations as well.
// To increase the odds even more, we also call |shrink_to_fit| below.
std::vector<T> result(size);
if (size == 0) {
if (num_bytes_to_consume != 0)
abort();
return result;
}
std::memcpy(result.data(), data_ptr_, num_bytes_to_consume);
Advance(num_bytes_to_consume);
// Even though |shrink_to_fit| is also implementation specific, we expect it
// to provide an additional assurance in case vector's constructor allocated
// a buffer which is larger than the actual amount of data we put inside it.
result.shrink_to_fit();
return result;
}
template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value) {
static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types.");
static_assert(!std::numeric_limits<TU>::is_signed,
"Source type must be unsigned.");
// TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream.
if (std::numeric_limits<TS>::is_modulo)
return static_cast<TS>(value);
// Avoid using implementation-defined unsigned to signer conversions.
// To learn more, see https://stackoverflow.com/questions/13150449.
if (value <= std::numeric_limits<TS>::max()) {
return static_cast<TS>(value);
} else {
constexpr auto TS_min = std::numeric_limits<TS>::min();
return TS_min + static_cast<char>(value - TS_min);
}
}
template <typename TS, typename TU> TS ConvertUnsignedToSigned(TU value);
const uint8_t *data_ptr_;
size_t remaining_bytes_;
};
// Returns a std::vector containing |num_bytes| of input data. If fewer than
// |num_bytes| of data remain, returns a shorter std::vector containing all
// of the data that's left. Can be used with any byte sized type, such as
// char, unsigned char, uint8_t, etc.
template <typename T>
std::vector<T> FuzzedDataProvider::ConsumeBytes(size_t num_bytes) {
num_bytes = std::min(num_bytes, remaining_bytes_);
return ConsumeBytes<T>(num_bytes, num_bytes);
}
// Similar to |ConsumeBytes|, but also appends the terminator value at the end
// of the resulting vector. Useful, when a mutable null-terminated C-string is
// needed, for example. But that is a rare case. Better avoid it, if possible,
// and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods.
template <typename T>
std::vector<T> FuzzedDataProvider::ConsumeBytesWithTerminator(size_t num_bytes,
T terminator) {
num_bytes = std::min(num_bytes, remaining_bytes_);
std::vector<T> result = ConsumeBytes<T>(num_bytes + 1, num_bytes);
result.back() = terminator;
return result;
}
// Returns a std::vector containing all remaining bytes of the input data.
template <typename T>
std::vector<T> FuzzedDataProvider::ConsumeRemainingBytes() {
return ConsumeBytes<T>(remaining_bytes_);
}
// Returns a std::string containing |num_bytes| of input data. Using this and
// |.c_str()| on the resulting string is the best way to get an immutable
// null-terminated C string. If fewer than |num_bytes| of data remain, returns
// a shorter std::string containing all of the data that's left.
inline std::string FuzzedDataProvider::ConsumeBytesAsString(size_t num_bytes) {
static_assert(sizeof(std::string::value_type) == sizeof(uint8_t),
"ConsumeBytesAsString cannot convert the data to a string.");
num_bytes = std::min(num_bytes, remaining_bytes_);
std::string result(
reinterpret_cast<const std::string::value_type *>(data_ptr_), num_bytes);
Advance(num_bytes);
return result;
}
// Returns a std::string of length from 0 to |max_length|. When it runs out of
// input data, returns what remains of the input. Designed to be more stable
// with respect to a fuzzer inserting characters than just picking a random
// length and then consuming that many bytes with |ConsumeBytes|.
inline std::string
FuzzedDataProvider::ConsumeRandomLengthString(size_t max_length) {
// Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\"
// followed by anything else to the end of the string. As a result of this
// logic, a fuzzer can insert characters into the string, and the string
// will be lengthened to include those new characters, resulting in a more
// stable fuzzer than picking the length of a string independently from
// picking its contents.
std::string result;
// Reserve the anticipated capaticity to prevent several reallocations.
result.reserve(std::min(max_length, remaining_bytes_));
for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) {
char next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
Advance(1);
if (next == '\\' && remaining_bytes_ != 0) {
next = ConvertUnsignedToSigned<char>(data_ptr_[0]);
Advance(1);
if (next != '\\')
break;
}
result += next;
}
result.shrink_to_fit();
return result;
}
// Returns a std::string of length from 0 to |remaining_bytes_|.
inline std::string FuzzedDataProvider::ConsumeRandomLengthString() {
return ConsumeRandomLengthString(remaining_bytes_);
}
// Returns a std::string containing all remaining bytes of the input data.
// Prefer using |ConsumeRemainingBytes| unless you actually need a std::string
// object.
inline std::string FuzzedDataProvider::ConsumeRemainingBytesAsString() {
return ConsumeBytesAsString(remaining_bytes_);
}
// Returns a number in the range [Type's min, Type's max]. The value might
// not be uniformly distributed in the given range. If there's no input data
// left, always returns |min|.
template <typename T> T FuzzedDataProvider::ConsumeIntegral() {
return ConsumeIntegralInRange(std::numeric_limits<T>::min(),
std::numeric_limits<T>::max());
}
// Returns a number in the range [min, max] by consuming bytes from the
// input data. The value might not be uniformly distributed in the given
// range. If there's no input data left, always returns |min|. |min| must
// be less than or equal to |max|.
template <typename T>
T FuzzedDataProvider::ConsumeIntegralInRange(T min, T max) {
static_assert(std::is_integral<T>::value, "An integral type is required.");
static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type.");
if (min > max)
abort();
// Use the biggest type possible to hold the range and the result.
uint64_t range = static_cast<uint64_t>(max) - min;
uint64_t result = 0;
size_t offset = 0;
while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 &&
remaining_bytes_ != 0) {
// Pull bytes off the end of the seed data. Experimentally, this seems to
// allow the fuzzer to more easily explore the input space. This makes
// sense, since it works by modifying inputs that caused new code to run,
// and this data is often used to encode length of data read by
// |ConsumeBytes|. Separating out read lengths makes it easier modify the
// contents of the data that is actually read.
--remaining_bytes_;
result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_];
offset += CHAR_BIT;
}
// Avoid division by 0, in case |range + 1| results in overflow.
if (range != std::numeric_limits<decltype(range)>::max())
result = result % (range + 1);
return static_cast<T>(min + result);
}
// Returns a floating point value in the range [Type's lowest, Type's max] by
// consuming bytes from the input data. If there's no input data left, always
// returns approximately 0.
template <typename T> T FuzzedDataProvider::ConsumeFloatingPoint() {
return ConsumeFloatingPointInRange<T>(std::numeric_limits<T>::lowest(),
std::numeric_limits<T>::max());
}
// Returns a floating point value in the given range by consuming bytes from
// the input data. If there's no input data left, returns |min|. Note that
// |min| must be less than or equal to |max|.
template <typename T>
T FuzzedDataProvider::ConsumeFloatingPointInRange(T min, T max) {
if (min > max)
abort();
T range = .0;
T result = min;
constexpr T zero(.0);
if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) {
// The diff |max - min| would overflow the given floating point type. Use
// the half of the diff as the range and consume a bool to decide whether
// the result is in the first of the second part of the diff.
range = (max / 2.0) - (min / 2.0);
if (ConsumeBool()) {
result += range;
}
} else {
range = max - min;
}
return result + range * ConsumeProbability<T>();
}
// Returns a floating point number in the range [0.0, 1.0]. If there's no
// input data left, always returns 0.
template <typename T> T FuzzedDataProvider::ConsumeProbability() {
static_assert(std::is_floating_point<T>::value,
"A floating point type is required.");
// Use different integral types for different floating point types in order
// to provide better density of the resulting values.
using IntegralType =
typename std::conditional<(sizeof(T) <= sizeof(uint32_t)), uint32_t,
uint64_t>::type;
T result = static_cast<T>(ConsumeIntegral<IntegralType>());
result /= static_cast<T>(std::numeric_limits<IntegralType>::max());
return result;
}
// Reads one byte and returns a bool, or false when no data remains.
inline bool FuzzedDataProvider::ConsumeBool() {
return 1 & ConsumeIntegral<uint8_t>();
}
// Returns an enum value. The enum must start at 0 and be contiguous. It must
// also contain |kMaxValue| aliased to its largest (inclusive) value. Such as:
// enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue };
template <typename T> T FuzzedDataProvider::ConsumeEnum() {
static_assert(std::is_enum<T>::value, "|T| must be an enum type.");
return static_cast<T>(
ConsumeIntegralInRange<uint32_t>(0, static_cast<uint32_t>(T::kMaxValue)));
}
// Returns a copy of the value selected from the given fixed-size |array|.
template <typename T, size_t size>
T FuzzedDataProvider::PickValueInArray(const T (&array)[size]) {
static_assert(size > 0, "The array must be non empty.");
return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
}
template <typename T, size_t size>
T FuzzedDataProvider::PickValueInArray(const std::array<T, size> &array) {
static_assert(size > 0, "The array must be non empty.");
return array[ConsumeIntegralInRange<size_t>(0, size - 1)];
}
template <typename T>
T FuzzedDataProvider::PickValueInArray(std::initializer_list<const T> list) {
// TODO(Dor1s): switch to static_assert once C++14 is allowed.
if (!list.size())
abort();
return *(list.begin() + ConsumeIntegralInRange<size_t>(0, list.size() - 1));
}
// Writes |num_bytes| of input data to the given destination pointer. If there
// is not enough data left, writes all remaining bytes. Return value is the
// number of bytes written.
// In general, it's better to avoid using this function, but it may be useful
// in cases when it's necessary to fill a certain buffer or object with
// fuzzing data.
inline size_t FuzzedDataProvider::ConsumeData(void *destination,
size_t num_bytes) {
num_bytes = std::min(num_bytes, remaining_bytes_);
CopyAndAdvance(destination, num_bytes);
return num_bytes;
}
// Private methods.
inline void FuzzedDataProvider::CopyAndAdvance(void *destination,
size_t num_bytes) {
std::memcpy(destination, data_ptr_, num_bytes);
Advance(num_bytes);
}
inline void FuzzedDataProvider::Advance(size_t num_bytes) {
if (num_bytes > remaining_bytes_)
abort();
data_ptr_ += num_bytes;
remaining_bytes_ -= num_bytes;
}
template <typename T>
std::vector<T> FuzzedDataProvider::ConsumeBytes(size_t size, size_t num_bytes) {
static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type.");
// The point of using the size-based constructor below is to increase the
// odds of having a vector object with capacity being equal to the length.
// That part is always implementation specific, but at least both libc++ and
// libstdc++ allocate the requested number of bytes in that constructor,
// which seems to be a natural choice for other implementations as well.
// To increase the odds even more, we also call |shrink_to_fit| below.
std::vector<T> result(size);
if (size == 0) {
if (num_bytes != 0)
abort();
return result;
}
CopyAndAdvance(result.data(), num_bytes);
// Even though |shrink_to_fit| is also implementation specific, we expect it
// to provide an additional assurance in case vector's constructor allocated
// a buffer which is larger than the actual amount of data we put inside it.
result.shrink_to_fit();
return result;
}
template <typename TS, typename TU>
TS FuzzedDataProvider::ConvertUnsignedToSigned(TU value) {
static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types.");
static_assert(!std::numeric_limits<TU>::is_signed,
"Source type must be unsigned.");
// TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream.
if (std::numeric_limits<TS>::is_modulo)
return static_cast<TS>(value);
// Avoid using implementation-defined unsigned to signed conversions.
// To learn more, see https://stackoverflow.com/questions/13150449.
if (value <= std::numeric_limits<TS>::max()) {
return static_cast<TS>(value);
} else {
constexpr auto TS_min = std::numeric_limits<TS>::min();
return TS_min + static_cast<char>(value - TS_min);
}
}
#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_

View file

@ -0,0 +1,351 @@
// Copyright (c) 2020-2021 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 <chain.h>
#include <chainparams.h>
#include <consensus/params.h>
#include <primitives/block.h>
#include <versionbits.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cstdint>
#include <limits>
#include <memory>
#include <vector>
namespace {
class TestConditionChecker : public AbstractThresholdConditionChecker
{
private:
mutable ThresholdConditionCache m_cache;
const Consensus::Params dummy_params{};
public:
const int64_t m_begin = 0;
const int64_t m_end = 0;
const int m_period = 0;
const int m_threshold = 0;
const int m_bit = 0;
TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int bit)
: m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_bit{bit}
{
assert(m_period > 0);
assert(0 <= m_threshold && m_threshold <= m_period);
assert(0 <= m_bit && m_bit <= 32 && m_bit < VERSIONBITS_NUM_BITS);
}
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
int64_t BeginTime(const Consensus::Params& params) const override { return m_begin; }
int64_t EndTime(const Consensus::Params& params) const override { return m_end; }
int Period(const Consensus::Params& params) const override { return m_period; }
int Threshold(const Consensus::Params& params) const override { return m_threshold; }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); }
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindexPrev, dummy_params); }
bool Condition(int32_t version) const
{
uint32_t mask = ((uint32_t)1) << m_bit;
return (((version & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (version & mask) != 0);
}
bool Condition(const CBlockIndex* pindex) const { return Condition(pindex->nVersion); }
};
/** Track blocks mined for test */
class Blocks
{
private:
std::vector<std::unique_ptr<CBlockIndex>> m_blocks;
const uint32_t m_start_time;
const uint32_t m_interval;
const int32_t m_signal;
const int32_t m_no_signal;
public:
Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal)
: m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {}
size_t size() const { return m_blocks.size(); }
CBlockIndex* tip() const
{
return m_blocks.empty() ? nullptr : m_blocks.back().get();
}
CBlockIndex* mine_block(bool signal)
{
CBlockHeader header;
header.nVersion = signal ? m_signal : m_no_signal;
header.nTime = m_start_time + m_blocks.size() * m_interval;
header.nBits = 0x1d00ffff;
auto current_block = std::make_unique<CBlockIndex>(header);
current_block->pprev = tip();
current_block->nHeight = m_blocks.size();
current_block->BuildSkip();
return m_blocks.emplace_back(std::move(current_block)).get();
}
};
} // namespace
std::unique_ptr<const CChainParams> g_params;
void initialize()
{
// this is actually comparatively slow, so only do it once
g_params = CreateChainParams(ArgsManager{}, CBaseChainParams::MAIN);
assert(g_params != nullptr);
}
constexpr uint32_t MAX_START_TIME = 4102444800; // 2100-01-01
void test_one_input(const std::vector<uint8_t>& buffer)
{
const CChainParams& params = *g_params;
const int64_t interval = params.GetConsensus().nPowTargetSpacing;
assert(interval > 1); // need to be able to halve it
assert(interval < std::numeric_limits<int32_t>::max());
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
// making period/max_periods larger slows these tests down significantly
const int period = 32;
const size_t max_periods = 16;
const size_t max_blocks = 2 * period * max_periods;
const int threshold = fuzzed_data_provider.ConsumeIntegralInRange(1, period);
assert(0 < threshold && threshold <= period); // must be able to both pass and fail threshold!
// too many blocks at 10min each might cause uint32_t time to overflow if
// block_start_time is at the end of the range above
assert(std::numeric_limits<uint32_t>::max() - MAX_START_TIME > interval * max_blocks);
const int64_t block_start_time = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(params.GenesisBlock().nTime, MAX_START_TIME);
// what values for version will we use to signal / not signal?
const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
// select deployment parameters: bit, start time, timeout
const int bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_NUM_BITS - 1);
bool always_active_test = false;
bool never_active_test = false;
int64_t start_time;
int64_t timeout;
if (fuzzed_data_provider.ConsumeBool()) {
// pick the timestamp to switch based on a block
// note states will change *after* these blocks because mediantime lags
int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(start_block, period * (max_periods - 3));
start_time = block_start_time + start_block * interval;
timeout = block_start_time + end_block * interval;
assert(start_time <= timeout);
// allow for times to not exactly match a block
if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2;
if (fuzzed_data_provider.ConsumeBool()) timeout += interval / 2;
// this may make timeout too early; if so, don't run the test
if (start_time > timeout) return;
} else {
if (fuzzed_data_provider.ConsumeBool()) {
start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
timeout = Consensus::BIP9Deployment::NO_TIMEOUT;
always_active_test = true;
} else {
start_time = 1199145601; // January 1, 2008
timeout = 1230767999; // December 31, 2008
never_active_test = true;
}
}
TestConditionChecker checker(start_time, timeout, period, threshold, bit);
// Early exit if the versions don't signal sensibly for the deployment
if (!checker.Condition(ver_signal)) return;
if (checker.Condition(ver_nosignal)) return;
if (ver_nosignal < 0) return;
// TOP_BITS should ensure version will be positive and meet min
// version requirement
assert(ver_signal > 0);
assert(ver_signal >= VERSIONBITS_LAST_OLD_BLOCK_VERSION);
// Now that we have chosen time and versions, setup to mine blocks
Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal);
/* Strategy:
* * we will mine a final period worth of blocks, with
* randomised signalling according to a mask
* * but before we mine those blocks, we will mine some
* randomised number of prior periods; with either all
* or no blocks in the period signalling
*
* We establish the mask first, then consume "bools" until
* we run out of fuzz data to work out how many prior periods
* there are and which ones will signal.
*/
// establish the mask
const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
// mine prior periods
while (fuzzed_data_provider.remaining_bytes() > 0) {
// all blocks in these periods either do or don't signal
bool signal = fuzzed_data_provider.ConsumeBool();
for (int b = 0; b < period; ++b) {
blocks.mine_block(signal);
}
// don't risk exceeding max_blocks or times may wrap around
if (blocks.size() + 2 * period > max_blocks) break;
}
// NOTE: fuzzed_data_provider may be fully consumed at this point and should not be used further
// now we mine the final period and check that everything looks sane
// count the number of signalling blocks
int blocks_sig = 0;
// get the info for the first block of the period
CBlockIndex* prev = blocks.tip();
const int exp_since = checker.GetStateSinceHeightFor(prev);
const ThresholdState exp_state = checker.GetStateFor(prev);
BIP9Stats last_stats = checker.GetStateStatisticsFor(prev);
int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1);
assert(exp_since <= prev_next_height);
// mine (period-1) blocks and check state
for (int b = 1; b < period; ++b) {
const bool signal = (signalling_mask >> (b % 32)) & 1;
if (signal) ++blocks_sig;
CBlockIndex* current_block = blocks.mine_block(signal);
// verify that signalling attempt was interpreted correctly
assert(checker.Condition(current_block) == signal);
// state and since don't change within the period
const ThresholdState state = checker.GetStateFor(current_block);
const int since = checker.GetStateSinceHeightFor(current_block);
assert(state == exp_state);
assert(since == exp_since);
// GetStateStatistics may crash when state is not STARTED
if (state != ThresholdState::STARTED) continue;
// check that after mining this block stats change as expected
const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
assert(stats.period == period);
assert(stats.threshold == threshold);
assert(stats.elapsed == b);
assert(stats.count == last_stats.count + (signal ? 1 : 0));
assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
last_stats = stats;
}
if (exp_state == ThresholdState::STARTED) {
// double check that stats.possible is sane
if (blocks_sig >= threshold - 1) assert(last_stats.possible);
}
// mine the final block
bool signal = (signalling_mask >> (period % 32)) & 1;
if (signal) ++blocks_sig;
CBlockIndex* current_block = blocks.mine_block(signal);
assert(checker.Condition(current_block) == signal);
// GetStateStatistics is safe on a period boundary
// and has progressed to a new period
const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
assert(stats.period == period);
assert(stats.threshold == threshold);
assert(stats.elapsed == 0);
assert(stats.count == 0);
assert(stats.possible == true);
// More interesting is whether the state changed.
const ThresholdState state = checker.GetStateFor(current_block);
const int since = checker.GetStateSinceHeightFor(current_block);
// since is straightforward:
assert(since % period == 0);
assert(0 <= since && since <= current_block->nHeight + 1);
if (state == exp_state) {
assert(since == exp_since);
} else {
assert(since == current_block->nHeight + 1);
}
// state is where everything interesting is
switch (state) {
case ThresholdState::DEFINED:
assert(since == 0);
assert(exp_state == ThresholdState::DEFINED);
assert(current_block->GetMedianTimePast() < checker.m_begin);
assert(current_block->GetMedianTimePast() < checker.m_end);
break;
case ThresholdState::STARTED:
assert(current_block->GetMedianTimePast() >= checker.m_begin);
assert(current_block->GetMedianTimePast() < checker.m_end);
if (exp_state == ThresholdState::STARTED) {
assert(blocks_sig < threshold);
} else {
assert(exp_state == ThresholdState::DEFINED);
}
break;
case ThresholdState::LOCKED_IN:
assert(exp_state == ThresholdState::STARTED);
assert(current_block->GetMedianTimePast() < checker.m_end);
assert(blocks_sig >= threshold);
break;
case ThresholdState::ACTIVE:
assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
break;
case ThresholdState::FAILED:
assert(current_block->GetMedianTimePast() >= checker.m_end);
assert(exp_state != ThresholdState::LOCKED_IN && exp_state != ThresholdState::ACTIVE);
break;
default:
assert(false);
}
if (blocks.size() >= period * max_periods) {
// we chose the timeout (and block times) so that by the time we have this many blocks it's all over
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
}
// "always active" has additional restrictions
if (always_active_test) {
assert(state == ThresholdState::ACTIVE);
assert(exp_state == ThresholdState::ACTIVE);
assert(since == 0);
} else {
// except for always active, the initial state is always DEFINED
assert(since > 0 || state == ThresholdState::DEFINED);
assert(exp_since > 0 || exp_state == ThresholdState::DEFINED);
}
// "never active" does too
if (never_active_test) {
assert(state == ThresholdState::FAILED);
assert(since == period);
if (exp_since == 0) {
assert(exp_state == ThresholdState::DEFINED);
} else {
assert(exp_state == ThresholdState::FAILED);
}
}
}

View file

@ -224,8 +224,22 @@ BOOST_AUTO_TEST_CASE(subnet_test)
// IPv4 address with IPv6 netmask or the other way around.
BOOST_CHECK(!CSubNet(ResolveIP("1.1.1.1"), ResolveIP("ffff::")).IsValid());
BOOST_CHECK(!CSubNet(ResolveIP("::1"), ResolveIP("255.0.0.0")).IsValid());
// Can't subnet TOR (or any other non-IPv4 and non-IPv6 network).
BOOST_CHECK(!CSubNet(ResolveIP("5wyqrzbvrdsumnok.onion"), ResolveIP("255.0.0.0")).IsValid());
// Create Non-IP subnets.
const CNetAddr tor_addr{
ResolveIP("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")};
subnet = CSubNet(tor_addr);
BOOST_CHECK(subnet.IsValid());
BOOST_CHECK_EQUAL(subnet.ToString(), tor_addr.ToString());
BOOST_CHECK(subnet.Match(tor_addr));
BOOST_CHECK(
!subnet.Match(ResolveIP("kpgvmscirrdqpekbqjsvw5teanhatztpp2gl6eee4zkowvwfxwenqaid.onion")));
BOOST_CHECK(!subnet.Match(ResolveIP("1.2.3.4")));
BOOST_CHECK(!CSubNet(tor_addr, 200).IsValid());
BOOST_CHECK(!CSubNet(tor_addr, ResolveIP("255.0.0.0")).IsValid());
subnet = ResolveSubNet("1.2.3.4/255.255.255.255");
BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32");
@ -440,8 +454,7 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0", 11), ret));
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com", 22), ret));
BOOST_CHECK(!LookupSubNet(std::string("1.2.3.0/24\0example.com\0", 23), ret));
// We only do subnetting for IPv4 and IPv6
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret));
BOOST_CHECK(LookupSubNet(std::string("5wyqrzbvrdsumnok.onion", 22), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0", 23), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com", 34), ret));
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));

View file

@ -14,6 +14,18 @@
/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, 0:55:36am */
static int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; }
static const std::string StateName(ThresholdState state)
{
switch (state) {
case ThresholdState::DEFINED: return "DEFINED";
case ThresholdState::STARTED: return "STARTED";
case ThresholdState::LOCKED_IN: return "LOCKED_IN";
case ThresholdState::ACTIVE: return "ACTIVE";
case ThresholdState::FAILED: return "FAILED";
} // no default case, so the compiler can warn about missing cases
return "";
}
static const Consensus::Params paramsDummy = Consensus::Params();
class TestConditionChecker : public AbstractThresholdConditionChecker
@ -38,6 +50,13 @@ public:
int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::ALWAYS_ACTIVE; }
};
class TestNeverActiveConditionChecker : public TestConditionChecker
{
public:
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
int64_t EndTime(const Consensus::Params& params) const override { return 1230768000; }
};
#define CHECKERS 6
class VersionBitsTester
@ -51,6 +70,8 @@ class VersionBitsTester
TestConditionChecker checker[CHECKERS];
// Another 6 that assume always active activation
TestAlwaysActiveConditionChecker checker_always[CHECKERS];
// Another 6 that assume never active activation
TestNeverActiveConditionChecker checker_never[CHECKERS];
// Test counter (to identify failures)
int num;
@ -65,6 +86,7 @@ public:
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
checker_always[i] = TestAlwaysActiveConditionChecker();
checker_never[i] = TestNeverActiveConditionChecker();
}
vpblock.clear();
return *this;
@ -92,66 +114,40 @@ public:
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == 0, strprintf("Test %i for StateSinceHeight (always active)", num));
// never active may go from DEFINED -> FAILED at the first period
const auto never_height = checker_never[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back());
BOOST_CHECK_MESSAGE(never_height == 0 || never_height == checker_never[i].Period(paramsDummy), strprintf("Test %i for StateSinceHeight (never active)", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestDefined() {
VersionBitsTester& TestState(ThresholdState exp) {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::DEFINED, strprintf("Test %i for DEFINED", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
const CBlockIndex* pindex = vpblock.empty() ? nullptr : vpblock.back();
ThresholdState got = checker[i].GetStateFor(pindex);
ThresholdState got_always = checker_always[i].GetStateFor(pindex);
ThresholdState got_never = checker_never[i].GetStateFor(pindex);
// nHeight of the next block. If vpblock is empty, the next (ie first)
// block should be the genesis block with nHeight == 0.
int height = pindex == nullptr ? 0 : pindex->nHeight + 1;
BOOST_CHECK_MESSAGE(got == exp, strprintf("Test %i for %s height %d (got %s)", num, StateName(exp), height, StateName(got)));
BOOST_CHECK_MESSAGE(got_always == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE height %d (got %s; always active case)", num, height, StateName(got_always)));
BOOST_CHECK_MESSAGE(got_never == ThresholdState::DEFINED|| got_never == ThresholdState::FAILED, strprintf("Test %i for DEFINED/FAILED height %d (got %s; never active case)", num, height, StateName(got_never)));
}
}
num++;
return *this;
}
VersionBitsTester& TestStarted() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::STARTED, strprintf("Test %i for STARTED", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestLockedIn() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestActive() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestFailed() {
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::FAILED, strprintf("Test %i for FAILED", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE (always active)", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestDefined() { return TestState(ThresholdState::DEFINED); }
VersionBitsTester& TestStarted() { return TestState(ThresholdState::STARTED); }
VersionBitsTester& TestLockedIn() { return TestState(ThresholdState::LOCKED_IN); }
VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
VersionBitsTester& TestFailed() { return TestState(ThresholdState::FAILED); }
CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : nullptr; }
};

View file

@ -9,6 +9,8 @@
#include <util/time.h>
#include <util/check.h>
#include <atomic>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <ctime>
@ -18,7 +20,7 @@
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
static std::atomic<int64_t> nMockTime(0); //!< For unit testing
static std::atomic<int64_t> nMockTime(0); //!< For testing
int64_t GetTime()
{
@ -46,6 +48,7 @@ template std::chrono::microseconds GetTime();
void SetMockTime(int64_t nMockTimeIn)
{
Assert(nMockTimeIn >= 0);
nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
}

View file

@ -221,35 +221,27 @@ static std::string LabelFromValue(const UniValue& value)
/**
* Update coin control with fee estimation based on the given parameters
*
* @param[in] pwallet Wallet pointer
* @param[in] wallet Wallet reference
* @param[in,out] cc Coin control to be updated
* @param[in] conf_target UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
* if a fee_rate is present, 0 is allowed here as a no-op positional placeholder
* @param[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
* if a fee_rate is present, "" is allowed here as a no-op positional placeholder
* @param[in] fee_rate UniValue real; fee rate in sat/vB;
* if a fee_rate is present, both conf_target and estimate_mode must either be null, or no-op
* if present, both conf_target and estimate_mode must either be null, or "unset"
* @param[in] override_min_fee bool; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead
* verify only that fee_rate is greater than 0
* @throws a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict
*/
static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee)
static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee)
{
if (!fee_rate.isNull()) {
if (!conf_target.isNull() && conf_target.get_int() > 0) {
if (!conf_target.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
}
if (!estimate_mode.isNull() && !estimate_mode.get_str().empty()) {
if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate");
}
CFeeRate fee_rate_in_sat_vb{CFeeRate(AmountFromValue(fee_rate), COIN)};
if (override_min_fee) {
if (fee_rate_in_sat_vb <= CFeeRate(0)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid fee_rate %s (must be greater than 0)", fee_rate_in_sat_vb.ToString(FeeEstimateMode::SAT_VB)));
}
cc.fOverrideFeeRate = true;
}
cc.m_feerate = fee_rate_in_sat_vb;
cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN);
if (override_min_fee) cc.fOverrideFeeRate = true;
// Default RBF to true for explicit fee_rate, if unset.
if (cc.m_signal_bip125_rbf == nullopt) cc.m_signal_bip125_rbf = true;
return;
@ -258,7 +250,7 @@ static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const U
throw JSONRPCError(RPC_INVALID_PARAMETER, InvalidEstimateModeErrorMessage());
}
if (!conf_target.isNull()) {
cc.m_confirm_target = ParseConfirmTarget(conf_target, pwallet->chain().estimateMaxBlocks());
cc.m_confirm_target = ParseConfirmTarget(conf_target, wallet.chain().estimateMaxBlocks());
}
}
@ -455,6 +447,12 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
{
EnsureWalletIsUnlocked(pwallet);
// This function is only used by sendtoaddress and sendmany.
// This should always try to sign, if we don't have private keys, don't try to do anything here.
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
// Shuffle recipient list
std::shuffle(recipients.begin(), recipients.end(), FastRandomContext());
@ -466,7 +464,7 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
FeeCalculation fee_calc_out;
auto blind_details = g_con_elementsmode ? MakeUnique<BlindDetails>() : nullptr;
if (blind_details) blind_details->ignore_blind_failure = ignore_blind_fail;
bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS), blind_details.get());
const bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true, blind_details.get());
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
}
@ -523,8 +521,8 @@ static RPCHelpMan sendtoaddress()
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1") +
"\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode using positional arguments\n"
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"sean's outpost\" false true 6 economical") +
"\nSend 0.1 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB, subtract fee from amount, BIP125-replaceable, using positional arguments\n"
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"drinks\" \"room77\" true true 0 \"\" 1") +
"\nSend 0.1 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + "/vB, subtract fee from amount, BIP125-replaceable, using positional arguments\n"
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"drinks\" \"room77\" true true null \"unset\" null 1.1") +
"\nSend 0.2 BTC with a confirmation target of 6 blocks in economical fee estimate mode using named arguments\n"
+ HelpExampleCli("-named sendtoaddress", "address=\"" + EXAMPLE_ADDRESS[0] + "\" amount=0.2 conf_target=6 estimate_mode=\"economical\"") +
"\nSend 0.5 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n"
@ -578,7 +576,7 @@ static RPCHelpMan sendtoaddress()
ignore_blind_fail = request.params[10].get_bool();
}
SetFeeEstimateMode(pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[11], /* override_min_fee */ false);
SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[11], /* override_min_fee */ false);
EnsureWalletIsUnlocked(pwallet);
@ -1045,7 +1043,7 @@ static RPCHelpMan sendmany()
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
SetFeeEstimateMode(pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[10], /* override_min_fee */ false);
SetFeeEstimateMode(*pwallet, coin_control, /* conf_target */ request.params[6], /* estimate_mode */ request.params[7], /* fee_rate */ request.params[10], /* override_min_fee */ false);
UniValue assets;
if (!request.params[8].isNull()) {
@ -2954,7 +2952,7 @@ static RPCHelpMan unloadwallet()
"Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
"Specifying the wallet name on a wallet endpoint is invalid.",
{
{"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC request", "The name of the wallet to unload."},
{"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC endpoint", "The name of the wallet to unload. Must be provided in the RPC endpoint or this parameter (but not both)."},
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
},
RPCResult{RPCResult::Type::OBJ, "", "", {
@ -2969,7 +2967,7 @@ static RPCHelpMan unloadwallet()
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
if (!request.params[0].isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot unload the requested wallet");
throw JSONRPCError(RPC_INVALID_PARAMETER, "Both the RPC endpoint wallet and wallet_name parameter were provided (only one allowed)");
}
} else {
wallet_name = request.params[0].get_str();
@ -3379,11 +3377,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("estimate_mode")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
}
CFeeRate fee_rate(AmountFromValue(options["feeRate"]));
if (fee_rate <= CFeeRate(0)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid feeRate %s (must be greater than 0)", fee_rate.ToString(FeeEstimateMode::BTC_KVB)));
}
coinControl.m_feerate = fee_rate;
coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
coinControl.fOverrideFeeRate = true;
}
@ -3393,7 +3387,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("replaceable")) {
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
SetFeeEstimateMode(pwallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
SetFeeEstimateMode(*pwallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
}
} else {
// if options is null and not a bool
@ -3703,7 +3697,7 @@ RPCHelpMan signrawtransactionwithwallet()
static RPCHelpMan bumpfee_helper(std::string method_name)
{
bool want_psbt = method_name == "psbtbumpfee";
const std::string incremental_fee{CFeeRate(DEFAULT_MIN_RELAY_TX_FEE).ToString(FeeEstimateMode::SAT_VB)};
const std::string incremental_fee{CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE).ToString(FeeEstimateMode::SAT_VB)};
return RPCHelpMan{method_name,
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
@ -3726,7 +3720,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
{"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks\n"},
{"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation",
"\nSpecify a fee rate in " + CURRENCY_ATOM + "/vB instead of relying on the built-in fee estimator.\n"
"Must be at least " + incremental_fee + " " + CURRENCY_ATOM + "/vB higher than the current transaction fee rate.\n"
"Must be at least " + incremental_fee + " higher than the current transaction fee rate.\n"
"WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB.\n"},
{"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n"
"marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
@ -3802,7 +3796,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
if (options.exists("replaceable")) {
coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
SetFeeEstimateMode(pwallet, coin_control, conf_target, options["estimate_mode"], options["fee_rate"], /* override_min_fee */ false);
SetFeeEstimateMode(*pwallet, coin_control, conf_target, options["estimate_mode"], options["fee_rate"], /* override_min_fee */ false);
}
// Make sure the results are valid at least up to the most recent block
@ -4488,10 +4482,10 @@ static RPCHelpMan send()
RPCExamples{""
"\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode\n"
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 6 economical\n") +
"Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB using positional arguments\n"
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' 0 \"\" 1\n") +
"Send 0.2 BTC with a fee rate of 1.1 " + CURRENCY_ATOM + "/vB using positional arguments\n"
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" 1.1\n") +
"Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB using the options argument\n"
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' '{\"fee_rate\": 1}'\n") +
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' null \"unset\" null '{\"fee_rate\": 1}'\n") +
"Send 0.3 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n"
+ HelpExampleCli("-named send", "outputs='{\"" + EXAMPLE_ADDRESS[0] + "\": 0.3}' fee_rate=25\n") +
"Create a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n"
@ -4503,7 +4497,7 @@ static RPCHelpMan send()
UniValueType(), // outputs (ARR or OBJ, checked later)
UniValue::VNUM, // conf_target
UniValue::VSTR, // estimate_mode
UniValue::VNUM, // fee_rate
UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
UniValue::VOBJ, // options
}, true
);
@ -4648,7 +4642,7 @@ static RPCHelpMan sethdseed()
// Do not do anything to non-HD wallets
if (!pwallet->CanSupportFeature(FEATURE_HD)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
}
EnsureWalletIsUnlocked(pwallet);
@ -5026,14 +5020,18 @@ static RPCHelpMan walletcreatefundedpsbt()
static RPCHelpMan upgradewallet()
{
return RPCHelpMan{"upgradewallet",
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified.\n"
"New keys may be generated and a new wallet backup will need to be made.",
{
{"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version"}
{"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version."}
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
{RPCResult::Type::NUM, "previous_version", "Version of wallet before this operation"},
{RPCResult::Type::NUM, "current_version", "Version of wallet after this operation"},
{RPCResult::Type::STR, "result", /* optional */ true, "Description of result, if no error"},
{RPCResult::Type::STR, "error", /* optional */ true, "Error message (if there is one)"}
},
},
@ -5056,11 +5054,27 @@ static RPCHelpMan upgradewallet()
version = request.params[0].get_int();
}
bilingual_str error;
if (!pwallet->UpgradeWallet(version, error)) {
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
const int previous_version{pwallet->GetVersion()};
const bool wallet_upgraded{pwallet->UpgradeWallet(version, error)};
const int current_version{pwallet->GetVersion()};
std::string result;
if (wallet_upgraded) {
if (previous_version == current_version) {
result = "Already at latest version. Wallet version unchanged.";
} else {
result = strprintf("Wallet upgraded successfully from version %i to version %i.", previous_version, current_version);
}
}
UniValue obj(UniValue::VOBJ);
if (!error.empty()) {
obj.pushKV("wallet_name", pwallet->GetName());
obj.pushKV("previous_version", previous_version);
obj.pushKV("current_version", current_version);
if (!result.empty()) {
obj.pushKV("result", result);
} else {
CHECK_NONFATAL(!error.empty());
obj.pushKV("error", error.original);
}
return obj;

View file

@ -460,7 +460,7 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, int new_version, bilingual
hd_upgrade = true;
}
// Upgrade to HD chain split if necessary
if (IsFeatureSupported(new_version, FEATURE_HD_SPLIT)) {
if (!IsFeatureSupported(prev_version, FEATURE_HD_SPLIT) && IsFeatureSupported(new_version, FEATURE_HD_SPLIT)) {
WalletLogPrintf("Upgrading wallet to use HD chain split\n");
m_storage.SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL);
split_upgrade = FEATURE_HD_SPLIT > prev_version;

View file

@ -35,7 +35,10 @@ static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
CoinEligibilityFilter filter_confirmed(1, 1, 0);
CoinEligibilityFilter filter_standard_extra(6, 6, 0);
CoinSelectionParams coin_selection_params(false, 0, 0, CFeeRate(0), 0);
CoinSelectionParams coin_selection_params(/* use_bnb= */ false, /* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
/* tx_no_inputs_size= */ 0);
// ELEMENTS: helper function wrapping a single-asset call to SelectCoinsMinConf
static bool SimpleSelectCoinsMinConf(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
@ -277,7 +280,10 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
}
// Make sure that effective value is working in SelectCoinsMinConf when BnB is used
CoinSelectionParams coin_selection_params_bnb(true, 0, 0, CFeeRate(3000), 0);
CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
/* tx_no_inputs_size= */ 0);
CoinSet setCoinsRet;
CAmount nValueRet;
bool bnb_used;
@ -309,7 +315,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
CCoinControl coin_control;
coin_control.fAllowOtherInputs = true;
coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
coin_selection_params_bnb.effective_fee = CFeeRate(0);
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
CAmountMap mapTargetValue;
mapTargetValue[CAsset()] = 10 * CENT;
CAmountMap mapValueRet;
@ -650,8 +656,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CAmount target = rand.randrange(balance - 1000) + 1000;
// Perform selection
CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0);
CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0);
CoinSelectionParams coin_selection_params_knapsack(/* use_bnb= */ false, /* change_output_size= */ 34,
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
/* tx_no_inputs_size= */ 0);
CoinSelectionParams coin_selection_params_bnb(/* use_bnb= */ true, /* change_output_size= */ 34,
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
/* tx_no_inputs_size= */ 0);
CoinSet out_set;
CAmount out_value = 0;
bool bnb_used = false;

View file

@ -2474,14 +2474,8 @@ bool CWallet::SelectCoinsMinConf(const CAmountMap& mapTargetValue, const CoinEli
}
// END ELEMENTS
// Get long term estimate
FeeCalculation feeCalc;
CCoinControl temp;
temp.m_confirm_target = 1008;
CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc);
// Calculate cost of change
CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
CAmount cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate effective value
for (OutputGroup& group : asset_groups) {
@ -2489,16 +2483,16 @@ bool CWallet::SelectCoinsMinConf(const CAmountMap& mapTargetValue, const CoinEli
if (coin_selection_params.m_subtract_fee_outputs) {
// Set the effective feerate to 0 as we don't want to use the effective value since the fees will be deducted from the output
group.SetFees(CFeeRate(0) /* effective_feerate */, long_term_feerate);
group.SetFees(CFeeRate(0) /* effective_feerate */, coin_selection_params.m_long_term_feerate);
} else {
group.SetFees(coin_selection_params.effective_fee, long_term_feerate);
group.SetFees(coin_selection_params.m_effective_feerate, coin_selection_params.m_long_term_feerate);
}
OutputGroup pos_group = group.GetPositiveOnlyGroup();
if (pos_group.effective_value > 0) utxo_pool.push_back(pos_group);
}
// Calculate the fees for things that aren't inputs
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
bnb_used = true;
CAmount nValueRet;
bool ret = SelectCoinsBnB(utxo_pool, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees);
@ -2604,7 +2598,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
coin_selection_params.use_bnb = false;
coin.m_input_bytes = 0;
}
coin.effective_value = coin.value - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
coin.effective_value = coin.value - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
if (coin_selection_params.use_bnb) {
value_to_select[coin.asset] -= coin.effective_value;
} else {
@ -3445,19 +3439,30 @@ bool CWallet::CreateTransactionInternal(
coin_selection_params.change_output_size += (MAX_RANGEPROOF_SIZE + DEFAULT_SURJECTIONPROOF_SIZE + WITNESS_SCALE_FACTOR - 1)/WITNESS_SCALE_FACTOR;
}
CFeeRate discard_rate = GetDiscardRate(*this);
// Set discard feerate
coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
// Get the fee rate to use effective values in coin selection
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc);
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
// provided one
if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) {
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), nFeeRateNeeded.ToString(FeeEstimateMode::SAT_VB));
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
return false;
}
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
}
// Get long term estimate
CCoinControl cc_temp;
cc_temp.m_confirm_target = chain().estimateMaxBlocks();
coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
// ELEMENTS:
// Start with tiny non-zero fee for issuance entropy and loop until there is enough fee
// Start with tiny non-zero fee for issuance entropy
nFeeRet = 1;
bool pick_new_inputs = true;
CAmountMap mapValueIn;
@ -3572,7 +3577,6 @@ bool CWallet::CreateTransactionInternal(
} else {
coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, mapValueToSelect, setCoins, mapValueIn, coin_control, coin_selection_params, bnb_used))
{
// If BnB was used, it was the first pass. No longer the first pass and continue loop with knapsack.
@ -3609,7 +3613,7 @@ bool CWallet::CreateTransactionInternal(
// Never create dust outputs; if we would, just
// add the dust to the fee.
// The nChange when BnB is used is always going to go to fees.
if (assetChange.first == policyAsset && (IsDust(newTxOut, discard_rate) || bnb_used))
if (assetChange.first == policyAsset && (IsDust(newTxOut, coin_selection_params.m_discard_feerate) || bnb_used))
{
vChangePosInOut.erase(assetChange.first);
nFeeRet += assetChange.second;
@ -3791,13 +3795,7 @@ bool CWallet::CreateTransactionInternal(
txNew = blind_details->tx_unblinded_unsigned;
}
nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false;
}
nFeeNeeded = coin_selection_params.m_effective_feerate.GetFee(nBytes);
if (nFeeRet >= nFeeNeeded) {
// Reduce fee to only the needed amount if possible. This
// prevents potential overpayment in fees if the coins
@ -3811,8 +3809,8 @@ bool CWallet::CreateTransactionInternal(
// change output. Only try this once.
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, nullptr);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
CAmount fee_needed_with_change = coin_selection_params.m_effective_feerate.GetFee(tx_size_with_change);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, coin_selection_params.m_discard_feerate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
one_more_try_20347 = bnb_used;

View file

@ -673,12 +673,23 @@ struct CoinSelectionParams
bool use_bnb = true;
size_t change_output_size = 0;
size_t change_spend_size = 0;
CFeeRate effective_fee = CFeeRate(0);
CFeeRate m_effective_feerate;
CFeeRate m_long_term_feerate;
CFeeRate m_discard_feerate;
size_t tx_noinputs_size = 0;
//! Indicate that we are subtracting the fee from outputs
bool m_subtract_fee_outputs = false;
CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), effective_fee(effective_fee), tx_noinputs_size(tx_noinputs_size) {}
CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_feerate,
CFeeRate long_term_feerate, CFeeRate discard_feerate, size_t tx_noinputs_size) :
use_bnb(use_bnb),
change_output_size(change_output_size),
change_spend_size(change_spend_size),
m_effective_feerate(effective_feerate),
m_long_term_feerate(long_term_feerate),
m_discard_feerate(discard_feerate),
tx_noinputs_size(tx_noinputs_size)
{}
CoinSelectionParams() {}
};

View file

@ -87,13 +87,9 @@ bool IsFeatureSupported(int wallet_version, int feature_version)
WalletFeature GetClosestWalletFeature(int version)
{
if (version >= FEATURE_LATEST) return FEATURE_LATEST;
if (version >= FEATURE_PRE_SPLIT_KEYPOOL) return FEATURE_PRE_SPLIT_KEYPOOL;
if (version >= FEATURE_NO_DEFAULT_KEY) return FEATURE_NO_DEFAULT_KEY;
if (version >= FEATURE_HD_SPLIT) return FEATURE_HD_SPLIT;
if (version >= FEATURE_HD) return FEATURE_HD;
if (version >= FEATURE_COMPRPUBKEY) return FEATURE_COMPRPUBKEY;
if (version >= FEATURE_WALLETCRYPT) return FEATURE_WALLETCRYPT;
if (version >= FEATURE_BASE) return FEATURE_BASE;
const std::array<WalletFeature, 8> wallet_features{{FEATURE_LATEST, FEATURE_PRE_SPLIT_KEYPOOL, FEATURE_NO_DEFAULT_KEY, FEATURE_HD_SPLIT, FEATURE_HD, FEATURE_COMPRPUBKEY, FEATURE_WALLETCRYPT, FEATURE_BASE}};
for (const WalletFeature& wf : wallet_features) {
if (version >= wf) return wf;
}
return static_cast<WalletFeature>(0);
}

View file

@ -531,7 +531,6 @@ def add_spender(spenders, *args, **kwargs):
def random_checksig_style(pubkey):
"""Creates a random CHECKSIG* tapscript that would succeed with only the valid signature on witness stack."""
return bytes(CScript([pubkey, OP_CHECKSIG]))
opcode = random.choice([OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_CHECKSIGADD])
if (opcode == OP_CHECKSIGVERIFY):
ret = CScript([pubkey, opcode, OP_1])
@ -1475,17 +1474,23 @@ class TaprootTest(BitcoinTestFramework):
g_genesis_hash = uint256_from_str(bytes.fromhex(self.nodes[1].getblockhash(0))[::-1])
self.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3])
# Transfer funds to pre-taproot node.
# Transfer value of the largest 500 coins to pre-taproot node.
addr = self.nodes[0].getnewaddress()
unsp = self.nodes[1].listunspent()
unsp = sorted(unsp, key=lambda i: i['amount'], reverse=True)
unsp = unsp[:500]
rawtx = self.nodes[1].createrawtransaction(
inputs=[{
'txid': i['txid'],
'vout': i['vout']
} for i in self.nodes[1].listunspent()],
outputs=[{addr: self.nodes[1].getbalance()['bitcoin']}],
} for i in unsp],
outputs=[{addr: sum(i['amount'] for i in unsp)}]
)
rawtx = self.nodes[1].signrawtransactionwithwallet(rawtx)['hex']
# Transaction is too large to fit into the mempool, so put it into a block
# Mine a block with the transaction
block = create_block(tmpl=self.nodes[1].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS), txlist=[rawtx])
add_witness_commitment(block)
block.rehash()

View file

@ -29,7 +29,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
def setup_network(self):
self.add_nodes(self.num_nodes, versions=[
150200, # oldest version supported by the test framework
190100, # oldest version with getmempoolinfo.loaded (used to avoid intermittent issues)
None,
])
self.start_nodes()
@ -72,5 +72,6 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
assert old_tx_hash in old_node.getrawmempool()
assert unbroadcasted_tx_hash in old_node.getrawmempool()
if __name__ == "__main__":
MempoolCompatibilityTest().main()

View file

@ -758,15 +758,21 @@ class RawTransactionsTest(BitcoinTestFramework):
result = node.fundrawtransaction(rawtx) # uses self.min_relay_tx_fee (set by settxfee)
btc_kvb_to_sat_vb = 100000 # (1e5)
result1 = node.fundrawtransaction(rawtx, {"fee_rate": 2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
result1 = node.fundrawtransaction(rawtx, {"fee_rate": str(2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee)})
result2 = node.fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
result3 = node.fundrawtransaction(rawtx, {"fee_rate": 10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
result4 = node.fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
result4 = node.fundrawtransaction(rawtx, {"feeRate": str(10 * self.min_relay_tx_fee)})
# Test that funding non-standard "zero-fee" transactions is valid.
result5 = self.nodes[3].fundrawtransaction(rawtx, {"fee_rate": 0})
result6 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 0})
result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
assert_fee_amount(result1['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
assert_fee_amount(result4['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
assert_fee_amount(result5['fee'], count_bytes(result5['hex']), 0)
assert_fee_amount(result6['fee'], count_bytes(result6['hex']), 0)
# With no arguments passed, expect fee of 141 satoshis.
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00002491, vspan=0.00000001)
@ -793,19 +799,15 @@ class RawTransactionsTest(BitcoinTestFramework):
node.fundrawtransaction, rawtx, {"estimate_mode": mode, "conf_target": n, "add_inputs": True})
self.log.info("Test invalid fee rate settings")
assert_raises_rpc_error(-8, "Invalid fee_rate 0.000 sat/vB (must be greater than 0)",
node.fundrawtransaction, rawtx, {"fee_rate": 0, "add_inputs": True})
assert_raises_rpc_error(-8, "Invalid feeRate 0.00000000 BTC/kvB (must be greater than 0)",
node.fundrawtransaction, rawtx, {"feeRate": 0, "add_inputs": True})
for param, value in {("fee_rate", 100000), ("feeRate", 1.000)}:
assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
node.fundrawtransaction, rawtx, {param: value, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount out of range",
node.fundrawtransaction, rawtx, {"fee_rate": -1, "add_inputs": True})
node.fundrawtransaction, rawtx, {param: -1, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount is not a number or string",
node.fundrawtransaction, rawtx, {"fee_rate": {"foo": "bar"}, "add_inputs": True})
node.fundrawtransaction, rawtx, {param: {"foo": "bar"}, "add_inputs": True})
assert_raises_rpc_error(-3, "Invalid amount",
node.fundrawtransaction, rawtx, {"fee_rate": "", "add_inputs": True})
node.fundrawtransaction, rawtx, {param: "", "add_inputs": True})
self.log.info("Test min fee rate checks are bypassed with fundrawtxn, e.g. a fee_rate under 1 sat/vB is allowed")
node.fundrawtransaction(rawtx, {"fee_rate": 0.99999999, "add_inputs": True})

View file

@ -249,28 +249,29 @@ class PSBTTest(BitcoinTestFramework):
#res2 =
self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": fee_rate_sb / 100000.0, "add_inputs": True})
#assert_approx(res2["fee"], 0.055, 0.005) # ELEMENTS: no "fee" field
self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed, e.g. a fee_rate under 1 sat/vB is allowed")
#res3 =
self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": 0.99999999, "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": "0.99999999", "add_inputs": True})
#assert_approx(res3["fee"], 0.00000381, 0.0000001) # ELEMENTS: no "fee" field
#res4 =
self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": 0.00000999, "add_inputs": True})
#assert_approx(res4["fee"], 0.00000381, 0.0000001) # ELEMENTS: no "fee" field
self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed and that funding non-standard 'zero-fee' transactions is valid")
for param in ["fee_rate", "feeRate"]:
assert_equal(self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {param: 0, "add_inputs": True})["fee"], 0)
self.log.info("Test invalid fee rate settings")
assert_raises_rpc_error(-8, "Invalid fee_rate 0.000 sat/vB (must be greater than 0)",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": 0, "add_inputs": True})
assert_raises_rpc_error(-8, "Invalid feeRate 0.00000000 BTC/kvB (must be greater than 0)",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"feeRate": 0, "add_inputs": True})
for param, value in {("fee_rate", 100000), ("feeRate", 1)}:
assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: value, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount out of range",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": -1, "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: -1, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount is not a number or string",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": {"foo": "bar"}, "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: {"foo": "bar"}, "add_inputs": True})
assert_raises_rpc_error(-3, "Invalid amount",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": "", "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: "", "add_inputs": True})
self.log.info("- raises RPC error if both feeRate and fee_rate are passed")
assert_raises_rpc_error(-8, "Cannot specify both fee_rate (sat/vB) and feeRate (BTC/kvB)",

View file

@ -15,6 +15,9 @@ class SetBanTests(BitcoinTestFramework):
self.setup_clean_chain = True
self.extra_args = [[],[]]
def is_banned(self, node, addr):
return any(e['address'] == addr for e in node.listbanned())
def run_test(self):
# Node 0 connects to Node 1, check that the noban permission is not granted
self.connect_nodes(0, 1)
@ -42,5 +45,18 @@ class SetBanTests(BitcoinTestFramework):
peerinfo = self.nodes[1].getpeerinfo()[0]
assert(not 'noban' in peerinfo['permissions'])
self.log.info("Test that a non-IP address can be banned/unbanned")
node = self.nodes[1]
tor_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"
ip_addr = "1.2.3.4"
assert(not self.is_banned(node, tor_addr))
assert(not self.is_banned(node, ip_addr))
node.setban(tor_addr, "add")
assert(self.is_banned(node, tor_addr))
assert(not self.is_banned(node, ip_addr))
node.setban(tor_addr, "remove")
assert(not self.is_banned(self.nodes[1], tor_addr))
assert(not self.is_banned(node, ip_addr))
if __name__ == '__main__':
SetBanTests().main()

View file

@ -4,23 +4,24 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test transaction signing using the signrawtransaction* RPCs."""
from test_framework.address import check_script, script_to_p2sh
from test_framework.address import check_script, script_to_p2sh, script_to_p2wsh
from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, find_vout_for_address, hex_str_to_bytes
from test_framework.messages import sha256
from test_framework.script import CScript, OP_0, OP_CHECKSIG
from test_framework.messages import sha256, CTransaction, CTxInWitness
from test_framework.script import CScript, OP_0, OP_CHECKSIG, OP_CHECKSEQUENCEVERIFY, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_TRUE
from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_p2wsh_script, script_to_p2wsh_script
from test_framework.wallet_util import bytes_to_wif
from decimal import Decimal
from decimal import Decimal, getcontext
from io import BytesIO
class SignRawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
elements_args = ["-blindedaddresses=1", "-initialfreecoins=2100000000000000", "-con_connect_genesis_outputs=1", "-anyonecanspendaremine=1", "-txindex=1"]
prefix_args = ["-pubkeyprefix=111", "-scriptprefix=196", "-secretprefix=239", "-extpubkeyprefix=043587CF", "-extprvkeyprefix=04358394", "-bech32_hrp=bcrt"]
prefix_args = ["-pubkeyprefix=111", "-scriptprefix=196", "-secretprefix=239", "-extpubkeyprefix=043587CF", "-extprvkeyprefix=04358394"]
self.extra_args = [prefix_args, prefix_args, elements_args]
def skip_test_if_missing_module(self):
@ -161,6 +162,19 @@ class SignRawTransactionsTest(BitcoinTestFramework):
assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"])
assert not rawTxSigned['errors'][0]['witness']
def test_fully_signed_tx(self):
self.log.info("Test signing a fully signed transaction does nothing")
self.nodes[0].walletpassphrase("password", 9999)
self.nodes[0].generate(101)
rawtx = self.nodes[0].createrawtransaction([], [{self.nodes[0].getnewaddress(): 10}])
fundedtx = self.nodes[0].fundrawtransaction(rawtx)
signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx["hex"])
assert_equal(signedtx["complete"], True)
signedtx2 = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"])
assert_equal(signedtx2["complete"], True)
assert_equal(signedtx["hex"], signedtx2["hex"])
self.nodes[0].walletlock()
def witness_script_test(self):
self.log.info("Test signing transaction to P2SH-P2WSH addresses without wallet")
# Create a new P2SH-P2WSH 1-of-1 multisig address:
@ -237,6 +251,78 @@ class SignRawTransactionsTest(BitcoinTestFramework):
txn = self.nodes[0].signrawtransactionwithwallet(hex_str, prev_txs)
assert txn["complete"]
def test_signing_with_csv(self):
self.log.info("Test signing a transaction containing a fully signed CSV input")
self.nodes[0].walletpassphrase("password", 9999)
getcontext().prec = 8
# Make sure CSV is active
self.nodes[0].generate(500)
# Create a P2WSH script with CSV
script = CScript([1, OP_CHECKSEQUENCEVERIFY, OP_DROP])
address = script_to_p2wsh(script)
# Fund that address and make the spend
txid = self.nodes[0].sendtoaddress(address, 1)
vout = find_vout_for_address(self.nodes[0], txid, address)
self.nodes[0].generate(1)
utxo = self.nodes[0].listunspent()[0]
amt = Decimal(1) + utxo["amount"] - Decimal(0.00001)
tx = self.nodes[0].createrawtransaction(
[{"txid": txid, "vout": vout, "sequence": 1},{"txid": utxo["txid"], "vout": utxo["vout"]}],
[{self.nodes[0].getnewaddress(): amt}, {"fee": 0.00001}],
self.nodes[0].getblockcount()
)
# Set the witness script
ctx = CTransaction()
ctx.deserialize(BytesIO(hex_str_to_bytes(tx)))
ctx.wit.vtxinwit.append(CTxInWitness())
ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
tx = ctx.serialize_with_witness().hex()
# Sign and send the transaction
signed = self.nodes[0].signrawtransactionwithwallet(tx)
assert_equal(signed["complete"], True)
self.nodes[0].sendrawtransaction(signed["hex"])
def test_signing_with_cltv(self):
self.log.info("Test signing a transaction containing a fully signed CLTV input")
self.nodes[0].walletpassphrase("password", 9999)
getcontext().prec = 8
# Make sure CSV is active
self.nodes[0].generate(1500)
# Create a P2WSH script with CLTV
script = CScript([1000, OP_CHECKLOCKTIMEVERIFY, OP_DROP])
address = script_to_p2wsh(script)
# Fund that address and make the spend
txid = self.nodes[0].sendtoaddress(address, 1)
vout = find_vout_for_address(self.nodes[0], txid, address)
self.nodes[0].generate(1)
utxo = self.nodes[0].listunspent()[0]
amt = Decimal(1) + utxo["amount"] - Decimal(0.00001)
tx = self.nodes[0].createrawtransaction(
[{"txid": txid, "vout": vout},{"txid": utxo["txid"], "vout": utxo["vout"]}],
[{self.nodes[0].getnewaddress(): amt}, {"fee": 0.00001}],
self.nodes[0].getblockcount()
)
# Set the witness script
ctx = CTransaction()
ctx.deserialize(BytesIO(hex_str_to_bytes(tx)))
ctx.wit.vtxinwit.append(CTxInWitness())
ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
tx = ctx.serialize_with_witness().hex()
# Sign and send the transaction
signed = self.nodes[0].signrawtransactionwithwallet(tx)
assert_equal(signed["complete"], True)
self.nodes[0].sendrawtransaction(signed["hex"])
def witness_blind_pubkey_test(self):
"""Create and compare signatures in multiple ways for a valid raw transaction with one input.
@ -314,6 +400,9 @@ class SignRawTransactionsTest(BitcoinTestFramework):
self.witness_script_test()
self.OP_1NEGATE_test()
self.test_with_lock_outputs()
self.test_fully_signed_tx()
self.test_signing_with_csv()
self.test_signing_with_cltv()
# Blinded descriptors not supported yet
if not self.options.descriptors:

View file

@ -10,6 +10,7 @@ Test corresponds to code in rpc/server.cpp.
import time
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_raises_rpc_error
class UptimeTest(BitcoinTestFramework):
@ -18,8 +19,12 @@ class UptimeTest(BitcoinTestFramework):
self.setup_clean_chain = True
def run_test(self):
self._test_negative_time()
self._test_uptime()
def _test_negative_time(self):
assert_raises_rpc_error(-8, "Mocktime can not be negative: -1.", self.nodes[0].setmocktime, -1)
def _test_uptime(self):
wait_time = 10
self.nodes[0].setmocktime(int(time.time() + wait_time))

View file

@ -397,9 +397,9 @@ class P2PInterface(P2PConnection):
assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED)
if message.nVersion >= 70016:
self.send_message(msg_wtxidrelay())
self.send_message(msg_verack())
if self.support_addrv2:
self.send_message(msg_sendaddrv2())
self.send_message(msg_verack())
self.nServices = message.nServices
# Connection helper methods

View file

@ -234,8 +234,8 @@ class WalletTest(BitcoinTestFramework):
fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
explicit_fee_rate_btc_kvb = Decimal(fee_rate_btc_kvb) / 1000
# Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
txid = self.nodes[2].sendmany(amounts={address: 10}, conf_target=0, estimate_mode="", fee_rate=fee_rate_sat_vb)
# Test passing fee_rate as a string
txid = self.nodes[2].sendmany(amounts={address: 10}, fee_rate=str(fee_rate_sat_vb))
self.nodes[2].generate(1)
self.sync_all(self.nodes[0:3])
balance = self.nodes[2].getbalance()['bitcoin']
@ -244,6 +244,17 @@ class WalletTest(BitcoinTestFramework):
node_0_bal += Decimal('10')
assert_equal(self.nodes[0].getbalance()['bitcoin'], node_0_bal)
# Test passing fee_rate as an integer
amount = Decimal("0.0001")
txid = self.nodes[2].sendmany(amounts={address: amount}, fee_rate=fee_rate_sat_vb)
self.nodes[2].generate(1)
self.sync_all(self.nodes[0:3])
balance = self.nodes[2].getbalance()['bitcoin']
node_2_bal = self.check_fee_amount(balance, node_2_bal - amount, explicit_fee_rate_btc_kvb, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
assert_equal(balance, node_2_bal)
node_0_bal += amount
assert_equal(self.nodes[0].getbalance()['bitcoin'], node_0_bal)
for key in ["totalFee", "feeRate"]:
assert_raises_rpc_error(-8, "Unknown named parameter key", self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=1, key=1)
@ -407,9 +418,8 @@ class WalletTest(BitcoinTestFramework):
amount = 3
fee_rate_sat_vb = 2
fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
# Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
txid = self.nodes[2].sendtoaddress(address=address, amount=amount, conf_target=0, estimate_mode="", fee_rate=fee_rate_sat_vb)
# Test passing fee_rate as an integer
txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=fee_rate_sat_vb)
tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
self.nodes[0].generate(1)
self.sync_all(self.nodes[0:3])
@ -417,6 +427,19 @@ class WalletTest(BitcoinTestFramework):
fee = prebalance - postbalance - Decimal(amount)
assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb))
prebalance = self.nodes[2].getbalance()['bitcoin']
amount = Decimal("0.001")
fee_rate_sat_vb = 1.23
fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
# Test passing fee_rate as a string
txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=str(fee_rate_sat_vb))
tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
self.nodes[0].generate(1)
self.sync_all(self.nodes[0:3])
postbalance = self.nodes[2].getbalance()['bitcoin']
fee = prebalance - postbalance - amount
assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb))
for key in ["totalFee", "feeRate"]:
assert_raises_rpc_error(-8, "Unknown named parameter key", self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=1, key=1)

View file

@ -109,12 +109,10 @@ class BumpFeeTest(BitcoinTestFramework):
assert_raises_rpc_error(-3, "Unexpected key {}".format(key), rbf_node.bumpfee, rbfid, {key: NORMAL})
# Bumping to just above minrelay should fail to increase the total fee enough.
assert_raises_rpc_error(-8, "Insufficient total fee 0.00000257, must be at least 0.00002284 (oldFee 0.00000999 + incrementalFee 0.00001285)",
rbf_node.bumpfee, rbfid, {"fee_rate": INSUFFICIENT})
assert_raises_rpc_error(-8, "Insufficient total fee 0.00000257", rbf_node.bumpfee, rbfid, {"fee_rate": INSUFFICIENT})
self.log.info("Test invalid fee rate settings")
assert_raises_rpc_error(-8, "Insufficient total fee 0.00, must be at least 0.00002284 (oldFee 0.00000999 + incrementalFee 0.00001285)",
rbf_node.bumpfee, rbfid, {"fee_rate": 0})
assert_raises_rpc_error(-8, "Insufficient total fee 0.00", rbf_node.bumpfee, rbfid, {"fee_rate": 0})
assert_raises_rpc_error(-4, "Specified or calculated fee 0.257 is too high (cannot be higher than -maxtxfee 0.10",
rbf_node.bumpfee, rbfid, {"fee_rate": TOO_HIGH})
assert_raises_rpc_error(-3, "Amount out of range", rbf_node.bumpfee, rbfid, {"fee_rate": -1})
@ -153,7 +151,7 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
self.sync_mempools((rbf_node, peer_node))
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
if mode == "fee_rate":
bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": NORMAL})
bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": str(NORMAL)})
bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": NORMAL})
else:
bumped_psbt = rbf_node.psbtbumpfee(rbfid)
@ -429,7 +427,7 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
self.sync_all()
# Create single-input PSBT for transaction to be bumped
psbt = watcher.walletcreatefundedpsbt([], [{dest_address: 0.0005}], 0, {"feeRate": 0.00001}, True)['psbt']
psbt = watcher.walletcreatefundedpsbt([], [{dest_address: 0.0005}], 0, {"fee_rate": 1}, True)['psbt']
psbt_signed = signer.walletprocesspsbt(psbt=psbt, sign=True, sighashtype="ALL", bip32derivs=True)
psbt_final = watcher.finalizepsbt(psbt_signed["psbt"])
original_txid = watcher.sendrawtransaction(psbt_final["hex"])

View file

@ -355,7 +355,8 @@ class MultiWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].unloadwallet)
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", self.nodes[0].unloadwallet, "dummy")
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", node.get_wallet_rpc("dummy").unloadwallet)
assert_raises_rpc_error(-8, "Cannot unload the requested wallet", w1.unloadwallet, "w2"),
assert_raises_rpc_error(-8, "Both the RPC endpoint wallet and wallet_name parameter were provided (only one allowed)", w1.unloadwallet, "w2"),
assert_raises_rpc_error(-8, "Both the RPC endpoint wallet and wallet_name parameter were provided (only one allowed)", w1.unloadwallet, "w1"),
# Successfully unload the specified wallet name
self.nodes[0].unloadwallet("w1")

View file

@ -261,21 +261,20 @@ class WalletSendTest(BitcoinTestFramework):
# ELEMENTS: we do not have the "fee" field, several lines are commented out here that should
# be revisited after #900
self.log.info("Test setting explicit fee rate")
#res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=1, add_to_wallet=False)
#res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=1, add_to_wallet=False)
#res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate="1", add_to_wallet=False)
#res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate="1", add_to_wallet=False)
#assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"], self.nodes[1].decodepsbt(res2["psbt"])["fee"])
# Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0, estimate_mode="", fee_rate=7, add_to_wallet=False)
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=7, add_to_wallet=False)
#fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
#assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.00007"))
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=2, add_to_wallet=False)
# "unset" and None are treated the same for estimate_mode
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=2, estimate_mode="unset", add_to_wallet=False)
#fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
#assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.00002"))
# Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=0, arg_estimate_mode="", arg_fee_rate=4.531, add_to_wallet=False)
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=4.531, add_to_wallet=False)
#fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
#assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.00004531"))
@ -308,10 +307,16 @@ class WalletSendTest(BitcoinTestFramework):
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=v, estimate_mode=mode,
expect_error=(-3, "Expected type number for conf_target, got {}".format(k)))
# Test setting explicit fee rate just below the minimum.
# Test setting explicit fee rate just below the minimum and at zero.
self.log.info("Explicit fee rate raises RPC error 'fee rate too low' if fee_rate of 0.99999999 is passed")
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0.99999999,
expect_error=(-4, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0.99999999,
expect_error=(-4, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0,
expect_error=(-4, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0,
expect_error=(-4, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
# TODO: Return hex if fee rate is below -maxmempool
# res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b", add_to_wallet=False)

View file

@ -22,9 +22,7 @@ from test_framework.messages import deser_compact_size, deser_string
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_is_hex_string,
assert_raises_rpc_error,
sha256sum_file,
)
@ -92,6 +90,32 @@ class UpgradeWalletTest(BitcoinTestFramework):
v16_3_node.submitblock(b)
assert_equal(v16_3_node.getblockcount(), to_height)
def test_upgradewallet(self, wallet, previous_version, requested_version=None, expected_version=None):
unchanged = expected_version == previous_version
new_version = previous_version if unchanged else expected_version if expected_version else requested_version
assert_equal(wallet.getwalletinfo()["walletversion"], previous_version)
assert_equal(wallet.upgradewallet(requested_version),
{
"wallet_name": "",
"previous_version": previous_version,
"current_version": new_version,
"result": "Already at latest version. Wallet version unchanged." if unchanged else "Wallet upgraded successfully from version {} to version {}.".format(previous_version, new_version),
}
)
assert_equal(wallet.getwalletinfo()["walletversion"], new_version)
def test_upgradewallet_error(self, wallet, previous_version, requested_version, msg):
assert_equal(wallet.getwalletinfo()["walletversion"], previous_version)
assert_equal(wallet.upgradewallet(requested_version),
{
"wallet_name": "",
"previous_version": previous_version,
"current_version": previous_version,
"error": msg,
}
)
assert_equal(wallet.getwalletinfo()["walletversion"], previous_version)
def run_test(self):
self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
self.dumb_sync_blocks()
@ -158,14 +182,8 @@ class UpgradeWalletTest(BitcoinTestFramework):
self.restart_node(0)
copy_v16()
wallet = node_master.get_wallet_rpc(self.default_wallet_name)
old_version = wallet.getwalletinfo()["walletversion"]
# calling upgradewallet without version arguments
# should return nothing if successful
assert_equal(wallet.upgradewallet(), {})
new_version = wallet.getwalletinfo()["walletversion"]
# upgraded wallet version should be greater than older one
assert_greater_than(new_version, old_version)
self.log.info("Test upgradewallet without a version argument")
self.test_upgradewallet(wallet, previous_version=159900, expected_version=169900)
# wallet should still contain the same balance
assert_equal(wallet.getbalance(), v16_3_balance)
@ -173,32 +191,27 @@ class UpgradeWalletTest(BitcoinTestFramework):
wallet = node_master.get_wallet_rpc(self.default_wallet_name)
# should have no master key hash before conversion
assert_equal('hdseedid' in wallet.getwalletinfo(), False)
# calling upgradewallet with explicit version number
# should return nothing if successful
assert_equal(wallet.upgradewallet(169900), {})
new_version = wallet.getwalletinfo()["walletversion"]
# upgraded wallet should have version 169900
assert_equal(new_version, 169900)
self.log.info("Test upgradewallet with explicit version number")
self.test_upgradewallet(wallet, previous_version=60000, requested_version=169900)
# after conversion master key hash should be present
assert_is_hex_string(wallet.getwalletinfo()['hdseedid'])
self.log.info('Intermediary versions don\'t effect anything')
self.log.info("Intermediary versions don't effect anything")
copy_non_hd()
# Wallet starts with 60000
assert_equal(60000, wallet.getwalletinfo()['walletversion'])
wallet.unloadwallet()
before_checksum = sha256sum_file(node_master_wallet)
node_master.loadwallet('')
# Can "upgrade" to 129999 which should have no effect on the wallet
wallet.upgradewallet(129999)
assert_equal(60000, wallet.getwalletinfo()['walletversion'])
# Test an "upgrade" from 60000 to 129999 has no effect, as the next version is 130000
self.test_upgradewallet(wallet, previous_version=60000, requested_version=129999, expected_version=60000)
wallet.unloadwallet()
assert_equal(before_checksum, sha256sum_file(node_master_wallet))
node_master.loadwallet('')
self.log.info('Wallets cannot be downgraded')
copy_non_hd()
assert_raises_rpc_error(-4, 'Cannot downgrade wallet', wallet.upgradewallet, 40000)
self.test_upgradewallet_error(wallet, previous_version=60000, requested_version=40000, msg="Cannot downgrade wallet")
wallet.unloadwallet()
assert_equal(before_checksum, sha256sum_file(node_master_wallet))
node_master.loadwallet('')
@ -208,8 +221,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
orig_kvs = dump_bdb_kv(node_master_wallet)
assert b'\x07hdchain' not in orig_kvs
# Upgrade to HD, no split
wallet.upgradewallet(130000)
assert_equal(130000, wallet.getwalletinfo()['walletversion'])
self.test_upgradewallet(wallet, previous_version=60000, requested_version=130000)
# Check that there is now a hd chain and it is version 1, no internal chain counter
new_kvs = dump_bdb_kv(node_master_wallet)
assert b'\x07hdchain' in new_kvs
@ -236,16 +248,12 @@ class UpgradeWalletTest(BitcoinTestFramework):
assert_equal('m/0\'/0\'/1\'', info['hdkeypath'])
self.log.info('Cannot upgrade to HD Split, needs Pre Split Keypool')
assert_raises_rpc_error(-4, 'Cannot upgrade a non HD split wallet without upgrading to support pre split keypool', wallet.upgradewallet, 139900)
assert_equal(130000, wallet.getwalletinfo()['walletversion'])
assert_raises_rpc_error(-4, 'Cannot upgrade a non HD split wallet without upgrading to support pre split keypool', wallet.upgradewallet, 159900)
assert_equal(130000, wallet.getwalletinfo()['walletversion'])
assert_raises_rpc_error(-4, 'Cannot upgrade a non HD split wallet without upgrading to support pre split keypool', wallet.upgradewallet, 169899)
assert_equal(130000, wallet.getwalletinfo()['walletversion'])
for version in [139900, 159900, 169899]:
self.test_upgradewallet_error(wallet, previous_version=130000, requested_version=version,
msg="Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified.")
self.log.info('Upgrade HD to HD chain split')
wallet.upgradewallet(169900)
assert_equal(169900, wallet.getwalletinfo()['walletversion'])
self.test_upgradewallet(wallet, previous_version=130000, requested_version=169900)
# Check that the hdchain updated correctly
new_kvs = dump_bdb_kv(node_master_wallet)
hd_chain = new_kvs[b'\x07hdchain']
@ -271,8 +279,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
self.log.info('Upgrade non-HD to HD chain split')
copy_non_hd()
wallet.upgradewallet(169900)
assert_equal(169900, wallet.getwalletinfo()['walletversion'])
self.test_upgradewallet(wallet, previous_version=60000, requested_version=169900)
# Check that the hdchain updated correctly
new_kvs = dump_bdb_kv(node_master_wallet)
hd_chain = new_kvs[b'\x07hdchain']
@ -333,8 +340,8 @@ class UpgradeWalletTest(BitcoinTestFramework):
# Check the wallet has a default key initially
old_kvs = dump_bdb_kv(node_master_wallet)
defaultkey = old_kvs[b'\x0adefaultkey']
# Upgrade the wallet. Should still have the same default key
wallet.upgradewallet(159900)
self.log.info("Upgrade the wallet. Should still have the same default key.")
self.test_upgradewallet(wallet, previous_version=139900, requested_version=159900)
new_kvs = dump_bdb_kv(node_master_wallet)
up_defaultkey = new_kvs[b'\x0adefaultkey']
assert_equal(defaultkey, up_defaultkey)
@ -342,5 +349,6 @@ class UpgradeWalletTest(BitcoinTestFramework):
v16_3_kvs = dump_bdb_kv(v16_3_wallet)
assert b'\x0adefaultkey' not in v16_3_kvs
if __name__ == '__main__':
UpgradeWalletTest().main()

View file

@ -2,7 +2,7 @@
# Copyright (c) 2018-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test createwallet arguments.
"""Test createwallet watchonly arguments.
"""
from test_framework.test_framework import BitcoinTestFramework
@ -50,6 +50,11 @@ class CreateWalletWatchonlyTest(BitcoinTestFramework):
assert_equal(len(wo_wallet.listtransactions()), 1)
assert_equal(wo_wallet.getbalance(include_watchonly=False)['bitcoin'], 0)
self.log.info('Test sending from a watch-only wallet raises RPC error')
msg = "Error: Private keys are disabled for this wallet"
assert_raises_rpc_error(-4, msg, wo_wallet.sendtoaddress, a1, 0.1)
assert_raises_rpc_error(-4, msg, wo_wallet.sendmany, amounts={a1: 0.1})
self.log.info('Testing listreceivedbyaddress watch-only defaults')
result = wo_wallet.listreceivedbyaddress()
assert_equal(len(result), 1)