in progress on auxpow support
This commit is contained in:
parent
be118de19a
commit
8c8c12398e
22 changed files with 1428 additions and 78 deletions
15
.travis.yml
15
.travis.yml
|
@ -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
|
||||
|
|
|
@ -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/])
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
237
src/auxpow.cpp
Normal 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
204
src/auxpow.h
Normal 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
|
|
@ -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;
|
||||
|
|
22
src/chain.h
22
src/chain.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -82,6 +82,8 @@ struct Params {
|
|||
int nMaxTakeoverWorkaroundHeight;
|
||||
|
||||
int nWitnessForkHeight;
|
||||
int nAuxPoWStartHeight;
|
||||
uint32_t nAuxPoWChainID;
|
||||
|
||||
int64_t nPowTargetSpacing;
|
||||
int64_t nPowTargetTimespan;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
22
src/primitives/pureheader.cpp
Normal file
22
src/primitives/pureheader.cpp
Normal 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());
|
||||
}
|
65
src/primitives/pureheader.h
Normal file
65
src/primitives/pureheader.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"} },
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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
445
src/test/auxpow_tests.cpp
Normal 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()
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue