Skip to content

Commit 94959b8

Browse files
committed
Add checkBlock to Mining interface
Use it in miner_tests. The getblocktemplate and generateblock RPC calls don't use this, because it would make the code more verbose.
1 parent 6077157 commit 94959b8

File tree

5 files changed

+81
-1
lines changed

5 files changed

+81
-1
lines changed

src/interfaces/mining.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ class Mining
114114
*/
115115
virtual std::unique_ptr<BlockTemplate> createNewBlock(const node::BlockCreateOptions& options = {}) = 0;
116116

117+
/**
118+
* Checks if a given block is valid.
119+
*
120+
* @param[in] block the block to check
121+
* @param[in] options verification options: the proof-of-work check can be
122+
* skipped in order to verify a template generated by
123+
* external software.
124+
* @param[out] reason failure reason (BIP22)
125+
* @param[out] debug more detailed rejection reason
126+
* @returns whether the block is valid
127+
*
128+
* For signets the challenge verification is skipped when check_pow is false.
129+
*/
130+
virtual bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) = 0;
131+
117132
//! Get internal node context. Useful for RPC and testing,
118133
//! but not accessible across processes.
119134
virtual node::NodeContext* context() { return nullptr; }

src/ipc/capnp/mining.capnp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ interface Mining $Proxy.wrap("interfaces::Mining") {
1818
getTip @2 (context :Proxy.Context) -> (result: Common.BlockRef, hasResult: Bool);
1919
waitTipChanged @3 (context :Proxy.Context, currentTip: Data, timeout: Float64) -> (result: Common.BlockRef);
2020
createNewBlock @4 (options: BlockCreateOptions) -> (result: BlockTemplate);
21+
checkBlock @5 (block: Data, options: BlockCheckOptions) -> (reason: Text, debug: Text, result: Bool);
2122
}
2223

2324
interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
@@ -44,3 +45,8 @@ struct BlockWaitOptions $Proxy.wrap("node::BlockWaitOptions") {
4445
timeout @0 : Float64 $Proxy.name("timeout");
4546
feeThreshold @1 : Int64 $Proxy.name("fee_threshold");
4647
}
48+
49+
struct BlockCheckOptions $Proxy.wrap("node::BlockCheckOptions") {
50+
checkMerkleRoot @0 :Bool $Proxy.name("check_merkle_root");
51+
checkPow @1 :Bool $Proxy.name("check_pow");
52+
}

src/node/interfaces.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,15 @@ class MinerImpl : public Mining
984984
return std::make_unique<BlockTemplateImpl>(assemble_options, BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(), m_node);
985985
}
986986

987+
bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) override
988+
{
989+
LOCK(chainman().GetMutex());
990+
BlockValidationState state{TestBlockValidity(chainman().ActiveChainstate(), block, /*check_pow=*/options.check_pow, /*=check_merkle_root=*/options.check_merkle_root)};
991+
reason = state.GetRejectReason();
992+
debug = state.GetDebugMessage();
993+
return state.IsValid();
994+
}
995+
987996
NodeContext* context() override { return &m_node; }
988997
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
989998
KernelNotifications& notifications() { return *Assert(m_node.notifications); }

src/node/types.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <cstddef>
1818
#include <policy/policy.h>
1919
#include <script/script.h>
20+
#include <uint256.h>
2021
#include <util/time.h>
2122

2223
namespace node {
@@ -85,6 +86,17 @@ struct BlockWaitOptions {
8586
CAmount fee_threshold{MAX_MONEY};
8687
};
8788

89+
struct BlockCheckOptions {
90+
/**
91+
* Set false to omit the merkle root check
92+
*/
93+
bool check_merkle_root{true};
94+
95+
/**
96+
* Set false to omit the proof-of-work check
97+
*/
98+
bool check_pow{true};
99+
};
88100
} // namespace node
89101

90102
#endif // BITCOIN_NODE_TYPES_H

src/test/miner_tests.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <util/translation.h>
2323
#include <validation.h>
2424
#include <versionbits.h>
25+
#include <pow.h>
2526

2627
#include <test/util/setup_common.h>
2728

@@ -666,7 +667,44 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
666667
CScript scriptPubKey = CScript() << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"_hex << OP_CHECKSIG;
667668
BlockAssembler::Options options;
668669
options.coinbase_output_script = scriptPubKey;
669-
std::unique_ptr<BlockTemplate> block_template;
670+
671+
// Create and check a simple template
672+
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
673+
BOOST_REQUIRE(block_template);
674+
{
675+
CBlock block{block_template->getBlock()};
676+
{
677+
std::string reason;
678+
std::string debug;
679+
BOOST_REQUIRE(!mining->checkBlock(block, {.check_pow = false}, reason, debug));
680+
BOOST_REQUIRE_EQUAL(reason, "bad-txnmrklroot");
681+
BOOST_REQUIRE_EQUAL(debug, "hashMerkleRoot mismatch");
682+
}
683+
684+
block.hashMerkleRoot = BlockMerkleRoot(block);
685+
686+
{
687+
std::string reason;
688+
std::string debug;
689+
BOOST_REQUIRE(mining->checkBlock(block, {.check_pow = false}, reason, debug));
690+
BOOST_REQUIRE_EQUAL(reason, "");
691+
BOOST_REQUIRE_EQUAL(debug, "");
692+
}
693+
694+
{
695+
// A block template does not have proof-of-work, but it might pass
696+
// verification by coincidence. Grind the nonce if needed:
697+
while (CheckProofOfWork(block.GetHash(), block.nBits, Assert(m_node.chainman)->GetParams().GetConsensus())) {
698+
block.nNonce++;
699+
}
700+
701+
std::string reason;
702+
std::string debug;
703+
BOOST_REQUIRE(!mining->checkBlock(block, {.check_pow = true}, reason, debug));
704+
BOOST_REQUIRE_EQUAL(reason, "high-hash");
705+
BOOST_REQUIRE_EQUAL(debug, "proof of work failed");
706+
}
707+
}
670708

671709
// We can't make transactions until we have inputs
672710
// Therefore, load 110 blocks :)

0 commit comments

Comments
 (0)