From 5c8ebea00cc5aa6c6dd200222acc90619ad75aab Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 10 Sep 2021 08:25:57 +0100 Subject: [PATCH] consensus: AuxPoW header Add the AuxPoW header to block storage, without yet adding code to mine or validate mined AuxPoW blocks. --- src/Makefile.test.include | 1 + src/rpc/auxpow_miner.cpp | 154 ++++++++++ src/rpc/auxpow_miner.h | 107 +++++++ src/test/auxpow_tests.cpp | 605 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 867 insertions(+) create mode 100644 src/rpc/auxpow_miner.cpp create mode 100644 src/rpc/auxpow_miner.h create mode 100644 src/test/auxpow_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 9902f53081f..fc2a0fe1a1c 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -214,6 +214,7 @@ BITCOIN_TESTS =\ test/addrman_tests.cpp \ test/amount_tests.cpp \ test/allocator_tests.cpp \ + test/auxpow_tests.cpp \ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ diff --git a/src/rpc/auxpow_miner.cpp b/src/rpc/auxpow_miner.cpp new file mode 100644 index 00000000000..518736af6aa --- /dev/null +++ b/src/rpc/auxpow_miner.cpp @@ -0,0 +1,154 @@ +// Copyright(c) 2018-2020 Daniel Kraft +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const CBlock* +AuxpowMiner::getCurrentBlock(const CTxMemPool& mempool, + const CScript& scriptPubKey, uint256& target) EXCLUSIVE_LOCKS_REQUIRED(cs) +{ + AssertLockHeld(cs); + const CBlock* pblockCur = nullptr; + + { + LOCK(cs_main); + CScriptID scriptID(scriptPubKey); + auto iter = curBlocks.find(scriptID); + if (iter != curBlocks.end()) + pblockCur = iter->second; + + if (pblockCur == nullptr + || pindexPrev != ::ChainActive().Tip() + ||(mempool.GetTransactionsUpdated() != txUpdatedLast + && GetTime() - startTime > MAX_BLOCK_TEMPLATE_AGE_SECONDS)) + { + if (pindexPrev != ::ChainActive().Tip()) + { + /* Clear old blocks since they're obsolete now. */ + blocks.clear(); + templates.clear(); + curBlocks.clear(); + } + + /* Create new block with nonce = 0 and extraNonce = 1. */ + std::unique_ptr newBlock + = BlockAssembler(mempool, Params()).CreateNewBlock(scriptPubKey); + if (newBlock == nullptr) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory"); + + /* Update state only when CreateNewBlock succeeded. */ + txUpdatedLast = mempool.GetTransactionsUpdated(); + pindexPrev = ::ChainActive().Tip(); + startTime = GetTime(); + + /* Finalise it by setting the version and building the merkle root. */ + IncrementExtraNonce(&newBlock->block, pindexPrev, extraNonce); + newBlock->block.SetAuxpowFlag(true); + + /* Save in our map of constructed blocks. */ + pblockCur = &newBlock->block; + curBlocks.emplace(scriptID, pblockCur); + blocks[pblockCur->GetHash()] = pblockCur; + templates.push_back(std::move(newBlock)); + } + } + + /* At this point, pblockCur is always initialised: If we make it here + without creating a new block above, it means that, in particular, + pindexPrev == ::ChainActive().Tip(). But for that to happen, we must + already have created a pblockCur in a previous call, as pindexPrev is + initialised only when pblockCur is. */ + CHECK_NONFATAL(pblockCur); + + arith_uint256 arithTarget; + bool fNegative, fOverflow; + arithTarget.SetCompact(pblockCur->nBits, &fNegative, &fOverflow); + if (fNegative || fOverflow || arithTarget == 0) + throw std::runtime_error("invalid difficulty bits in block"); + target = ArithToUint256(arithTarget); + + return pblockCur; +} + +const CBlock* +AuxpowMiner::lookupSavedBlock(const uint256 hash) const EXCLUSIVE_LOCKS_REQUIRED(cs) +{ + AssertLockHeld(cs); + + const auto iter = blocks.find(hash); + if (iter == blocks.end()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown"); + + return iter->second; +} + +UniValue +AuxpowMiner::createAuxBlock(const CTxMemPool& mempool, + const CScript& scriptPubKey) +{ + LOCK(cs); + + uint256 target; + const CBlock* pblock = getCurrentBlock(mempool, scriptPubKey, target); + + UniValue result(UniValue::VOBJ); + result.pushKV("hash", pblock->GetHash().GetHex()); + result.pushKV("chainid", pblock->GetChainId()); + result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); + result.pushKV("coinbasevalue", + static_cast(pblock->vtx[0]->vout[0].nValue)); + result.pushKV("bits", strprintf("%08x", pblock->nBits)); + result.pushKV("height", static_cast(pindexPrev->nHeight + 1)); + result.pushKV("_target", HexStr(target)); + + return result; +} + +bool +AuxpowMiner::submitAuxBlock(ChainstateManager& chainman, + const uint256 hash, + const std::string& auxpowHex) const +{ + std::shared_ptr shared_block; + { + LOCK(cs); + const CBlock* pblock = lookupSavedBlock(hash); + shared_block = std::make_shared(*pblock); + } + + const std::vector vchAuxPow = ParseHex(auxpowHex); + CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION); + std::unique_ptr pow(new CAuxPow()); + ss >> *pow; + shared_block->SetAuxpow(std::move(pow)); + CHECK_NONFATAL(shared_block->GetHash() == hash); + + return chainman.ProcessNewBlock(Params(), shared_block, true, nullptr); +} + +AuxpowMiner& +AuxpowMiner::get() +{ + static AuxpowMiner* instance = nullptr; + static RecursiveMutex lock; + + LOCK(lock); + if (instance == nullptr) + instance = new AuxpowMiner(); + + return *instance; +} diff --git a/src/rpc/auxpow_miner.h b/src/rpc/auxpow_miner.h new file mode 100644 index 00000000000..553f89cf856 --- /dev/null +++ b/src/rpc/auxpow_miner.h @@ -0,0 +1,107 @@ +// Copyright (c) 2018-2020 Daniel Kraft +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_AUXPOW_MINER_H +#define BITCOIN_RPC_AUXPOW_MINER_H + +#include +#include +#include