mining: add new getCoinbaseTx() returning a struct
Introduce a new method intended to replace getCoinbaseRawTx(), which provides a struct with everything clients need to construct a coinbase. This is safer than providing a raw dummy coinbase that clients then have to manipulate. The CoinbaseTx data is populated during the dummy transaction generation and stored in struct CBlockTemplate. Expand the interface_ipc.py functional test to document its usage and ensure equivalence.
This commit is contained in:
9 files changed
+219
-6
No files matched your search
@@ -1,5 +1,8 @@
|
|||||||
Mining IPC
|
Mining IPC
|
||||||
----------
|
----------
|
||||||
|
|
||||||
- The `getCoinbaseTx()` method is renamed to `getCoinbaseRawTx()`.
|
- The `getCoinbaseTx()` method is renamed to `getCoinbaseRawTx()` and deprecated.
|
||||||
IPC clients do not use the function name, so they're not affected. (#33819)
|
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)
|
||||||
@@ -44,16 +44,26 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return serialized dummy coinbase transaction.
|
* Return serialized dummy coinbase transaction.
|
||||||
|
*
|
||||||
|
* @note deprecated: use getCoinbaseTx()
|
||||||
*/
|
*/
|
||||||
virtual CTransactionRef getCoinbaseRawTx() = 0;
|
virtual CTransactionRef getCoinbaseRawTx() = 0;
|
||||||
|
|
||||||
|
/** Return fields needed to construct a coinbase transaction */
|
||||||
|
virtual node::CoinbaseTx getCoinbaseTx() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return scriptPubKey with SegWit OP_RETURN.
|
* Return scriptPubKey with SegWit OP_RETURN.
|
||||||
|
*
|
||||||
|
* @note deprecated: use getCoinbaseTx()
|
||||||
*/
|
*/
|
||||||
virtual std::vector<unsigned char> getCoinbaseCommitment() = 0;
|
virtual std::vector<unsigned char> getCoinbaseCommitment() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return which output in the dummy coinbase contains the SegWit OP_RETURN.
|
* 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;
|
virtual int getWitnessCommitmentIndex() = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
|
|||||||
getTxFees @3 (context: Proxy.Context) -> (result: List(Int64));
|
getTxFees @3 (context: Proxy.Context) -> (result: List(Int64));
|
||||||
getTxSigops @4 (context: Proxy.Context) -> (result: List(Int64));
|
getTxSigops @4 (context: Proxy.Context) -> (result: List(Int64));
|
||||||
getCoinbaseRawTx @5 (context: Proxy.Context) -> (result: Data);
|
getCoinbaseRawTx @5 (context: Proxy.Context) -> (result: Data);
|
||||||
|
getCoinbaseTx @12 (context: Proxy.Context) -> (result: CoinbaseTx);
|
||||||
getCoinbaseCommitment @6 (context: Proxy.Context) -> (result: Data);
|
getCoinbaseCommitment @6 (context: Proxy.Context) -> (result: Data);
|
||||||
getWitnessCommitmentIndex @7 (context: Proxy.Context) -> (result: Int32);
|
getWitnessCommitmentIndex @7 (context: Proxy.Context) -> (result: Int32);
|
||||||
getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data));
|
getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data));
|
||||||
@@ -51,3 +52,13 @@ struct BlockCheckOptions $Proxy.wrap("node::BlockCheckOptions") {
|
|||||||
checkMerkleRoot @0 :Bool $Proxy.name("check_merkle_root");
|
checkMerkleRoot @0 :Bool $Proxy.name("check_merkle_root");
|
||||||
checkPow @1 :Bool $Proxy.name("check_pow");
|
checkPow @1 :Bool $Proxy.name("check_pow");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CoinbaseTx $Proxy.wrap("node::CoinbaseTx") {
|
||||||
|
version @0 :UInt32 $Proxy.name("version");
|
||||||
|
sequence @1 :UInt32 $Proxy.name("sequence");
|
||||||
|
scriptSigPrefix @2 :Data $Proxy.name("script_sig_prefix");
|
||||||
|
witness @3 :Data $Proxy.name("witness");
|
||||||
|
blockRewardRemaining @4 :Int64 $Proxy.name("block_reward_remaining");
|
||||||
|
requiredOutputs @5 :List(Data) $Proxy.name("required_outputs");
|
||||||
|
lockTime @6 :UInt32 $Proxy.name("lock_time");
|
||||||
|
}
|
||||||
@@ -84,6 +84,7 @@ using interfaces::WalletLoader;
|
|||||||
using kernel::ChainstateRole;
|
using kernel::ChainstateRole;
|
||||||
using node::BlockAssembler;
|
using node::BlockAssembler;
|
||||||
using node::BlockWaitOptions;
|
using node::BlockWaitOptions;
|
||||||
|
using node::CoinbaseTx;
|
||||||
using util::Join;
|
using util::Join;
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
@@ -893,6 +894,11 @@ public:
|
|||||||
return m_block_template->block.vtx[0];
|
return m_block_template->block.vtx[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoinbaseTx getCoinbaseTx() override
|
||||||
|
{
|
||||||
|
return m_block_template->m_coinbase_tx;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<unsigned char> getCoinbaseCommitment() override
|
std::vector<unsigned char> getCoinbaseCommitment() override
|
||||||
{
|
{
|
||||||
return m_block_template->vchCoinbaseCommitment;
|
return m_block_template->vchCoinbaseCommitment;
|
||||||
|
|||||||
+35
-1
@@ -158,18 +158,51 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
|
|||||||
|
|
||||||
// Create coinbase transaction.
|
// Create coinbase transaction.
|
||||||
CMutableTransaction coinbaseTx;
|
CMutableTransaction coinbaseTx;
|
||||||
|
|
||||||
|
// Construct coinbase transaction struct in parallel
|
||||||
|
CoinbaseTx& coinbase_tx{pblocktemplate->m_coinbase_tx};
|
||||||
|
coinbase_tx.version = coinbaseTx.version;
|
||||||
|
|
||||||
coinbaseTx.vin.resize(1);
|
coinbaseTx.vin.resize(1);
|
||||||
coinbaseTx.vin[0].prevout.SetNull();
|
coinbaseTx.vin[0].prevout.SetNull();
|
||||||
coinbaseTx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; // Make sure timelock is enforced.
|
coinbaseTx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; // Make sure timelock is enforced.
|
||||||
|
coinbase_tx.sequence = coinbaseTx.vin[0].nSequence;
|
||||||
|
|
||||||
|
// Add an output that spends the full coinbase reward.
|
||||||
coinbaseTx.vout.resize(1);
|
coinbaseTx.vout.resize(1);
|
||||||
coinbaseTx.vout[0].scriptPubKey = m_options.coinbase_output_script;
|
coinbaseTx.vout[0].scriptPubKey = m_options.coinbase_output_script;
|
||||||
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
// Block subsidy + fees
|
||||||
|
const CAmount block_reward{nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus())};
|
||||||
|
coinbaseTx.vout[0].nValue = block_reward;
|
||||||
|
coinbase_tx.block_reward_remaining = block_reward;
|
||||||
|
|
||||||
|
// Start the coinbase scriptSig with the block height as required by BIP34.
|
||||||
|
// The trailing OP_0 (historically an extranonce) is optional padding and
|
||||||
|
// could be removed without a consensus change. Mining clients are expected
|
||||||
|
// to append extra data to this prefix, so increasing its length would reduce
|
||||||
|
// the space they can use and may break existing clients.
|
||||||
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
|
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
|
||||||
|
coinbase_tx.script_sig_prefix = coinbaseTx.vin[0].scriptSig;
|
||||||
Assert(nHeight > 0);
|
Assert(nHeight > 0);
|
||||||
coinbaseTx.nLockTime = static_cast<uint32_t>(nHeight - 1);
|
coinbaseTx.nLockTime = static_cast<uint32_t>(nHeight - 1);
|
||||||
|
coinbase_tx.lock_time = coinbaseTx.nLockTime;
|
||||||
|
|
||||||
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
|
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
|
||||||
pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
|
pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
|
||||||
|
|
||||||
|
const CTransactionRef& final_coinbase{pblock->vtx[0]};
|
||||||
|
if (final_coinbase->HasWitness()) {
|
||||||
|
const auto& witness_stack{final_coinbase->vin[0].scriptWitness.stack};
|
||||||
|
// Consensus requires the coinbase witness stack to have exactly one
|
||||||
|
// element of 32 bytes.
|
||||||
|
Assert(witness_stack.size() == 1 && witness_stack[0].size() == 32);
|
||||||
|
coinbase_tx.witness = uint256(witness_stack[0]);
|
||||||
|
}
|
||||||
|
if (const int witness_index = GetWitnessCommitmentIndex(*pblock); witness_index != NO_WITNESS_COMMITMENT) {
|
||||||
|
Assert(witness_index >= 0 && static_cast<size_t>(witness_index) < final_coinbase->vout.size());
|
||||||
|
coinbase_tx.required_outputs.push_back(final_coinbase->vout[witness_index]);
|
||||||
|
}
|
||||||
|
|
||||||
LogInfo("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
|
LogInfo("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
|
||||||
|
|
||||||
// Fill in header
|
// Fill in header
|
||||||
@@ -440,4 +473,5 @@ std::optional<BlockRef> WaitTipChanged(ChainstateManager& chainman, KernelNotifi
|
|||||||
// avoid deadlocks.
|
// avoid deadlocks.
|
||||||
return GetTip(chainman);
|
return GetTip(chainman);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
@@ -50,6 +50,11 @@ struct CBlockTemplate
|
|||||||
/* A vector of package fee rates, ordered by the sequence in which
|
/* A vector of package fee rates, ordered by the sequence in which
|
||||||
* packages are selected for inclusion in the block template.*/
|
* packages are selected for inclusion in the block template.*/
|
||||||
std::vector<FeePerVSize> m_package_feerates;
|
std::vector<FeePerVSize> m_package_feerates;
|
||||||
|
/*
|
||||||
|
* Template containing all coinbase transaction fields that are set by our
|
||||||
|
* miner code.
|
||||||
|
*/
|
||||||
|
CoinbaseTx m_coinbase_tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Generate a new block, without valid proof-of-work */
|
/** Generate a new block, without valid proof-of-work */
|
||||||
@@ -157,6 +162,7 @@ std::optional<BlockRef> GetTip(ChainstateManager& chainman);
|
|||||||
/* Waits for the connected tip to change until timeout has elapsed. During node initialization, this will wait until the tip is connected (regardless of `timeout`).
|
/* Waits for the connected tip to change until timeout has elapsed. During node initialization, this will wait until the tip is connected (regardless of `timeout`).
|
||||||
* Returns the current tip, or nullopt if the node is shutting down. */
|
* Returns the current tip, or nullopt if the node is shutting down. */
|
||||||
std::optional<BlockRef> WaitTipChanged(ChainstateManager& chainman, KernelNotifications& kernel_notifications, const uint256& current_tip, MillisecondsDouble& timeout);
|
std::optional<BlockRef> WaitTipChanged(ChainstateManager& chainman, KernelNotifications& kernel_notifications, const uint256& current_tip, MillisecondsDouble& timeout);
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
#endif // BITCOIN_NODE_MINER_H
|
#endif // BITCOIN_NODE_MINER_H
|
||||||
@@ -16,10 +16,13 @@
|
|||||||
#include <consensus/amount.h>
|
#include <consensus/amount.h>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
#include <primitives/transaction.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
enum class TransactionError {
|
enum class TransactionError {
|
||||||
@@ -99,6 +102,53 @@ struct BlockCheckOptions {
|
|||||||
bool check_pow{true};
|
bool check_pow{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template containing all coinbase transaction fields that are set by our
|
||||||
|
* miner code. Clients are expected to add their own outputs and typically
|
||||||
|
* also expand the scriptSig.
|
||||||
|
*/
|
||||||
|
struct CoinbaseTx {
|
||||||
|
/* nVersion */
|
||||||
|
uint32_t version;
|
||||||
|
/* nSequence for the only coinbase transaction input */
|
||||||
|
uint32_t sequence;
|
||||||
|
/**
|
||||||
|
* Prefix which needs to be placed at the beginning of the scriptSig.
|
||||||
|
* Clients may append extra data to this as long as the overall scriptSig
|
||||||
|
* size is 100 bytes or less, to avoid the block being rejected with
|
||||||
|
* "bad-cb-length" error.
|
||||||
|
*
|
||||||
|
* Currently with BIP 34, the prefix is guaranteed to be less than 8 bytes,
|
||||||
|
* but future soft forks could require longer prefixes.
|
||||||
|
*/
|
||||||
|
CScript script_sig_prefix;
|
||||||
|
/**
|
||||||
|
* The first (and only) witness stack element of the coinbase input.
|
||||||
|
*
|
||||||
|
* Omitted for block templates without witness data.
|
||||||
|
*
|
||||||
|
* This is currently the BIP 141 witness reserved value, and can be chosen
|
||||||
|
* arbitrarily by the node, but future soft forks may constrain it.
|
||||||
|
*/
|
||||||
|
std::optional<uint256> witness;
|
||||||
|
/**
|
||||||
|
* Block subsidy plus fees, minus any non-zero required_outputs.
|
||||||
|
*
|
||||||
|
* Currently there are no non-zero required_outputs, so block_reward_remaining
|
||||||
|
* is the entire block reward. See also required_outputs.
|
||||||
|
*/
|
||||||
|
CAmount block_reward_remaining;
|
||||||
|
/*
|
||||||
|
* To be included as the last outputs in the coinbase transaction.
|
||||||
|
* Currently this is only the witness commitment OP_RETURN, but future
|
||||||
|
* softforks or a custom mining patch could add more.
|
||||||
|
*
|
||||||
|
* The dummy output that spends the full reward is excluded.
|
||||||
|
*/
|
||||||
|
std::vector<CTxOut> required_outputs;
|
||||||
|
uint32_t lock_time;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How to broadcast a local transaction.
|
* How to broadcast a local transaction.
|
||||||
* Used to influence `BroadcastTransaction()` and its callers.
|
* Used to influence `BroadcastTransaction()` and its callers.
|
||||||
|
|||||||
@@ -6,16 +6,38 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
from contextlib import asynccontextmanager, AsyncExitStack
|
from contextlib import asynccontextmanager, AsyncExitStack
|
||||||
|
from dataclasses import dataclass
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import shutil
|
import shutil
|
||||||
from test_framework.messages import (CBlock, CTransaction, ser_uint256, COIN)
|
from test_framework.blocktools import NULL_OUTPOINT
|
||||||
|
from test_framework.messages import (
|
||||||
|
CBlock,
|
||||||
|
CTransaction,
|
||||||
|
CTxIn,
|
||||||
|
CTxOut,
|
||||||
|
CTxInWitness,
|
||||||
|
ser_uint256,
|
||||||
|
COIN,
|
||||||
|
)
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
assert_equal,
|
assert_equal,
|
||||||
assert_not_equal
|
assert_not_equal
|
||||||
)
|
)
|
||||||
from test_framework.wallet import MiniWallet
|
from test_framework.wallet import MiniWallet
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
# Stores the result of getCoinbaseTx()
|
||||||
|
@dataclass
|
||||||
|
class CoinbaseTxData:
|
||||||
|
version: int
|
||||||
|
sequence: int
|
||||||
|
scriptSigPrefix: bytes
|
||||||
|
witness: Optional[bytes]
|
||||||
|
blockRewardRemaining: int
|
||||||
|
requiredOutputs: list[bytes]
|
||||||
|
lockTime: int
|
||||||
|
|
||||||
# Test may be skipped and not have capnp installed
|
# Test may be skipped and not have capnp installed
|
||||||
try:
|
try:
|
||||||
@@ -123,11 +145,32 @@ class IPCInterfaceTest(BitcoinTestFramework):
|
|||||||
return block
|
return block
|
||||||
|
|
||||||
async def parse_and_deserialize_coinbase_tx(self, block_template, ctx):
|
async def parse_and_deserialize_coinbase_tx(self, block_template, ctx):
|
||||||
|
assert block_template is not None
|
||||||
coinbase_data = BytesIO((await block_template.getCoinbaseRawTx(ctx)).result)
|
coinbase_data = BytesIO((await block_template.getCoinbaseRawTx(ctx)).result)
|
||||||
tx = CTransaction()
|
tx = CTransaction()
|
||||||
tx.deserialize(coinbase_data)
|
tx.deserialize(coinbase_data)
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
|
async def parse_and_deserialize_coinbase(self, block_template, ctx) -> CoinbaseTxData:
|
||||||
|
assert block_template is not None
|
||||||
|
# Note: the template_capnp struct will be garbage-collected when this
|
||||||
|
# method returns, so it is important to copy any Data fields from it
|
||||||
|
# which need to be accessed later using the bytes() cast. Starting with
|
||||||
|
# pycapnp v2.2.0, Data fields have type `memoryview` and are ephemeral.
|
||||||
|
template_capnp = (await block_template.getCoinbaseTx(ctx)).result
|
||||||
|
witness: Optional[bytes] = None
|
||||||
|
if template_capnp._has("witness"):
|
||||||
|
witness = bytes(template_capnp.witness)
|
||||||
|
return CoinbaseTxData(
|
||||||
|
version=int(template_capnp.version),
|
||||||
|
sequence=int(template_capnp.sequence),
|
||||||
|
scriptSigPrefix=bytes(template_capnp.scriptSigPrefix),
|
||||||
|
witness=witness,
|
||||||
|
blockRewardRemaining=int(template_capnp.blockRewardRemaining),
|
||||||
|
requiredOutputs=[bytes(output) for output in template_capnp.requiredOutputs],
|
||||||
|
lockTime=int(template_capnp.lockTime),
|
||||||
|
)
|
||||||
|
|
||||||
def run_echo_test(self):
|
def run_echo_test(self):
|
||||||
self.log.info("Running echo test")
|
self.log.info("Running echo test")
|
||||||
async def async_routine():
|
async def async_routine():
|
||||||
@@ -142,6 +185,54 @@ class IPCInterfaceTest(BitcoinTestFramework):
|
|||||||
echo.destroy(ctx)
|
echo.destroy(ctx)
|
||||||
asyncio.run(capnp.run(async_routine()))
|
asyncio.run(capnp.run(async_routine()))
|
||||||
|
|
||||||
|
async def build_coinbase_test(self, template, ctx, miniwallet):
|
||||||
|
self.log.debug("Build coinbase transaction using getCoinbaseTx()")
|
||||||
|
assert template is not None
|
||||||
|
coinbase_res = await self.parse_and_deserialize_coinbase(template, ctx)
|
||||||
|
coinbase_tx = CTransaction()
|
||||||
|
coinbase_tx.version = coinbase_res.version
|
||||||
|
coinbase_tx.vin = [CTxIn()]
|
||||||
|
coinbase_tx.vin[0].prevout = NULL_OUTPOINT
|
||||||
|
coinbase_tx.vin[0].nSequence = coinbase_res.sequence
|
||||||
|
# Typically a mining pool appends its name and an extraNonce
|
||||||
|
coinbase_tx.vin[0].scriptSig = coinbase_res.scriptSigPrefix
|
||||||
|
|
||||||
|
# We currently always provide a coinbase witness, even for empty
|
||||||
|
# blocks, but this may change, so always check:
|
||||||
|
has_witness = coinbase_res.witness is not None
|
||||||
|
if has_witness:
|
||||||
|
coinbase_tx.wit.vtxinwit = [CTxInWitness()]
|
||||||
|
coinbase_tx.wit.vtxinwit[0].scriptWitness.stack = [coinbase_res.witness]
|
||||||
|
|
||||||
|
# First output is our payout
|
||||||
|
coinbase_tx.vout = [CTxOut()]
|
||||||
|
coinbase_tx.vout[0].scriptPubKey = miniwallet.get_output_script()
|
||||||
|
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 provided by the deprecated getCoinbaseTx()
|
||||||
|
coinbase_legacy = await self.parse_and_deserialize_coinbase_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
|
||||||
|
|
||||||
def run_mining_test(self):
|
def run_mining_test(self):
|
||||||
self.log.info("Running mining test")
|
self.log.info("Running mining test")
|
||||||
block_hash_size = 32
|
block_hash_size = 32
|
||||||
@@ -248,9 +339,9 @@ class IPCInterfaceTest(BitcoinTestFramework):
|
|||||||
check_opts = self.capnp_modules['mining'].BlockCheckOptions()
|
check_opts = self.capnp_modules['mining'].BlockCheckOptions()
|
||||||
async with destroying((await mining.createNewBlock(opts)).result, ctx) as template:
|
async with destroying((await mining.createNewBlock(opts)).result, ctx) as template:
|
||||||
block = await self.parse_and_deserialize_block(template, ctx)
|
block = await self.parse_and_deserialize_block(template, ctx)
|
||||||
coinbase = await self.parse_and_deserialize_coinbase_tx(template, ctx)
|
|
||||||
balance = miniwallet.get_balance()
|
balance = miniwallet.get_balance()
|
||||||
coinbase.vout[0].scriptPubKey = miniwallet.get_output_script()
|
coinbase = await self.build_coinbase_test(template, ctx, miniwallet)
|
||||||
|
# Reduce payout for balance comparison simplicity
|
||||||
coinbase.vout[0].nValue = COIN
|
coinbase.vout[0].nValue = COIN
|
||||||
block.vtx[0] = coinbase
|
block.vtx[0] = coinbase
|
||||||
block.hashMerkleRoot = block.calc_merkle_root()
|
block.hashMerkleRoot = block.calc_merkle_root()
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ COINBASE_MATURITY = 100
|
|||||||
# From BIP141
|
# From BIP141
|
||||||
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
|
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
|
||||||
|
|
||||||
|
NULL_OUTPOINT = COutPoint(0, 0xffffffff)
|
||||||
|
|
||||||
NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
|
NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
|
||||||
VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
|
VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
|
||||||
MIN_BLOCKS_TO_KEEP = 288
|
MIN_BLOCKS_TO_KEEP = 288
|
||||||
@@ -177,7 +179,7 @@ def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_scr
|
|||||||
script. This is useful to pad block weight/sigops as needed. """
|
script. This is useful to pad block weight/sigops as needed. """
|
||||||
coinbase = CTransaction()
|
coinbase = CTransaction()
|
||||||
coinbase.nLockTime = height - 1
|
coinbase.nLockTime = height - 1
|
||||||
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), script_BIP34_coinbase_height(height), MAX_SEQUENCE_NONFINAL))
|
coinbase.vin.append(CTxIn(NULL_OUTPOINT, script_BIP34_coinbase_height(height), MAX_SEQUENCE_NONFINAL))
|
||||||
coinbaseoutput = CTxOut()
|
coinbaseoutput = CTxOut()
|
||||||
coinbaseoutput.nValue = nValue * COIN
|
coinbaseoutput.nValue = nValue * COIN
|
||||||
if nValue == 50:
|
if nValue == 50:
|
||||||
|
|||||||
Reference in new issue
Block a user