mirror of
https://github.com/namecoin/namecoin-core
synced 2025-10-05 16:13:07 +02:00
Merge branch 'auxpow'
This commit is contained in:
@@ -89,7 +89,7 @@ Translations
|
||||
**Translation workflow is not yet set up for Namecoin Core. For strings which are common to Bitcoin Core, see below.**
|
||||
|
||||
Changes to translations as well as new translations can be submitted to
|
||||
[Bitcoin Core's Transifex page](https://www.transifex.com/bitcoin/bitcoin/).
|
||||
[Bitcoin Core's Transifex page](https://explore.transifex.com/bitcoin/bitcoin/).
|
||||
|
||||
Translations are periodically pulled from Transifex and merged into the git repository. See the
|
||||
[translation process](doc/translation_process.md) for details on how this works.
|
||||
|
@@ -4,8 +4,6 @@ $(package)_download_path=$(native_$(package)_download_path)
|
||||
$(package)_download_file=$(native_$(package)_download_file)
|
||||
$(package)_file_name=$(native_$(package)_file_name)
|
||||
$(package)_sha256_hash=$(native_$(package)_sha256_hash)
|
||||
$(package)_patches=abi_placement_new.patch
|
||||
$(package)_patches += fix_openbsd_build.patch
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts := -DBUILD_TESTING=OFF
|
||||
@@ -14,11 +12,6 @@ define $(package)_set_vars
|
||||
$(package)_cxxflags += -fdebug-prefix-map=$($(package)_extract_dir)=/usr -fmacro-prefix-map=$($(package)_extract_dir)=/usr
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p2 < $($(package)_patch_dir)/abi_placement_new.patch && \
|
||||
patch -p2 < $($(package)_patch_dir)/fix_openbsd_build.patch
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
$($(package)_cmake) .
|
||||
endef
|
||||
|
@@ -1,10 +1,9 @@
|
||||
package=native_capnp
|
||||
$(package)_version=1.1.0
|
||||
$(package)_version=1.2.0
|
||||
$(package)_download_path=https://capnproto.org/
|
||||
$(package)_download_file=capnproto-c++-$($(package)_version).tar.gz
|
||||
$(package)_file_name=capnproto-cxx-$($(package)_version).tar.gz
|
||||
$(package)_sha256_hash=07167580e563f5e821e3b2af1c238c16ec7181612650c5901330fa9a0da50939
|
||||
$(package)_patches=fix_openbsd_build.patch
|
||||
$(package)_sha256_hash=ed00e44ecbbda5186bc78a41ba64a8dc4a861b5f8d4e822959b0144ae6fd42ef
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts := -DBUILD_TESTING=OFF
|
||||
@@ -12,10 +11,6 @@ define $(package)_set_vars
|
||||
$(package)_config_opts += -DWITH_ZLIB=OFF
|
||||
endef
|
||||
|
||||
define $(package)_preprocess_cmds
|
||||
patch -p2 < $($(package)_patch_dir)/fix_openbsd_build.patch
|
||||
endef
|
||||
|
||||
define $(package)_config_cmds
|
||||
$($(package)_cmake) .
|
||||
endef
|
||||
|
@@ -1,71 +0,0 @@
|
||||
From 74560f26f75dda4257dce541ca362a1e763b2971 Mon Sep 17 00:00:00 2001
|
||||
From: Ryan Ofsky <ryan@ofsky.org>
|
||||
Date: Thu, 6 Feb 2025 08:39:05 -0500
|
||||
Subject: [PATCH 1/1] Avoid gcc/clang ABI incompatibility caused by
|
||||
PlacementNew
|
||||
|
||||
GCC and clang do not use same calling convention for passing empty struct
|
||||
parameters. There is more information about this in
|
||||
https://itanium-cxx-abi.github.io/cxx-abi/cxx-abi-dev/archives/2015-December/002869.html
|
||||
|
||||
Unfortunately this can create an issue in capnproto if it is built without
|
||||
optimizations in GCC, and the resulting static libraries are used in a clang
|
||||
program, or vice versa.
|
||||
|
||||
Depending on what order libraries are specified on the linker command line, and
|
||||
whether code compiled with the other compiler is calling any header functions
|
||||
that cause weak a `operator new(unsigned int, kj::_::PlacementNew, void*)`
|
||||
symbol to be defined in its own objects, this can cause the linker to link a
|
||||
GCC-generated `kj::ctor` with a clang-generated `operator new`, and the
|
||||
resulting program to crash due to the compilers using different calling
|
||||
conventions for `operator new`.
|
||||
|
||||
This problem is difficult to avoid in general, but pretty easy to avoid here by
|
||||
changing `operator new` parameter order so the empty struct parameter is last.
|
||||
|
||||
This change should be beneficial for capnproto users that may be compiling it
|
||||
without optimizations, and not necessarily using a single compiler to build all
|
||||
their dependencies.
|
||||
|
||||
The problem does not occur if any optimizations are enabled because `operator
|
||||
new` calls are inlined in that case.
|
||||
---
|
||||
c++/src/kj/common.h | 11 +++++++----
|
||||
1 file changed, 7 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/c++/src/kj/common.h b/c++/src/kj/common.h
|
||||
index b8edde3c..28ab11d6 100644
|
||||
--- a/c++/src/kj/common.h
|
||||
+++ b/c++/src/kj/common.h
|
||||
@@ -1034,24 +1034,27 @@ private:
|
||||
|
||||
// We want placement new, but we don't want to #include <new>. operator new cannot be defined in
|
||||
// a namespace, and defining it globally conflicts with the definition in <new>. So we have to
|
||||
-// define a dummy type and an operator new that uses it.
|
||||
+// define a dummy type and an operator new that uses it. The dummy type is intentionally passed
|
||||
+// as the last parameter so clang and GCC ABI calling conventions for empty struct struct parameters
|
||||
+// are compatible, and there are not segfaults trying to call clang operator new/delete from GCC or
|
||||
+// vice versa.
|
||||
|
||||
namespace _ { // private
|
||||
struct PlacementNew {};
|
||||
} // namespace _ (private)
|
||||
} // namespace kj
|
||||
|
||||
-inline void* operator new(size_t, kj::_::PlacementNew, void* __p) noexcept {
|
||||
+inline void* operator new(size_t, void* __p, kj::_::PlacementNew) noexcept {
|
||||
return __p;
|
||||
}
|
||||
|
||||
-inline void operator delete(void*, kj::_::PlacementNew, void* __p) noexcept {}
|
||||
+inline void operator delete(void*, void* __p, kj::_::PlacementNew) noexcept {}
|
||||
|
||||
namespace kj {
|
||||
|
||||
template <typename T, typename... Params>
|
||||
inline void ctor(T& location, Params&&... params) {
|
||||
- new (_::PlacementNew(), &location) T(kj::fwd<Params>(params)...);
|
||||
+ new (&location, _::PlacementNew()) T(kj::fwd<Params>(params)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
@@ -1,71 +0,0 @@
|
||||
From 0cd1792332dce6a3afae6e2bc2939da69fea65fa Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
|
||||
Date: Sat, 31 May 2025 00:49:44 +0200
|
||||
Subject: [PATCH 1/2] In cidr.c++, include <netinet/in.h> on all non-Windows
|
||||
systems
|
||||
|
||||
The motivation for this commit is to fix the build for OpenBSD,
|
||||
but it may also solves the same potential problem for other systems
|
||||
without causing any harm.
|
||||
|
||||
Suggested already twice, see
|
||||
https://github.com/capnproto/capnproto/pull/1846#discussion_r1399499535
|
||||
https://github.com/capnproto/capnproto/pull/1907#discussion_r1452602424
|
||||
---
|
||||
c++/src/kj/cidr.c++ | 3 ---
|
||||
1 file changed, 3 deletions(-)
|
||||
|
||||
diff --git a/c++/src/kj/cidr.c++ b/c++/src/kj/cidr.c++
|
||||
index 6a1fa32e..9432b8f4 100644
|
||||
--- a/c++/src/kj/cidr.c++
|
||||
+++ b/c++/src/kj/cidr.c++
|
||||
@@ -39,9 +39,6 @@
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
-#endif
|
||||
-
|
||||
-#if __FreeBSD__
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 2e76d17db3fc484061487c0779b16495939d30c3 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
|
||||
Date: Sat, 31 May 2025 01:06:42 +0200
|
||||
Subject: [PATCH 2/2] Don't set KJ_USE_KQUEUE on OpenBSD
|
||||
|
||||
OpenBSD doesn't support user event filters yet, hence
|
||||
the build fails as it misses the symbol EVFILT_USER in
|
||||
the kqueue implementation in async-unix.c++. Fix that
|
||||
by not setting KJ_USE_KQUEUE on OpenBSD, so the poll()-
|
||||
based implementation is used instead.
|
||||
|
||||
Suggested in
|
||||
https://github.com/capnproto/capnproto/pull/1907/commits/829d3f03735f8f6762a50fc346db56bf02140f02#r1452600300
|
||||
---
|
||||
c++/src/kj/async-unix.h | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/c++/src/kj/async-unix.h b/c++/src/kj/async-unix.h
|
||||
index 665305ea..e66ad8e4 100644
|
||||
--- a/c++/src/kj/async-unix.h
|
||||
+++ b/c++/src/kj/async-unix.h
|
||||
@@ -37,8 +37,9 @@ KJ_BEGIN_HEADER
|
||||
#if __linux__
|
||||
// Default to epoll on Linux.
|
||||
#define KJ_USE_EPOLL 1
|
||||
-#elif __APPLE__ || __FreeBSD__ || __OpenBSD__ || __NetBSD__ || __DragonFly__
|
||||
-// MacOS and BSDs prefer kqueue() for event notification.
|
||||
+#elif __APPLE__ || __FreeBSD__ || __NetBSD__ || __DragonFly__
|
||||
+// MacOS and most BSDs prefer kqueue() for event notification.
|
||||
+// (Note that OpenBSD's kqueue(2) doesn't support user event filters yet)
|
||||
#define KJ_USE_KQUEUE 1
|
||||
#endif
|
||||
#endif
|
||||
--
|
||||
2.49.0
|
||||
|
@@ -1,71 +0,0 @@
|
||||
From 0cd1792332dce6a3afae6e2bc2939da69fea65fa Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
|
||||
Date: Sat, 31 May 2025 00:49:44 +0200
|
||||
Subject: [PATCH 1/2] In cidr.c++, include <netinet/in.h> on all non-Windows
|
||||
systems
|
||||
|
||||
The motivation for this commit is to fix the build for OpenBSD,
|
||||
but it may also solves the same potential problem for other systems
|
||||
without causing any harm.
|
||||
|
||||
Suggested already twice, see
|
||||
https://github.com/capnproto/capnproto/pull/1846#discussion_r1399499535
|
||||
https://github.com/capnproto/capnproto/pull/1907#discussion_r1452602424
|
||||
---
|
||||
c++/src/kj/cidr.c++ | 3 ---
|
||||
1 file changed, 3 deletions(-)
|
||||
|
||||
diff --git a/c++/src/kj/cidr.c++ b/c++/src/kj/cidr.c++
|
||||
index 6a1fa32e..9432b8f4 100644
|
||||
--- a/c++/src/kj/cidr.c++
|
||||
+++ b/c++/src/kj/cidr.c++
|
||||
@@ -39,9 +39,6 @@
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
-#endif
|
||||
-
|
||||
-#if __FreeBSD__
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
--
|
||||
2.49.0
|
||||
|
||||
|
||||
From 2e76d17db3fc484061487c0779b16495939d30c3 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
|
||||
Date: Sat, 31 May 2025 01:06:42 +0200
|
||||
Subject: [PATCH 2/2] Don't set KJ_USE_KQUEUE on OpenBSD
|
||||
|
||||
OpenBSD doesn't support user event filters yet, hence
|
||||
the build fails as it misses the symbol EVFILT_USER in
|
||||
the kqueue implementation in async-unix.c++. Fix that
|
||||
by not setting KJ_USE_KQUEUE on OpenBSD, so the poll()-
|
||||
based implementation is used instead.
|
||||
|
||||
Suggested in
|
||||
https://github.com/capnproto/capnproto/pull/1907/commits/829d3f03735f8f6762a50fc346db56bf02140f02#r1452600300
|
||||
---
|
||||
c++/src/kj/async-unix.h | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/c++/src/kj/async-unix.h b/c++/src/kj/async-unix.h
|
||||
index 665305ea..e66ad8e4 100644
|
||||
--- a/c++/src/kj/async-unix.h
|
||||
+++ b/c++/src/kj/async-unix.h
|
||||
@@ -37,8 +37,9 @@ KJ_BEGIN_HEADER
|
||||
#if __linux__
|
||||
// Default to epoll on Linux.
|
||||
#define KJ_USE_EPOLL 1
|
||||
-#elif __APPLE__ || __FreeBSD__ || __OpenBSD__ || __NetBSD__ || __DragonFly__
|
||||
-// MacOS and BSDs prefer kqueue() for event notification.
|
||||
+#elif __APPLE__ || __FreeBSD__ || __NetBSD__ || __DragonFly__
|
||||
+// MacOS and most BSDs prefer kqueue() for event notification.
|
||||
+// (Note that OpenBSD's kqueue(2) doesn't support user event filters yet)
|
||||
#define KJ_USE_KQUEUE 1
|
||||
#endif
|
||||
#endif
|
||||
--
|
||||
2.49.0
|
||||
|
@@ -58,7 +58,8 @@ BIPs that are implemented by Bitcoin Core:
|
||||
Validation rules for Taproot (including Schnorr signatures and Tapscript
|
||||
leaves) are implemented as of **v0.21.0** ([PR 19953](https://github.com/bitcoin/bitcoin/pull/19953)),
|
||||
with mainnet activation as of **v0.21.1** ([PR 21377](https://github.com/bitcoin/bitcoin/pull/21377),
|
||||
[PR 21686](https://github.com/bitcoin/bitcoin/pull/21686)).
|
||||
[PR 21686](https://github.com/bitcoin/bitcoin/pull/21686)),
|
||||
always active as of **v24.0** ([PR 23536](https://github.com/bitcoin/bitcoin/pull/23536)).
|
||||
* [`BIP 350`](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki): Addresses for native v1+ segregated Witness outputs use Bech32m instead of Bech32 as of **v22.0** ([PR 20861](https://github.com/bitcoin/bitcoin/pull/20861)).
|
||||
* [`BIP 371`](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki): Taproot fields for PSBT as of **v24.0** ([PR 22558](https://github.com/bitcoin/bitcoin/pull/22558)).
|
||||
* [`BIP 379`](https://github.com/bitcoin/bips/blob/master/bip-0379.md): Miniscript was partially implemented in **v24.0** ([PR 24148](https://github.com/bitcoin/bitcoin/pull/24148)), and fully implemented as of **v26.0** ([PR 27255](https://github.com/bitcoin/bitcoin/pull/27255)).
|
||||
|
@@ -19,6 +19,7 @@ Table of Contents
|
||||
* [More conflict context with `merge.conflictstyle diff3`](#more-conflict-context-with-mergeconflictstyle-diff3)
|
||||
* [Reviewing code](#reviewing-code)
|
||||
* [Reduce mental load with `git diff` options](#reduce-mental-load-with-git-diff-options)
|
||||
* [Fetch commits directly](#fetch-commits-directly)
|
||||
* [Reference PRs easily with `refspec`s](#reference-prs-easily-with-refspecs)
|
||||
* [Diff the diffs with `git range-diff`](#diff-the-diffs-with-git-range-diff)
|
||||
|
||||
@@ -164,9 +165,17 @@ When reviewing patches that change symbol names in many places, use `git diff --
|
||||
|
||||
When reviewing patches that move code around, try using `git diff --patience commit~:old/file.cpp commit:new/file/name.cpp`, and ignoring everything except the moved body of code which should show up as neither `+` or `-` lines. In case it was not a pure move, this may even work when combined with the `-w` or `--word-diff` options described above. `--color-moved=dimmed-zebra` will also dim the coloring of moved hunks in the diff on compatible terminals.
|
||||
|
||||
### Fetch commits directly
|
||||
|
||||
Before inspecting any remotely created commit locally, it has to be fetched.
|
||||
This is possible via `git fetch origin <full_commit_hash>`. Even commits not
|
||||
part of any branch or tag can be fetched as long as the remote has not garbage
|
||||
collected them.
|
||||
|
||||
|
||||
### Reference PRs easily with `refspec`s
|
||||
|
||||
When looking at other's pull requests, it may make sense to add the following section to your `.git/config` file:
|
||||
As an alternative to fetching commits directly, when looking at pull requests by others, it may make sense to add the following section to your `.git/config` file:
|
||||
|
||||
```
|
||||
[remote "upstream-pull"]
|
||||
|
@@ -96,4 +96,4 @@ Thanks to everyone who directly contributed to this release:
|
||||
|
||||
|
||||
As well as to everyone that helped with translations on
|
||||
[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
|
||||
[Transifex](https://explore.transifex.com/bitcoin/bitcoin/).
|
||||
|
@@ -59,10 +59,10 @@ Release Process
|
||||
- Clear the release notes and move them to the wiki (see "Write the release notes" below).
|
||||
- ( **Not in Namecoin yet.** ) Translations on Transifex
|
||||
- Pull translations from Transifex into the master branch.
|
||||
- Create [a new resource](https://www.transifex.com/bitcoin/bitcoin/content/) named after the major version with the slug `qt-translation-<RRR>x`, where `RRR` is the major branch number padded with zeros. Use `src/qt/locale/bitcoin_en.xlf` to create it.
|
||||
- Create [a new resource](https://app.transifex.com/bitcoin/bitcoin/content/) named after the major version with the slug `qt-translation-<RRR>x`, where `RRR` is the major branch number padded with zeros. Use `src/qt/locale/bitcoin_en.xlf` to create it.
|
||||
- In the project workflow settings, ensure that [Translation Memory Fill-up](https://help.transifex.com/en/articles/6224817-setting-up-translation-memory-fill-up) is enabled and that [Translation Memory Context Matching](https://help.transifex.com/en/articles/6224753-translation-memory-with-context) is disabled.
|
||||
- Update the Transifex slug in [`.tx/config`](/.tx/config) to the slug of the resource created in the first step. This identifies which resource the translations will be synchronized from.
|
||||
- Make an announcement that translators can start translating for the new version. You can use one of the [previous announcements](https://www.transifex.com/bitcoin/communication/) as a template.
|
||||
- Make an announcement that translators can start translating for the new version. You can use one of the [previous announcements](https://app.transifex.com/bitcoin/communication/) as a template.
|
||||
- Change the auto-update URL for the resource to `master`, e.g. `https://raw.githubusercontent.com/bitcoin/bitcoin/master/src/qt/locale/bitcoin_en.xlf`. (Do this only after the previous steps, to prevent an auto-update from interfering.)
|
||||
|
||||
#### After branch-off (on the major release branch)
|
||||
|
@@ -41,7 +41,7 @@ git commit
|
||||
### Creating a Transifex account
|
||||
Visit the [Transifex Signup](https://app.transifex.com/signup/open-source/) page to create an account. Take note of your username and password, as they will be required to configure the command-line tool.
|
||||
|
||||
You can find the Bitcoin translation project at [https://www.transifex.com/bitcoin/bitcoin/](https://explore.transifex.com/bitcoin/bitcoin/).
|
||||
You can find the Bitcoin translation project at [https://explore.transifex.com/bitcoin/bitcoin/](https://explore.transifex.com/bitcoin/bitcoin/).
|
||||
|
||||
### Installing the Transifex client command-line tool
|
||||
The client is used to fetch updated translations. Please check installation instructions and any other details at https://developers.transifex.com/docs/cli.
|
||||
|
@@ -55,7 +55,7 @@ static void ReadRawBlockBench(benchmark::Bench& bench)
|
||||
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
||||
auto& blockman{testing_setup->m_node.chainman->m_blockman};
|
||||
const auto pos{blockman.WriteBlock(CreateTestBlock(), 413'567)};
|
||||
std::vector<uint8_t> block_data;
|
||||
std::vector<std::byte> block_data;
|
||||
blockman.ReadRawBlock(block_data, pos); // warmup
|
||||
bench.run([&] {
|
||||
const auto success{blockman.ReadRawBlock(block_data, pos)};
|
||||
|
@@ -1699,7 +1699,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||
|
||||
#ifdef ENABLE_ZMQ
|
||||
g_zmq_notification_interface = CZMQNotificationInterface::Create(
|
||||
[&chainman = node.chainman](std::vector<uint8_t>& block, const CBlockIndex& index) {
|
||||
[&chainman = node.chainman](std::vector<std::byte>& block, const CBlockIndex& index) {
|
||||
assert(chainman);
|
||||
return chainman->m_blockman.ReadRawBlock(block, WITH_LOCK(cs_main, return index.GetBlockPos()));
|
||||
});
|
||||
|
@@ -114,6 +114,21 @@ public:
|
||||
*/
|
||||
virtual std::unique_ptr<BlockTemplate> createNewBlock(const node::BlockCreateOptions& options = {}) = 0;
|
||||
|
||||
/**
|
||||
* Checks if a given block is valid.
|
||||
*
|
||||
* @param[in] block the block to check
|
||||
* @param[in] options verification options: the proof-of-work check can be
|
||||
* skipped in order to verify a template generated by
|
||||
* external software.
|
||||
* @param[out] reason failure reason (BIP22)
|
||||
* @param[out] debug more detailed rejection reason
|
||||
* @returns whether the block is valid
|
||||
*
|
||||
* For signets the challenge verification is skipped when check_pow is false.
|
||||
*/
|
||||
virtual bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) = 0;
|
||||
|
||||
//! Get internal node context. Useful for RPC and testing,
|
||||
//! but not accessible across processes.
|
||||
virtual node::NodeContext* context() { return nullptr; }
|
||||
|
@@ -14,13 +14,7 @@
|
||||
#include <validation.h>
|
||||
|
||||
namespace mp {
|
||||
// Custom serialization for BlockValidationState.
|
||||
void CustomBuildMessage(InvokeContext& invoke_context,
|
||||
const BlockValidationState& src,
|
||||
ipc::capnp::messages::BlockValidationState::Builder&& builder);
|
||||
void CustomReadMessage(InvokeContext& invoke_context,
|
||||
const ipc::capnp::messages::BlockValidationState::Reader& reader,
|
||||
BlockValidationState& dest);
|
||||
// Custom serializations
|
||||
} // namespace mp
|
||||
|
||||
#endif // BITCOIN_IPC_CAPNP_MINING_TYPES_H
|
||||
|
@@ -18,6 +18,7 @@ interface Mining $Proxy.wrap("interfaces::Mining") {
|
||||
getTip @2 (context :Proxy.Context) -> (result: Common.BlockRef, hasResult: Bool);
|
||||
waitTipChanged @3 (context :Proxy.Context, currentTip: Data, timeout: Float64) -> (result: Common.BlockRef);
|
||||
createNewBlock @4 (options: BlockCreateOptions) -> (result: BlockTemplate);
|
||||
checkBlock @5 (block: Data, options: BlockCheckOptions) -> (reason: Text, debug: Text, result: Bool);
|
||||
}
|
||||
|
||||
interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
|
||||
@@ -45,12 +46,7 @@ struct BlockWaitOptions $Proxy.wrap("node::BlockWaitOptions") {
|
||||
feeThreshold @1 : Int64 $Proxy.name("fee_threshold");
|
||||
}
|
||||
|
||||
# Note: serialization of the BlockValidationState C++ type is somewhat fragile
|
||||
# and using the struct can be awkward. It would be good if testBlockValidity
|
||||
# method were changed to return validity information in a simpler format.
|
||||
struct BlockValidationState {
|
||||
mode @0 :Int32;
|
||||
result @1 :Int32;
|
||||
rejectReason @2 :Text;
|
||||
debugMessage @3 :Text;
|
||||
struct BlockCheckOptions $Proxy.wrap("node::BlockCheckOptions") {
|
||||
checkMerkleRoot @0 :Bool $Proxy.name("check_merkle_root");
|
||||
checkPow @1 :Bool $Proxy.name("check_pow");
|
||||
}
|
||||
|
@@ -8,40 +8,4 @@
|
||||
#include <mp/proxy-types.h>
|
||||
|
||||
namespace mp {
|
||||
void CustomBuildMessage(InvokeContext& invoke_context,
|
||||
const BlockValidationState& src,
|
||||
ipc::capnp::messages::BlockValidationState::Builder&& builder)
|
||||
{
|
||||
if (src.IsValid()) {
|
||||
builder.setMode(0);
|
||||
} else if (src.IsInvalid()) {
|
||||
builder.setMode(1);
|
||||
} else if (src.IsError()) {
|
||||
builder.setMode(2);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
builder.setResult(static_cast<int>(src.GetResult()));
|
||||
builder.setRejectReason(src.GetRejectReason());
|
||||
builder.setDebugMessage(src.GetDebugMessage());
|
||||
}
|
||||
|
||||
void CustomReadMessage(InvokeContext& invoke_context,
|
||||
const ipc::capnp::messages::BlockValidationState::Reader& reader,
|
||||
BlockValidationState& dest)
|
||||
{
|
||||
if (reader.getMode() == 0) {
|
||||
assert(reader.getResult() == 0);
|
||||
assert(reader.getRejectReason().size() == 0);
|
||||
assert(reader.getDebugMessage().size() == 0);
|
||||
} else if (reader.getMode() == 1) {
|
||||
dest.Invalid(static_cast<BlockValidationResult>(reader.getResult()), reader.getRejectReason(), reader.getDebugMessage());
|
||||
} else if (reader.getMode() == 2) {
|
||||
assert(reader.getResult() == 0);
|
||||
dest.Error(reader.getRejectReason());
|
||||
assert(reader.getDebugMessage().size() == 0);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
} // namespace mp
|
||||
|
@@ -2307,7 +2307,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
||||
} else if (inv.IsMsgWitnessBlk()) {
|
||||
// Fast-path: in this case it is possible to serve the block directly from disk,
|
||||
// as the network format matches the format on disk
|
||||
std::vector<uint8_t> block_data;
|
||||
std::vector<std::byte> block_data;
|
||||
if (!m_chainman.m_blockman.ReadRawBlock(block_data, block_pos)) {
|
||||
if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) {
|
||||
LogDebug(BCLog::NET, "Block was pruned before it could be read, %s\n", pfrom.DisconnectMsg(fLogIPs));
|
||||
|
@@ -1002,7 +1002,7 @@ bool ReadBlockOrHeader(T& block, const FlatFilePos& pos, const BlockManager& blo
|
||||
block.SetNull();
|
||||
|
||||
// Open history file to read
|
||||
std::vector<uint8_t> block_data;
|
||||
std::vector<std::byte> block_data;
|
||||
if (!blockman.ReadRawBlock(block_data, pos)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1062,7 +1062,7 @@ bool BlockManager::ReadBlockHeader(CBlockHeader& block, const CBlockIndex& index
|
||||
return ReadBlockOrHeader(block, index, *this);
|
||||
}
|
||||
|
||||
bool BlockManager::ReadRawBlock(std::vector<uint8_t>& block, const FlatFilePos& pos) const
|
||||
bool BlockManager::ReadRawBlock(std::vector<std::byte>& block, const FlatFilePos& pos) const
|
||||
{
|
||||
if (pos.nPos < STORAGE_HEADER_BYTES) {
|
||||
// If nPos is less than STORAGE_HEADER_BYTES, we can't read the header that precedes the block data
|
||||
@@ -1096,7 +1096,7 @@ bool BlockManager::ReadRawBlock(std::vector<uint8_t>& block, const FlatFilePos&
|
||||
}
|
||||
|
||||
block.resize(blk_size); // Zeroing of memory is intentional here
|
||||
filein.read(MakeWritableByteSpan(block));
|
||||
filein.read(block);
|
||||
} catch (const std::exception& e) {
|
||||
LogError("Read from block file failed: %s for %s while reading raw block", e.what(), pos.ToString());
|
||||
return false;
|
||||
|
@@ -417,7 +417,7 @@ public:
|
||||
/** Functions for disk access for blocks */
|
||||
bool ReadBlock(CBlock& block, const FlatFilePos& pos, const std::optional<uint256>& expected_hash = {}) const;
|
||||
bool ReadBlock(CBlock& block, const CBlockIndex& index) const;
|
||||
bool ReadRawBlock(std::vector<uint8_t>& block, const FlatFilePos& pos) const;
|
||||
bool ReadRawBlock(std::vector<std::byte>& block, const FlatFilePos& pos) const;
|
||||
bool ReadBlockHeader(CBlockHeader& block, const CBlockIndex& pindex) const;
|
||||
|
||||
bool ReadBlockUndo(CBlockUndo& blockundo, const CBlockIndex& index) const;
|
||||
|
@@ -431,7 +431,7 @@ public:
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(misc-no-recursion)
|
||||
bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock, const CChain& active, const BlockManager& blockman)
|
||||
bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& lock, const CChain& active, const BlockManager& blockman) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
{
|
||||
if (!index) return false;
|
||||
if (block.m_hash) *block.m_hash = index->GetBlockHash();
|
||||
@@ -443,7 +443,7 @@ bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<Rec
|
||||
if (block.m_locator) { *block.m_locator = GetLocator(index); }
|
||||
if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active, blockman);
|
||||
if (block.m_data) {
|
||||
REVERSE_LOCK(lock);
|
||||
REVERSE_LOCK(lock, cs_main);
|
||||
if (!blockman.ReadBlock(*block.m_data, *index)) block.m_data->SetNull();
|
||||
}
|
||||
block.found = true;
|
||||
@@ -984,6 +984,15 @@ public:
|
||||
return std::make_unique<BlockTemplateImpl>(assemble_options, BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(), m_node);
|
||||
}
|
||||
|
||||
bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) override
|
||||
{
|
||||
LOCK(chainman().GetMutex());
|
||||
BlockValidationState state{TestBlockValidity(chainman().ActiveChainstate(), block, /*check_pow=*/options.check_pow, /*=check_merkle_root=*/options.check_merkle_root)};
|
||||
reason = state.GetRejectReason();
|
||||
debug = state.GetDebugMessage();
|
||||
return state.IsValid();
|
||||
}
|
||||
|
||||
NodeContext* context() override { return &m_node; }
|
||||
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
|
||||
KernelNotifications& notifications() { return *Assert(m_node.notifications); }
|
||||
|
@@ -184,10 +184,10 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
|
||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
|
||||
pblock->nNonce = 0;
|
||||
|
||||
BlockValidationState state;
|
||||
if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev,
|
||||
/*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) {
|
||||
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
|
||||
if (m_options.test_block_validity) {
|
||||
if (BlockValidationState state{TestBlockValidity(m_chainstate, *pblock, /*check_pow=*/false, /*check_merkle_root=*/false)}; !state.IsValid()) {
|
||||
throw std::runtime_error(strprintf("TestBlockValidity failed: %s", state.ToString()));
|
||||
}
|
||||
}
|
||||
const auto time_2{SteadyClock::now()};
|
||||
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <cstddef>
|
||||
#include <policy/policy.h>
|
||||
#include <script/script.h>
|
||||
#include <uint256.h>
|
||||
#include <util/time.h>
|
||||
|
||||
namespace node {
|
||||
@@ -85,6 +86,17 @@ struct BlockWaitOptions {
|
||||
CAmount fee_threshold{MAX_MONEY};
|
||||
};
|
||||
|
||||
struct BlockCheckOptions {
|
||||
/**
|
||||
* Set false to omit the merkle root check
|
||||
*/
|
||||
bool check_merkle_root{true};
|
||||
|
||||
/**
|
||||
* Set false to omit the proof-of-work check
|
||||
*/
|
||||
bool check_pow{true};
|
||||
};
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_TYPES_H
|
||||
|
@@ -370,7 +370,7 @@ static bool rest_block(const std::any& context,
|
||||
pos = pblockindex->GetBlockPos();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> block_data{};
|
||||
std::vector<std::byte> block_data{};
|
||||
if (!chainman.m_blockman.ReadRawBlock(block_data, pos)) {
|
||||
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
||||
}
|
||||
@@ -378,7 +378,7 @@ static bool rest_block(const std::any& context,
|
||||
switch (rf) {
|
||||
case RESTResponseFormat::BINARY: {
|
||||
req->WriteHeader("Content-Type", "application/octet-stream");
|
||||
req->WriteReply(HTTP_OK, std::as_bytes(std::span{block_data}));
|
||||
req->WriteReply(HTTP_OK, block_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -739,9 +739,9 @@ static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex& blockin
|
||||
return block;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> GetRawBlockChecked(BlockManager& blockman, const CBlockIndex& blockindex)
|
||||
static std::vector<std::byte> GetRawBlockChecked(BlockManager& blockman, const CBlockIndex& blockindex)
|
||||
{
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<std::byte> data{};
|
||||
FlatFilePos pos{};
|
||||
{
|
||||
LOCK(cs_main);
|
||||
@@ -892,7 +892,7 @@ static RPCHelpMan getblock()
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> block_data{GetRawBlockChecked(chainman.m_blockman, *pblockindex)};
|
||||
const std::vector<std::byte> block_data{GetRawBlockChecked(chainman.m_blockman, *pblockindex)};
|
||||
|
||||
if (verbosity <= 0) {
|
||||
return HexStr(block_data);
|
||||
|
@@ -390,8 +390,7 @@ static RPCHelpMan generateblock()
|
||||
block.vtx.insert(block.vtx.end(), txs.begin(), txs.end());
|
||||
RegenerateCommitments(block, chainman);
|
||||
|
||||
BlockValidationState state;
|
||||
if (!TestBlockValidity(state, chainman.GetParams(), chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) {
|
||||
if (BlockValidationState state{TestBlockValidity(chainman.ActiveChainstate(), block, /*check_pow=*/false, /*check_merkle_root=*/false)}; !state.IsValid()) {
|
||||
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
|
||||
}
|
||||
}
|
||||
@@ -747,13 +746,7 @@ static RPCHelpMan getblocktemplate()
|
||||
return "duplicate-inconclusive";
|
||||
}
|
||||
|
||||
// TestBlockValidity only supports blocks built on the current Tip
|
||||
if (block.hashPrevBlock != tip) {
|
||||
return "inconclusive-not-best-prevblk";
|
||||
}
|
||||
BlockValidationState state;
|
||||
TestBlockValidity(state, chainman.GetParams(), chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/true);
|
||||
return BIP22ValidationResult(state);
|
||||
return BIP22ValidationResult(TestBlockValidity(chainman.ActiveChainstate(), block, /*check_pow=*/false, /*check_merkle_root=*/true));
|
||||
}
|
||||
|
||||
const UniValue& aClientRules = oparam.find_value("rules");
|
||||
|
@@ -56,7 +56,7 @@ void CScheduler::serviceQueue()
|
||||
{
|
||||
// Unlock before calling f, so it can reschedule itself or another task
|
||||
// without deadlocking:
|
||||
REVERSE_LOCK(lock);
|
||||
REVERSE_LOCK(lock, newTaskMutex);
|
||||
f();
|
||||
}
|
||||
} catch (...) {
|
||||
|
@@ -100,13 +100,14 @@ private:
|
||||
class SpanReader
|
||||
{
|
||||
private:
|
||||
std::span<const unsigned char> m_data;
|
||||
std::span<const std::byte> m_data;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param[in] data Referenced byte vector to overwrite/append
|
||||
*/
|
||||
explicit SpanReader(std::span<const unsigned char> data) : m_data{data} {}
|
||||
explicit SpanReader(std::span<const unsigned char> data) : m_data{std::as_bytes(data)} {}
|
||||
explicit SpanReader(std::span<const std::byte> data) : m_data{data} {}
|
||||
|
||||
template<typename T>
|
||||
SpanReader& operator>>(T&& obj)
|
||||
|
16
src/sync.h
16
src/sync.h
@@ -14,6 +14,7 @@
|
||||
#include <threadsafety.h> // IWYU pragma: export
|
||||
#include <util/macros.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -212,16 +213,19 @@ public:
|
||||
/**
|
||||
* An RAII-style reverse lock. Unlocks on construction and locks on destruction.
|
||||
*/
|
||||
class reverse_lock {
|
||||
class SCOPED_LOCKABLE reverse_lock {
|
||||
public:
|
||||
explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
|
||||
explicit reverse_lock(UniqueLock& _lock, const MutexType& mutex, const char* _guardname, const char* _file, int _line) UNLOCK_FUNCTION(mutex) : lock(_lock), file(_file), line(_line) {
|
||||
// Ensure that mutex passed back for thread-safety analysis is indeed the original
|
||||
assert(std::addressof(mutex) == lock.mutex());
|
||||
|
||||
CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
|
||||
lock.unlock();
|
||||
LeaveCritical();
|
||||
lock.swap(templock);
|
||||
}
|
||||
|
||||
~reverse_lock() {
|
||||
~reverse_lock() UNLOCK_FUNCTION() {
|
||||
templock.swap(lock);
|
||||
EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
|
||||
lock.lock();
|
||||
@@ -240,7 +244,11 @@ public:
|
||||
friend class reverse_lock;
|
||||
};
|
||||
|
||||
#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
|
||||
// clang's thread-safety analyzer is unable to deal with aliases of mutexes, so
|
||||
// it is not possible to use the lock's copy of the mutex for that purpose.
|
||||
// Instead, the original mutex needs to be passed back to the reverse_lock for
|
||||
// the sake of thread-safety analysis, but it is not actually used otherwise.
|
||||
#define REVERSE_LOCK(g, cs) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, cs, #g, __FILE__, __LINE__)
|
||||
|
||||
// When locking a Mutex, require negative capability to ensure the lock
|
||||
// is not already held
|
||||
|
@@ -74,59 +74,6 @@ struct FuzzedWallet {
|
||||
}
|
||||
}
|
||||
CScript GetScriptPubKey(FuzzedDataProvider& fuzzed_data_provider) { return GetScriptForDestination(GetDestination(fuzzed_data_provider)); }
|
||||
void FundTx(FuzzedDataProvider& fuzzed_data_provider, CMutableTransaction tx)
|
||||
{
|
||||
// The fee of "tx" is 0, so this is the total input and output amount
|
||||
const CAmount total_amt{
|
||||
std::accumulate(tx.vout.begin(), tx.vout.end(), CAmount{}, [](CAmount t, const CTxOut& out) { return t + out.nValue; })};
|
||||
const uint32_t tx_size(GetVirtualTransactionSize(CTransaction{tx}));
|
||||
std::set<int> subtract_fee_from_outputs;
|
||||
if (fuzzed_data_provider.ConsumeBool()) {
|
||||
for (size_t i{}; i < tx.vout.size(); ++i) {
|
||||
if (fuzzed_data_provider.ConsumeBool()) {
|
||||
subtract_fee_from_outputs.insert(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<CRecipient> recipients;
|
||||
for (size_t idx = 0; idx < tx.vout.size(); idx++) {
|
||||
const CTxOut& tx_out = tx.vout[idx];
|
||||
CTxDestination dest;
|
||||
ExtractDestination(tx_out.scriptPubKey, dest);
|
||||
CRecipient recipient = {dest, tx_out.nValue, subtract_fee_from_outputs.count(idx) == 1};
|
||||
recipients.push_back(recipient);
|
||||
}
|
||||
CCoinControl coin_control;
|
||||
coin_control.m_allow_other_inputs = fuzzed_data_provider.ConsumeBool();
|
||||
CallOneOf(
|
||||
fuzzed_data_provider, [&] { coin_control.destChange = GetDestination(fuzzed_data_provider); },
|
||||
[&] { coin_control.m_change_type.emplace(fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES)); },
|
||||
[&] { /* no op (leave uninitialized) */ });
|
||||
coin_control.fAllowWatchOnly = fuzzed_data_provider.ConsumeBool();
|
||||
coin_control.m_include_unsafe_inputs = fuzzed_data_provider.ConsumeBool();
|
||||
{
|
||||
auto& r{coin_control.m_signal_bip125_rbf};
|
||||
CallOneOf(
|
||||
fuzzed_data_provider, [&] { r = true; }, [&] { r = false; }, [&] { r = std::nullopt; });
|
||||
}
|
||||
coin_control.m_feerate = CFeeRate{
|
||||
// A fee of this range should cover all cases
|
||||
fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, 2 * total_amt),
|
||||
tx_size,
|
||||
};
|
||||
if (fuzzed_data_provider.ConsumeBool()) {
|
||||
*coin_control.m_feerate += GetMinimumFeeRate(*wallet, coin_control, nullptr);
|
||||
}
|
||||
coin_control.fOverrideFeeRate = fuzzed_data_provider.ConsumeBool();
|
||||
// Add solving data (m_external_provider and SelectExternal)?
|
||||
|
||||
int change_position{fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, tx.vout.size() - 1)};
|
||||
bilingual_str error;
|
||||
// Clear tx.vout since it is not meant to be used now that we are passing outputs directly.
|
||||
// This sets us up for a future PR to completely remove tx from the function signature in favor of passing inputs directly
|
||||
tx.vout.clear();
|
||||
(void)FundTransaction(*wallet, tx, recipients, change_position, /*lockUnspents=*/false, coin_control);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,5 @@ interface FooInterface $Proxy.wrap("FooImplementation") {
|
||||
passUniValue @2 (arg :Text) -> (result :Text);
|
||||
passTransaction @3 (arg :Data) -> (result :Data);
|
||||
passVectorChar @4 (arg :Data) -> (result :Data);
|
||||
passBlockState @5 (arg :Mining.BlockValidationState) -> (result :Mining.BlockValidationState);
|
||||
passScript @6 (arg :Data) -> (result :Data);
|
||||
passScript @5 (arg :Data) -> (result :Data);
|
||||
}
|
||||
|
@@ -102,25 +102,6 @@ void IpcPipeTest()
|
||||
std::vector<char> vec2{foo->passVectorChar(vec1)};
|
||||
BOOST_CHECK_EQUAL(std::string_view(vec1.begin(), vec1.end()), std::string_view(vec2.begin(), vec2.end()));
|
||||
|
||||
BlockValidationState bs1;
|
||||
bs1.Invalid(BlockValidationResult::BLOCK_MUTATED, "reject reason", "debug message");
|
||||
BlockValidationState bs2{foo->passBlockState(bs1)};
|
||||
BOOST_CHECK_EQUAL(bs1.IsValid(), bs2.IsValid());
|
||||
BOOST_CHECK_EQUAL(bs1.IsError(), bs2.IsError());
|
||||
BOOST_CHECK_EQUAL(bs1.IsInvalid(), bs2.IsInvalid());
|
||||
BOOST_CHECK_EQUAL(static_cast<int>(bs1.GetResult()), static_cast<int>(bs2.GetResult()));
|
||||
BOOST_CHECK_EQUAL(bs1.GetRejectReason(), bs2.GetRejectReason());
|
||||
BOOST_CHECK_EQUAL(bs1.GetDebugMessage(), bs2.GetDebugMessage());
|
||||
|
||||
BlockValidationState bs3;
|
||||
BlockValidationState bs4{foo->passBlockState(bs3)};
|
||||
BOOST_CHECK_EQUAL(bs3.IsValid(), bs4.IsValid());
|
||||
BOOST_CHECK_EQUAL(bs3.IsError(), bs4.IsError());
|
||||
BOOST_CHECK_EQUAL(bs3.IsInvalid(), bs4.IsInvalid());
|
||||
BOOST_CHECK_EQUAL(static_cast<int>(bs3.GetResult()), static_cast<int>(bs4.GetResult()));
|
||||
BOOST_CHECK_EQUAL(bs3.GetRejectReason(), bs4.GetRejectReason());
|
||||
BOOST_CHECK_EQUAL(bs3.GetDebugMessage(), bs4.GetDebugMessage());
|
||||
|
||||
auto script1{CScript() << OP_11};
|
||||
auto script2{foo->passScript(script1)};
|
||||
BOOST_CHECK_EQUAL(HexStr(script1), HexStr(script2));
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include <util/translation.h>
|
||||
#include <validation.h>
|
||||
#include <versionbits.h>
|
||||
#include <pow.h>
|
||||
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
@@ -666,7 +667,44 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
CScript scriptPubKey = CScript() << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"_hex << OP_CHECKSIG;
|
||||
BlockAssembler::Options options;
|
||||
options.coinbase_output_script = scriptPubKey;
|
||||
std::unique_ptr<BlockTemplate> block_template;
|
||||
|
||||
// Create and check a simple template
|
||||
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
||||
BOOST_REQUIRE(block_template);
|
||||
{
|
||||
CBlock block{block_template->getBlock()};
|
||||
{
|
||||
std::string reason;
|
||||
std::string debug;
|
||||
BOOST_REQUIRE(!mining->checkBlock(block, {.check_pow = false}, reason, debug));
|
||||
BOOST_REQUIRE_EQUAL(reason, "bad-txnmrklroot");
|
||||
BOOST_REQUIRE_EQUAL(debug, "hashMerkleRoot mismatch");
|
||||
}
|
||||
|
||||
block.hashMerkleRoot = BlockMerkleRoot(block);
|
||||
|
||||
{
|
||||
std::string reason;
|
||||
std::string debug;
|
||||
BOOST_REQUIRE(mining->checkBlock(block, {.check_pow = false}, reason, debug));
|
||||
BOOST_REQUIRE_EQUAL(reason, "");
|
||||
BOOST_REQUIRE_EQUAL(debug, "");
|
||||
}
|
||||
|
||||
{
|
||||
// A block template does not have proof-of-work, but it might pass
|
||||
// verification by coincidence. Grind the nonce if needed:
|
||||
while (CheckProofOfWork(block.GetHash(), block.nBits, Assert(m_node.chainman)->GetParams().GetConsensus())) {
|
||||
block.nNonce++;
|
||||
}
|
||||
|
||||
std::string reason;
|
||||
std::string debug;
|
||||
BOOST_REQUIRE(!mining->checkBlock(block, {.check_pow = true}, reason, debug));
|
||||
BOOST_REQUIRE_EQUAL(reason, "high-hash");
|
||||
BOOST_REQUIRE_EQUAL(debug, "proof of work failed");
|
||||
}
|
||||
}
|
||||
|
||||
// We can't make transactions until we have inputs
|
||||
// Therefore, load 110 blocks :)
|
||||
|
@@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(reverselock_basics)
|
||||
|
||||
BOOST_CHECK(lock.owns_lock());
|
||||
{
|
||||
REVERSE_LOCK(lock);
|
||||
REVERSE_LOCK(lock, mutex);
|
||||
BOOST_CHECK(!lock.owns_lock());
|
||||
}
|
||||
BOOST_CHECK(lock.owns_lock());
|
||||
@@ -33,9 +33,9 @@ BOOST_AUTO_TEST_CASE(reverselock_multiple)
|
||||
|
||||
// Make sure undoing two locks succeeds
|
||||
{
|
||||
REVERSE_LOCK(lock);
|
||||
REVERSE_LOCK(lock, mutex);
|
||||
BOOST_CHECK(!lock.owns_lock());
|
||||
REVERSE_LOCK(lock2);
|
||||
REVERSE_LOCK(lock2, mutex2);
|
||||
BOOST_CHECK(!lock2.owns_lock());
|
||||
}
|
||||
BOOST_CHECK(lock.owns_lock());
|
||||
@@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
|
||||
g_debug_lockorder_abort = false;
|
||||
|
||||
// Make sure trying to reverse lock a previous lock fails
|
||||
BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock2), std::logic_error, HasReason("lock2 was not most recent critical section locked"));
|
||||
BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock2, mutex2), std::logic_error, HasReason("lock2 was not most recent critical section locked"));
|
||||
BOOST_CHECK(lock2.owns_lock());
|
||||
|
||||
g_debug_lockorder_abort = prev;
|
||||
@@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
|
||||
|
||||
bool failed = false;
|
||||
try {
|
||||
REVERSE_LOCK(lock);
|
||||
REVERSE_LOCK(lock, mutex);
|
||||
} catch(...) {
|
||||
failed = true;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(reverselock_errors)
|
||||
lock.lock();
|
||||
BOOST_CHECK(lock.owns_lock());
|
||||
{
|
||||
REVERSE_LOCK(lock);
|
||||
REVERSE_LOCK(lock, mutex);
|
||||
BOOST_CHECK(!lock.owns_lock());
|
||||
}
|
||||
|
||||
|
@@ -15,23 +15,24 @@
|
||||
// See https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
|
||||
// for documentation. The clang compiler can do advanced static analysis
|
||||
// of locking when given the -Wthread-safety option.
|
||||
#define LOCKABLE __attribute__((lockable))
|
||||
#define LOCKABLE __attribute__((capability("")))
|
||||
#define SCOPED_LOCKABLE __attribute__((scoped_lockable))
|
||||
#define GUARDED_BY(x) __attribute__((guarded_by(x)))
|
||||
#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
|
||||
#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
|
||||
#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
|
||||
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
|
||||
#define SHARED_LOCK_FUNCTION(...) __attribute__((shared_lock_function(__VA_ARGS__)))
|
||||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((exclusive_trylock_function(__VA_ARGS__)))
|
||||
#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((shared_trylock_function(__VA_ARGS__)))
|
||||
#define UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__)))
|
||||
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((acquire_capability(__VA_ARGS__)))
|
||||
#define SHARED_LOCK_FUNCTION(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
|
||||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) __attribute__((try_acquire_capability(__VA_ARGS__)))
|
||||
#define SHARED_TRYLOCK_FUNCTION(...) __attribute__((try_acquire_shared_capability(__VA_ARGS__)))
|
||||
#define UNLOCK_FUNCTION(...) __attribute__((release_capability(__VA_ARGS__)))
|
||||
#define SHARED_UNLOCK_FUNCTION(...) __attribute__((release_shared_capability(__VA_ARGS__)))
|
||||
#define LOCK_RETURNED(x) __attribute__((lock_returned(x)))
|
||||
#define LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
|
||||
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
|
||||
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
|
||||
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((requires_capability(__VA_ARGS__)))
|
||||
#define SHARED_LOCKS_REQUIRED(...) __attribute__((requires_shared_capability(__VA_ARGS__)))
|
||||
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
|
||||
#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__)))
|
||||
#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_capability(__VA_ARGS__)))
|
||||
#else
|
||||
#define LOCKABLE
|
||||
#define SCOPED_LOCKABLE
|
||||
@@ -44,6 +45,7 @@
|
||||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
|
||||
#define SHARED_TRYLOCK_FUNCTION(...)
|
||||
#define UNLOCK_FUNCTION(...)
|
||||
#define SHARED_UNLOCK_FUNCTION(...)
|
||||
#define LOCK_RETURNED(x)
|
||||
#define LOCKS_EXCLUDED(...)
|
||||
#define EXCLUSIVE_LOCKS_REQUIRED(...)
|
||||
|
@@ -4721,43 +4721,79 @@ MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef&
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TestBlockValidity(BlockValidationState& state,
|
||||
const CChainParams& chainparams,
|
||||
Chainstate& chainstate,
|
||||
const CBlock& block,
|
||||
CBlockIndex* pindexPrev,
|
||||
bool fCheckPOW,
|
||||
bool fCheckMerkleRoot)
|
||||
|
||||
BlockValidationState TestBlockValidity(
|
||||
Chainstate& chainstate,
|
||||
const CBlock& block,
|
||||
const bool check_pow,
|
||||
const bool check_merkle_root)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip());
|
||||
std::set<valtype> namesDummy;
|
||||
CCoinsViewCache viewNew(&chainstate.CoinsTip());
|
||||
// Lock must be held throughout this function for two reasons:
|
||||
// 1. We don't want the tip to change during several of the validation steps
|
||||
// 2. To prevent a CheckBlock() race condition for fChecked, see ProcessNewBlock()
|
||||
AssertLockHeld(chainstate.m_chainman.GetMutex());
|
||||
|
||||
BlockValidationState state;
|
||||
CBlockIndex* tip{Assert(chainstate.m_chain.Tip())};
|
||||
|
||||
if (block.hashPrevBlock != *Assert(tip->phashBlock)) {
|
||||
state.Invalid({}, "inconclusive-not-best-prevblk");
|
||||
return state;
|
||||
}
|
||||
|
||||
// For signets CheckBlock() verifies the challenge iff fCheckPow is set.
|
||||
if (!CheckBlock(block, state, chainstate.m_chainman.GetConsensus(), /*fCheckPow=*/check_pow, /*fCheckMerkleRoot=*/check_merkle_root)) {
|
||||
// This should never happen, but belt-and-suspenders don't approve the
|
||||
// block if it does.
|
||||
if (state.IsValid()) NONFATAL_UNREACHABLE();
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* At this point ProcessNewBlock would call AcceptBlock(), but we
|
||||
* don't want to store the block or its header. Run individual checks
|
||||
* instead:
|
||||
* - skip AcceptBlockHeader() because:
|
||||
* - we don't want to update the block index
|
||||
* - we do not care about duplicates
|
||||
* - we already ran CheckBlockHeader() via CheckBlock()
|
||||
* - we already checked for prev-blk-not-found
|
||||
* - we know the tip is valid, so no need to check bad-prevblk
|
||||
* - we already ran CheckBlock()
|
||||
* - do run ContextualCheckBlockHeader()
|
||||
* - do run ContextualCheckBlock()
|
||||
*/
|
||||
|
||||
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, tip)) {
|
||||
if (state.IsValid()) NONFATAL_UNREACHABLE();
|
||||
return state;
|
||||
}
|
||||
|
||||
if (!ContextualCheckBlock(block, state, chainstate.m_chainman, tip)) {
|
||||
if (state.IsValid()) NONFATAL_UNREACHABLE();
|
||||
return state;
|
||||
}
|
||||
|
||||
// We don't want ConnectBlock to update the actual chainstate, so create
|
||||
// a cache on top of it, along with a dummy block index.
|
||||
CBlockIndex index_dummy{block};
|
||||
uint256 block_hash(block.GetHash());
|
||||
CBlockIndex indexDummy(block);
|
||||
indexDummy.pprev = pindexPrev;
|
||||
indexDummy.nHeight = pindexPrev->nHeight + 1;
|
||||
indexDummy.phashBlock = &block_hash;
|
||||
index_dummy.pprev = tip;
|
||||
index_dummy.nHeight = tip->nHeight + 1;
|
||||
index_dummy.phashBlock = &block_hash;
|
||||
CCoinsViewCache view_dummy(&chainstate.CoinsTip());
|
||||
|
||||
// NOTE: CheckBlockHeader is called by CheckBlock
|
||||
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev)) {
|
||||
LogError ("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
|
||||
return false;
|
||||
// Set fJustCheck to true in order to update, and not clear, validation caches.
|
||||
std::set<valtype> namesDummy;
|
||||
if(!chainstate.ConnectBlock(block, state, &index_dummy, view_dummy, namesDummy, /*fJustCheck=*/true)) {
|
||||
if (state.IsValid()) NONFATAL_UNREACHABLE();
|
||||
return state;
|
||||
}
|
||||
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot)) {
|
||||
LogError ("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
||||
return false;
|
||||
}
|
||||
if (!ContextualCheckBlock(block, state, chainstate.m_chainman, pindexPrev)) {
|
||||
LogError ("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString());
|
||||
return false;
|
||||
}
|
||||
if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, namesDummy, true)) {
|
||||
return false;
|
||||
}
|
||||
assert(state.IsValid());
|
||||
|
||||
return true;
|
||||
// Ensure no check returned successfully while also setting an invalid state.
|
||||
if (!state.IsValid()) NONFATAL_UNREACHABLE();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/* This function is called from the RPC code for pruneblockchain */
|
||||
|
@@ -388,14 +388,28 @@ bool CheckDbLockLimit(const std::vector<CTransactionRef>& vtx);
|
||||
/** Context-independent validity checks */
|
||||
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
|
||||
|
||||
/** Check a block is completely valid from start to finish (only works on top of our current best block) */
|
||||
bool TestBlockValidity(BlockValidationState& state,
|
||||
const CChainParams& chainparams,
|
||||
Chainstate& chainstate,
|
||||
const CBlock& block,
|
||||
CBlockIndex* pindexPrev,
|
||||
bool fCheckPOW = true,
|
||||
bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
/**
|
||||
* Verify a block, including transactions.
|
||||
*
|
||||
* @param[in] block The block we want to process. Must connect to the
|
||||
* current tip.
|
||||
* @param[in] chainstate The chainstate to connect to.
|
||||
* @param[in] check_pow perform proof-of-work check, nBits in the header
|
||||
* is always checked
|
||||
* @param[in] check_merkle_root check the merkle root
|
||||
*
|
||||
* @return Valid or Invalid state. This doesn't currently return an Error state,
|
||||
* and shouldn't unless there is something wrong with the existing
|
||||
* chainstate. (This is different from functions like AcceptBlock which
|
||||
* can fail trying to save new data.)
|
||||
*
|
||||
* For signets the challenge verification is skipped when check_pow is false.
|
||||
*/
|
||||
BlockValidationState TestBlockValidity(
|
||||
Chainstate& chainstate,
|
||||
const CBlock& block,
|
||||
bool check_pow,
|
||||
bool check_merkle_root) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
/** Check with the proof of work on each blockheader matches the value in nBits */
|
||||
bool HasValidProofOfWork(const std::vector<CBlockHeader>& headers, const Consensus::Params& consensusParams);
|
||||
|
@@ -83,7 +83,7 @@ public:
|
||||
for (auto it = m_list.begin(); it != m_list.end();) {
|
||||
++it->count;
|
||||
{
|
||||
REVERSE_LOCK(lock);
|
||||
REVERSE_LOCK(lock, m_mutex);
|
||||
f(*it->callbacks);
|
||||
}
|
||||
it = --it->count ? std::next(it) : m_list.erase(it);
|
||||
|
@@ -26,16 +26,7 @@ std::vector<std::pair<fs::path, std::string>> ListDatabases(const fs::path& wall
|
||||
std::error_code ec;
|
||||
|
||||
for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
|
||||
if (ec) {
|
||||
if (fs::is_directory(*it)) {
|
||||
it.disable_recursion_pending();
|
||||
LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path()));
|
||||
} else {
|
||||
LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(!ec); // Loop should exit on error.
|
||||
try {
|
||||
const fs::path path{it->path().lexically_relative(wallet_dir)};
|
||||
|
||||
@@ -65,10 +56,18 @@ std::vector<std::pair<fs::path, std::string>> ListDatabases(const fs::path& wall
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what());
|
||||
LogWarning("Error while scanning wallet dir item: %s [%s].", e.what(), fs::PathToString(it->path()));
|
||||
it.disable_recursion_pending();
|
||||
}
|
||||
}
|
||||
if (ec) {
|
||||
// Loop could have exited with an error due to one of:
|
||||
// * wallet_dir itself not being scannable.
|
||||
// * increment() failure. (Observed on Windows native builds when
|
||||
// removing the ACL read permissions of a wallet directory after the
|
||||
// process started).
|
||||
LogWarning("Error scanning directory entries under %s: %s", fs::PathToString(wallet_dir), ec.message());
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
@@ -100,7 +99,7 @@ bool IsBDBFile(const fs::path& path)
|
||||
// This check also prevents opening lock files.
|
||||
std::error_code ec;
|
||||
auto size = fs::file_size(path, ec);
|
||||
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
|
||||
if (ec) LogWarning("Error reading file_size: %s [%s]", ec.message(), fs::PathToString(path));
|
||||
if (size < 4096) return false;
|
||||
|
||||
std::ifstream file{path, std::ios::binary};
|
||||
@@ -124,7 +123,7 @@ bool IsSQLiteFile(const fs::path& path)
|
||||
// A SQLite Database file is at least 512 bytes.
|
||||
std::error_code ec;
|
||||
auto size = fs::file_size(path, ec);
|
||||
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path));
|
||||
if (ec) LogWarning("Error reading file_size: %s [%s]", ec.message(), fs::PathToString(path));
|
||||
if (size < 512) return false;
|
||||
|
||||
std::ifstream file{path, std::ios::binary};
|
||||
|
@@ -45,14 +45,14 @@ bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::uni
|
||||
return true;
|
||||
}
|
||||
|
||||
ExternalSigner ExternalSignerScriptPubKeyMan::GetExternalSigner() {
|
||||
util::Result<ExternalSigner> ExternalSignerScriptPubKeyMan::GetExternalSigner() {
|
||||
const std::string command = gArgs.GetArg("-signer", "");
|
||||
if (command == "") throw std::runtime_error(std::string(__func__) + ": restart bitcoind with -signer=<cmd>");
|
||||
if (command == "") return util::Error{Untranslated("restart bitcoind with -signer=<cmd>")};
|
||||
std::vector<ExternalSigner> signers;
|
||||
ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString());
|
||||
if (signers.empty()) throw std::runtime_error(std::string(__func__) + ": No external signers found");
|
||||
if (signers.empty()) return util::Error{Untranslated("No external signers found")};
|
||||
// TODO: add fingerprint argument instead of failing in case of multiple signers.
|
||||
if (signers.size() > 1) throw std::runtime_error(std::string(__func__) + ": More than one external signer found. Please connect only one at a time.");
|
||||
if (signers.size() > 1) return util::Error{Untranslated("More than one external signer found. Please connect only one at a time.")};
|
||||
return signers[0];
|
||||
}
|
||||
|
||||
@@ -93,9 +93,15 @@ std::optional<PSBTError> ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySigned
|
||||
}
|
||||
if (complete) return {};
|
||||
|
||||
std::string strFailReason;
|
||||
if(!GetExternalSigner().SignTransaction(psbt, strFailReason)) {
|
||||
tfm::format(std::cerr, "Failed to sign: %s\n", strFailReason);
|
||||
auto signer{GetExternalSigner()};
|
||||
if (!signer) {
|
||||
LogWarning("%s", util::ErrorString(signer).original);
|
||||
return PSBTError::EXTERNAL_SIGNER_NOT_FOUND;
|
||||
}
|
||||
|
||||
std::string failure_reason;
|
||||
if(!signer->SignTransaction(psbt, failure_reason)) {
|
||||
LogWarning("Failed to sign: %s\n", failure_reason);
|
||||
return PSBTError::EXTERNAL_SIGNER_FAILED;
|
||||
}
|
||||
if (finalize) FinalizePSBT(psbt); // This won't work in a multisig setup
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <wallet/scriptpubkeyman.h>
|
||||
|
||||
#include <memory>
|
||||
#include <util/result.h>
|
||||
|
||||
struct bilingual_str;
|
||||
|
||||
@@ -27,7 +28,7 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
|
||||
*/
|
||||
bool SetupDescriptor(WalletBatch& batch, std::unique_ptr<Descriptor>desc);
|
||||
|
||||
static ExternalSigner GetExternalSigner();
|
||||
static util::Result<ExternalSigner> GetExternalSigner();
|
||||
|
||||
/**
|
||||
* Display address on the device and verify that the returned value matches.
|
||||
|
@@ -2623,8 +2623,9 @@ util::Result<void> CWallet::DisplayAddress(const CTxDestination& dest)
|
||||
if (signer_spk_man == nullptr) {
|
||||
continue;
|
||||
}
|
||||
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
|
||||
return signer_spk_man->DisplayAddress(dest, signer);
|
||||
auto signer{ExternalSignerScriptPubKeyMan::GetExternalSigner()};
|
||||
if (!signer) throw std::runtime_error(util::ErrorString(signer).original);
|
||||
return signer_spk_man->DisplayAddress(dest, *signer);
|
||||
}
|
||||
return util::Error{_("There is no ScriptPubKeyManager for this address")};
|
||||
}
|
||||
@@ -3665,11 +3666,12 @@ void CWallet::SetupDescriptorScriptPubKeyMans()
|
||||
return true;
|
||||
})) throw std::runtime_error("Error: cannot process db transaction for descriptors setup");
|
||||
} else {
|
||||
ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
|
||||
auto signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
|
||||
if (!signer) throw std::runtime_error(util::ErrorString(signer).original);
|
||||
|
||||
// TODO: add account parameter
|
||||
int account = 0;
|
||||
UniValue signer_res = signer.GetDescriptors(account);
|
||||
UniValue signer_res = signer->GetDescriptors(account);
|
||||
|
||||
if (!signer_res.isObject()) throw std::runtime_error(std::string(__func__) + ": Unexpected result");
|
||||
|
||||
|
@@ -40,7 +40,7 @@ std::list<const CZMQAbstractNotifier*> CZMQNotificationInterface::GetActiveNotif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<CZMQNotificationInterface> CZMQNotificationInterface::Create(std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> get_block_by_index)
|
||||
std::unique_ptr<CZMQNotificationInterface> CZMQNotificationInterface::Create(std::function<bool(std::vector<std::byte>&, const CBlockIndex&)> get_block_by_index)
|
||||
{
|
||||
std::map<std::string, CZMQNotifierFactory> factories;
|
||||
factories["pubhashblock"] = CZMQAbstractNotifier::Create<CZMQPublishHashBlockNotifier>;
|
||||
|
@@ -26,7 +26,7 @@ public:
|
||||
|
||||
std::list<const CZMQAbstractNotifier*> GetActiveNotifiers() const;
|
||||
|
||||
static std::unique_ptr<CZMQNotificationInterface> Create(std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> get_block_by_index);
|
||||
static std::unique_ptr<CZMQNotificationInterface> Create(std::function<bool(std::vector<std::byte>&, const CBlockIndex&)> get_block_by_index);
|
||||
|
||||
protected:
|
||||
bool Initialize();
|
||||
|
@@ -243,7 +243,7 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
|
||||
{
|
||||
LogDebug(BCLog::ZMQ, "Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);
|
||||
|
||||
std::vector<uint8_t> block{};
|
||||
std::vector<std::byte> block{};
|
||||
if (!m_get_block_by_index(block, *pindex)) {
|
||||
zmqError("Can't read block from disk");
|
||||
return false;
|
||||
|
@@ -49,10 +49,10 @@ public:
|
||||
class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier
|
||||
{
|
||||
private:
|
||||
const std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> m_get_block_by_index;
|
||||
const std::function<bool(std::vector<std::byte>&, const CBlockIndex&)> m_get_block_by_index;
|
||||
|
||||
public:
|
||||
CZMQPublishRawBlockNotifier(std::function<bool(std::vector<uint8_t>&, const CBlockIndex&)> get_block_by_index)
|
||||
CZMQPublishRawBlockNotifier(std::function<bool(std::vector<std::byte>&, const CBlockIndex&)> get_block_by_index)
|
||||
: m_get_block_by_index{std::move(get_block_by_index)} {}
|
||||
bool NotifyBlock(const CBlockIndex *pindex) override;
|
||||
};
|
||||
|
@@ -144,8 +144,10 @@ class BIP68Test(BitcoinTestFramework):
|
||||
# between height/time locking). Small random chance of making the locks
|
||||
# all pass.
|
||||
for _ in range(400):
|
||||
available_utxos = len(utxos)
|
||||
|
||||
# Randomly choose up to 10 inputs
|
||||
num_inputs = random.randint(1, 10)
|
||||
num_inputs = random.randint(1, min(10, available_utxos))
|
||||
random.shuffle(utxos)
|
||||
|
||||
# Track whether any sequence locks used should fail
|
||||
|
@@ -5,8 +5,10 @@
|
||||
"""Test mining RPCs
|
||||
|
||||
- getmininginfo
|
||||
- getblocktemplate proposal mode
|
||||
- submitblock"""
|
||||
- getblocktemplate
|
||||
- submitblock
|
||||
|
||||
mining_template_verification.py tests getblocktemplate in proposal mode"""
|
||||
|
||||
import copy
|
||||
from decimal import Decimal
|
||||
@@ -56,18 +58,6 @@ VERSIONBITS_TOP_BITS = 0x20000000
|
||||
VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
|
||||
DEFAULT_BLOCK_MIN_TX_FEE = 1000 # default `-blockmintxfee` setting [sat/kvB]
|
||||
|
||||
|
||||
def assert_template(node, block, expect, rehash=True):
|
||||
if rehash:
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
rsp = node.getblocktemplate(template_request={
|
||||
'data': block.serialize().hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
assert_equal(rsp, expect)
|
||||
|
||||
|
||||
class MiningTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 3
|
||||
@@ -227,8 +217,13 @@ class MiningTest(BitcoinTestFramework):
|
||||
block.nBits = int(tmpl["bits"], 16)
|
||||
block.nNonce = 0
|
||||
block.vtx = [create_coinbase(height=int(tmpl["height"]))]
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.solve()
|
||||
assert_template(node, block, None)
|
||||
assert_equal(node.getblocktemplate(template_request={
|
||||
'data': block.serialize().hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
}), None)
|
||||
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nTime = t
|
||||
@@ -378,12 +373,6 @@ class MiningTest(BitcoinTestFramework):
|
||||
self.wallet = MiniWallet(node)
|
||||
self.mine_chain()
|
||||
|
||||
def assert_submitblock(block, result_str_1, result_str_2=None):
|
||||
block.solve()
|
||||
result_str_2 = result_str_2 or 'duplicate-invalid'
|
||||
assert_equal(result_str_1, node.submitblock(hexdata=block.serialize().hex()))
|
||||
assert_equal(result_str_2, node.submitblock(hexdata=block.serialize().hex()))
|
||||
|
||||
self.log.info('getmininginfo')
|
||||
mining_info = node.getmininginfo()
|
||||
assert_equal(mining_info['blocks'], 200)
|
||||
@@ -392,14 +381,13 @@ class MiningTest(BitcoinTestFramework):
|
||||
assert 'currentblockweight' not in mining_info
|
||||
assert_equal(mining_info['bits'], nbits_str(REGTEST_N_BITS))
|
||||
assert_equal(mining_info['target'], target_str(REGTEST_TARGET))
|
||||
assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10'))
|
||||
assert_equal(mining_info['next'], {
|
||||
'height': 201,
|
||||
'target': target_str(REGTEST_TARGET),
|
||||
'bits': nbits_str(REGTEST_N_BITS),
|
||||
'difficulty': Decimal('4.656542373906925E-10')
|
||||
})
|
||||
assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334'))
|
||||
# We don't care about precision, round to avoid mismatch under Valgrind:
|
||||
assert_equal(round(mining_info['difficulty'], 10), Decimal('0.0000000005'))
|
||||
assert_equal(mining_info['next']['height'], 201)
|
||||
assert_equal(mining_info['next']['target'], target_str(REGTEST_TARGET))
|
||||
assert_equal(mining_info['next']['bits'], nbits_str(REGTEST_N_BITS))
|
||||
assert_equal(round(mining_info['next']['difficulty'], 10), Decimal('0.0000000005'))
|
||||
assert_equal(round(mining_info['networkhashps'], 5), Decimal('0.00333'))
|
||||
assert_equal(mining_info['pooledtx'], 0)
|
||||
|
||||
self.log.info("getblocktemplate: Test default witness commitment")
|
||||
@@ -435,103 +423,24 @@ class MiningTest(BitcoinTestFramework):
|
||||
block.nBits = int(tmpl["bits"], 16)
|
||||
block.nNonce = 0
|
||||
block.vtx = [coinbase_tx]
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
|
||||
self.log.info("getblocktemplate: segwit rule must be set")
|
||||
assert_raises_rpc_error(-8, "getblocktemplate must be called with the segwit rule set", node.getblocktemplate, {})
|
||||
|
||||
self.log.info("getblocktemplate: Test valid block")
|
||||
assert_template(node, block, None)
|
||||
|
||||
self.log.info("submitblock: Test block decode failure")
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, block.serialize()[:-15].hex())
|
||||
|
||||
self.log.info("getblocktemplate: Test bad input hash for coinbase transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].vin[0].prevout.hash += 1
|
||||
assert_template(node, bad_block, 'bad-cb-missing')
|
||||
|
||||
self.log.info("submitblock: Test bad input hash for coinbase transaction")
|
||||
bad_block.solve()
|
||||
assert_equal("bad-cb-missing", node.submitblock(hexdata=bad_block.serialize().hex()))
|
||||
|
||||
self.log.info("submitblock: Test block with no transactions")
|
||||
no_tx_block = copy.deepcopy(block)
|
||||
no_tx_block.vtx.clear()
|
||||
no_tx_block.hashMerkleRoot = 0
|
||||
no_tx_block.solve()
|
||||
assert_equal("bad-blk-length", node.submitblock(hexdata=no_tx_block.serialize().hex()))
|
||||
|
||||
self.log.info("submitblock: Test empty block")
|
||||
assert_equal('high-hash', node.submitblock(hexdata=CBlock().serialize().hex()))
|
||||
|
||||
self.log.info("getblocktemplate: Test truncated final transaction")
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
|
||||
'data': block.serialize()[:-1].hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
|
||||
self.log.info("getblocktemplate: Test duplicate transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx.append(bad_block.vtx[0])
|
||||
assert_template(node, bad_block, 'bad-txns-duplicate')
|
||||
assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate')
|
||||
|
||||
self.log.info("getblocktemplate: Test invalid transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_tx = copy.deepcopy(bad_block.vtx[0])
|
||||
bad_tx.vin[0].prevout.hash = 255
|
||||
bad_block.vtx.append(bad_tx)
|
||||
assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
|
||||
assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent')
|
||||
|
||||
self.log.info("getblocktemplate: Test nonfinal transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].nLockTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'bad-txns-nonfinal')
|
||||
assert_submitblock(bad_block, 'bad-txns-nonfinal')
|
||||
|
||||
self.log.info("getblocktemplate: Test bad tx count")
|
||||
# The tx count is immediately after the block header
|
||||
bad_block_sn = bytearray(block.serialize())
|
||||
assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
|
||||
bad_block_sn[BLOCK_HEADER_SIZE] += 1
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
|
||||
'data': bad_block_sn.hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
|
||||
self.log.info("getblocktemplate: Test bad bits")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nBits = 469762303 # impossible in the real world
|
||||
assert_template(node, bad_block, 'bad-diffbits')
|
||||
|
||||
self.log.info("getblocktemplate: Test bad merkle root")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashMerkleRoot += 1
|
||||
assert_template(node, bad_block, 'bad-txnmrklroot', False)
|
||||
assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot')
|
||||
|
||||
self.log.info("getblocktemplate: Test bad timestamps")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'time-too-new')
|
||||
assert_submitblock(bad_block, 'time-too-new', 'time-too-new')
|
||||
bad_block.nTime = 0
|
||||
assert_template(node, bad_block, 'time-too-old')
|
||||
assert_submitblock(bad_block, 'time-too-old', 'time-too-old')
|
||||
|
||||
self.log.info("getblocktemplate: Test not best block")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashPrevBlock = 123
|
||||
assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
|
||||
assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found')
|
||||
|
||||
self.log.info('submitheader tests')
|
||||
assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
|
||||
assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE-2)))
|
||||
assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, bad_block).serialize().hex()))
|
||||
|
||||
missing_ancestor_block = copy.deepcopy(block)
|
||||
missing_ancestor_block.hashPrevBlock = 123
|
||||
assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata=super(CBlock, missing_ancestor_block).serialize().hex()))
|
||||
|
||||
block.nTime += 1
|
||||
block.solve()
|
||||
|
302
test/functional/mining_template_verification.py
Executable file
302
test/functional/mining_template_verification.py
Executable file
@@ -0,0 +1,302 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2024-Present The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test getblocktemplate RPC in proposal mode
|
||||
|
||||
Generate several blocks and test them against the getblocktemplate RPC.
|
||||
"""
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import copy
|
||||
|
||||
from test_framework.blocktools import (
|
||||
create_block,
|
||||
create_coinbase,
|
||||
add_witness_commitment,
|
||||
)
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
from test_framework.messages import (
|
||||
BLOCK_HEADER_SIZE,
|
||||
uint256_from_compact,
|
||||
)
|
||||
|
||||
from test_framework.wallet import (
|
||||
MiniWallet,
|
||||
)
|
||||
|
||||
def assert_template(node, block, expect, *, rehash=True, submit=True, solve=True, expect_submit=None):
|
||||
if rehash:
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
|
||||
rsp = node.getblocktemplate(template_request={
|
||||
'data': block.serialize().hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
assert_equal(rsp, expect)
|
||||
# Only attempt to submit invalid templates
|
||||
if submit and expect is not None:
|
||||
# submitblock runs checks in a different order, so may not return
|
||||
# the same error
|
||||
if expect_submit is None:
|
||||
expect_submit = expect
|
||||
if solve:
|
||||
block.solve()
|
||||
assert_equal(node.submitblock(block.serialize().hex()), expect_submit)
|
||||
|
||||
class MiningTemplateVerificationTest(BitcoinTestFramework):
|
||||
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
|
||||
def valid_block_test(self, node, block):
|
||||
self.log.info("Valid block")
|
||||
assert_template(node, block, None)
|
||||
|
||||
def cb_missing_test(self, node, block):
|
||||
self.log.info("Bad input hash for coinbase transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].vin[0].prevout.hash += 1
|
||||
assert_template(node, bad_block, 'bad-cb-missing')
|
||||
|
||||
def block_without_transactions_test(self, node, block):
|
||||
self.log.info("Block with no transactions")
|
||||
|
||||
no_tx_block = copy.deepcopy(block)
|
||||
no_tx_block.vtx.clear()
|
||||
no_tx_block.hashMerkleRoot = 0
|
||||
no_tx_block.solve()
|
||||
assert_template(node, no_tx_block, 'bad-blk-length', rehash=False)
|
||||
|
||||
def truncated_final_transaction_test(self, node, block):
|
||||
self.log.info("Truncated final transaction")
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate,
|
||||
template_request={
|
||||
"data": block.serialize()[:-1].hex(),
|
||||
"mode": "proposal",
|
||||
"rules": ["segwit"],
|
||||
}
|
||||
)
|
||||
|
||||
def duplicate_transaction_test(self, node, block):
|
||||
self.log.info("Duplicate transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx.append(bad_block.vtx[0])
|
||||
assert_template(node, bad_block, 'bad-txns-duplicate')
|
||||
|
||||
def thin_air_spending_test(self, node, block):
|
||||
self.log.info("Transaction that spends from thin air")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_tx = copy.deepcopy(bad_block.vtx[0])
|
||||
bad_tx.vin[0].prevout.hash = 255
|
||||
bad_block.vtx.append(bad_tx)
|
||||
assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
|
||||
|
||||
def non_final_transaction_test(self, node, block):
|
||||
self.log.info("Non-final transaction")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.vtx[0].nLockTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'bad-txns-nonfinal')
|
||||
|
||||
def bad_tx_count_test(self, node, block):
|
||||
self.log.info("Bad tx count")
|
||||
# The tx count is immediately after the block header
|
||||
bad_block_sn = bytearray(block.serialize())
|
||||
assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
|
||||
bad_block_sn[BLOCK_HEADER_SIZE] += 1
|
||||
assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {
|
||||
'data': bad_block_sn.hex(),
|
||||
'mode': 'proposal',
|
||||
'rules': ['segwit'],
|
||||
})
|
||||
|
||||
def nbits_test(self, node, block):
|
||||
self.log.info("Extremely high nBits")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nBits = 469762303 # impossible in the real world
|
||||
assert_template(node, bad_block, "bad-diffbits", solve=False, expect_submit="high-hash")
|
||||
|
||||
self.log.info("Lowering nBits should make the block invalid")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nBits -= 1
|
||||
assert_template(node, bad_block, "bad-diffbits")
|
||||
|
||||
def merkle_root_test(self, node, block):
|
||||
self.log.info("Bad merkle root")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashMerkleRoot += 1
|
||||
assert_template(node, bad_block, 'bad-txnmrklroot', rehash=False)
|
||||
|
||||
def bad_timestamp_test(self, node, block):
|
||||
self.log.info("Bad timestamps")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.nTime = 2**32 - 1
|
||||
assert_template(node, bad_block, 'time-too-new')
|
||||
bad_block.nTime = 0
|
||||
assert_template(node, bad_block, 'time-too-old')
|
||||
|
||||
def current_tip_test(self, node, block):
|
||||
self.log.info("Block must build on the current tip")
|
||||
bad_block = copy.deepcopy(block)
|
||||
bad_block.hashPrevBlock = 123
|
||||
bad_block.solve()
|
||||
|
||||
assert_template(node, bad_block, "inconclusive-not-best-prevblk", expect_submit="prev-blk-not-found")
|
||||
|
||||
def pow_test(self, node, block):
|
||||
'''Modifies block with the generated PoW'''
|
||||
self.log.info("Generate a block")
|
||||
target = uint256_from_compact(block.nBits)
|
||||
# Ensure that it doesn't meet the target by coincidence
|
||||
while block.sha256 <= target:
|
||||
block.nNonce += 1
|
||||
block.rehash()
|
||||
self.log.debug("Found a nonce")
|
||||
|
||||
self.log.info("A block template doesn't need PoW")
|
||||
assert_template(node, block, None)
|
||||
|
||||
self.log.info("Add proof of work")
|
||||
block.solve()
|
||||
assert_template(node, block, None)
|
||||
|
||||
def submit_test(self, node, block_0_height, block):
|
||||
self.log.info("getblocktemplate call in previous tests did not submit the block")
|
||||
assert_equal(node.getblockcount(), block_0_height + 1)
|
||||
|
||||
self.log.info("Submitting this block should succeed")
|
||||
assert_equal(node.submitblock(block.serialize().hex()), None)
|
||||
node.waitforblockheight(2)
|
||||
|
||||
def transaction_test(self, node, block_0_height, tx):
|
||||
self.log.info("make block template with a transaction")
|
||||
|
||||
block_1 = node.getblock(node.getblockhash(block_0_height + 1))
|
||||
block_2_hash = node.getblockhash(block_0_height + 2)
|
||||
|
||||
block_3 = create_block(
|
||||
int(block_2_hash, 16),
|
||||
create_coinbase(block_0_height + 3),
|
||||
block_1["mediantime"] + 1,
|
||||
txlist=[tx["hex"]],
|
||||
)
|
||||
assert_equal(len(block_3.vtx), 2)
|
||||
add_witness_commitment(block_3)
|
||||
block_3.solve()
|
||||
assert_template(node, block_3, None)
|
||||
|
||||
self.log.info("checking block validity did not update the UTXO set")
|
||||
# Call again to ensure the UTXO set wasn't updated
|
||||
assert_template(node, block_3, None)
|
||||
|
||||
def overspending_transaction_test(self, node, block_0_height, tx):
|
||||
self.log.info("Add an transaction that spends too much")
|
||||
|
||||
block_1 = node.getblock(node.getblockhash(block_0_height + 1))
|
||||
block_2_hash = node.getblockhash(block_0_height + 2)
|
||||
|
||||
bad_tx = copy.deepcopy(tx)
|
||||
bad_tx["tx"].vout[0].nValue = 10000000000
|
||||
bad_tx_hex = bad_tx["tx"].serialize().hex()
|
||||
assert_equal(
|
||||
node.testmempoolaccept([bad_tx_hex])[0]["reject-reason"],
|
||||
"bad-txns-in-belowout",
|
||||
)
|
||||
block_3 = create_block(
|
||||
int(block_2_hash, 16),
|
||||
create_coinbase(block_0_height + 3),
|
||||
block_1["mediantime"] + 1,
|
||||
txlist=[bad_tx_hex],
|
||||
)
|
||||
assert_equal(len(block_3.vtx), 2)
|
||||
add_witness_commitment(block_3)
|
||||
block_3.solve()
|
||||
|
||||
assert_template(node, block_3, "bad-txns-in-belowout")
|
||||
|
||||
def spend_twice_test(self, node, block_0_height, tx):
|
||||
block_1 = node.getblock(node.getblockhash(block_0_height + 1))
|
||||
block_2_hash = node.getblockhash(block_0_height + 2)
|
||||
|
||||
self.log.info("Can't spend coins twice")
|
||||
tx_hex = tx["tx"].serialize().hex()
|
||||
tx_2 = copy.deepcopy(tx)
|
||||
tx_2_hex = tx_2["tx"].serialize().hex()
|
||||
# Nothing wrong with these transactions individually
|
||||
assert_equal(node.testmempoolaccept([tx_hex])[0]["allowed"], True)
|
||||
assert_equal(node.testmempoolaccept([tx_2_hex])[0]["allowed"], True)
|
||||
# But can't be combined
|
||||
assert_equal(
|
||||
node.testmempoolaccept([tx_hex, tx_2_hex])[0]["package-error"],
|
||||
"package-contains-duplicates",
|
||||
)
|
||||
block_3 = create_block(
|
||||
int(block_2_hash, 16),
|
||||
create_coinbase(block_0_height + 3),
|
||||
block_1["mediantime"] + 1,
|
||||
txlist=[tx_hex, tx_2_hex],
|
||||
)
|
||||
assert_equal(len(block_3.vtx), 3)
|
||||
add_witness_commitment(block_3)
|
||||
|
||||
assert_template(node, block_3, "bad-txns-inputs-missingorspent", submit=False)
|
||||
|
||||
return block_3
|
||||
|
||||
def parallel_test(self, node, block_3):
|
||||
# Ensure that getblocktemplate can be called concurrently by many threads.
|
||||
self.log.info("Check blocks in parallel")
|
||||
check_50_blocks = lambda n: [
|
||||
assert_template(n, block_3, "bad-txns-inputs-missingorspent", submit=False)
|
||||
for _ in range(50)
|
||||
]
|
||||
rpcs = [node.cli for _ in range(6)]
|
||||
with ThreadPoolExecutor(max_workers=len(rpcs)) as threads:
|
||||
list(threads.map(check_50_blocks, rpcs))
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
|
||||
block_0_height = node.getblockcount()
|
||||
self.generate(node, sync_fun=self.no_op, nblocks=1)
|
||||
block_1 = node.getblock(node.getbestblockhash())
|
||||
block_2 = create_block(
|
||||
int(block_1["hash"], 16),
|
||||
create_coinbase(block_0_height + 2),
|
||||
block_1["mediantime"] + 1,
|
||||
)
|
||||
|
||||
self.valid_block_test(node, block_2)
|
||||
self.cb_missing_test(node, block_2)
|
||||
self.block_without_transactions_test(node, block_2)
|
||||
self.truncated_final_transaction_test(node, block_2)
|
||||
self.duplicate_transaction_test(node, block_2)
|
||||
self.thin_air_spending_test(node, block_2)
|
||||
self.non_final_transaction_test(node, block_2)
|
||||
self.bad_tx_count_test(node, block_2)
|
||||
self.nbits_test(node, block_2)
|
||||
self.merkle_root_test(node, block_2)
|
||||
self.bad_timestamp_test(node, block_2)
|
||||
self.current_tip_test(node, block_2)
|
||||
# This sets the PoW for the next test
|
||||
self.pow_test(node, block_2)
|
||||
self.submit_test(node, block_0_height, block_2)
|
||||
|
||||
self.log.info("Generate a transaction")
|
||||
tx = MiniWallet(node).create_self_transfer()
|
||||
|
||||
self.transaction_test(node, block_0_height, tx)
|
||||
self.overspending_transaction_test(node, block_0_height, tx)
|
||||
block_3 = self.spend_twice_test(node, block_0_height, tx)
|
||||
self.parallel_test(node, block_3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
MiningTemplateVerificationTest(__file__).main()
|
29
test/functional/mocks/no_signer.py
Executable file
29
test/functional/mocks/no_signer.py
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2025-present The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
def enumerate(args):
|
||||
sys.stdout.write(json.dumps([]))
|
||||
|
||||
parser = argparse.ArgumentParser(prog='./no_signer.py', description='No external signer connected mock')
|
||||
|
||||
subparsers = parser.add_subparsers(description='Commands', dest='command')
|
||||
subparsers.required = True
|
||||
|
||||
parser_enumerate = subparsers.add_parser('enumerate', help='list available signers')
|
||||
parser_enumerate.set_defaults(func=enumerate)
|
||||
|
||||
|
||||
if not sys.stdin.isatty():
|
||||
buffer = sys.stdin.read()
|
||||
if buffer and buffer.rstrip() != "":
|
||||
sys.argv.extend(buffer.rstrip().split(" "))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
args.func(args)
|
@@ -14,3 +14,7 @@ index,secret key,public key,aux_rand,message,signature,verification result,comme
|
||||
12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size
|
||||
13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order
|
||||
14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size
|
||||
15,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,,71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63,TRUE,message of size 0 (added 2022-12)
|
||||
16,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,11,08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF,TRUE,message of size 1 (added 2022-12)
|
||||
17,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,0102030405060708090A0B0C0D0E0F1011,5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5,TRUE,message of size 17 (added 2022-12)
|
||||
18,0340034003400340034003400340034003400340034003400340034003400340,778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117,0000000000000000000000000000000000000000000000000000000000000000,99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367,TRUE,message of size 100 (added 2022-12)
|
||||
|
|
@@ -242,10 +242,9 @@ def verify_schnorr(key, sig, msg):
|
||||
|
||||
- key is a 32-byte xonly pubkey (computed using compute_xonly_pubkey).
|
||||
- sig is a 64-byte Schnorr signature
|
||||
- msg is a 32-byte message
|
||||
- msg is a variable-length message
|
||||
"""
|
||||
assert len(key) == 32
|
||||
assert len(msg) == 32
|
||||
assert len(sig) == 64
|
||||
|
||||
P = secp256k1.GE.from_bytes_xonly(key)
|
||||
@@ -272,7 +271,6 @@ def sign_schnorr(key, msg, aux=None, flip_p=False, flip_r=False):
|
||||
aux = bytes(32)
|
||||
|
||||
assert len(key) == 32
|
||||
assert len(msg) == 32
|
||||
assert len(aux) == 32
|
||||
|
||||
sec = int.from_bytes(key, 'big')
|
||||
|
@@ -209,6 +209,7 @@ BASE_SCRIPTS = [
|
||||
'rpc_decodescript.py',
|
||||
'rpc_blockchain.py --v1transport',
|
||||
'rpc_blockchain.py --v2transport',
|
||||
'mining_template_verification.py',
|
||||
'rpc_deprecated.py',
|
||||
'wallet_disable.py',
|
||||
'wallet_change_address.py',
|
||||
|
@@ -78,6 +78,24 @@ class MultiWalletTest(BitcoinTestFramework):
|
||||
self.stop_nodes()
|
||||
assert_equal(os.path.isfile(wallet_dir(self.default_wallet_name, self.wallet_data_filename)), True)
|
||||
|
||||
self.log.info("Verify warning is emitted when failing to scan the wallets directory")
|
||||
if platform.system() == 'Windows':
|
||||
self.log.warning('Skipping test involving chmod as Windows does not support it.')
|
||||
elif os.geteuid() == 0:
|
||||
self.log.warning('Skipping test involving chmod as it requires a non-root user.')
|
||||
else:
|
||||
self.start_node(0)
|
||||
with self.nodes[0].assert_debug_log(unexpected_msgs=['Error scanning directory entries under'], expected_msgs=[]):
|
||||
result = self.nodes[0].listwalletdir()
|
||||
assert_equal(result, {'wallets': [{'name': 'default_wallet', 'warnings': []}]})
|
||||
os.chmod(data_dir('wallets'), 0)
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=['Error scanning directory entries under']):
|
||||
result = self.nodes[0].listwalletdir()
|
||||
assert_equal(result, {'wallets': []})
|
||||
self.stop_node(0)
|
||||
# Restore permissions
|
||||
os.chmod(data_dir('wallets'), stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
|
||||
# create symlink to verify wallet directory path can be referenced
|
||||
# through symlink
|
||||
os.mkdir(wallet_dir('w7'))
|
||||
@@ -129,7 +147,7 @@ class MultiWalletTest(BitcoinTestFramework):
|
||||
os.mkdir(wallet_dir('no_access'))
|
||||
os.chmod(wallet_dir('no_access'), 0)
|
||||
try:
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=['Error scanning']):
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=["Error while scanning wallet dir"]):
|
||||
walletlist = self.nodes[0].listwalletdir()['wallets']
|
||||
finally:
|
||||
# Need to ensure access is restored for cleanup
|
||||
|
@@ -23,6 +23,10 @@ class WalletSignerTest(BitcoinTestFramework):
|
||||
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'signer.py')
|
||||
return sys.executable + " " + path
|
||||
|
||||
def mock_no_connected_signer_path(self):
|
||||
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'no_signer.py')
|
||||
return sys.executable + " " + path
|
||||
|
||||
def mock_invalid_signer_path(self):
|
||||
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'invalid_signer.py')
|
||||
return sys.executable + " " + path
|
||||
@@ -52,6 +56,7 @@ class WalletSignerTest(BitcoinTestFramework):
|
||||
|
||||
def run_test(self):
|
||||
self.test_valid_signer()
|
||||
self.test_disconnected_signer()
|
||||
self.restart_node(1, [f"-signer={self.mock_invalid_signer_path()}", "-keypool=10"])
|
||||
self.test_invalid_signer()
|
||||
self.restart_node(1, [f"-signer={self.mock_multi_signers_path()}", "-keypool=10"])
|
||||
@@ -234,6 +239,28 @@ class WalletSignerTest(BitcoinTestFramework):
|
||||
# )
|
||||
# self.clear_mock_result(self.nodes[4])
|
||||
|
||||
def test_disconnected_signer(self):
|
||||
self.log.info('Test disconnected external signer')
|
||||
|
||||
# First create a wallet with the signer connected
|
||||
self.nodes[1].createwallet(wallet_name='hww_disconnect', disable_private_keys=True, external_signer=True)
|
||||
hww = self.nodes[1].get_wallet_rpc('hww_disconnect')
|
||||
assert_equal(hww.getwalletinfo()["external_signer"], True)
|
||||
|
||||
# Fund wallet
|
||||
self.nodes[0].sendtoaddress(hww.getnewaddress(address_type="bech32m"), 1)
|
||||
self.generate(self.nodes[0], 1)
|
||||
|
||||
# Restart node with no signer connected
|
||||
self.log.debug(f"-signer={self.mock_no_connected_signer_path()}")
|
||||
self.restart_node(1, [f"-signer={self.mock_no_connected_signer_path()}", "-keypool=10"])
|
||||
self.nodes[1].loadwallet('hww_disconnect')
|
||||
hww = self.nodes[1].get_wallet_rpc('hww_disconnect')
|
||||
|
||||
# Try to spend
|
||||
dest = hww.getrawchangeaddress()
|
||||
assert_raises_rpc_error(-25, "External signer not found", hww.send, outputs=[{dest:0.5}])
|
||||
|
||||
def test_invalid_signer(self):
|
||||
self.log.debug(f"-signer={self.mock_invalid_signer_path()}")
|
||||
self.log.info('Test invalid external signer')
|
||||
@@ -243,7 +270,7 @@ class WalletSignerTest(BitcoinTestFramework):
|
||||
self.log.debug(f"-signer={self.mock_multi_signers_path()}")
|
||||
self.log.info('Test multiple external signers')
|
||||
|
||||
assert_raises_rpc_error(-1, "GetExternalSigner: More than one external signer found", self.nodes[1].createwallet, wallet_name='multi_hww', disable_private_keys=True, external_signer=True)
|
||||
assert_raises_rpc_error(-1, "More than one external signer found", self.nodes[1].createwallet, wallet_name='multi_hww', disable_private_keys=True, external_signer=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletSignerTest(__file__).main()
|
||||
|
Reference in New Issue
Block a user