From c79ad5fe0cd15d242823fef2bb60c2c88cb09526 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 22 Oct 2019 13:30:36 -0400 Subject: [PATCH] getnewblockhex: Take data push for arbitrary data in block coinbase commitment --- src/miner.cpp | 6 +++++- src/miner.h | 2 +- src/rpc/mining.cpp | 15 ++++++++++++--- test/functional/feature_blocksign.py | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index b202eb9348..6d311b74c2 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -102,7 +102,7 @@ void BlockAssembler::resetBlock() Optional BlockAssembler::m_last_block_num_txs{nullopt}; Optional BlockAssembler::m_last_block_weight{nullopt}; -std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, int min_tx_age, DynaFedParamEntry* proposed_entry) +std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, int min_tx_age, DynaFedParamEntry* proposed_entry, CScript const* commit_script) { assert(min_tx_age >= 0); int64_t nTimeStart = GetTimeMicros(); @@ -195,6 +195,10 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc } } coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + // Non-consensus commitment output before finishing coinbase transaction + if (commit_script) { + coinbaseTx.vout.insert(coinbaseTx.vout.begin(), CTxOut(policyAsset, 0, *commit_script)); + } pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; diff --git a/src/miner.h b/src/miner.h index ab8844069f..a9c71d0436 100644 --- a/src/miner.h +++ b/src/miner.h @@ -159,7 +159,7 @@ class BlockAssembler BlockAssembler(const CChainParams& params, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn. min_tx_age is in seconds */ - std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, int min_tx_age=0, DynaFedParamEntry* = nullptr); + std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, int min_tx_age=0, DynaFedParamEntry* = nullptr, CScript const* commit_script = nullptr); static Optional m_last_block_num_txs; static Optional m_last_block_weight; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 49ccef665b..5875c61be4 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -997,7 +997,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) UniValue getnewblockhex(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() > 2) + if (request.fHelp || request.params.size() > 3) throw std::runtime_error( RPCHelpMan{"getnewblockhex", "\nGets hex representation of a proposed, unmined new block\n", @@ -1015,6 +1015,7 @@ UniValue getnewblockhex(const JSONRPCRequest& request) }, }, "proposed_parameters"}, + {"commit_data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Data in hex to be committed to in an additional coinbase output."}, }, RPCResult{ "blockhex (hex) The block hex\n" @@ -1069,9 +1070,17 @@ UniValue getnewblockhex(const JSONRPCRequest& request) proposed.m_serialize_type = 2; } + // Any commitment required for non-consensus reasons. + // This will be placed in the first coinbase output. + CScript data_commitment; + if (!request.params[2].isNull()) { + std::vector data_bytes = ParseHex(request.params[2].get_str()); + data_commitment = CScript() << OP_RETURN << data_bytes; + } + CScript feeDestinationScript = Params().GetConsensus().mandatory_coinbase_destination; if (feeDestinationScript == CScript()) feeDestinationScript = CScript() << OP_TRUE; - std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(feeDestinationScript, required_wait, &proposed)); + std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(feeDestinationScript, required_wait, &proposed, data_commitment.empty() ? nullptr : &data_commitment)); if (!pblocktemplate.get()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty"); } @@ -1472,7 +1481,7 @@ static const CRPCCommand commands[] = { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, { "generating", "combineblocksigs", &combineblocksigs, {"blockhex","signatures"} }, { "mining", "submitheader", &submitheader, {"hexdata"} }, - { "generating", "getnewblockhex", &getnewblockhex, {"min_tx_age", "proposed_parameters"} }, + { "generating", "getnewblockhex", &getnewblockhex, {"min_tx_age", "proposed_parameters", "commit_data"} }, { "generating", "getcompactsketch", &getcompactsketch, {"block_hex"} }, { "generating", "consumecompactsketch", &consumecompactsketch, {"sketch"} }, { "generating", "consumegetblocktxn", &consumegetblocktxn, {"full_block", "block_tx_req"} }, diff --git a/test/functional/feature_blocksign.py b/test/functional/feature_blocksign.py index 865de8c367..24693fb672 100755 --- a/test/functional/feature_blocksign.py +++ b/test/functional/feature_blocksign.py @@ -9,6 +9,13 @@ address, key, ) +from test_framework.messages import ( + FromHex, + CBlock, +) +from test_framework.script import ( + CScript +) # Generate wallet import format from private key. def wif(pk): @@ -113,6 +120,14 @@ def mine_block(self, make_transactions): miner.sendtoaddress(miner_next.getnewaddress(), int(miner.getbalance()['bitcoin']/10), "", "", True) # miner makes a block block = miner.getnewblockhex() + block_struct = FromHex(CBlock(), block) + + # make another block with the commitment field filled out + dummy_block = miner.getnewblockhex(commit_data="deadbeef") + dummy_struct = FromHex(CBlock(), dummy_block) + assert_equal(len(dummy_struct.vtx[0].vout), len(block_struct.vtx[0].vout) + 1) + # OP_RETURN deadbeef + assert_equal(CScript(dummy_struct.vtx[0].vout[0].scriptPubKey).hex(), '6a04deadbeef') # All nodes get compact blocks, first node may get complete # block in 0.5 RTT even with transactions thanks to p2p connection