Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: speculative templates #54

Open
wants to merge 34 commits into
base: sv2
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
59eacd2
Add sv2 log category for Stratum v2
Sjors Nov 30, 2023
f69fd62
Add sv2 noise protocol
Sjors Jul 11, 2024
176795a
Add sv2 message CoinbaseOutputDataSize
Sjors Jun 20, 2024
c4f2ef9
Convert between Sv2NetMsg and CSerializedNetMsg
Sjors Jun 21, 2024
938570a
Introduce Sv2Transport
Sjors Jun 21, 2024
750f75a
Add sv2 SETUP_CONNECTION messages
Sjors Jul 15, 2024
fc81013
Add strings for Sv2MsgType
Sjors Jul 1, 2024
88e7fbd
test: put the generic parts from StaticContentsSock into a separate c…
vasild Dec 6, 2022
d1e3e3b
test: add a mocked Sock that allows inspecting what has been Send() t…
vasild Dec 6, 2022
3cf779b
Add Sv2Connman
Sjors Jul 17, 2024
a1c8e3c
Chainparams: add default sv2 port
Sjors Nov 29, 2023
fa33ea5
Stratum v2 template provider scaffold
Sjors Jun 18, 2024
0ba8584
Add remaining sv2 messages for TemplateProvider
Sjors Jun 24, 2024
fb31d44
Have createNewBlock return BlockTemplate interface
Sjors Jul 17, 2024
7e2a12f
Add GetCoinBaseMerklePath helper
Sjors Jul 16, 2024
6489500
Add getCoinbaseMerklePath() to Mining interface
Sjors Jul 12, 2024
6c36412
Add submitSolution to BlockTemplate interface
Sjors Jul 15, 2024
3fa41d1
Add waitTipChanged to Mining interface
Sjors Jul 8, 2024
2a9571f
Sv2: construct and submit block templates
Sjors Dec 28, 2023
0e1370c
Handle REQUEST_TRANSACTION_DATA
Sjors Jul 2, 2024
630cf57
Reject RequestTransactionData for stale templates
Sjors Jul 2, 2024
9328d23
Handle SUBMIT_SOLUTION
Sjors Jul 2, 2024
bdfee73
Introduce waitFeesChanged() mining interface
Sjors Jul 18, 2024
e02477f
Incrementally update sv2 block template
Sjors Feb 16, 2024
2bb2838
CKey: add Serialize and Unserialize
Sjors Feb 1, 2024
dabc32c
Persist static key for Template Provider
Sjors Jan 11, 2024
29b37d8
Start Template Provider with -sv2
Sjors Jul 17, 2024
2da69c0
signet: miner skips PSBT step for OP_TRUE
Sjors Dec 8, 2023
17e580a
testnet: Introduce Testnet4
fjahr Mar 31, 2024
998d646
testnet: Add Testnet4 difficulty adjustment rules fix
fjahr May 3, 2024
128f3dc
testnet: Add timewarp attack prevention for Testnet4
fjahr May 3, 2024
718a9ee
doc: explain Stratum v2 design, testing and usage
Sjors Dec 28, 2023
fe730f1
ci: skip Github CI on branch pushes for forks
Sjors Jan 18, 2024
abc852d
WIP: speculative templates
Sjors Jul 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Have createNewBlock return BlockTemplate interface
An external program that uses the Mining interface may need quick access to some information in the block template, while it can wait a bit longer for the full raw transaction data.

