in progress on auxpow support

This commit is contained in:
Brannon King 2021-03-02 23:44:11 -05:00
parent be118de19a
commit 8c8c12398e
22 changed files with 1428 additions and 78 deletions

View file

@ -14,7 +14,7 @@ jobs:
- &build-template
stage: build
name: linux
env: NAME=linux EXT=
env: NAME=linux DOCKER_IMAGE=lbry/build_lbrycrd_gcc EXT=
os: linux
dist: xenial
language: minimal
@ -22,8 +22,9 @@ jobs:
- docker
install:
- mkdir -p ${HOME}/ccache
- docker pull $DOCKER_BUILD_IMAGE
- docker pull $DOCKER_IMAGE
script:
- echo "build..."
- docker run -v "$(pwd):/lbrycrd" -v "${HOME}/ccache:/ccache" -w /lbrycrd -e CCACHE_DIR=/ccache ${DOCKER_IMAGE} packaging/build_${NAME}_64bit.sh
before_deploy:
- mkdir -p dist
@ -47,11 +48,11 @@ jobs:
- <<: *build-template
name: windows
env: NAME=windows EXT=.exe
env: NAME=windows DOCKER_IMAGE=lbry/build_lbrycrd EXT=.exe
- <<: *build-template
name: osx
env: NAME=darwin EXT=
env: NAME=darwin DOCKER_IMAGE=lbry/build_lbrycrd EXT=
before_install:
- mkdir -p ./depends/SDKs && pushd depends/SDKs && curl -C - ${MAC_OS_SDK} | tar --skip-old-files -xJ && popd
@ -62,7 +63,7 @@ jobs:
dist: xenial
language: minimal
git:
depth: 3
clone: false
install:
- mkdir -p testrun && cd testrun
- curl http://build.lbry.io/lbrycrd/${TRAVIS_BRANCH}/lbrycrd-${NAME}-test.zip -o temp.zip
@ -77,8 +78,8 @@ jobs:
services:
- docker
script:
- docker pull $DOCKER_WINE_IMAGE
- docker run -v "$(pwd):/test" -e "WINEDEBUG=-all" -e "TRIEHASH_FUZZER_BLOCKS=1000" -it $DOCKER_WINE_IMAGE wine "/test/test_lbrycrd.exe"
- docker pull lbry/wine
- docker run -v "$(pwd):/test" -e "WINEDEBUG=-all" -e "TRIEHASH_FUZZER_BLOCKS=1000" -it lbry/wine wine "/test/test_lbrycrd.exe"
- <<: *test-template
os: osx

View file

@ -3,9 +3,9 @@ AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 0)
define(_CLIENT_VERSION_MINOR, 17)
define(_CLIENT_VERSION_REVISION, 3)
define(_CLIENT_VERSION_BUILD, 2)
define(_CLIENT_VERSION_BUILD, 3)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2019)
define(_COPYRIGHT_YEAR, 2021)
define(_COPYRIGHT_HOLDERS,[The %s developers])
define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[LBRYcrd Core]])
AC_INIT([LBRYcrd Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/lbryio/lbrycrd/issues],[lbrycrd],[https://lbry.com/])

View file

@ -92,6 +92,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
auxpow.h \
base58.h \
bech32.h \
bloom.h \
@ -225,6 +226,7 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
auxpow.cpp \
bloom.cpp \
blockencodings.cpp \
chain.cpp \
@ -366,6 +368,8 @@ libbitcoin_consensus_a_SOURCES = \
prevector.h \
primitives/block.cpp \
primitives/block.h \
primitives/pureheader.cpp \
primitives/pureheader.h \
primitives/transaction.cpp \
primitives/transaction.h \
pubkey.cpp \

View file

@ -33,6 +33,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 \

237
src/auxpow.cpp Normal file
View file

@ -0,0 +1,237 @@
// Copyright(c) 2009-2010 Satoshi Nakamoto
// Copyright(c) 2011 Vince Durham
// Copyright(c) 2009-2014 The Bitcoin developers
// Copyright(c) 2014-2016 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 "chainparams.h"
#include "compat/endian.h"
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.h"
#include "script/script.h"
#include "txmempool.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validation.h"
#include <algorithm>
/* Moved from wallet.cpp. CMerkleTx is necessary for auxpow, independent
of an enabled(or disabled) wallet. Always include the code. */
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
{
// Update the tx's hashBlock
hashBlock = pindex->GetBlockHash();
// set the position of the transaction in the block
nIndex = posInBlock;
}
static std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position);
void CMerkleTx::InitMerkleBranch(const CBlock& block, int posInBlock)
{
hashBlock = block.GetHash();
nIndex = posInBlock;
vMerkleBranch = BlockMerkleBranch(block, nIndex);
}
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
{
if(hashUnset())
return 0;
AssertLockHeld(cs_main);
// Find the block it claims to be in
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if(mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex =(*mi).second;
if(!pindex || !chainActive.Contains(pindex))
return 0;
pindexRet = pindex;
return((nIndex == -1) ?(-1) : 1) *(chainActive.Height() - pindex->nHeight + 1);
}
int CMerkleTx::GetBlocksToMaturity() const
{
if(!IsCoinBase())
return 0;
return std::max(0,(COINBASE_MATURITY + 1) - GetDepthInMainChain());
}
bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
bool missing = true;
return ::AcceptToMemoryPool(mempool, state, tx, &missing, NULL, NULL, false, nAbsurdFee);
}
/* ************************************************************************** */
bool
CAuxPow::check(const uint256& hashAuxBlock, const Consensus::Params& params) const
{
if(nIndex != 0)
return error("AuxPow is not a generate");
if(nNonce != params.nAuxPoWChainID)
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(GetHash(), vMerkleBranch, nIndex) != parentBlock.hashMerkleRoot)
return error("Aux POW merkle root incorrect");
const CScript script = tx->vin[0].scriptSig;
// Check that the same work is not submitted twice to our chain.
CScript::const_iterator pcHead =
std::search(script.begin(), script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader));
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(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)))
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");
uint32_t nSize;
memcpy(&nSize, &pc[0], 4);
nSize = le32toh(nSize);
const unsigned merkleHeight = vChainMerkleBranch.size();
if(nSize !=(1u << merkleHeight))
return error("Aux POW merkle branch size does not match parent coinbase");
uint32_t nNonce;
memcpy(&nNonce, &pc[4], 4);
nNonce = le32toh(nNonce);
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 %(1 << 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(BEGIN(*it), END(*it), BEGIN(hash), END(hash));
else
hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it));
nIndex >>= 1;
}
return hash;
}
void
CAuxPow::initAuxPow(CBlockHeader& header)
{
/* Set auxpow flag right now, since we take the block hash below. */
header.SetAuxpowFlag(true);
/* 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. */
header.SetAuxpow(new CAuxPow(coinbaseRef));
assert(header.auxpow->vChainMerkleBranch.empty());
header.auxpow->nChainIndex = 0;
assert(header.auxpow->vMerkleBranch.empty());
header.auxpow->nIndex = 0;
header.auxpow->parentBlock = parent;
}

204
src/auxpow.h Normal file
View file

@ -0,0 +1,204 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
// Copyright (c) 2014-2016 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 "consensus/validation.h"
#include "primitives/pureheader.h"
#include "primitives/transaction.h"
#include "serialize.h"
#include "uint256.h"
#include <vector>
class CBlock;
class CBlockHeader;
class CBlockIndex;
/** Header for merge-mining data in the coinbase. */
static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' };
/* Because it is needed for auxpow, the definition of CMerkleTx is moved
here from wallet.h. */
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx
{
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
CTransactionRef tx;
uint256 hashBlock;
std::vector<uint256> vMerkleBranch;
/* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
* block in the chain we know this or any in-wallet dependency conflicts
* with. Older clients interpret nIndex == -1 as unconfirmed for backward
* compatibility.
*/
int nIndex = -1;
CMerkleTx()
{
SetTx(MakeTransactionRef());
}
CMerkleTx(CTransactionRef arg)
{
SetTx(std::move(arg));
}
/** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
* TODO: adapt callers and remove this operator. */
operator const CTransaction&() const { return *tx; }
void SetTx(CTransactionRef arg)
{
tx = std::move(arg);
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
}
/**
* Actually compute the Merkle branch. This is used for unit tests when
* constructing an auxpow. It is not needed for actual production, since
* we do not care in the lbrycrd client how the auxpow is constructed
* by a miner.
*/
void InitMerkleBranch(const CBlock& block, int posInBlock);
void SetMerkleBranch(const CBlockIndex* pindex, int posInBlock);
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
int GetBlocksToMaturity() const;
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
};
/**
* Data for the merge-mining auxpow. This is a merkle tx (the parent block's
* coinbase tx) that can be verified to be in the parent block, and this
* transaction's input (the coinbase script) contains the reference
* to the actual merge-mined block.
*/
class CAuxPow : public CMerkleTx
{
/* Public for the unit tests. */
public:
/** 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;
public:
/* Prevent accidental conversion. */
inline explicit CAuxPow(CTransactionRef txIn)
: CMerkleTx(txIn)
{
}
inline CAuxPow()
: CMerkleTx()
{
}
ADD_SERIALIZE_METHODS;
template<typename Stream, typename Operation>
inline void
SerializationOp (Stream& s, Operation ser_action)
{
READWRITE (*static_cast<CMerkleTx*> (this));
READWRITE (vChainMerkleBranch);
READWRITE (nChainIndex);
READWRITE (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, const Consensus::Params& params) const;
/**
* Get the parent block's hash. This is used to verify that it
* satisfies the PoW requirement.
* @return The parent block hash.
*/
inline uint256
getParentBlockPoWHash() const
{
return parentBlock.GetPoWHash();
}
/**
* 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);
/**
* 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);
/**
* Initialise the auxpow of the given block header. This constructs
* a minimal CAuxPow object with a minimal parent block and sets
* it on the block header. The auxpow is not necessarily valid, but
* can be "mined" to make it valid.
* @param header The header to set the auxpow on.
*/
static void initAuxPow(CBlockHeader& header);
};
#endif // BITCOIN_AUXPOW_H

View file

@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <streams.h>
/**
* CChain implementation
@ -118,6 +119,73 @@ void CBlockIndex::BuildSkip()
pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
}
/* Generic implementation of block reading that can handle
both a block and its header. */
template<typename T>
static bool ReadBlockOrHeader(T& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams, bool fCheckPOW)
{
block.SetNull();
// Open history file to read
CAutoFile filein( OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString());
// Read block
try {
filein >> block;
}
catch (const std::exception& e) {
return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
}
// Check the header
if (fCheckPOW && !CheckAuxPowProofOfWork(block, consensusParams))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
return true;
}
template<typename T>
static bool ReadBlockOrHeader(T& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams, bool fCheckPOW)
{
if (!ReadBlockOrHeader(block, pindex->GetBlockPos(), consensusParams, fCheckPOW))
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockOrHeader(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
pindex->ToString(), pindex->GetBlockPos().ToString());
return true;
}
bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams, bool fCheckPOW=false)
{
return ReadBlockOrHeader(block, pindex, consensusParams, fCheckPOW);
}
CBlockHeader CBlockIndex::GetBlockHeader(const Consensus::Params& consensusParams) const {
CBlockHeader block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
/* 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);
return block;
}
block.hashMerkleRoot = hashMerkleRoot;
block.hashClaimTrie = hashClaimTrie;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
arith_uint256 GetBlockProof(const CBlockIndex& block)
{
arith_uint256 bnTarget;

View file

@ -7,6 +7,7 @@
#define BITCOIN_CHAIN_H
#include <arith_uint256.h>
#include <chainparams.h>
#include <consensus/params.h>
#include <primitives/block.h>
#include <tinyformat.h>
@ -279,18 +280,15 @@ public:
return ret;
}
CBlockHeader GetBlockHeader() const
CBlockHeader GetBlockHeader(const Consensus::Params& consensusParams) const;
/**
* Check if the auxpow flag is set in the version.
* @return True if this block version is marked as auxpow.
*/
inline bool IsAuxpow() const
{
CBlockHeader block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.hashClaimTrie = hashClaimTrie;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
return nVersion & CPureBlockHeader::VERSION_AUXPOW;
}
uint256 GetBlockHash() const
@ -300,7 +298,7 @@ public:
uint256 GetBlockPoWHash() const
{
return GetBlockHeader().GetPoWHash();
return GetBlockHeader(Params().GetConsensus()).GetPoWHash();
}
int64_t GetBlockTime() const

View file

@ -147,6 +147,8 @@ public:
consensus.nMaxTakeoverWorkaroundHeight = 658300; // targeting 30 Oct 2019
consensus.nWitnessForkHeight = 680770; // targeting 11 Dec 2019
consensus.nAllClaimsInMerkleForkHeight = 658310; // targeting 30 Oct 2019
consensus.nAuxPoWStartHeight = 1000000;
consensus.nAuxPoWChainID = 0x4C425259; // LBRY
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1916; // 95% of a half week
@ -265,6 +267,8 @@ public:
consensus.nMaxTakeoverWorkaroundHeight = 1198550; // targeting 30 Sep 2019
consensus.nWitnessForkHeight = 1198600;
consensus.nAllClaimsInMerkleForkHeight = 1198560; // targeting 30 Sep 2019
consensus.nAuxPoWStartHeight = 2000000;
consensus.nAuxPoWChainID = 0x4C424374; // LBCt
consensus.fPowAllowMinDifficultyBlocks = true;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
@ -372,6 +376,8 @@ public:
consensus.nMaxTakeoverWorkaroundHeight = -1;
consensus.nWitnessForkHeight = 150;
consensus.nAllClaimsInMerkleForkHeight = 350;
consensus.nAuxPoWStartHeight = 450;
consensus.nAuxPoWChainID = 0x4C424372; // LBCr
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains

View file

@ -82,6 +82,8 @@ struct Params {
int nMaxTakeoverWorkaroundHeight;
int nWitnessForkHeight;
int nAuxPoWStartHeight;
uint32_t nAuxPoWChainID;
int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan;

View file

@ -147,12 +147,14 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
CBlockIndex* pindexPrev = chainActive.Tip();
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
auto& params = chainparams.GetConsensus();
pblock->nVersion = ComputeBlockVersion(pindexPrev, params);
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion);
pblock->nNonce = params.nChainID;
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();

View file

@ -5,19 +5,27 @@
#include <primitives/block.h>
#include <auxpow.h>
#include <hash.h>
#include <tinyformat.h>
#include <utilstrencodings.h>
#include <crypto/common.h>
#include <streams.h>
uint256 CBlockHeader::GetHash() const
void CBlockHeader::SetAuxpow(CAuxPow* apow)
{
return SerializeHash(*this);
if (apow)
{
auxpow.reset(apow);
SetAuxpowFlag(true);
} else
{
auxpow.reset();
SetAuxpowFlag(false);
}
}
uint256 CBlockHeader::GetPoWHash() const
{
uint256 CBlockHeader::GetPoWHash() const {
CDataStream ds(SER_GETHASH, PROTOCOL_VERSION);
ds << *this;
std::vector<unsigned char> input(ds.begin(), ds.end());
@ -35,8 +43,18 @@ std::string CBlock::ToString() const
hashClaimTrie.ToString(),
nTime, nBits, nNonce,
vtx.size());
for (const auto& tx : vtx) {
s << " " << tx->ToString() << "\n";
for (unsigned int i = 0; i < vtx.size(); i++)
{
s << " " << vtx[i]->ToString() << "\n";
}
return s.str();
}
int64_t GetBlockWeight(const CBlock& block)
{
// This implements the weight = (stripped_size * 4) + witness_size formula,
// using only serialization with and without witness data. As witness_size
// is equal to total_size - stripped_size, this formula is identical to:
// weight = (stripped_size * 3) + total_size.
return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
}

View file

@ -6,6 +6,8 @@
#ifndef BITCOIN_PRIMITIVES_BLOCK_H
#define BITCOIN_PRIMITIVES_BLOCK_H
#include <auxpow.h>
#include <primitives/pureheader.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <uint256.h>
@ -17,63 +19,74 @@
* in the block is a special one that creates a new coin owned by the creator
* of the block.
*/
class CBlockHeader
class CBlockHeader : public CPureBlockHeader
{
public:
// header
int32_t nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
/* Modifiers to the version. */
static const int32_t VERSION_AUXPOW = (1 << 8);
uint256 hashClaimTrie;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
CBlockHeader()
{
SetNull();
}
std::shared_ptr<CAuxPow> auxpow; // (if this is a merge-minded block)
CBlockHeader() = default;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
READWRITE(nVersion);
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(hashClaimTrie);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
if (IsAuxpow())
{
if (ser_action.ForRead())
auxpow.reset(new CAuxPow());
assert(auxpow);
READWRITE(*auxpow);
} else if (ser_action.ForRead())
auxpow.reset();
}
void SetNull()
{
nVersion = 0;
hashPrevBlock.SetNull();
hashMerkleRoot.SetNull();
CPureBlockHeader::SetNull();
hashClaimTrie.SetNull();
nTime = 0;
nBits = 0;
nNonce = 0;
auxpow.reset();
}
bool IsNull() const
{
return (nBits == 0);
}
uint256 GetHash() const;
uint256 GetPoWHash() const;
int64_t GetBlockTime() const
{
return (int64_t)nTime;
/**
* Set the block's auxpow (or unset it). This takes care of updating
* the version accordingly.
* @param apow Pointer to the auxpow to use or NULL.
*/
void SetAuxpow(CAuxPow* apow);
/**
* Check if the auxpow flag is set in the version.
* @return True iff this block version is marked as auxpow.
*/
inline bool IsAuxpow() const {
return nVersion & VERSION_AUXPOW;
}
/**
* Set the auxpow flag. This is used for testing.
* @param auxpow Whether to mark auxpow as true.
*/
inline void SetAuxpowFlag(bool auxpow) {
if (auxpow)
nVersion |= VERSION_AUXPOW;
else
nVersion &= ~VERSION_AUXPOW;
}
};
class CBlock : public CBlockHeader
{
public:
@ -83,22 +96,20 @@ public:
// memory only
mutable bool fChecked;
CBlock()
CBlock() : fChecked(false)
{
SetNull();
}
CBlock(const CBlockHeader &header)
CBlock(const CBlockHeader &header) : fChecked(false)
{
SetNull();
*(static_cast<CBlockHeader*>(this)) = header;
*((CBlockHeader*)this) = header;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITEAS(CBlockHeader, *this);
READWRITE(*(CBlockHeader*)this);
READWRITE(vtx);
}
@ -119,6 +130,7 @@ public:
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
block.auxpow = auxpow;
return block;
}
@ -158,4 +170,7 @@ struct CBlockLocator
}
};
/** Compute the consensus-critical block weight (see BIP 141). */
int64_t GetBlockWeight(const CBlock& tx);
#endif // BITCOIN_PRIMITIVES_BLOCK_H

View file

@ -0,0 +1,22 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <primitives/pureheader.h>
#include <hash.h>
#include <streams.h>
uint256 CPureBlockHeader::GetHash() const
{
return SerializeHash(*this);
}
uint256 CPureBlockHeader::GetPoWHash() const
{
CDataStream ds(SER_GETHASH, PROTOCOL_VERSION);
ds << *this;
auto once = Hash(ds.begin(), ds.end());
return Hash(once.begin(), once.end());
}

View file

@ -0,0 +1,65 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PRIMITIVES_PUREHEADER_H
#define BITCOIN_PRIMITIVES_PUREHEADER_H
#include <serialize.h>
#include <uint256.h>
/**
* A block header without auxpow information. This "intermediate step"
* in constructing the full header is useful, because it breaks the cyclic
* dependency between auxpow (referencing a parent block header) and
* the block header (referencing an auxpow). The parent block header
* does not have auxpow itself, so it is a pure header.
*/
class CPureBlockHeader { // by "pure" we mean "bitcoin"
public:
// header
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
int32_t nVersion;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
CPureBlockHeader() : nVersion(0), nTime(0), nBits(0), nNonce(0) {
}
ADD_SERIALIZE_METHODS;
template<typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
READWRITE(nVersion);
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
}
void SetNull() {
nVersion = 0;
hashPrevBlock.SetNull();
hashMerkleRoot.SetNull();
nTime = 0;
nBits = 0;
nNonce = 0;
}
bool IsNull() const {
return (nBits == 0);
}
uint256 GetHash() const;
uint256 GetPoWHash() const;
int64_t GetBlockTime() const {
return (int64_t) nTime;
}
};
#endif // BITCOIN_PRIMITIVES_PUREHEADER_H

