Merge bitcoin/bitcoin#34568: mining: Break compatibility with existing IPC mining clients

f700609e8ada3b48fd45ec19979cd72d943d47a6 doc: Release notes for mining IPC interface bump (Ryan Ofsky)
9453c153612ae9b30308362048099bc53afcde6f ipc mining: break compatibility with existing clients (version bump) (Sjors Provoost)
70de5cc2d205672743379f2e1a94290ee8b4b84b ipc mining: pass missing context to BlockTemplate methods (incompatible schema change) (Sjors Provoost)
2278f017afad4d2c570527b15df776ee64fc1ee2 ipc mining: remove deprecated methods (incompatible schema change) (Ryan Ofsky)
c6638fa7c5e97f9fd7a5ea8feb29f8caeac788bd ipc mining: provide default option values (incompatible schema change) (Ryan Ofsky)
a4603ac77412790b6498ab1750017e31353740bf ipc mining: declare constants for default field values (Ryan Ofsky)
ff995b50cf9e1ea521f3cf546339f05d10b79a4d ipc test: add workaround to block_reserved_weight exception test (Ryan Ofsky)
b970cdf20fce43fb58dde1cbf713e97ff21d7a2e test framework: expand expected_stderr, expected_ret_code options (Ryan Ofsky)
df53a3e5ec8781833c29682ff9e459fca489fa7b rpc refactor: stop using deprecated getCoinbaseCommitment method (Ryan Ofsky)

Pull request description:

  This PR increments the field number of the `Init.makeMining` method and makes the old `makeMining` method return an error, so IPC mining clients not using the latest schema file will get an error and not be able to access the Mining interface.

  Normally, there shouldn't be a need to break compatibility this way, but the mining interface has evolved a lot since it was first introduced, with old clients using the original methods less stable and performant than newer clients. So now is a good time to introduce a cutoff, drop deprecated methods, and stop supporting old clients which can't function as well.

  Bumping the field number is also an opportunity to make other improvements that would be awkward to implement compatibly:
  - Making Cap'n Proto default parameter and field values match default values of corresponding C++ methods and structs.
  - Adding missing Context parameters to Mining.createNewBlock and checkBlock methods so these methods will be executed on separate execution threads and not block the Cap'n Proto event loop thread.

  More details about these changes are in the commit messages.

ACKs for top commit:
  Sjors:
    ACK f700609e8ada3b48fd45ec19979cd72d943d47a6
  enirox001:
    ACK f700609e8ada3b48fd45ec19979cd72d943d47a6
  ismaelsadeeq:
    ACK f700609e8ada3b48fd45ec19979cd72d943d47a6
  sedited:
    ACK f700609e8ada3b48fd45ec19979cd72d943d47a6

Tree-SHA512: 0901886af00214c138643b33cec21647de5671dfff2021afe06d78dfd970664a844cde9a1e28f685bb27edccaf6e0c3f2d1e6bb4164bde6b84f42955946e366d
This commit is contained in:
merge-script 2026-02-20 11:06:06 +01:00
commit cb3473a680
No known key found for this signature in database
GPG Key ID: 9B79B45691DB4173
19 changed files with 103 additions and 122 deletions

View File

@ -1,8 +0,0 @@
Mining IPC
----------
- The `getCoinbaseTx()` method is renamed to `getCoinbaseRawTx()` and deprecated.
IPC clients do not use the function name, so they're not affected. (#33819)
- Adds `getCoinbaseTx()` which clients should use instead of `getCoinbaseRawTx()`. It
contains all fields required to construct a coinbase transaction, and omits the
dummy output which Bitcoin Core uses internally. (#33819)

View File

@ -0,0 +1,11 @@
Mining IPC
----------
The IPC mining interface now requires mining clients to use the latest `mining.capnp` schema. Clients built against older schemas will fail when calling `Init.makeMining` and receive an RPC error indicating the old mining interface is no longer supported. Mining clients must update to the latest schema and regenerate bindings to continue working. (#34568)
Notable IPC mining interface changes since the last release:
- `Mining.createNewBlock` and `Mining.checkBlock` now require a `context` parameter.
- `Mining.waitTipChanged` now has a default `timeout` (effectively infinite / `maxDouble`) if the client omits it.
- `BlockTemplate.getCoinbaseTx()` now returns a structured `CoinbaseTx` instead of raw bytes.
- Removed `BlockTemplate.getCoinbaseCommitment()` and `BlockTemplate.getWitnessCommitmentIndex()`.
- Capn Proto default values were updated to match the corresponding C++ defaults for mining-related option structs (e.g. `BlockCreateOptions`, `BlockWaitOptions`, `BlockCheckOptions`).

View File

@ -39,6 +39,7 @@ public:
virtual Ipc* ipc() { return nullptr; }
virtual bool canListenIpc() { return false; }
virtual const char* exeName() { return nullptr; }
virtual void makeMiningOld2() { throw std::runtime_error("Old mining interface (@2) not supported. Please update your client!"); }
};
//! Return implementation of Init interface for the node process. If the argv

View File

@ -42,31 +42,9 @@ public:
// Sigop cost per transaction, not including coinbase transaction.
virtual std::vector<int64_t> getTxSigops() = 0;
/**
* Return serialized dummy coinbase transaction.
*
* @note deprecated: use getCoinbaseTx()
*/
virtual CTransactionRef getCoinbaseRawTx() = 0;
/** Return fields needed to construct a coinbase transaction */
virtual node::CoinbaseTx getCoinbaseTx() = 0;
/**
* Return scriptPubKey with SegWit OP_RETURN.
*
* @note deprecated: use getCoinbaseTx()
*/
virtual std::vector<unsigned char> getCoinbaseCommitment() = 0;
/**
* Return which output in the dummy coinbase contains the SegWit OP_RETURN.
*
* @note deprecated. Scan outputs from getCoinbaseTx() outputs field for the
* SegWit marker.
*/
virtual int getWitnessCommitmentIndex() = 0;
/**
* Compute merkle path to the coinbase transaction
*

View File

@ -19,5 +19,8 @@ using Mining = import "mining.capnp";
interface Init $Proxy.wrap("interfaces::Init") {
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo);
makeMining @2 (context :Proxy.Context) -> (result :Mining.Mining);
makeMining @3 (context :Proxy.Context) -> (result :Mining.Mining);
# DEPRECATED: no longer supported; server returns an error.
makeMiningOld2 @2 () -> ();
}

View File

@ -12,13 +12,18 @@ using Proxy = import "/mp/proxy.capnp";
$Proxy.include("interfaces/mining.h");
$Proxy.includeTypes("ipc/capnp/mining-types.h");
const maxMoney :Int64 = 2100000000000000;
const maxDouble :Float64 = 1.7976931348623157e308;
const defaultBlockReservedWeight :UInt32 = 8000;
const defaultCoinbaseOutputMaxAdditionalSigops :UInt32 = 400;
interface Mining $Proxy.wrap("interfaces::Mining") {
isTestChain @0 (context :Proxy.Context) -> (result: Bool);
isInitialBlockDownload @1 (context :Proxy.Context) -> (result: Bool);
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);
waitTipChanged @3 (context :Proxy.Context, currentTip: Data, timeout: Float64 = .maxDouble) -> (result: Common.BlockRef);
createNewBlock @4 (context :Proxy.Context, options: BlockCreateOptions) -> (result: BlockTemplate);
checkBlock @5 (context :Proxy.Context, block: Data, options: BlockCheckOptions) -> (reason: Text, debug: Text, result: Bool);
}
interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
@ -27,30 +32,27 @@ interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
getBlock @2 (context: Proxy.Context) -> (result: Data);
getTxFees @3 (context: Proxy.Context) -> (result: List(Int64));
getTxSigops @4 (context: Proxy.Context) -> (result: List(Int64));
getCoinbaseRawTx @5 (context: Proxy.Context) -> (result: Data);
getCoinbaseTx @12 (context: Proxy.Context) -> (result: CoinbaseTx);
getCoinbaseCommitment @6 (context: Proxy.Context) -> (result: Data);
getWitnessCommitmentIndex @7 (context: Proxy.Context) -> (result: Int32);
getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data));
submitSolution @9 (context: Proxy.Context, version: UInt32, timestamp: UInt32, nonce: UInt32, coinbase :Data) -> (result: Bool);
waitNext @10 (context: Proxy.Context, options: BlockWaitOptions) -> (result: BlockTemplate);
interruptWait @11() -> ();
getCoinbaseTx @5 (context: Proxy.Context) -> (result: CoinbaseTx);
getCoinbaseMerklePath @6 (context: Proxy.Context) -> (result: List(Data));
submitSolution @7 (context: Proxy.Context, version: UInt32, timestamp: UInt32, nonce: UInt32, coinbase :Data) -> (result: Bool);
waitNext @8 (context: Proxy.Context, options: BlockWaitOptions) -> (result: BlockTemplate);
interruptWait @9() -> ();
}
struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") {
useMempool @0 :Bool $Proxy.name("use_mempool");
blockReservedWeight @1 :UInt64 $Proxy.name("block_reserved_weight");
coinbaseOutputMaxAdditionalSigops @2 :UInt64 $Proxy.name("coinbase_output_max_additional_sigops");
useMempool @0 :Bool = true $Proxy.name("use_mempool");
blockReservedWeight @1 :UInt64 = .defaultBlockReservedWeight $Proxy.name("block_reserved_weight");
coinbaseOutputMaxAdditionalSigops @2 :UInt64 = .defaultCoinbaseOutputMaxAdditionalSigops $Proxy.name("coinbase_output_max_additional_sigops");
}
struct BlockWaitOptions $Proxy.wrap("node::BlockWaitOptions") {
timeout @0 : Float64 $Proxy.name("timeout");
feeThreshold @1 : Int64 $Proxy.name("fee_threshold");
timeout @0 : Float64 = .maxDouble $Proxy.name("timeout");
feeThreshold @1 : Int64 = .maxMoney $Proxy.name("fee_threshold");
}
struct BlockCheckOptions $Proxy.wrap("node::BlockCheckOptions") {
checkMerkleRoot @0 :Bool $Proxy.name("check_merkle_root");
checkPow @1 :Bool $Proxy.name("check_pow");
checkMerkleRoot @0 :Bool = true $Proxy.name("check_merkle_root");
checkPow @1 :Bool = true $Proxy.name("check_pow");
}
struct CoinbaseTx $Proxy.wrap("node::CoinbaseTx") {

View File

@ -8,6 +8,7 @@
#include <ipc/protocol.h>
#include <logging.h>
#include <mp/proxy-types.h>
#include <ipc/capnp/mining.capnp.h>
#include <ipc/test/ipc_test.capnp.h>
#include <ipc/test/ipc_test.capnp.proxy.h>
#include <ipc/test/ipc_test.h>
@ -23,6 +24,11 @@
#include <boost/test/unit_test.hpp>
static_assert(ipc::capnp::messages::MAX_MONEY == MAX_MONEY);
static_assert(ipc::capnp::messages::MAX_DOUBLE == std::numeric_limits<double>::max());
static_assert(ipc::capnp::messages::DEFAULT_BLOCK_RESERVED_WEIGHT == DEFAULT_BLOCK_RESERVED_WEIGHT);
static_assert(ipc::capnp::messages::DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS == DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS);
//! Remote init class.
class TestInit : public interfaces::Init
{

View File

@ -890,26 +890,11 @@ public:
return m_block_template->vTxSigOpsCost;
}
CTransactionRef getCoinbaseRawTx() override
{
return m_block_template->block.vtx[0];
}
CoinbaseTx getCoinbaseTx() override
{
return m_block_template->m_coinbase_tx;
}
std::vector<unsigned char> getCoinbaseCommitment() override
{
return m_block_template->vchCoinbaseCommitment;
}
int getWitnessCommitmentIndex() override
{
return GetWitnessCommitmentIndex(m_block_template->block);
}
std::vector<uint256> getCoinbaseMerklePath() override
{
return TransactionMerklePath(m_block_template->block, 0);

View File

@ -197,7 +197,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
coinbase_tx.lock_time = coinbaseTx.nLockTime;
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
const CTransactionRef& final_coinbase{pblock->vtx[0]};
if (final_coinbase->HasWitness()) {

View File

@ -46,7 +46,6 @@ struct CBlockTemplate
std::vector<CAmount> vTxFees;
// Sigops per transaction, not including coinbase transaction (unlike CBlock::vtx).
std::vector<int64_t> vTxSigOpsCost;
std::vector<unsigned char> vchCoinbaseCommitment;
/* A vector of package fee rates, ordered by the sequence in which
* packages are selected for inclusion in the block template.*/
std::vector<FeePerVSize> m_package_feerates;

View File

@ -55,7 +55,7 @@ struct BlockCreateOptions {
* The maximum additional sigops which the pool will add in coinbase
* transaction outputs.
*/
size_t coinbase_output_max_additional_sigops{400};
size_t coinbase_output_max_additional_sigops{DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS};
/**
* Script to put in the coinbase transaction. The default is an
* anyone-can-spend dummy.

View File

@ -24,6 +24,8 @@ class CScript;
static constexpr unsigned int DEFAULT_BLOCK_MAX_WEIGHT{MAX_BLOCK_WEIGHT};
/** Default for -blockreservedweight **/
static constexpr unsigned int DEFAULT_BLOCK_RESERVED_WEIGHT{8000};
/** Default sigops cost to reserve for coinbase transaction outputs when creating block templates. */
static constexpr unsigned int DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS{400};
/** This accounts for the block header, var_int encoding of the transaction count and a minimally viable
* coinbase transaction. It adds an additional safety margin, because even with a thorough understanding
* of block serialization, it's easy to make a costly mistake when trying to squeeze every last byte.

View File

@ -1015,8 +1015,9 @@ static RPCHelpMan getblocktemplate()
result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge));
}
if (!block_template->getCoinbaseCommitment().empty()) {
result.pushKV("default_witness_commitment", HexStr(block_template->getCoinbaseCommitment()));
if (auto coinbase{block_template->getCoinbaseTx()}; coinbase.required_outputs.size() > 0) {
CHECK_NONFATAL(coinbase.required_outputs.size() == 1); // Only one output is currently expected
result.pushKV("default_witness_commitment", HexStr(coinbase.required_outputs[0].scriptPubKey));
}
return result;

View File

@ -4026,9 +4026,8 @@ void ChainstateManager::UpdateUncommittedBlockStructures(CBlock& block, const CB
}
}
std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const
void ChainstateManager::GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const
{
std::vector<unsigned char> commitment;
int commitpos = GetWitnessCommitmentIndex(block);
std::vector<unsigned char> ret(32, 0x00);
if (commitpos == NO_WITNESS_COMMITMENT) {
@ -4044,13 +4043,11 @@ std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock&
out.scriptPubKey[4] = 0xa9;
out.scriptPubKey[5] = 0xed;
memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
CMutableTransaction tx(*block.vtx[0]);
tx.vout.push_back(out);
block.vtx[0] = MakeTransactionRef(std::move(tx));
}
UpdateUncommittedBlockStructures(block, pindexPrev);
return commitment;
}
bool HasValidProofOfWork(std::span<const CBlockHeader> headers, const Consensus::Params& consensusParams)

View File

@ -1311,7 +1311,7 @@ public:
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev) const;
/** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */
std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const;
void GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev) const;
/** This is used by net_processing to report pre-synchronization progress of headers, as
* headers are not yet fed to validation during that time, but validation is (for now)

View File

@ -68,9 +68,23 @@ class IPCInterfaceTest(BitcoinTestFramework):
asyncio.run(capnp.run(async_routine()))
def run_deprecated_mining_test(self):
self.log.info("Running deprecated mining interface test")
async def async_routine():
ctx, init = await make_capnp_init_ctx(self)
self.log.debug("Calling deprecated makeMiningOld2 should raise an error")
try:
await init.makeMiningOld2()
raise AssertionError("makeMiningOld2 unexpectedly succeeded")
except capnp.KjException as e:
assert_equal(e.description, "remote exception: std::exception: Old mining interface (@2) not supported. Please update your client!")
assert_equal(e.type, "FAILED")
asyncio.run(capnp.run(async_routine()))
def run_test(self):
self.run_echo_test()
self.run_mining_test()
self.run_deprecated_mining_test()
if __name__ == '__main__':
IPCInterfaceTest(__file__).main()

View File

@ -6,6 +6,7 @@
import asyncio
from contextlib import AsyncExitStack
from io import BytesIO
import re
from test_framework.blocktools import NULL_OUTPOINT
from test_framework.messages import (
MAX_BLOCK_WEIGHT,
@ -33,7 +34,6 @@ from test_framework.ipc_util import (
make_capnp_init_ctx,
mining_get_block,
mining_get_coinbase_tx,
mining_get_coinbase_raw_tx,
mining_wait_next_template,
wait_and_do,
)
@ -92,27 +92,12 @@ class IPCMiningTest(BitcoinTestFramework):
coinbase_tx.vout[0].nValue = coinbase_res.blockRewardRemaining
# Add SegWit OP_RETURN. This is currently always present even for
# empty blocks, but this may change.
found_witness_op_return = False
# Compare SegWit OP_RETURN to getCoinbaseCommitment()
coinbase_commitment = (await template.getCoinbaseCommitment(ctx)).result
for output_data in coinbase_res.requiredOutputs:
output = CTxOut()
output.deserialize(BytesIO(output_data))
coinbase_tx.vout.append(output)
if output.scriptPubKey == coinbase_commitment:
found_witness_op_return = True
assert_equal(has_witness, found_witness_op_return)
coinbase_tx.nLockTime = coinbase_res.lockTime
# Compare to dummy coinbase transaction provided by the deprecated
# getCoinbaseRawTx()
coinbase_legacy = await mining_get_coinbase_raw_tx(template, ctx)
assert_equal(coinbase_legacy.vout[0].nValue, coinbase_res.blockRewardRemaining)
# Swap dummy output for our own
coinbase_legacy.vout[0].scriptPubKey = coinbase_tx.vout[0].scriptPubKey
assert_equal(coinbase_tx.serialize().hex(), coinbase_legacy.serialize().hex())
return coinbase_tx
async def make_mining_ctx(self):
@ -242,9 +227,6 @@ class IPCMiningTest(BitcoinTestFramework):
async with AsyncExitStack() as stack:
opts = self.capnp_modules['mining'].BlockCreateOptions()
opts.useMempool = True
opts.blockReservedWeight = 4000
opts.coinbaseOutputMaxAdditionalSigops = 0
template = await mining_create_block_template(mining, stack, ctx, opts)
assert template is not None
block = await mining_get_block(template, ctx)
@ -257,19 +239,28 @@ class IPCMiningTest(BitcoinTestFramework):
empty_block = await mining_get_block(empty_template, ctx)
assert_equal(len(empty_block.vtx), 1)
self.log.debug("Enforce minimum reserved weight for IPC clients too")
opts.blockReservedWeight = 0
try:
await mining.createNewBlock(opts)
raise AssertionError("createNewBlock unexpectedly succeeded")
except capnp.lib.capnp.KjException as e:
self.log.debug("Enforce minimum reserved weight for IPC clients too")
opts.blockReservedWeight = 0
try:
await mining.createNewBlock(ctx, opts)
raise AssertionError("createNewBlock unexpectedly succeeded")
except capnp.lib.capnp.KjException as e:
if e.type == "DISCONNECTED":
# The remote exception isn't caught currently and leads to a
# std::terminate call. Just detect and restart in this case.
# This bug is fixed with
# https://github.com/bitcoin-core/libmultiprocess/pull/218
assert_equal(e.description, "Peer disconnected.")
self.nodes[0].wait_until_stopped(expected_ret_code=(-11, -6, 1, 66), expected_stderr=re.compile(""))
self.start_node(0)
else:
assert_equal(e.description, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
assert_equal(e.type, "FAILED")
asyncio.run(capnp.run(async_routine()))
def run_coinbase_and_submission_test(self):
"""Test coinbase construction (getCoinbaseTx, getCoinbaseCommitment) and block submission (submitSolution)."""
"""Test coinbase construction (getCoinbaseTx) and block submission (submitSolution)."""
self.log.info("Running coinbase construction and submission test")
async def async_routine():
@ -278,7 +269,7 @@ class IPCMiningTest(BitcoinTestFramework):
current_block_height = self.nodes[0].getchaintips()[0]["height"]
check_opts = self.capnp_modules['mining'].BlockCheckOptions()
async with destroying((await mining.createNewBlock(self.default_block_create_options)).result, ctx) as template:
async with destroying((await mining.createNewBlock(ctx, self.default_block_create_options)).result, ctx) as template:
block = await mining_get_block(template, ctx)
balance = self.miniwallet.get_balance()
coinbase = await self.build_coinbase_test(template, ctx, self.miniwallet)
@ -291,7 +282,7 @@ class IPCMiningTest(BitcoinTestFramework):
self.log.debug("Submit a block with a bad version")
block.nVersion = 0
block.solve()
check = await mining.checkBlock(block.serialize(), check_opts)
check = await mining.checkBlock(ctx, block.serialize(), check_opts)
assert_equal(check.result, False)
assert_equal(check.reason, "bad-version(0x00000000)")
submitted = (await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize())).result
@ -301,7 +292,7 @@ class IPCMiningTest(BitcoinTestFramework):
block.solve()
self.log.debug("First call checkBlock()")
block_valid = (await mining.checkBlock(block.serialize(), check_opts)).result
block_valid = (await mining.checkBlock(ctx, block.serialize(), check_opts)).result
assert_equal(block_valid, True)
# The remote template block will be mutated, capture the original:
@ -333,7 +324,7 @@ class IPCMiningTest(BitcoinTestFramework):
self.miniwallet.rescan_utxos()
assert_equal(self.miniwallet.get_balance(), balance + 1)
self.log.debug("Check block should fail now, since it is a duplicate")
check = await mining.checkBlock(block.serialize(), check_opts)
check = await mining.checkBlock(ctx, block.serialize(), check_opts)
assert_equal(check.result, False)
assert_equal(check.reason, "inconclusive-not-best-prevblk")
@ -341,12 +332,7 @@ class IPCMiningTest(BitcoinTestFramework):
def run_test(self):
self.miniwallet = MiniWallet(self.nodes[0])
self.default_block_create_options = self.capnp_modules['mining'].BlockCreateOptions(
useMempool=True,
blockReservedWeight=4000,
coinbaseOutputMaxAdditionalSigops=0
)
self.default_block_create_options = self.capnp_modules['mining'].BlockCreateOptions()
self.run_mining_interface_test()
self.run_block_template_test()
self.run_coinbase_and_submission_test()

View File

@ -12,7 +12,7 @@ from pathlib import Path
import shutil
from typing import Optional
from test_framework.messages import CBlock, CTransaction
from test_framework.messages import CBlock
# Test may be skipped and not have capnp installed
try:
@ -108,7 +108,7 @@ async def make_capnp_init_ctx(self):
async def mining_create_block_template(mining, stack, ctx, opts):
"""Call mining.createNewBlock() and return template, then call template.destroy() when stack exits."""
response = await mining.createNewBlock(opts)
response = await mining.createNewBlock(ctx, opts)
if not response._has("result"):
return None
return await stack.enter_async_context(destroying(response.result, ctx))
@ -129,14 +129,6 @@ async def mining_get_block(block_template, ctx):
return block
async def mining_get_coinbase_raw_tx(block_template, ctx):
assert block_template is not None
coinbase_data = BytesIO((await block_template.getCoinbaseRawTx(ctx)).result)
tx = CTransaction()
tx.deserialize(coinbase_data)
return tx
async def mining_get_coinbase_tx(block_template, ctx) -> CoinbaseTxData:
assert block_template is not None
# Note: the template_capnp struct will be garbage-collected when this

View File

@ -22,6 +22,7 @@ import collections
import shlex
import shutil
import sys
from collections.abc import Iterable
from pathlib import Path
from .authproxy import (
@ -469,6 +470,12 @@ class TestNode():
"""Checks whether the node has stopped.
Returns True if the node has stopped. False otherwise.
If the process has exited, asserts that the exit code matches
`expected_ret_code` (which may be a single value or an iterable of values),
and that stderr matches `expected_stderr` exactly or, if a regex pattern is
provided, contains the pattern.
This method is responsible for freeing resources (self.process)."""
if not self.running:
return True
@ -477,12 +484,17 @@ class TestNode():
return False
# process has stopped. Assert that it didn't return an error code.
assert return_code == expected_ret_code, self._node_msg(
if not isinstance(expected_ret_code, Iterable):
expected_ret_code = (expected_ret_code,)
assert return_code in expected_ret_code, self._node_msg(
f"Node returned unexpected exit code ({return_code}) vs ({expected_ret_code}) when stopping")
# Check that stderr is as expected
self.stderr.seek(0)
stderr = self.stderr.read().decode('utf-8').strip()
if stderr != expected_stderr:
if isinstance(expected_stderr, re.Pattern):
if not expected_stderr.search(stderr):
raise AssertionError(f"Unexpected stderr {stderr!r} does not contain {expected_stderr.pattern!r}")
elif stderr != expected_stderr:
raise AssertionError("Unexpected stderr {} != {}".format(stderr, expected_stderr))
self.stdout.close()