0
0
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:
Daniel Kraft
2025-06-23 07:40:50 +02:00
55 changed files with 691 additions and 599 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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)).

View File

@@ -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"]

View File

@@ -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/).

View File

@@ -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)

View File

@@ -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.

View File

@@ -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)};

View File

@@ -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()));
});

View File

@@ -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; }

View File

@@ -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

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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));

View File

@@ -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;

View File

@@ -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;

View File

@@ -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); }

View File

@@ -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()};

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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");

View File

@@ -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 (...) {

View File

@@ -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)

View File

@@ -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

View File

@@ -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);
}
};
}

View File

@@ -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);
}

View File

@@ -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));

View File

@@ -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 :)

View File

@@ -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());
}

View File

@@ -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(...)

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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};

View File

@@ -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

View File

@@ -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.

View File

@@ -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");

View File

@@ -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>;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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()

View 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()

View 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)

View File

@@ -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)
1 index secret key public key aux_rand message signature verification result comment
14 12 DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659 243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B FALSE sig[0:32] is equal to field size
15 13 DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659 243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89 6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 FALSE sig[32:64] is equal to curve order
16 14 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30 243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89 6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B FALSE public key is not a valid X coordinate because it exceeds the field size
17 15 0340034003400340034003400340034003400340034003400340034003400340 778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117 0000000000000000000000000000000000000000000000000000000000000000 71535DB165ECD9FBBC046E5FFAEA61186BB6AD436732FCCC25291A55895464CF6069CE26BF03466228F19A3A62DB8A649F2D560FAC652827D1AF0574E427AB63 TRUE message of size 0 (added 2022-12)
18 16 0340034003400340034003400340034003400340034003400340034003400340 778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117 0000000000000000000000000000000000000000000000000000000000000000 11 08A20A0AFEF64124649232E0693C583AB1B9934AE63B4C3511F3AE1134C6A303EA3173BFEA6683BD101FA5AA5DBC1996FE7CACFC5A577D33EC14564CEC2BACBF TRUE message of size 1 (added 2022-12)
19 17 0340034003400340034003400340034003400340034003400340034003400340 778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117 0000000000000000000000000000000000000000000000000000000000000000 0102030405060708090A0B0C0D0E0F1011 5130F39A4059B43BC7CAC09A19ECE52B5D8699D1A71E3C52DA9AFDB6B50AC370C4A482B77BF960F8681540E25B6771ECE1E5A37FD80E5A51897C5566A97EA5A5 TRUE message of size 17 (added 2022-12)
20 18 0340034003400340034003400340034003400340034003400340034003400340 778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117 0000000000000000000000000000000000000000000000000000000000000000 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 403B12B0D8555A344175EA7EC746566303321E5DBFA8BE6F091635163ECA79A8585ED3E3170807E7C03B720FC54C7B23897FCBA0E9D0B4A06894CFD249F22367 TRUE message of size 100 (added 2022-12)

View File

@@ -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')

View File

@@ -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',

View File

@@ -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

View File

@@ -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()