View file

@ -44,6 +44,9 @@
#include <mutex>
#include <condition_variable>
#include "rawtransaction.h"
struct CUpdatedBlock
{
uint256 hash;
@ -81,6 +84,42 @@ double GetDifficulty(const CBlockIndex* blockindex)
return dDiff;
}
UniValue AuxpowToJSON(const CAuxPow& auxpow)
{
UniValue result(UniValue::VOBJ);
{
UniValue tx(UniValue::VOBJ);
tx.push_back(Pair("hex", EncodeHexTx(auxpow)));
TxToJSON(auxpow, auxpow.parentBlock.GetHash(), tx);
result.push_back(Pair("tx", tx));
}
result.push_back(Pair("index", auxpow.nIndex));
result.push_back(Pair("chainindex", auxpow.nChainIndex));
{
UniValue branch(UniValue::VARR);
for (const uint256& node : auxpow.vMerkleBranch)
branch.push_back(node.GetHex());
result.push_back(Pair("merklebranch", branch));
}
{
UniValue branch(UniValue::VARR);
for (const uint256& node : auxpow.vChainMerkleBranch)
branch.push_back(node.GetHex());
result.push_back(Pair("chainmerklebranch", branch));
}
CDataStream ssParent(SER_NETWORK, PROTOCOL_VERSION);
ssParent << auxpow.parentBlock;
const std::string strHex = HexStr(ssParent.begin(), ssParent.end());
result.push_back(Pair("parentblock", strHex));
return result;
}
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
AssertLockHeld(cs_main);
@ -151,6 +190,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
result.pushKV("nTx", (uint64_t)blockindex->nTx);
if (block.auxpow)
result.push_back(Pair("auxpow", AuxpowToJSON(*block.auxpow)));
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
CBlockIndex *pnext = chainActive.Next(blockindex);
@ -733,7 +775,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
if (!fVerbose)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << pblockindex->GetBlockHeader();
ssBlock << pblockindex->GetBlockHeader(Params().GetConsensus());
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
return strHex;
}

