From 5ec9e0918a57566a5b64b94a5d7b91104c1ffe52 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Tue, 2 Sep 2025 10:35:09 +0200 Subject: [PATCH] Sv2NewTemplate: add coinbase_witness Adding coinbase_witness to the Sv2NewTemplate message so that it is automatically propagated to other roles in the Stratum v2 ecosystem, rather than assumed to be 0x00...00. This ensures that if this value ever gets consensus meaning, miners only need to upgrade their node software, not the rest of the mining stack. This is a breaking change requiring an updated Stratum v2 spec as well as support on the SRI side. --- src/sv2/messages.cpp | 7 +++++++ src/sv2/messages.h | 13 +++++++++++++ src/test/sv2_messages_tests.cpp | 8 ++++++-- src/test/sv2_template_provider_tests.cpp | 1 + 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/sv2/messages.cpp b/src/sv2/messages.cpp index 0bdb0037..403c7709 100644 --- a/src/sv2/messages.cpp +++ b/src/sv2/messages.cpp @@ -13,6 +13,13 @@ node::Sv2NewTemplateMsg::Sv2NewTemplateMsg(const CBlockHeader& header, const CTr m_coinbase_tx_version = coinbase_tx->CURRENT_VERSION; m_coinbase_prefix = coinbase_tx->vin[0].scriptSig; + if (coinbase_tx->HasWitness()) { + const auto& witness_stack{coinbase_tx->vin[0].scriptWitness.stack}; + Assert(witness_stack.size() == 1 || witness_stack[0].size() == 32); + m_coinbase_witness = uint256(witness_stack[0]); + } else { + m_coinbase_witness = uint256(0); + } m_coinbase_tx_input_sequence = coinbase_tx->vin[0].nSequence; // The coinbase nValue already contains the nFee + the Block Subsidy when built using CreateBlock(). diff --git a/src/sv2/messages.h b/src/sv2/messages.h index 31b38ef4..59e3a9e7 100644 --- a/src/sv2/messages.h +++ b/src/sv2/messages.h @@ -282,6 +282,18 @@ struct Sv2NewTemplateMsg */ CScript m_coinbase_prefix; + /** + * 32 byte array of the first (and only) witness stack element of the coinbase. + * + * If there is no segwit commitment in m_coinbase_tx_outputs this value + * must be ignored. + * + * This is the BIP 141 witness reserved value. A future soft fork may move + * the witness reserved value elsewhere. In that case this field still + * represents the coinbase witness, for backward compatibility. + */ + uint256 m_coinbase_witness; + /** * The coinbase transaction input’s nSequence field. */ @@ -324,6 +336,7 @@ struct Sv2NewTemplateMsg << m_version << m_coinbase_tx_version << m_coinbase_prefix + << m_coinbase_witness << m_coinbase_tx_input_sequence << m_coinbase_tx_value_remaining << m_coinbase_tx_outputs_count; diff --git a/src/test/sv2_messages_tests.cpp b/src/test/sv2_messages_tests.cpp index e4453f55..d3cd54e9 100644 --- a/src/test/sv2_messages_tests.cpp +++ b/src/test/sv2_messages_tests.cpp @@ -113,6 +113,7 @@ BOOST_AUTO_TEST_CASE(Sv2NewTemplate_test) // U32 02000000 coinbase tx version // B0_255 04 coinbase_prefix len // 03012100 coinbase prefix + // U256 0000000000000000000000000000000000000000000000000000000000000000 - witness // U32 ffffffff coinbase tx input sequence // U64 0040075af0750700 coinbase tx value remaining // U32 01000000 coinbase tx outputs count @@ -122,7 +123,7 @@ BOOST_AUTO_TEST_CASE(Sv2NewTemplate_test) // U32 dbc80d00 coinbase lock time (height 903,387) // SEQ0_255[U256] 01 merkle path length // 1a6240823de4c8d6aaf826851bdf2b0e8d5acf7c31e8578cff4c394b5a32bd4e - merkle path - std::string expected{"01000000000000000000000030020000000403012100ffffffff0040075af0750700010000000c000100000000000000036a012adbc80d00011a6240823de4c8d6aaf826851bdf2b0e8d5acf7c31e8578cff4c394b5a32bd4e"}; + std::string expected{"010000000000000000000000300200000004030121000000000000000000000000000000000000000000000000000000000000000000ffffffff0040075af0750700010000000c000100000000000000036a012adbc80d00011a6240823de4c8d6aaf826851bdf2b0e8d5acf7c31e8578cff4c394b5a32bd4e"}; node::Sv2NewTemplateMsg new_template; new_template.m_template_id = 1; @@ -134,6 +135,8 @@ BOOST_AUTO_TEST_CASE(Sv2NewTemplate_test) CScript prefix(coinbase_prefix.begin(), coinbase_prefix.end()); new_template.m_coinbase_prefix = prefix; + new_template.m_coinbase_witness = uint256(0); + new_template.m_coinbase_tx_input_sequence = 4294967295; new_template.m_coinbase_tx_value_remaining = MAX_MONEY; @@ -167,6 +170,7 @@ BOOST_AUTO_TEST_CASE(Sv2NewTemplate_MultipleOutputs_test) // U32 02000000 coinbase tx version // B0_255 04 coinbase_prefix len // 03012100 coinbase prefix + // U256 0000000000000000000000000000000000000000000000000000000000000000 - witness // U32 ffffffff coinbase tx input sequence // U64 0040075af0750700 coinbase tx value remaining // U32 03000000 coinbase tx outputs count (3 OP_RETURN outputs, dummy filtered) @@ -180,7 +184,7 @@ BOOST_AUTO_TEST_CASE(Sv2NewTemplate_MultipleOutputs_test) // U32 dbc80d00 coinbase lock time (height 903,387) // SEQ0_255[U256] 01 merkle path length // 1a6240823de4c8d6aaf826851bdf2b0e8d5acf7c31e8578cff4c394b5a32bd4e - merkle path - std::string expected{"01000000000000000000000030020000000403012100ffffffff0040075af07507000300000021006400000000000000026a51c800000000000000026a522c01000000000000026a53dbc80d00011a6240823de4c8d6aaf826851bdf2b0e8d5acf7c31e8578cff4c394b5a32bd4e"}; + std::string expected{"010000000000000000000000300200000004030121000000000000000000000000000000000000000000000000000000000000000000ffffffff0040075af07507000300000021006400000000000000026a51c800000000000000026a522c01000000000000026a53dbc80d00011a6240823de4c8d6aaf826851bdf2b0e8d5acf7c31e8578cff4c394b5a32bd4e"}; // Create realistic coinbase transaction with dummy anyone-can-spend output CMutableTransaction coinbase_tx; diff --git a/src/test/sv2_template_provider_tests.cpp b/src/test/sv2_template_provider_tests.cpp index a7e3f9f2..222d87a8 100644 --- a/src/test/sv2_template_provider_tests.cpp +++ b/src/test/sv2_template_provider_tests.cpp @@ -63,6 +63,7 @@ BOOST_AUTO_TEST_CASE(client_tests) 4 + // version 4 + // coinbase_tx_version 2 + // coinbase_prefix (CompactSize(1) + 1-byte OP_0) + 32 + // coinbase_witness (fixed-size reserved value) 4 + // coinbase_tx_input_sequence 8 + // coinbase_tx_value_remaining 4 + // coinbase_tx_outputs_count (2 - mock creates 3, only 2 OP_RETURN outputs pass filter)