mirror of
https://github.com/dogecoin/dogecoin.git
synced 2026-03-02 00:56:02 +00:00
consensus: AuxPoW header
Add the AuxPoW header to block storage, without yet adding code to mine or validate mined AuxPoW blocks.
This commit is contained in:
parent
aa61e37d74
commit
e73ab3f90f
@ -107,6 +107,7 @@ BITCOIN_CORE_H = \
|
||||
addrdb.h \
|
||||
addrman.h \
|
||||
attributes.h \
|
||||
auxpow.h \
|
||||
banman.h \
|
||||
base58.h \
|
||||
bech32.h \
|
||||
@ -489,6 +490,7 @@ libbitcoin_consensus_a_SOURCES = \
|
||||
libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_common_a_SOURCES = \
|
||||
auxpow.cpp \
|
||||
base58.cpp \
|
||||
bech32.cpp \
|
||||
bloom.cpp \
|
||||
|
||||
216
src/auxpow.cpp
Normal file
216
src/auxpow.cpp
Normal file
@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2011 Vince Durham
|
||||
// Copyright (c) 2009-2014 The Bitcoin developers
|
||||
// Copyright (c) 2014-2019 Daniel Kraft
|
||||
// Copyright (c) 2021 The Dogecoin Core developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <auxpow.h>
|
||||
|
||||
#include <consensus/consensus.h>
|
||||
#include <consensus/merkle.h>
|
||||
#include <hash.h>
|
||||
#include <primitives/block.h>
|
||||
#include <script/script.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/system.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* Decodes a 32-bit little endian integer from raw bytes.
|
||||
*/
|
||||
uint32_t
|
||||
DecodeLE32(const unsigned char* bytes)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
res <<= 8;
|
||||
res |= bytes[3 - i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool
|
||||
CAuxPow::check(const uint256& hashAuxBlock, int nChainId,
|
||||
const Consensus::Params& params) const
|
||||
{
|
||||
if (params.fStrictChainId && parentBlock.GetChainId() == nChainId)
|
||||
return error("Aux POW parent has our chain ID");
|
||||
|
||||
if (vChainMerkleBranch.size() > 30)
|
||||
return error("Aux POW chain merkle branch too long");
|
||||
|
||||
// Check that the chain merkle root is in the coinbase
|
||||
const uint256 nRootHash
|
||||
= CheckMerkleBranch(hashAuxBlock, vChainMerkleBranch, nChainIndex);
|
||||
std::vector<unsigned char> vchRootHash(nRootHash.begin(), nRootHash.end());
|
||||
std::reverse(vchRootHash.begin(), vchRootHash.end()); // correct endian
|
||||
|
||||
// Check that we are in the parent block merkle tree
|
||||
if (CheckMerkleBranch(coinbaseTx->GetHash(), vMerkleBranch, 0)
|
||||
!= parentBlock.hashMerkleRoot)
|
||||
return error("Aux POW merkle root incorrect");
|
||||
|
||||
// Check that there is at least one input.
|
||||
if (coinbaseTx->vin.empty())
|
||||
return error("Aux POW coinbase has no inputs");
|
||||
|
||||
const CScript script = coinbaseTx->vin[0].scriptSig;
|
||||
|
||||
// Check that the same work is not submitted twice to our chain.
|
||||
//
|
||||
|
||||
const unsigned char* const mmHeaderBegin = pchMergedMiningHeader;
|
||||
const unsigned char* const mmHeaderEnd
|
||||
= mmHeaderBegin + sizeof(pchMergedMiningHeader);
|
||||
CScript::const_iterator pcHead =
|
||||
std::search(script.begin(), script.end(), mmHeaderBegin, mmHeaderEnd);
|
||||
|
||||
CScript::const_iterator pc =
|
||||
std::search(script.begin(), script.end(), vchRootHash.begin(), vchRootHash.end());
|
||||
|
||||
if (pc == script.end())
|
||||
return error("Aux POW missing chain merkle root in parent coinbase");
|
||||
|
||||
if (pcHead != script.end())
|
||||
{
|
||||
// Enforce only one chain merkle root by checking that a single instance of the merged
|
||||
// mining header exists just before.
|
||||
if (script.end() != std::search(pcHead + 1, script.end(),
|
||||
mmHeaderBegin, mmHeaderEnd))
|
||||
return error("Multiple merged mining headers in coinbase");
|
||||
if (pcHead + sizeof(pchMergedMiningHeader) != pc)
|
||||
return error("Merged mining header is not just before chain merkle root");
|
||||
}
|
||||
else
|
||||
{
|
||||
// For backward compatibility.
|
||||
// Enforce only one chain merkle root by checking that it starts early in the coinbase.
|
||||
// 8-12 bytes are enough to encode extraNonce and nBits.
|
||||
if (pc - script.begin() > 20)
|
||||
return error("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase");
|
||||
}
|
||||
|
||||
|
||||
// Ensure we are at a deterministic point in the merkle leaves by hashing
|
||||
// a nonce and our chain ID and comparing to the index.
|
||||
pc += vchRootHash.size();
|
||||
if (script.end() - pc < 8)
|
||||
return error("Aux POW missing chain merkle tree size and nonce in parent coinbase");
|
||||
|
||||
const uint32_t nSize = DecodeLE32(&pc[0]);
|
||||
const unsigned merkleHeight = vChainMerkleBranch.size();
|
||||
if (nSize != (1u << merkleHeight))
|
||||
return error("Aux POW merkle branch size does not match parent coinbase");
|
||||
|
||||
const uint32_t nNonce = DecodeLE32(&pc[4]);
|
||||
if (nChainIndex != getExpectedIndex(nNonce, nChainId, merkleHeight))
|
||||
return error("Aux POW wrong index");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
CAuxPow::getExpectedIndex(uint32_t nNonce, int nChainId, unsigned h)
|
||||
{
|
||||
// Choose a pseudo-random slot in the chain merkle tree
|
||||
// but have it be fixed for a size/nonce/chain combination.
|
||||
//
|
||||
// This prevents the same work from being used twice for the
|
||||
// same chain while reducing the chance that two chains clash
|
||||
// for the same slot.
|
||||
|
||||
/* This computation can overflow the uint32 used. This is not an issue,
|
||||
though, since we take the mod against a power-of-two in the end anyway.
|
||||
This also ensures that the computation is, actually, consistent
|
||||
even if done in 64 bits as it was in the past on some systems.
|
||||
|
||||
Note that h is always <= 30 (enforced by the maximum allowed chain
|
||||
merkle branch length), so that 32 bits are enough for the computation. */
|
||||
|
||||
uint32_t rand = nNonce;
|
||||
rand = rand * 1103515245 + 12345;
|
||||
rand += nChainId;
|
||||
rand = rand * 1103515245 + 12345;
|
||||
|
||||
return rand % (1u << h);
|
||||
}
|
||||
|
||||
uint256
|
||||
CAuxPow::CheckMerkleBranch(uint256 hash,
|
||||
const std::vector<uint256>& vMerkleBranch,
|
||||
int nIndex)
|
||||
{
|
||||
if (nIndex == -1)
|
||||
return uint256 ();
|
||||
for (std::vector<uint256>::const_iterator it(vMerkleBranch.begin());
|
||||
it != vMerkleBranch.end(); ++it)
|
||||
{
|
||||
if (nIndex & 1)
|
||||
hash = Hash(*it, hash);
|
||||
else
|
||||
hash = Hash(hash, *it);
|
||||
nIndex >>= 1;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::unique_ptr<CAuxPow>
|
||||
CAuxPow::createAuxPow(const CPureBlockHeader& header)
|
||||
{
|
||||
assert(header.IsAuxpow());
|
||||
|
||||
/* Build a minimal coinbase script input for merge-mining. */
|
||||
const uint256 blockHash = header.GetHash();
|
||||
std::vector<unsigned char> inputData(blockHash.begin(), blockHash.end());
|
||||
std::reverse(inputData.begin(), inputData.end());
|
||||
inputData.push_back(1);
|
||||
inputData.insert(inputData.end(), 7, 0);
|
||||
|
||||
/* Fake a parent-block coinbase with just the required input
|
||||
script and no outputs. */
|
||||
CMutableTransaction coinbase;
|
||||
coinbase.vin.resize(1);
|
||||
coinbase.vin[0].prevout.SetNull();
|
||||
coinbase.vin[0].scriptSig = (CScript () << inputData);
|
||||
assert(coinbase.vout.empty());
|
||||
CTransactionRef coinbaseRef = MakeTransactionRef(coinbase);
|
||||
|
||||
/* Build a fake parent block with the coinbase. */
|
||||
CBlock parent;
|
||||
parent.nVersion = 1;
|
||||
parent.vtx.resize(1);
|
||||
parent.vtx[0] = coinbaseRef;
|
||||
parent.hashMerkleRoot = BlockMerkleRoot(parent);
|
||||
|
||||
/* Construct the auxpow object. */
|
||||
std::unique_ptr<CAuxPow> auxpow(new CAuxPow(std::move(coinbaseRef)));
|
||||
assert(auxpow->vMerkleBranch.empty());
|
||||
assert(auxpow->vChainMerkleBranch.empty());
|
||||
auxpow->nChainIndex = 0;
|
||||
auxpow->parentBlock = parent;
|
||||
|
||||
return auxpow;
|
||||
}
|
||||
|
||||
CPureBlockHeader&
|
||||
CAuxPow::initAuxPow(CBlockHeader& header)
|
||||
{
|
||||
/* Set auxpow flag right now, since we take the block hash below when creating
|
||||
the minimal auxpow for header. */
|
||||
header.SetAuxpowFlag(true);
|
||||
|
||||
std::unique_ptr<CAuxPow> apow = createAuxPow(header);
|
||||
CPureBlockHeader& result = apow->parentBlock;
|
||||
header.SetAuxpow(std::move (apow));
|
||||
|
||||
return result;
|
||||
}
|
||||
158
src/auxpow.h
Normal file
158
src/auxpow.h
Normal file
@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2014 The Bitcoin developers
|
||||
// Copyright (c) 2014-2019 Daniel Kraft
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_AUXPOW_H
|
||||
#define BITCOIN_AUXPOW_H
|
||||
|
||||
#include <consensus/params.h>
|
||||
#include <primitives/pureheader.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class CBlock;
|
||||
class CBlockHeader;
|
||||
class CBlockIndex;
|
||||
class CValidationState;
|
||||
class UniValue;
|
||||
|
||||
namespace auxpow_tests
|
||||
{
|
||||
class CAuxPowForTest;
|
||||
}
|
||||
|
||||
/** Header for merge-mining data in the coinbase. */
|
||||
static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' };
|
||||
|
||||
/**
|
||||
* Data for the merge-mining auxpow. This uses a merkle tx (the parent block's
|
||||
* coinbase tx) and a second merkle branch to link the actual Namecoin block
|
||||
* header to the parent block header, which is mined to satisfy the PoW.
|
||||
*/
|
||||
class CAuxPow
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/** The parent block's coinbase transaction. */
|
||||
CTransactionRef coinbaseTx;
|
||||
|
||||
/** The Merkle branch of the coinbase tx to the parent block's root. */
|
||||
std::vector<uint256> vMerkleBranch;
|
||||
|
||||
/** The merkle branch connecting the aux block to our coinbase. */
|
||||
std::vector<uint256> vChainMerkleBranch;
|
||||
|
||||
/** Merkle tree index of the aux block header in the coinbase. */
|
||||
int nChainIndex;
|
||||
|
||||
/** Parent block header (on which the real PoW is done). */
|
||||
CPureBlockHeader parentBlock;
|
||||
|
||||
/**
|
||||
* Check a merkle branch. This used to be in CBlock, but was removed
|
||||
* upstream. Thus include it here now.
|
||||
*/
|
||||
static uint256 CheckMerkleBranch (uint256 hash,
|
||||
const std::vector<uint256>& vMerkleBranch,
|
||||
int nIndex);
|
||||
|
||||
friend UniValue AuxpowToJSON(const CAuxPow& auxpow);
|
||||
friend class auxpow_tests::CAuxPowForTest;
|
||||
|
||||
public:
|
||||
|
||||
/* Prevent accidental conversion. */
|
||||
inline explicit CAuxPow (CTransactionRef&& txIn)
|
||||
: coinbaseTx (std::move (txIn))
|
||||
{}
|
||||
|
||||
CAuxPow () = default;
|
||||
|
||||
SERIALIZE_METHODS (CAuxPow, obj)
|
||||
{
|
||||
/* The coinbase Merkle tx' hashBlock field is never actually verified
|
||||
or used in the code for an auxpow (and never was). The parent block
|
||||
is known anyway directly, so this is also redundant. By setting the
|
||||
value to zero (for serialising), we make sure that the format is
|
||||
backwards compatible but the data can be compressed. */
|
||||
uint256 hashBlock;
|
||||
|
||||
/* The index of the parent coinbase tx is always zero. */
|
||||
int nIndex = 0;
|
||||
|
||||
/* Data from the coinbase transaction as Merkle tx. */
|
||||
READWRITE (obj.coinbaseTx, hashBlock, obj.vMerkleBranch, nIndex);
|
||||
|
||||
/* Additional data for the auxpow itself. */
|
||||
READWRITE (obj.vChainMerkleBranch, obj.nChainIndex, obj.parentBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the auxpow, given the merge-mined block's hash and our chain ID.
|
||||
* Note that this does not verify the actual PoW on the parent block! It
|
||||
* just confirms that all the merkle branches are valid.
|
||||
* @param hashAuxBlock Hash of the merge-mined block.
|
||||
* @param nChainId The auxpow chain ID of the block to check.
|
||||
* @param params Consensus parameters.
|
||||
* @return True if the auxpow is valid.
|
||||
*/
|
||||
bool check (const uint256& hashAuxBlock, int nChainId,
|
||||
const Consensus::Params& params) const;
|
||||
|
||||
/**
|
||||
* Returns the parent block hash. This is used to validate the PoW.
|
||||
*/
|
||||
inline uint256
|
||||
getParentBlockPoWHash() const
|
||||
{
|
||||
return parentBlock.GetPoWHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parent block. This is only used for the temporary parentblock
|
||||
* auxpow version check.
|
||||
* @return The parent block.
|
||||
*/
|
||||
/* FIXME: Remove after the hardfork. */
|
||||
inline const CPureBlockHeader&
|
||||
getParentBlock () const
|
||||
{
|
||||
return parentBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the expected index in the merkle tree. This is also used
|
||||
* for the test-suite.
|
||||
* @param nNonce The coinbase's nonce value.
|
||||
* @param nChainId The chain ID.
|
||||
* @param h The merkle block height.
|
||||
* @return The expected index for the aux hash.
|
||||
*/
|
||||
static int getExpectedIndex (uint32_t nNonce, int nChainId, unsigned h);
|
||||
|
||||
/**
|
||||
* Constructs a minimal CAuxPow object for the given block header and
|
||||
* returns it. The caller should make sure to set the auxpow flag on the
|
||||
* header already, since the block hash to which the auxpow commits depends
|
||||
* on that!
|
||||
*/
|
||||
static std::unique_ptr<CAuxPow> createAuxPow (const CPureBlockHeader& header);
|
||||
|
||||
/**
|
||||
* Initialises the auxpow of the given block header. This builds a minimal
|
||||
* auxpow object like createAuxPow and sets it on the block header. Returns
|
||||
* a reference to the parent header so it can be mined as a follow-up.
|
||||
*/
|
||||
static CPureBlockHeader& initAuxPow (CBlockHeader& header);
|
||||
|
||||
};
|
||||
|
||||
#endif // BITCOIN_AUXPOW_H
|
||||
@ -5,6 +5,10 @@
|
||||
|
||||
#include <chain.h>
|
||||
|
||||
// Dogecoin: Include the single function signature we need, to avoid the entire
|
||||
// validation.h being pulled in and creating a cyclic include loop
|
||||
bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams, const bool fCheckPOW = true);
|
||||
|
||||
/* Moved here from the header, because we need auxpow and the logic
|
||||
becomes more involved. */
|
||||
CBlockHeader CBlockIndex::GetBlockHeader(const Consensus::Params& consensusParams, bool fCheckPOW) const
|
||||
@ -13,6 +17,15 @@ CBlockHeader CBlockIndex::GetBlockHeader(const Consensus::Params& consensusParam
|
||||
|
||||
block.nVersion = nVersion;
|
||||
|
||||
/* The CBlockIndex object's block header is missing the auxpow.
|
||||
So if this is an auxpow block, read it from disk instead. We only
|
||||
have to read the actual *header*, not the full block. */
|
||||
if (block.IsAuxpow())
|
||||
{
|
||||
ReadBlockHeaderFromDisk(block, this, consensusParams, fCheckPOW);
|
||||
return block;
|
||||
}
|
||||
|
||||
if (pprev)
|
||||
block.hashPrevBlock = pprev->GetBlockHash();
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
|
||||
@ -10,6 +10,19 @@
|
||||
#include <crypto/common.h>
|
||||
#include <crypto/scrypt.h>
|
||||
|
||||
void CBlockHeader::SetAuxpow (std::unique_ptr<CAuxPow> apow)
|
||||
{
|
||||
if (apow != nullptr)
|
||||
{
|
||||
auxpow.reset(apow.release());
|
||||
SetAuxpowFlag(true);
|
||||
} else
|
||||
{
|
||||
auxpow.reset();
|
||||
SetAuxpowFlag(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CBlock::ToString() const
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
@ -6,11 +6,14 @@
|
||||
#ifndef BITCOIN_PRIMITIVES_BLOCK_H
|
||||
#define BITCOIN_PRIMITIVES_BLOCK_H
|
||||
|
||||
#include <auxpow.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <primitives/pureheader.h>
|
||||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
/** Nodes collect new transactions into a block, hash them into a hash tree,
|
||||
* and scan through nonce values to make the block's hash satisfy proof-of-work
|
||||
* requirements. When they solve the proof-of-work, they broadcast the block
|
||||
@ -22,6 +25,9 @@ class CBlockHeader : public CPureBlockHeader
|
||||
{
|
||||
public:
|
||||
|
||||
// auxpow (if this is a merge-minded block)
|
||||
std::shared_ptr<CAuxPow> auxpow;
|
||||
|
||||
CBlockHeader()
|
||||
{
|
||||
SetNull();
|
||||
@ -30,13 +36,29 @@ public:
|
||||
SERIALIZE_METHODS(CBlockHeader, obj)
|
||||
{
|
||||
READWRITEAS(CPureBlockHeader, obj);
|
||||
|
||||
if (obj.IsAuxpow())
|
||||
{
|
||||
SER_READ(obj, obj.auxpow = std::make_shared<CAuxPow>());
|
||||
assert(obj.auxpow != nullptr);
|
||||
READWRITE(*obj.auxpow);
|
||||
} else
|
||||
{
|
||||
SER_READ(obj, obj.auxpow.reset());
|
||||
}
|
||||
}
|
||||
|
||||
void SetNull()
|
||||
{
|
||||
CPureBlockHeader::SetNull();
|
||||
auxpow.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block's auxpow (or unset it). This takes care of updating
|
||||
* the version accordingly.
|
||||
*/
|
||||
void SetAuxpow (std::unique_ptr<CAuxPow> apow);
|
||||
};
|
||||
|
||||
|
||||
@ -82,6 +104,7 @@ public:
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
block.auxpow = auxpow;
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user