View file

@ -972,6 +972,190 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
return result;
}
/* ************************************************************************** */
/* Merge mining. */
UniValue getauxblockbip22(const JSONRPCRequest& request)
{
if (request.fHelp
|| (request.params.size() != 0 && request.params.size() != 2))
throw std::runtime_error(
"getauxblock (hash auxpow)\n"
"\nCreate or submit a merge-mined block.\n"
"\nWithout arguments, create a new block and return information\n"
"required to merge-mine it. With arguments, submit a solved\n"
"auxpow for a previously returned block.\n"
"\nArguments:\n"
"1. hash (string, optional) hash of the block to submit\n"
"2. auxpow (string, optional) serialised auxpow found\n"
"\nResult (without arguments):\n"
"{\n"
" \"hash\" (string) hash of the created block\n"
" \"chainid\" (numeric) chain ID for this block\n"
" \"previousblockhash\" (string) hash of the previous block\n"
" \"coinbasevalue\" (numeric) value of the block's coinbase\n"
" \"bits\" (string) compressed target of the block\n"
" \"height\" (numeric) height of the block\n"
" \"target\" (string) target in reversed byte order\n"
"}\n"
"\nResult (with arguments):\n"
"xxxxx (boolean) whether the submitted block was correct\n"
"\nExamples:\n"
+ HelpExampleCli("getauxblock", "")
+ HelpExampleCli("getauxblock", "\"hash\" \"serialised auxpow\"")
+ HelpExampleRpc("getauxblock", "")
);
boost::shared_ptr<CReserveScript> coinbaseScript;
GetMainSignals().ScriptForMining(coinbaseScript);
// If the keypool is exhausted, no script is returned at all. Catch this.
if (!coinbaseScript)
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
//throw an error if no script was provided
if (!coinbaseScript->reserveScript.size())
throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)");
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 && !Params().MineBlocksOnDemand())
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Dogecoin is not connected!");
if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
"Dogecoin is downloading blocks...");
/* This should never fail, since the chain is already
past the point of merge-mining start. Check nevertheless. */
{
LOCK(cs_main);
if (chainActive.Height() < Params().GetConsensus().nAuxPoWStartHeight)
throw std::runtime_error("getauxblock method is not yet available");
}
/* The variables below are used to keep track of created and not yet
submitted auxpow blocks. Lock them to be sure even for multiple
RPC threads running in parallel. */
static CCriticalSection cs_auxblockCache;
LOCK(cs_auxblockCache);
static std::map<uint256, CBlock*> mapNewBlock;
static std::vector<std::unique_ptr<CBlockTemplate>> vNewBlockTemplate;
/* Create a new block? */
if (request.params.size() == 0)
{
static unsigned nTransactionsUpdatedLast;
static const CBlockIndex* pindexPrev = nullptr;
static uint64_t nStart;
static CBlock* pblock = nullptr;
static unsigned nExtraNonce = 0;
// Update block
// Dogecoin: Never mine witness tx
const bool fMineWitnessTx = false;
{
LOCK(cs_main);
if (pindexPrev != chainActive.Tip()
|| (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast
&& GetTime() - nStart > 60))
{
if (pindexPrev != chainActive.Tip())
{
// Clear old blocks since they're obsolete now.
mapNewBlock.clear();
vNewBlockTemplate.clear();
pblock = nullptr;
}
// Create new block with nonce = 0 and extraNonce = 1
std::unique_ptr<CBlockTemplate> newBlock(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript, fMineWitnessTx));
if (!newBlock)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory");
// Update state only when CreateNewBlock succeeded
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
pindexPrev = chainActive.Tip();
nStart = GetTime();
// Finalise it by setting the version and building the merkle root
IncrementExtraNonce(&newBlock->block, pindexPrev, nExtraNonce);
newBlock->block.SetAuxpowFlag(true);
// Save
pblock = &newBlock->block;
mapNewBlock[pblock->GetHash()] = pblock;
vNewBlockTemplate.push_back(std::move(newBlock));
}
}
arith_uint256 target;
bool fNegative, fOverflow;
target.SetCompact(pblock->nBits, &fNegative, &fOverflow);
if (fNegative || fOverflow || target == 0)
throw std::runtime_error("invalid difficulty bits in block");
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hash", pblock->GetHash().GetHex()));
result.push_back(Pair("chainid", pblock->GetChainId()));
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue));
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", static_cast<int64_t> (pindexPrev->nHeight + 1)));
result.push_back(Pair("target", HexStr(BEGIN(target), END(target))));
return result;
}
/* Submit a block instead. Note that this need not lock cs_main,
since ProcessNewBlock below locks it instead. */
assert(request.params.size() == 2);
uint256 hash;
hash.SetHex(request.params[0].get_str());
const std::map<uint256, CBlock*>::iterator mit = mapNewBlock.find(hash);
if (mit == mapNewBlock.end())
throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown");
CBlock& block = *mit->second;
const std::vector<unsigned char> vchAuxPow
= ParseHex(request.params[1].get_str());
CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION);
CAuxPow pow;
ss >> pow;
block.SetAuxpow(new CAuxPow(pow));
assert(block.GetHash() == hash);
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
std::shared_ptr<const CBlock> shared_block
= std::make_shared<const CBlock>(block);
bool fAccepted = ProcessNewBlock(Params(), shared_block, true, nullptr);
UnregisterValidationInterface(&sc);
if (fAccepted)
coinbaseScript->KeepScript();
return BIP22ValidationResult(sc.state);
}
UniValue getauxblock(const JSONRPCRequest& request)
{
const UniValue response = getauxblockbip22(request);
// this is a request for a new blocktemplate: return response
if (request.params.size() == 0)
return response;
// this is a new block submission: return bool
return response.isNull();
}
/* ************************************************************************** */
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@ -980,6 +1164,7 @@ static const CRPCCommand commands[] =
{ "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} },
{ "mining", "getblocktemplate", &getblocktemplate, {"template_request"} },
{ "mining", "submitblock", &submitblock, {"hexdata","dummy"} },
{ "mining", "getauxblock", &getauxblock, {"hash", "auxpow"} },
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },

View file

@ -37,7 +37,7 @@
#include <univalue.h>
static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//

View file

@ -15,4 +15,6 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBas
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
#endif // BITCOIN_RPC_RAWTRANSACTION_H

445
src/test/auxpow_tests.cpp Normal file
View file

@ -0,0 +1,445 @@
// Copyright (c) 2014-2015 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "auxpow.h"
#include "chainparams.h"
#include "coins.h"
#include "consensus/merkle.h"
#include "primitives/block.h"
#include "script/script.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include "validation.h"
#include "test/test_bitcoin.h"
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <vector>
BOOST_FIXTURE_TEST_SUITE(auxpow_tests, BasicTestingSetup)
/* ************************************************************************** */
/**
* 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);
}
/**
* 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]);
}
/**
* 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 = CAuxPow::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);
CAuxPow res(tx);
res.InitMerkleBranch(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(), UBEGIN(pchMergedMiningHeader),
UEND(pchMergedMiningHeader));
res.insert(res.end(), auxRoot.begin(), auxRoot.end());
const int size = (1 << h);
res.insert(res.end(), UBEGIN(size), UEND(size));
res.insert(res.end(), UBEGIN(nonce), UEND(nonce));
return res;
}
/* ************************************************************************** */
BOOST_AUTO_TEST_CASE(check_auxpow)
{
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) + COINBASE_FLAGS;
scr = (scr << OP_2 << data);
builder.setCoinbase(scr);
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) + COINBASE_FLAGS;
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) + COINBASE_FLAGS;
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(0)));
else
BOOST_CHECK(!CheckProofOfWork(block.GetPoWHash(), nBits, Params().GetConsensus(0)));
}
BOOST_AUTO_TEST_CASE(auxpow_pow)
{
/* Use regtest parameters to allow mining with easy difficulty. */
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus(371337);
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));
// Dogecoin block version 2 can be both AuxPoW and regular, so test 3
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(new CAuxPow(builder.get()));
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
mineBlock(builder.parentBlock, true, block.nBits);
block.SetAuxpow(new CAuxPow(builder.get()));
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(new CAuxPow(builder.get()));
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(new CAuxPow(builder.get()));
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
tamperWith(block.hashMerkleRoot);
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
}
/* ************************************************************************** */
BOOST_AUTO_TEST_SUITE_END()

View file

@ -71,7 +71,8 @@ std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
while (!CheckProofOfWork(pblock->GetPoWHash(), pblock->nBits, Params().GetConsensus())) {
auto& miningHeader = CAuxPow::initAuxPow(*pblock);
while (!CheckProofOfWork(miningHeader->GetPoWHash(), pblock->nBits, Params().GetConsensus())) {
++(pblock->nNonce);
}

View file

@ -926,6 +926,32 @@ static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMes
return true;
}
bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params)
{
/* If there is no auxpow, just check the block hash. */
if (!block.auxpow) {
if (block.IsAuxpow())
return error("%s : no auxpow on block with auxpow version",
__func__);
if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, params))
return error("%s : non-AUX proof of work failed", __func__);
return true;
}
/* We have auxpow. Check it. */
if (!block.IsAuxpow())
return error("%s : auxpow on block with non-auxpow version", __func__);
if (!block.auxpow->check(block.GetHash(), params))
return error("%s : AUX POW is not valid", __func__);
if (!CheckProofOfWork(block.auxpow->getParentBlockPoWHash(), block.nBits, params))
return error("%s : AUX proof of work failed", __func__);
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
block.SetNull();
@ -944,7 +970,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:
}
// Check the header
if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, consensusParams))
if (!CheckAuxPowProofOfWork(block, consensusParams))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
return true;
@ -1642,6 +1668,8 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para
}
}
TODO: include aux
return nVersion;
}
@ -3101,7 +3129,7 @@ static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos,
static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(), block.nBits, consensusParams))
if (fCheckPOW && !CheckAuxPowProofOfWork(block, consensusParams))
return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
return true;
@ -3257,6 +3285,10 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
if (block.IsAuxpow() && nHeight < consensusParams.nAuxPoWStartHeight)
return state.DoS(100, error("%s : auxpow blocks are not allowed at height %d",
__func__, pindexPrev->nHeight + 1), REJECT_INVALID, "early-auxpow-block");
// Check against checkpoints
if (fCheckpointsEnabled) {
// Don't accept any forks from the main chain prior to last checkpoint.