This would be the case for a Stratum v2 Template Provider which needs to send a NewTemplate message (which doesn't include transactions) as quickly as possible.
Sjors committed Jul 19, 2024
commit fb31d446068e6e02384b7eadf0c5f89c07baa046
24 changes: 21 additions & 3 deletions src/interfaces/mining.h
Original file line number Diff line number Diff line change
@@ -5,26 +5,44 @@
#ifndef BITCOIN_INTERFACES_MINING_H
#define BITCOIN_INTERFACES_MINING_H

#include <consensus/amount.h>
#include <primitives/transaction.h>
#include <node/types.h>
#include <uint256.h>

#include <memory>
#include <optional>

namespace node {
struct CBlockTemplate;
struct NodeContext;
} // namespace node

class BlockValidationState;
class CBlock;
class CBlockHeader;
class CScript;

namespace interfaces {

//! Block template interface
class BlockTemplate
{
public:
virtual ~BlockTemplate() = default;

virtual CBlockHeader getBlockHeader() = 0;
virtual CBlock getBlock() = 0;

virtual std::vector<CAmount> getTxFees() = 0;
virtual std::vector<int64_t> getTxSigops() = 0;

virtual CTransactionRef getCoinbaseTx() = 0;
virtual std::vector<unsigned char> getCoinbaseCommitment() = 0;
virtual int getWitnessCommitmentIndex() = 0;
};

//! Interface giving clients (RPC, Stratum v2 Template Provider in the future)
//! ability to create block templates.

class Mining
{
public:
@@ -46,7 +64,7 @@ class Mining
* @param[in] options options for creating the block
* @returns a block template
*/
virtual std::unique_ptr<node::CBlockTemplate> createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options={}) = 0;
virtual std::unique_ptr<BlockTemplate> createNewBlock(const CScript& script_pub_key, const node::BlockCreateOptions& options={}) = 0;

/**
* Processes new block. A valid new block is automatically relayed to peers.
48 changes: 46 additions & 2 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@

#include <boost/signals2/signal.hpp>

using interfaces::BlockTemplate;
using interfaces::BlockTip;
using interfaces::Chain;
using interfaces::FoundBlock;
@@ -838,6 +839,49 @@ class ChainImpl : public Chain
NodeContext& m_node;
};

class BlockTemplateImpl : public BlockTemplate
{
public:
explicit BlockTemplateImpl(std::unique_ptr<CBlockTemplate> block_template) : m_block_template(std::move(block_template)) {}

CBlockHeader getBlockHeader() override
{
return Assert(m_block_template)->block;
}

CBlock getBlock() override
{
return m_block_template->block;
}

std::vector<CAmount> getTxFees() override
{
return m_block_template->vTxFees;
}

std::vector<int64_t> getTxSigops() override
{
return m_block_template->vTxSigOpsCost;
}

CTransactionRef getCoinbaseTx() override
{
return m_block_template->block.vtx[0];
}

std::vector<unsigned char> getCoinbaseCommitment() override
{
return m_block_template->vchCoinbaseCommitment;
}

int getWitnessCommitmentIndex() override
{
return GetWitnessCommitmentIndex(m_block_template->block);
}

std::unique_ptr<CBlockTemplate> m_block_template;
};

class MinerImpl : public Mining
{
public:
@@ -884,11 +928,11 @@ class MinerImpl : public Mining
return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, tip, /*fCheckPOW=*/false, check_merkle_root);
}

std::unique_ptr<CBlockTemplate> createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override
std::unique_ptr<BlockTemplate> createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override
{
BlockAssembler::Options assemble_options{options};
ApplyArgsManOptions(*Assert(m_node.args), assemble_options);
return BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key);
return std::make_unique<BlockTemplateImpl>(BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key));
}

NodeContext* context() override { return &m_node; }
59 changes: 31 additions & 28 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@
#include <stdint.h>

using node::BlockAssembler;
using node::CBlockTemplate;
using interfaces::BlockTemplate;
using interfaces::Mining;
using node::NodeContext;
using node::RegenerateCommitments;
@@ -129,7 +129,7 @@ static RPCHelpMan getnetworkhashps()
};
}

static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock& block, uint64_t& max_tries, std::shared_ptr<const CBlock>& block_out, bool process_new_block)
static bool GenerateBlock(ChainstateManager& chainman, Mining& miner, CBlock&& block, uint64_t& max_tries, std::shared_ptr<const CBlock>& block_out, bool process_new_block)
{
block_out.reset();
block.hashMerkleRoot = BlockMerkleRoot(block);
@@ -160,12 +160,12 @@ static UniValue generateBlocks(ChainstateManager& chainman, Mining& miner, const
{
UniValue blockHashes(UniValue::VARR);
while (nGenerate > 0 && !chainman.m_interrupt) {
std::unique_ptr<CBlockTemplate> pblocktemplate(miner.createNewBlock(coinbase_script));
if (!pblocktemplate.get())
std::unique_ptr<BlockTemplate> block_template(miner.createNewBlock(coinbase_script));
if (!block_template.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");

std::shared_ptr<const CBlock> block_out;
if (!GenerateBlock(chainman, miner, pblocktemplate->block, nMaxTries, block_out, /*process_new_block=*/true)) {
if (!GenerateBlock(chainman, miner, block_template->getBlock(), nMaxTries, block_out, /*process_new_block=*/true)) {
break;
}

@@ -371,11 +371,11 @@ static RPCHelpMan generateblock()

ChainstateManager& chainman = EnsureChainman(node);
{
std::unique_ptr<CBlockTemplate> blocktemplate{miner.createNewBlock(coinbase_script, {.use_mempool = false})};
if (!blocktemplate) {
std::unique_ptr<BlockTemplate> block_template{miner.createNewBlock(coinbase_script, {.use_mempool = false})};
if (!block_template) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
block = blocktemplate->block;
block = block_template->getBlock();
}

CHECK_NONFATAL(block.vtx.size() == 1);
@@ -394,7 +394,7 @@ static RPCHelpMan generateblock()
std::shared_ptr<const CBlock> block_out;
uint64_t max_tries{DEFAULT_MAX_TRIES};

if (!GenerateBlock(chainman, miner, block, max_tries, block_out, process_new_block) || !block_out) {
if (!GenerateBlock(chainman, miner, std::move(block), max_tries, block_out, process_new_block) || !block_out) {
throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block.");
}

@@ -800,7 +800,7 @@ static RPCHelpMan getblocktemplate()
// Update block
static CBlockIndex* pindexPrev;
static int64_t time_start;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
static std::unique_ptr<BlockTemplate> block_template;
if (!pindexPrev || pindexPrev->GetBlockHash() != tip ||
(miner.getTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - time_start > 5))
{
@@ -814,20 +814,20 @@ static RPCHelpMan getblocktemplate()

// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
pblocktemplate = miner.createNewBlock(scriptDummy);
if (!pblocktemplate) {
block_template = miner.createNewBlock(scriptDummy);
if (!block_template) {
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
}

// Need to update only after we know createNewBlock succeeded
pindexPrev = pindexPrevNew;
}
CHECK_NONFATAL(pindexPrev);
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
CBlock block{block_template->getBlock()};

// Update nTime
UpdateTime(pblock, consensusParams, pindexPrev);
pblock->nNonce = 0;
UpdateTime(&block, consensusParams, pindexPrev);
block.nNonce = 0;

// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_SEGWIT);
@@ -836,8 +836,11 @@ static RPCHelpMan getblocktemplate()

UniValue transactions(UniValue::VARR);
std::map<uint256, int64_t> setTxIndex;
std::vector<CAmount> tx_fees{block_template->getTxFees()};
std::vector<CAmount> tx_sigops{block_template->getTxSigops()};

int i = 0;
for (const auto& it : pblock->vtx) {
for (const auto& it : block.vtx) {
const CTransaction& tx = *it;
uint256 txHash = tx.GetHash();
setTxIndex[txHash] = i++;
@@ -860,8 +863,8 @@ static RPCHelpMan getblocktemplate()
entry.pushKV("depends", std::move(deps));

int index_in_template = i - 1;
entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]);
int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
entry.pushKV("fee", tx_fees.at(index_in_template));
int64_t nTxSigOps{tx_sigops.at(index_in_template)};
if (fPreSegWit) {
CHECK_NONFATAL(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
nTxSigOps /= WITNESS_SCALE_FACTOR;
@@ -874,7 +877,7 @@ static RPCHelpMan getblocktemplate()

UniValue aux(UniValue::VOBJ);

arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
arith_uint256 hashTarget = arith_uint256().SetCompact(block.nBits);

UniValue aMutable(UniValue::VARR);
aMutable.push_back("time");
@@ -904,7 +907,7 @@ static RPCHelpMan getblocktemplate()
break;
case ThresholdState::LOCKED_IN:
// Ensure bit is set in block version
pblock->nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
block.nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos);
[[fallthrough]];
case ThresholdState::STARTED:
{
@@ -913,7 +916,7 @@ static RPCHelpMan getblocktemplate()
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
if (!vbinfo.gbt_force) {
// If the client doesn't support this, don't indicate it in the [default] version
pblock->nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
block.nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos);
}
}
break;
@@ -933,15 +936,15 @@ static RPCHelpMan getblocktemplate()
}
}
}
result.pushKV("version", pblock->nVersion);
result.pushKV("version", block.nVersion);
result.pushKV("rules", std::move(aRules));
result.pushKV("vbavailable", std::move(vbavailable));
result.pushKV("vbrequired", int(0));

result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
result.pushKV("previousblockhash", block.hashPrevBlock.GetHex());
result.pushKV("transactions", std::move(transactions));
result.pushKV("coinbaseaux", std::move(aux));
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
result.pushKV("coinbasevalue", (int64_t)block.vtx[0]->vout[0].nValue);
result.pushKV("longpollid", tip.GetHex() + ToString(nTransactionsUpdatedLast));
result.pushKV("target", hashTarget.GetHex());
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
@@ -960,16 +963,16 @@ static RPCHelpMan getblocktemplate()
if (!fPreSegWit) {
result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT);
}
result.pushKV("curtime", pblock->GetBlockTime());
result.pushKV("bits", strprintf("%08x", pblock->nBits));
result.pushKV("curtime", block.GetBlockTime());
result.pushKV("bits", strprintf("%08x", block.nBits));
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));

if (consensusParams.signet_blocks) {
result.pushKV("signet_challenge", HexStr(consensusParams.signet_challenge));
}

if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment));
if (!block_template->getCoinbaseCommitment().empty()) {
result.pushKV("default_witness_commitment", HexStr(block_template->getCoinbaseCommitment()));
}

return result;