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
8baefe39fb
commit
d4eb11a140
|
@ -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 \
|
||||
|
|
215
src/auxpow.cpp
Normal file
215
src/auxpow.cpp
Normal file
|
@ -0,0 +1,215 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2011 Vince Durham
|
||||
// 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.
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
609
src/test/auxpow_tests.cpp
Normal file
609
src/test/auxpow_tests.cpp
Normal file
|
@ -0,0 +1,609 @@
|
|||
// Copyright (c) 2014-2020 Daniel Kraft
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <arith_uint256.h>
|
||||
#include <auxpow.h>
|
||||
#include <chainparams.h>
|
||||
#include <coins.h>
|
||||
#include <consensus/merkle.h>
|
||||
#include <dogecoin.h>
|
||||
#include <validation.h>
|
||||
#include <pow.h>
|
||||
#include <primitives/block.h>
|
||||
#include <rpc/auxpow_miner.h>
|
||||
#include <script/script.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/time.h>
|
||||
#include <uint256.h>
|
||||
#include <univalue.h>
|
||||
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
/* No space between BOOST_AUTO_TEST_SUITE and '(', so that extraction of
|
||||
the test-suite name works with grep as done in the Makefile. */
|
||||
BOOST_AUTO_TEST_SUITE(auxpow_tests)
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* Tamper with a uint256 (modify it).
|
||||
* @param num The number to modify.
|
||||
*/
|
||||
static void
|
||||
tamperWith(uint256& num)
|
||||
{
|
||||
arith_uint256 modifiable = UintToArith256(num);
|
||||
modifiable += 1;
|
||||
num = ArithToUint256(modifiable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that is friend to CAuxPow and makes the internals accessible
|
||||
* to the test code.
|
||||
*/
|
||||
class CAuxPowForTest : public CAuxPow
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
explicit inline CAuxPowForTest (CTransactionRef txIn)
|
||||
: CAuxPow (std::move (txIn))
|
||||
{}
|
||||
|
||||
using CAuxPow::coinbaseTx;
|
||||
using CAuxPow::vMerkleBranch;
|
||||
using CAuxPow::vChainMerkleBranch;
|
||||
using CAuxPow::nChainIndex;
|
||||
using CAuxPow::parentBlock;
|
||||
|
||||
using CAuxPow::CheckMerkleBranch;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class to construct auxpow's and manipulate them. This is used
|
||||
* to simulate various scenarios.
|
||||
*/
|
||||
class CAuxpowBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
/** The parent block (with coinbase, not just header). */
|
||||
CBlock parentBlock;
|
||||
|
||||
/** The auxpow's merkle branch (connecting it to the coinbase). */
|
||||
std::vector<uint256> auxpowChainMerkleBranch;
|
||||
/** The auxpow's merkle tree index. */
|
||||
int auxpowChainIndex;
|
||||
|
||||
/**
|
||||
* Initialise everything.
|
||||
* @param baseVersion The parent block's base version to use.
|
||||
* @param chainId The parent block's chain ID to use.
|
||||
*/
|
||||
CAuxpowBuilder(int baseVersion, int chainId);
|
||||
|
||||
/**
|
||||
* Set the coinbase's script.
|
||||
* @param scr Set it to this script.
|
||||
*/
|
||||
void setCoinbase(const CScript& scr);
|
||||
|
||||
/**
|
||||
* Build the auxpow merkle branch. The member variables will be
|
||||
* set accordingly. This has to be done before constructing the coinbase
|
||||
* itself (which must contain the root merkle hash). When we have the
|
||||
* coinbase afterwards, the member variables can be used to initialise
|
||||
* the CAuxPow object from it.
|
||||
* @param hashAux The merge-mined chain's block hash.
|
||||
* @param h Height of the merkle tree to build.
|
||||
* @param index Index to use in the merkle tree.
|
||||
* @return The root hash, with reversed endian.
|
||||
*/
|
||||
std::vector<unsigned char> buildAuxpowChain(const uint256& hashAux, unsigned h, int index);
|
||||
|
||||
/**
|
||||
* Build the finished CAuxPow object. We assume that the auxpowChain
|
||||
* member variables are already set. We use the passed in transaction
|
||||
* as the base. It should (probably) be the parent block's coinbase.
|
||||
* @param tx The base tx to use.
|
||||
* @return The constructed CAuxPow object.
|
||||
*/
|
||||
CAuxPow get(const CTransactionRef tx) const;
|
||||
|
||||
/**
|
||||
* Build the finished CAuxPow object from the parent block's coinbase.
|
||||
* @return The constructed CAuxPow object.
|
||||
*/
|
||||
inline CAuxPow
|
||||
get() const
|
||||
{
|
||||
assert (!parentBlock.vtx.empty());
|
||||
return get(parentBlock.vtx[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the finished CAuxPow object and returns it as std::unique_ptr.
|
||||
*/
|
||||
inline std::unique_ptr<CAuxPow>
|
||||
getUnique() const
|
||||
{
|
||||
return std::unique_ptr<CAuxPow>(new CAuxPow(get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a data vector to be included in the coinbase. It consists
|
||||
* of the aux hash, the merkle tree size and the nonce. Optionally,
|
||||
* the header can be added as well.
|
||||
* @param header Add the header?
|
||||
* @param hashAux The aux merkle root hash.
|
||||
* @param h Height of the merkle tree.
|
||||
* @param nonce The nonce value to use.
|
||||
* @return The constructed data.
|
||||
*/
|
||||
static std::vector<unsigned char> buildCoinbaseData(bool header, const std::vector<unsigned char>& auxRoot,
|
||||
unsigned h, int nonce);
|
||||
|
||||
};
|
||||
|
||||
CAuxpowBuilder::CAuxpowBuilder(int baseVersion, int chainId)
|
||||
: auxpowChainIndex(-1)
|
||||
{
|
||||
parentBlock.SetBaseVersion(baseVersion, chainId);
|
||||
}
|
||||
|
||||
void
|
||||
CAuxpowBuilder::setCoinbase(const CScript& scr)
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize (1);
|
||||
mtx.vin[0].prevout.SetNull ();
|
||||
mtx.vin[0].scriptSig = scr;
|
||||
|
||||
parentBlock.vtx.clear();
|
||||
parentBlock.vtx.push_back(MakeTransactionRef (std::move (mtx)));
|
||||
parentBlock.hashMerkleRoot = BlockMerkleRoot (parentBlock);
|
||||
}
|
||||
|
||||
std::vector<unsigned char>
|
||||
CAuxpowBuilder::buildAuxpowChain(const uint256& hashAux, unsigned h, int index)
|
||||
{
|
||||
auxpowChainIndex = index;
|
||||
|
||||
/* Just use "something" for the branch. Doesn't really matter. */
|
||||
auxpowChainMerkleBranch.clear();
|
||||
for (unsigned i = 0; i < h; ++i)
|
||||
auxpowChainMerkleBranch.push_back(ArithToUint256 (arith_uint256 (i)));
|
||||
|
||||
const uint256 hash
|
||||
= CAuxPowForTest::CheckMerkleBranch(hashAux, auxpowChainMerkleBranch,
|
||||
index);
|
||||
|
||||
std::vector<unsigned char> res = ToByteVector (hash);
|
||||
std::reverse (res.begin (), res.end ());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
CAuxPow
|
||||
CAuxpowBuilder::get(const CTransactionRef tx) const
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
CAuxPowForTest res(tx);
|
||||
res.vMerkleBranch = merkle_tests::BlockMerkleBranch (parentBlock, 0);
|
||||
|
||||
res.vChainMerkleBranch = auxpowChainMerkleBranch;
|
||||
res.nChainIndex = auxpowChainIndex;
|
||||
res.parentBlock = parentBlock;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<unsigned char>
|
||||
CAuxpowBuilder::buildCoinbaseData(bool header, const std::vector<unsigned char>& auxRoot,
|
||||
unsigned h, int nonce)
|
||||
{
|
||||
std::vector<unsigned char> res;
|
||||
|
||||
if (header)
|
||||
res.insert (res.end (),
|
||||
pchMergedMiningHeader,
|
||||
pchMergedMiningHeader + sizeof (pchMergedMiningHeader));
|
||||
res.insert (res.end (), auxRoot.begin (), auxRoot.end ());
|
||||
|
||||
int size = (1 << h);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
res.insert (res.end (), size & 0xFF);
|
||||
size >>= 8;
|
||||
}
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
res.insert (res.end (), nonce & 0xFF);
|
||||
nonce >>= 8;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(check_auxpow, BasicTestingSetup)
|
||||
{
|
||||
const Consensus::Params& params = Params().GetConsensus();
|
||||
CAuxpowBuilder builder(5, 42);
|
||||
CAuxPow auxpow;
|
||||
|
||||
const uint256 hashAux = ArithToUint256(arith_uint256(12345));
|
||||
const int32_t ourChainId = params.nAuxpowChainId;
|
||||
const unsigned height = 30;
|
||||
const int nonce = 7;
|
||||
int index;
|
||||
|
||||
std::vector<unsigned char> auxRoot, data;
|
||||
CScript scr;
|
||||
|
||||
/* Build a correct auxpow. The height is the maximally allowed one. */
|
||||
index = CAuxPow::getExpectedIndex (nonce, ourChainId, height);
|
||||
auxRoot = builder.buildAuxpowChain(hashAux, height, index);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
scr = (CScript () << 2809 << 2013);
|
||||
scr = (scr << OP_2 << data);
|
||||
builder.setCoinbase(scr);
|
||||
BOOST_CHECK(builder.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* An auxpow without any inputs in the parent coinbase tx should be
|
||||
handled gracefully (and be considered invalid). */
|
||||
CMutableTransaction mtx(*builder.parentBlock.vtx[0]);
|
||||
mtx.vin.clear();
|
||||
builder.parentBlock.vtx.clear();
|
||||
builder.parentBlock.vtx.push_back(MakeTransactionRef (std::move (mtx)));
|
||||
builder.parentBlock.hashMerkleRoot = BlockMerkleRoot (builder.parentBlock);
|
||||
BOOST_CHECK(!builder.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* Check that the auxpow is invalid if we change either the aux block's
|
||||
hash or the chain ID. */
|
||||
uint256 modifiedAux(hashAux);
|
||||
tamperWith (modifiedAux);
|
||||
BOOST_CHECK(!builder.get().check(modifiedAux, ourChainId, params));
|
||||
BOOST_CHECK(!builder.get().check(hashAux, ourChainId + 1, params));
|
||||
|
||||
/* Non-coinbase parent tx should fail. Note that we can't just copy
|
||||
the coinbase literally, as we have to get a tx with different hash. */
|
||||
const CTransactionRef oldCoinbase = builder.parentBlock.vtx[0];
|
||||
builder.setCoinbase(scr << 5);
|
||||
builder.parentBlock.vtx.push_back(oldCoinbase);
|
||||
builder.parentBlock.hashMerkleRoot = BlockMerkleRoot (builder.parentBlock);
|
||||
auxpow = builder.get(builder.parentBlock.vtx[0]);
|
||||
BOOST_CHECK(auxpow.check(hashAux, ourChainId, params));
|
||||
auxpow = builder.get(builder.parentBlock.vtx[1]);
|
||||
BOOST_CHECK(!auxpow.check(hashAux, ourChainId, params));
|
||||
|
||||
/* The parent chain can't have the same chain ID. */
|
||||
CAuxpowBuilder builder2(builder);
|
||||
builder2.parentBlock.SetChainId(100);
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
builder2.parentBlock.SetChainId(ourChainId);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* Disallow too long merkle branches. */
|
||||
builder2 = builder;
|
||||
index = CAuxPow::getExpectedIndex (nonce, ourChainId, height + 1);
|
||||
auxRoot = builder2.buildAuxpowChain(hashAux, height + 1, index);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height + 1, nonce);
|
||||
scr = (CScript () << 2809 << 2013);
|
||||
scr = (scr << OP_2 << data);
|
||||
builder2.setCoinbase(scr);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* Verify that we compare correctly to the parent block's merkle root. */
|
||||
builder2 = builder;
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
tamperWith (builder2.parentBlock.hashMerkleRoot);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* Build a non-header legacy version and check that it is also accepted. */
|
||||
builder2 = builder;
|
||||
index = CAuxPow::getExpectedIndex (nonce, ourChainId, height);
|
||||
auxRoot = builder2.buildAuxpowChain(hashAux, height, index);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(false, auxRoot, height, nonce);
|
||||
scr = (CScript () << 2809 << 2013);
|
||||
scr = (scr << OP_2 << data);
|
||||
builder2.setCoinbase(scr);
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* However, various attempts at smuggling two roots in should be detected. */
|
||||
|
||||
const std::vector<unsigned char> wrongAuxRoot
|
||||
= builder2.buildAuxpowChain(modifiedAux, height, index);
|
||||
std::vector<unsigned char> data2
|
||||
= CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot, height, nonce);
|
||||
builder2.setCoinbase(CScript () << data << data2);
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
builder2.setCoinbase(CScript () << data2 << data);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
data2 = CAuxpowBuilder::buildCoinbaseData(true, wrongAuxRoot, height, nonce);
|
||||
builder2.setCoinbase(CScript () << data << data2);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
builder2.setCoinbase(CScript () << data2 << data);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
builder2.setCoinbase(CScript () << data << data2);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
builder2.setCoinbase(CScript () << data2 << data);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot,
|
||||
height, nonce);
|
||||
builder2.setCoinbase(CScript () << data << data2);
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
builder2.setCoinbase(CScript () << data2 << data);
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* Verify that the appended nonce/size values are checked correctly. */
|
||||
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
builder2.setCoinbase(CScript () << data);
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
data.pop_back ();
|
||||
builder2.setCoinbase(CScript () << data);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height - 1, nonce);
|
||||
builder2.setCoinbase(CScript () << data);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce + 3);
|
||||
builder2.setCoinbase(CScript () << data);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
/* Put the aux hash in an invalid merkle tree position. */
|
||||
|
||||
auxRoot = builder.buildAuxpowChain(hashAux, height, index + 1);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
builder2.setCoinbase(CScript () << data);
|
||||
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
|
||||
|
||||
auxRoot = builder.buildAuxpowChain(hashAux, height, index);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
builder2.setCoinbase(CScript () << data);
|
||||
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* Mine a block (assuming minimal difficulty) that either matches
|
||||
* or doesn't match the difficulty target specified in the block header.
|
||||
* @param block The block to mine (by updating nonce).
|
||||
* @param ok Whether the block should be ok for PoW.
|
||||
* @param nBits Use this as difficulty if specified.
|
||||
*/
|
||||
static void
|
||||
mineBlock(CBlockHeader& block, bool ok, int nBits = -1)
|
||||
{
|
||||
if (nBits == -1)
|
||||
nBits = block.nBits;
|
||||
|
||||
arith_uint256 target;
|
||||
target.SetCompact (nBits);
|
||||
|
||||
block.nNonce = 0;
|
||||
while (true)
|
||||
{
|
||||
const bool nowOk = (UintToArith256(block.GetPoWHash()) <= target);
|
||||
if ((ok && nowOk) || (!ok && !nowOk))
|
||||
break;
|
||||
|
||||
++block.nNonce;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
BOOST_CHECK(CheckProofOfWork(block.GetPoWHash(), nBits, Params().GetConsensus()));
|
||||
else
|
||||
BOOST_CHECK(!CheckProofOfWork(block.GetPoWHash(), nBits, Params().GetConsensus()));
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(auxpow_pow, BasicTestingSetup)
|
||||
{
|
||||
/* Use regtest parameters to allow mining with easy difficulty. */
|
||||
SelectParams (CBaseChainParams::REGTEST);
|
||||
const Consensus::Params& params = Params ().GetConsensus ();
|
||||
|
||||
const arith_uint256 target = (~arith_uint256 (0) >> 1);
|
||||
CBlockHeader block;
|
||||
block.nBits = target.GetCompact ();
|
||||
|
||||
/* Verify the block version checks. */
|
||||
|
||||
block.nVersion = 1;
|
||||
mineBlock(block, true);
|
||||
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
block.nVersion = 3;
|
||||
mineBlock(block, true);
|
||||
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
block.SetBaseVersion (2, params.nAuxpowChainId);
|
||||
mineBlock(block, true);
|
||||
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
block.SetChainId (params.nAuxpowChainId + 1);
|
||||
mineBlock(block, true);
|
||||
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
/* Check the case when the block does not have auxpow (this is true
|
||||
right now). */
|
||||
|
||||
block.SetChainId (params.nAuxpowChainId);
|
||||
block.SetAuxpowFlag(true);
|
||||
mineBlock(block, true);
|
||||
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
block.SetAuxpowFlag(false);
|
||||
mineBlock(block, true);
|
||||
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
|
||||
mineBlock(block, false);
|
||||
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
/* ****************************************** */
|
||||
/* Check the case that the block has auxpow. */
|
||||
|
||||
CAuxpowBuilder builder(5, 42);
|
||||
CAuxPow auxpow;
|
||||
const int32_t ourChainId = params.nAuxpowChainId;
|
||||
const unsigned height = 3;
|
||||
const int nonce = 7;
|
||||
const int index = CAuxPow::getExpectedIndex(nonce, ourChainId, height);
|
||||
std::vector<unsigned char> auxRoot, data;
|
||||
|
||||
/* Valid auxpow, PoW check of parent block. */
|
||||
block.SetAuxpowFlag(true);
|
||||
auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
builder.setCoinbase(CScript () << data);
|
||||
mineBlock(builder.parentBlock, false, block.nBits);
|
||||
block.SetAuxpow(builder.getUnique());
|
||||
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
|
||||
mineBlock(builder.parentBlock, true, block.nBits);
|
||||
block.SetAuxpow(builder.getUnique());
|
||||
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
/* Mismatch between auxpow being present and block.nVersion. Note that
|
||||
block.SetAuxpow sets also the version and that we want to ensure
|
||||
that the block hash itself doesn't change due to version changes.
|
||||
This requires some work arounds. */
|
||||
block.SetAuxpowFlag(false);
|
||||
const uint256 hashAux = block.GetHash();
|
||||
auxRoot = builder.buildAuxpowChain(hashAux, height, index);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
builder.setCoinbase(CScript () << data);
|
||||
mineBlock(builder.parentBlock, true, block.nBits);
|
||||
block.SetAuxpow(builder.getUnique());
|
||||
BOOST_CHECK(hashAux != block.GetHash());
|
||||
block.SetAuxpowFlag(false);
|
||||
BOOST_CHECK(hashAux == block.GetHash());
|
||||
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
|
||||
|
||||
/* Modifying the block invalidates the PoW. */
|
||||
block.SetAuxpowFlag(true);
|
||||
auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index);
|
||||
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
|
||||
builder.setCoinbase(CScript () << data);
|
||||
mineBlock(builder.parentBlock, true, block.nBits);
|
||||
block.SetAuxpow(builder.getUnique());
|
||||
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
|
||||
tamperWith (block.hashMerkleRoot);
|
||||
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
/**
|
||||
* Helper class that is friend to AuxpowMiner and makes the tested methods
|
||||
* accessible to the test code.
|
||||
*/
|
||||
class AuxpowMinerForTest : public AuxpowMiner
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
using AuxpowMiner::cs;
|
||||
|
||||
using AuxpowMiner::getCurrentBlock;
|
||||
using AuxpowMiner::lookupSavedBlock;
|
||||
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(auxpow_miner_blockRegeneration, TestChain240Setup)
|
||||
{
|
||||
CTxMemPool mempool;
|
||||
AuxpowMinerForTest miner;
|
||||
LOCK (miner.cs);
|
||||
|
||||
/* We use mocktime so that we can control GetTime() as it is used in the
|
||||
logic that determines whether or not to reconstruct a block. The "base"
|
||||
time is set such that the blocks we have from the fixture are fresh. */
|
||||
const int64_t baseTime = ::ChainActive().Tip()->GetMedianTimePast () + 1;
|
||||
SetMockTime(baseTime);
|
||||
|
||||
/* Construct a first block. */
|
||||
CScript scriptPubKey;
|
||||
uint256 target;
|
||||
const CBlock* pblock1 = miner.getCurrentBlock(mempool, scriptPubKey, target);
|
||||
BOOST_CHECK(pblock1 != nullptr);
|
||||
const uint256 hash1 = pblock1->GetHash();
|
||||
|
||||
/* Verify target computation. */
|
||||
arith_uint256 expected;
|
||||
expected.SetCompact (pblock1->nBits);
|
||||
BOOST_CHECK(target == ArithToUint256 (expected));
|
||||
|
||||
/* Calling the method again should return the same, cached block a second
|
||||
time (even if we advance the clock, since there are no new
|
||||
transactions). */
|
||||
SetMockTime(baseTime + 10);
|
||||
const CBlock* pblock = miner.getCurrentBlock(mempool, scriptPubKey, target);
|
||||
BOOST_CHECK(pblock == pblock1 && pblock->GetHash() == hash1);
|
||||
|
||||
/* Mine a block, then we should get a new auxpow block constructed. Note that
|
||||
it can be the same *pointer* if the memory was reused after clearing it,
|
||||
so we can only verify that the hash is different. */
|
||||
CreateAndProcessBlock({}, scriptPubKey);
|
||||
const CBlock* pblock2 = miner.getCurrentBlock(mempool, scriptPubKey, target);
|
||||
BOOST_CHECK(pblock2 != nullptr);
|
||||
const uint256 hash2 = pblock2->GetHash();
|
||||
BOOST_CHECK(hash2 != hash1);
|
||||
|
||||
/* Add a new transaction to the mempool. */
|
||||
TestMemPoolEntryHelper entry;
|
||||
CMutableTransaction mtx;
|
||||
mtx.vout.emplace_back(1234, scriptPubKey);
|
||||
{
|
||||
LOCK(cs_main);
|
||||
LOCK(mempool.cs);
|
||||
mempool.addUnchecked (entry.FromTx (mtx));
|
||||
}
|
||||
|
||||
/* We should still get back the cached block, for now. */
|
||||
SetMockTime(baseTime + 20);
|
||||
pblock = miner.getCurrentBlock(mempool, scriptPubKey, target);
|
||||
BOOST_CHECK(pblock == pblock2);
|
||||
BOOST_CHECK(pblock->GetHash() == hash2);
|
||||
|
||||
/* With time advanced too far, we get a new block. This time, we should also
|
||||
definitely get a different pointer, as there is no clearing. The old
|
||||
blocks are freed only after a new tip is found. */
|
||||
SetMockTime(baseTime + 21);
|
||||
const CBlock* pblock3 = miner.getCurrentBlock(mempool, scriptPubKey, target);
|
||||
BOOST_CHECK(pblock3 != pblock2);
|
||||
BOOST_CHECK(pblock3->GetHash() != hash2);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(auxpow_miner_createAndLookupBlock, TestChain240Setup)
|
||||
{
|
||||
CTxMemPool mempool;
|
||||
AuxpowMinerForTest miner;
|
||||
LOCK (miner.cs);
|
||||
|
||||
CScript scriptPubKey;
|
||||
uint256 target;
|
||||
const CBlock* pblock = miner.getCurrentBlock(mempool, scriptPubKey, target);
|
||||
BOOST_CHECK(pblock != nullptr);
|
||||
|
||||
BOOST_CHECK(miner.lookupSavedBlock(pblock->GetHash()) == pblock);
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ()
|
|
@ -116,7 +116,7 @@ static std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leav
|
|||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
|
||||
std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
|
||||
{
|
||||
std::vector<uint256> leaves;
|
||||
leaves.resize(block.vtx.size());
|
||||
|
|
|
@ -254,3 +254,12 @@ CBlock getBlock13b8a()
|
|||
stream >> block;
|
||||
return block;
|
||||
}
|
||||
|
||||
//! equality test
|
||||
bool operator==(const Coin &a, const Coin &b) {
|
||||
// Empty Coin objects are always equal.
|
||||
if (a.IsSpent() && b.IsSpent()) return true;
|
||||
return a.fCoinBase == b.fCoinBase &&
|
||||
a.nHeight == b.nHeight &&
|
||||
a.out == b.out;
|
||||
}
|
||||
|
|
|
@ -172,4 +172,9 @@ private:
|
|||
const std::string m_reason;
|
||||
};
|
||||
|
||||
/* This is defined in merkle_tests.cpp, but also used by auxpow_tests.cpp. */
|
||||
namespace merkle_tests {
|
||||
std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue