Merge pull request #1546 from rnicoll/1.17-auxpow

1.17 AuxPoW support
This commit is contained in:
Max K 2019-07-14 19:35:30 +02:00 committed by GitHub
commit cee13699a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 2499 additions and 178 deletions

View File

@ -138,6 +138,8 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[
AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists])
if test "x$TARGET_OS" = xwindows; then
_BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows])
_BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal])
+ _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsPrinterSupportPlugin)],[-lwindowsprintersupport])
AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows])
elif test "x$TARGET_OS" = xlinux; then
_BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static])
@ -359,6 +361,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[
if test -d "$qt_plugin_path/accessible"; then
QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible"
fi
QT_LIBS="$QT_LIBS -L$qt_plugin_path/printsupport"
if test "x$use_pkgconfig" = xyes; then
: dnl
m4_ifdef([PKG_CHECK_MODULES],[
@ -428,7 +431,7 @@ dnl Outputs: have_qt_test and have_qt_dbus are set (if applicable) to yes|no.
AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITH_PKGCONFIG],[
m4_ifdef([PKG_CHECK_MODULES],[
QT_LIB_PREFIX=Qt5
qt5_modules="Qt5Core Qt5Gui Qt5Network Qt5Widgets"
qt5_modules="Qt5Core Qt5Gui Qt5Network Qt5Widgets Qt5PrintSupport"
BITCOIN_QT_CHECK([
PKG_CHECK_MODULES([QT5], [$qt5_modules], [QT_INCLUDES="$QT5_CFLAGS"; QT_LIBS="$QT5_LIBS" have_qt=yes],[have_qt=no])
@ -460,7 +463,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[
TEMP_LIBS="$LIBS"
BITCOIN_QT_CHECK([
if test "x$qt_include_path" != x; then
QT_INCLUDES="-I$qt_include_path -I$qt_include_path/QtCore -I$qt_include_path/QtGui -I$qt_include_path/QtWidgets -I$qt_include_path/QtNetwork -I$qt_include_path/QtTest -I$qt_include_path/QtDBus"
QT_INCLUDES="-I$qt_include_path -I$qt_include_path/QtCore -I$qt_include_path/QtGui -I$qt_include_path/QtWidgets -I$qt_include_path/QtNetwork -I$qt_include_path/QtTest -I$qt_include_path/QtDBus -I$qt_include_path/QtPrintSupport"
CPPFLAGS="$QT_INCLUDES $CPPFLAGS"
fi
])
@ -502,6 +505,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[
BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Gui] ,[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Gui not found)))
BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Network],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Network not found)))
BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}Widgets],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}Widgets not found)))
BITCOIN_QT_CHECK(AC_CHECK_LIB([${QT_LIB_PREFIX}PrintSupport],[main],,BITCOIN_QT_FAIL(lib${QT_LIB_PREFIX}PrintSupport not found)))
QT_LIBS="$LIBS"
LIBS="$TEMP_LIBS"

View File

@ -1,8 +1,8 @@
package=bdb
$(package)_version=4.8.30
$(package)_version=5.1.29
$(package)_download_path=http://download.oracle.com/berkeley-db
$(package)_file_name=db-$($(package)_version).NC.tar.gz
$(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef
$(package)_sha256_hash=08238e59736d1aacdd47cfb8e68684c695516c37f4fbe1b8267dde58dc3a576c
$(package)_build_subdir=build_unix
define $(package)_set_vars
@ -13,8 +13,9 @@ $(package)_cxxflags=-std=c++11
endef
define $(package)_preprocess_cmds
sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h && \
sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \
sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' src/dbinc/atomic.h && \
sed -i.old 's/atomic_init/atomic_init_db/' src/dbinc/atomic.h src/mp/mp_region.c src/mp/mp_mvcc.c src/mp/mp_fget.c src/mutex/mut_method.c src/mutex/mut_tas.c && \
sed -i.old 's/WinIoCtl\.h/winioctl\.h/g' src/dbinc/win_db.h && \
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist
endef
@ -23,7 +24,7 @@ define $(package)_config_cmds
endef
define $(package)_build_cmds
$(MAKE) libdb_cxx-4.8.a libdb-4.8.a
$(MAKE) libdb_cxx-5.1.a libdb-5.1.a
endef
define $(package)_stage_cmds

View File

@ -1,8 +1,8 @@
package=openssl
$(package)_version=1.0.1k
$(package)_version=1.0.1l
$(package)_download_path=https://www.openssl.org/source
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=8f9faeaebad088e772f4ef5e38252d472be4d878c6b3a2718c10a4fcebe7a41c
$(package)_sha256_hash=b2cf4d48fe5d49f240c61c9e624193a6f232b5ed0baf010681e725963c40d1d4
define $(package)_set_vars
$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"

View File

@ -7,7 +7,7 @@ $(package)_sha256_hash=eed620cb268b199bd83b3fc6a471c51d51e1dc2dbb5374fc97a0cc75f
$(package)_dependencies=openssl zlib
$(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext
$(package)_build_subdir=qtbase
$(package)_qt_libs=corelib network widgets gui plugins testlib
$(package)_qt_libs=corelib network widgets gui plugins testlib printsupport
$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch fix_rcc_determinism.patch xkb-default.patch
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)

View File

@ -92,6 +92,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
auxpow.h \
base58.h \
bech32.h \
bloom.h \
@ -151,6 +152,7 @@ BITCOIN_CORE_H = \
random.h \
reverse_iterator.h \
reverselock.h \
rpc/auxpow_miner.h \
rpc/blockchain.h \
rpc/client.h \
rpc/mining.h \
@ -241,6 +243,7 @@ libbitcoin_server_a_SOURCES = \
policy/rbf.cpp \
pow.cpp \
rest.cpp \
rpc/auxpow_miner.cpp \
rpc/blockchain.cpp \
rpc/mining.cpp \
rpc/misc.cpp \
@ -353,6 +356,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 \
@ -377,6 +382,7 @@ libbitcoin_consensus_a_SOURCES = \
libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
auxpow.cpp \
base58.cpp \
bech32.cpp \
chainparams.cpp \

View File

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

198
src/auxpow.cpp Normal file
View File

@ -0,0 +1,198 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 Vince Durham
// Copyright (c) 2009-2014 The Bitcoin developers
// Copyright (c) 2014-2017 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 <compat/endian.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <hash.h>
#include <primitives/block.h>
#include <script/script.h>
#include <util.h>
#include <utilstrencodings.h>
#include <algorithm>
#include <cassert>
bool
CAuxPow::check (const uint256& hashAuxBlock, int nChainId,
const Consensus::Params& params) const
{
if (coinbaseTx.nIndex != 0)
return error("AuxPow is not a generate");
if (params.fStrictChainId && parentBlock.GetChainId () == nChainId)
return error("Aux POW parent has our chain ID");
if (vChainMerkleBranch.size() > 30)
return error("Aux POW chain merkle branch too long");
// Check that the chain merkle root is in the coinbase
const uint256 nRootHash
= CheckMerkleBranch (hashAuxBlock, vChainMerkleBranch, nChainIndex);
std::vector<unsigned char> vchRootHash(nRootHash.begin (), nRootHash.end ());
std::reverse (vchRootHash.begin (), vchRootHash.end ()); // correct endian
// Check that we are in the parent block merkle tree
if (CheckMerkleBranch(coinbaseTx.GetHash(), coinbaseTx.vMerkleBranch,
coinbaseTx.nIndex)
!= parentBlock.hashMerkleRoot)
return error("Aux POW merkle root incorrect");
const CScript script = coinbaseTx.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 % (1u << h);
}
uint256
CAuxPow::CheckMerkleBranch (uint256 hash,
const std::vector<uint256>& vMerkleBranch,
int nIndex)
{
if (nIndex == -1)
return uint256 ();
for (std::vector<uint256>::const_iterator it(vMerkleBranch.begin ());
it != vMerkleBranch.end (); ++it)
{
if (nIndex & 1)
hash = Hash (BEGIN (*it), END (*it), BEGIN (hash), END (hash));
else
hash = Hash (BEGIN (hash), END (hash), BEGIN (*it), END (*it));
nIndex >>= 1;
}
return hash;
}
std::unique_ptr<CAuxPow>
CAuxPow::createAuxPow (const CPureBlockHeader& header)
{
assert (header.IsAuxpow ());
/* Build a minimal coinbase script input for merge-mining. */
const uint256 blockHash = header.GetHash ();
std::vector<unsigned char> inputData(blockHash.begin (), blockHash.end ());
std::reverse (inputData.begin (), inputData.end ());
inputData.push_back (1);
inputData.insert (inputData.end (), 7, 0);
/* Fake a parent-block coinbase with just the required input
script and no outputs. */
CMutableTransaction coinbase;
coinbase.vin.resize (1);
coinbase.vin[0].prevout.SetNull ();
coinbase.vin[0].scriptSig = (CScript () << inputData);
assert (coinbase.vout.empty ());
CTransactionRef coinbaseRef = MakeTransactionRef (coinbase);
/* Build a fake parent block with the coinbase. */
CBlock parent;
parent.nVersion = 1;
parent.vtx.resize (1);
parent.vtx[0] = coinbaseRef;
parent.hashMerkleRoot = BlockMerkleRoot (parent);
/* Construct the auxpow object. */
std::unique_ptr<CAuxPow> auxpow(new CAuxPow (coinbaseRef));
assert (auxpow->vChainMerkleBranch.empty ());
auxpow->nChainIndex = 0;
assert (auxpow->coinbaseTx.vMerkleBranch.empty ());
auxpow->coinbaseTx.nIndex = 0;
auxpow->parentBlock = parent;
return auxpow;
}
CPureBlockHeader&
CAuxPow::initAuxPow (CBlockHeader& header)
{
/* Set auxpow flag right now, since we take the block hash below when creating
the minimal auxpow for header. */
header.SetAuxpowFlag(true);
std::unique_ptr<CAuxPow> apow = createAuxPow (header);
CPureBlockHeader& result = apow->parentBlock;
header.SetAuxpow (std::move (apow));
return result;
}

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 <primitives/pureheader.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <uint256.h>
#include <memory>
#include <vector>
class CBlock;
class CBlockHeader;
class CBlockIndex;
class CValidationState;
class UniValue;
namespace auxpow_tests
{
class CAuxPowForTest;
}
/** Header for merge-mining data in the coinbase. */
static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' };
/* 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 CBaseMerkleTx
{
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;
CBaseMerkleTx()
{
SetTx(MakeTransactionRef());
Init();
}
explicit CBaseMerkleTx(CTransactionRef arg)
{
SetTx(std::move(arg));
Init();
}
void Init()
{
hashBlock = uint256();
nIndex = -1;
}
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);
}
const uint256& GetHash() const { return tx->GetHash(); }
};
/**
* Data for the merge-mining auxpow. This uses a merkle tx (the parent block's
* coinbase tx) and a manual merkle branch to link the actual Namecoin block
* header to the parent block header, which is mined to satisfy the PoW.
*/
class CAuxPow
{
private:
/**
* The parent block's coinbase tx, which is used to link the auxpow from
* the tx input to the parent block header.
*/
CBaseMerkleTx coinbaseTx;
/** The merkle branch connecting the aux block to our coinbase. */
std::vector<uint256> vChainMerkleBranch;
/** Merkle tree index of the aux block header in the coinbase. */
int nChainIndex;
/** Parent block header (on which the real PoW is done). */
CPureBlockHeader parentBlock;
/**
* Check a merkle branch. This used to be in CBlock, but was removed
* upstream. Thus include it here now.
*/
static uint256 CheckMerkleBranch (uint256 hash,
const std::vector<uint256>& vMerkleBranch,
int nIndex);
friend UniValue AuxpowToJSON(const CAuxPow& auxpow);
friend class auxpow_tests::CAuxPowForTest;
public:
/* Prevent accidental conversion. */
inline explicit CAuxPow (CTransactionRef txIn)
: coinbaseTx (txIn)
{}
CAuxPow () = default;
ADD_SERIALIZE_METHODS;
template<typename Stream, typename Operation>
inline void
SerializationOp (Stream& s, Operation ser_action)
{
READWRITE (coinbaseTx);
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, int nChainId,
const Consensus::Params& params) const;
/**
* Returns the parent block hash. This is used to validate the PoW.
*/
inline uint256
getParentBlockPoWHash () const
{
return parentBlock.GetPoWHash ();
}
/**
* Return parent block. This is only used for the temporary parentblock
* auxpow version check.
* @return The parent block.
*/
/* FIXME: Remove after the hardfork. */
inline const CPureBlockHeader&
getParentBlock () const
{
return parentBlock;
}
/**
* Calculate the expected index in the merkle tree. This is also used
* for the test-suite.
* @param nNonce The coinbase's nonce value.
* @param nChainId The chain ID.
* @param h The merkle block height.
* @return The expected index for the aux hash.
*/
static int getExpectedIndex (uint32_t nNonce, int nChainId, unsigned h);
/**
* Constructs a minimal CAuxPow object for the given block header and
* returns it. The caller should make sure to set the auxpow flag on the
* header already, since the block hash to which the auxpow commits depends
* on that!
*/
static std::unique_ptr<CAuxPow> createAuxPow (const CPureBlockHeader& header);
/**
* Initialises the auxpow of the given block header. This builds a minimal
* auxpow object like createAuxPow and sets it on the block header. Returns
* a reference to the parent header so it can be mined as a follow-up.
*/
static CPureBlockHeader& initAuxPow (CBlockHeader& header);
};
#endif // BITCOIN_AUXPOW_H

View File

@ -4,6 +4,34 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <validation.h>
using namespace std;
/* Moved here from the header, because we need auxpow and the logic
becomes more involved. */
CBlockHeader CBlockIndex::GetBlockHeader(const Consensus::Params& consensusParams) const
{
CBlockHeader block;
/* 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.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
/**
* CChain implementation

View File

@ -213,9 +213,6 @@ public:
uint32_t nBits;
uint32_t nNonce;
// Dogecoin: Keep the Scrypt hash as well as SHA256
uint256 hashBlockPoW;
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
int32_t nSequenceId;
@ -243,7 +240,6 @@ public:
nTime = 0;
nBits = 0;
nNonce = 0;
hashBlockPoW = uint256();
}
CBlockIndex()
@ -260,7 +256,6 @@ public:
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
hashBlockPoW = block.GetPoWHash();
}
CDiskBlockPos GetBlockPos() const {
@ -281,29 +276,13 @@ public:
return ret;
}
CBlockHeader GetBlockHeader() const
{
CBlockHeader block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
CBlockHeader GetBlockHeader(const Consensus::Params& consensusParams) const;
uint256 GetBlockHash() const
{
return *phashBlock;
}
uint256 GetBlockPoWHash() const
{
return hashBlockPoW;
}
int64_t GetBlockTime() const
{
return (int64_t)nTime;
@ -367,6 +346,13 @@ public:
//! Efficiently find an ancestor of this block.
CBlockIndex* GetAncestor(int height);
const CBlockIndex* GetAncestor(int height) const;
/* Analyse the block version. */
inline int GetBaseVersion() const
{
return CPureBlockHeader::GetBaseVersion(nVersion);
}
};
arith_uint256 GetBlockProof(const CBlockIndex& block);
@ -415,7 +401,6 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
READWRITE(hashBlockPoW);
}
uint256 GetBlockHash() const

View File

@ -109,7 +109,11 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x0000000000000000002e63058c023a9a1de233554f28c7b21380b6c9003f36a8"); //534292
/**
consensus.nAuxpowChainId = 0x0062; // 98 - Josh Wise!
consensus.nAuxpowStartHeight = 371337;
consensus.fStrictChainId = true;
consensus.nLegacyBlocksBefore = 371337;
/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
* a large 32-bit integer with any alignment.
@ -122,6 +126,7 @@ public:
nPruneAfterHeight = 100000;
genesis = CreateGenesisBlock(1386325540, 99943, 0x1e0ffff0, 1, 88 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
assert(consensus.hashGenesisBlock == uint256S("0x1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691"));
assert(genesis.hashMerkleRoot == uint256S("0x5b2a3f53f605d62c53e62932dac6925e3d74afa5a4b459745c36d42d0ed26a69"));
@ -222,6 +227,10 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x0000000000000037a8cd3e06cd5edbfe9dd1dbcc5dacab279376ef7cfc2b4c75"); //1354312
consensus.nAuxpowStartHeight = 158100;
consensus.fStrictChainId = false;
consensus.nLegacyBlocksBefore = -1;
pchMessageStart[0] = 0xfc;
pchMessageStart[1] = 0xc1;
pchMessageStart[2] = 0xb7;
@ -312,6 +321,8 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x00");
consensus.fStrictChainId = true;
consensus.nLegacyBlocksBefore = 0;
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
pchMessageStart[2] = 0xb5;

View File

@ -81,6 +81,7 @@ public:
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
protected:
CChainParams() {}

View File

@ -75,6 +75,24 @@ struct Params {
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
uint256 nMinimumChainWork;
uint256 defaultAssumeValid;
/** Auxpow parameters */
int32_t nAuxpowChainId;
int nAuxpowStartHeight;
bool fStrictChainId;
int nLegacyBlocksBefore; // -1 for "always allow"
/**
* Check whether or not to allow legacy blocks at the given height.
* @param nHeight Height of the block to check.
* @return True if it is allowed to have a legacy version.
*/
bool AllowLegacyBlocks(unsigned nHeight) const
{
if (nLegacyBlocksBefore < 0)
return true;
return static_cast<int> (nHeight) < nLegacyBlocksBefore;
}
};
} // namespace Consensus

View File

@ -5,7 +5,10 @@
#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>
#include "dogecoin.h"
#include <arith_uint256.h>
#include <dogecoin.h>
#include <pow.h>
#include <util.h>
int static generateMTRandom(unsigned int s, int range)
{
@ -14,6 +17,121 @@ int static generateMTRandom(unsigned int s, int range)
return dist(gen);
}
// Dogecoin: Normally minimum difficulty blocks can only occur in between
// retarget blocks. However, once we introduce Digishield every block is
// a retarget, so we need to handle minimum difficulty on all blocks.
bool AllowDigishieldMinDifficultyForBlock(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
// check if the chain allows minimum difficulty blocks
if (!params.fPowAllowMinDifficultyBlocks)
return false;
// check if the chain allows minimum difficulty blocks on recalc blocks
if (pindexLast->nHeight < 157500)
// if (!params.fPowAllowDigishieldMinDifficultyBlocks)
return false;
// Allow for a minimum block time if the elapsed time > 2*nTargetSpacing
return (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2);
}
unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
int nHeight = pindexLast->nHeight + 1;
bool fNewDifficultyProtocol = (nHeight >= 145000);
// bool fNewDifficultyProtocol = (nHeight >= params.GetDigiShieldForkBlock());
const int64_t retargetTimespan = fNewDifficultyProtocol ? 60 // params.DigiShieldTargetTimespan()
:
params.nPowTargetTimespan;
const int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
int64_t nModulatedTimespan = nActualTimespan;
int64_t nMaxTimespan;
int64_t nMinTimespan;
if (fNewDifficultyProtocol) //DigiShield implementation - thanks to RealSolid & WDC for this code
{
// amplitude filter - thanks to daft27 for this code
nModulatedTimespan = retargetTimespan + (nModulatedTimespan - retargetTimespan) / 8;
nMinTimespan = retargetTimespan - (retargetTimespan / 4);
nMaxTimespan = retargetTimespan + (retargetTimespan / 2);
} else if (nHeight > 10000) {
nMinTimespan = retargetTimespan / 4;
nMaxTimespan = retargetTimespan * 4;
} else if (nHeight > 5000) {
nMinTimespan = retargetTimespan / 8;
nMaxTimespan = retargetTimespan * 4;
} else {
nMinTimespan = retargetTimespan / 16;
nMaxTimespan = retargetTimespan * 4;
}
// Limit adjustment step
if (nModulatedTimespan < nMinTimespan)
nModulatedTimespan = nMinTimespan;
else if (nModulatedTimespan > nMaxTimespan)
nModulatedTimespan = nMaxTimespan;
// Retarget
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
arith_uint256 bnNew;
arith_uint256 bnOld;
bnNew.SetCompact(pindexLast->nBits);
bnOld = bnNew;
bnNew *= nModulatedTimespan;
bnNew /= retargetTimespan;
if (bnNew > bnPowLimit)
bnNew = bnPowLimit;
/// debug print
LogPrintf("GetNextWorkRequired RETARGET\n");
LogPrintf("params.nPowTargetTimespan = %d nActualTimespan = %d\n", params.nPowTargetTimespan, nActualTimespan);
LogPrintf("Before: %08x %s\n", pindexLast->nBits, bnOld.ToString());
LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.ToString());
return bnNew.GetCompact();
}
bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params)
{
/* Except for legacy blocks with full version 1, ensure that
the chain ID is correct. Legacy blocks are not allowed since
the merge-mining start, which is checked in AcceptBlockHeader
where the height is known. */
if (!block.IsLegacy() && params.fStrictChainId
&& block.GetChainId() != params.nAuxpowChainId)
return error("%s : block does not have our chain ID"
" (got %d, expected %d, full nVersion %d)",
__func__, block.GetChainId(),
params.nAuxpowChainId, block.nVersion);
/* 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(), block.GetChainId(), 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;
}
CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash)
{
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;

View File

@ -2,7 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chainparams.h"
#include "amount.h"
#include "chain.h"
#include "chainparams.h"
bool AllowDigishieldMinDifficultyForBlock(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params);
CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash);
unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, int64_t nLastRetargetTime, const Consensus::Params& params);
/**
* Check proof-of-work of a block header, taking auxpow into account.
* @param block The block header.
* @param params Consensus parameters.
* @return True iff the PoW is correct.
*/
bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params);

View File

@ -29,6 +29,8 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <rpc/auxpow_miner.h>
#include <rpc/mining.h>
#include <rpc/server.h>
#include <rpc/register.h>
#include <rpc/blockchain.h>
@ -211,6 +213,10 @@ void Shutdown()
if (g_connman) g_connman->Stop();
if (g_txindex) g_txindex->Stop();
if (g_auxpow_miner != nullptr) {
g_auxpow_miner.reset();
}
StopTorControl();
// After everything has been shut down, but before things get flushed, stop the
@ -1281,6 +1287,8 @@ bool AppInitMain()
RegisterZMQRPCCommands(tableRPC);
#endif
g_auxpow_miner.reset(new AuxpowMiner());
/* Start the RPC server already. It will be started in "warmup" mode
* and not really process calls already (but it will signify connections
* that the server is there and will be ready later). Warmup mode will

View File

@ -36,6 +36,7 @@
// its ancestors.
uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;
uint64_t nLastBlockWeight = 0;
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
@ -88,6 +89,7 @@ void BlockAssembler::resetBlock()
inBlock.clear();
// Reserve space for coinbase tx
nBlockSize = 1000;
nBlockWeight = 4000;
nBlockSigOpsCost = 400;
fIncludeWitness = false;
@ -119,11 +121,15 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
assert(pindexPrev != nullptr);
nHeight = pindexPrev->nHeight + 1;
pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
const int32_t nChainId = chainparams.GetConsensus ().nAuxpowChainId;
// FIXME: Active version bits after the always-auxpow fork!
//const int32_t nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
const int32_t nVersion = 4;
pblock->SetBaseVersion(nVersion, nChainId);
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion);
pblock->SetBaseVersion(gArgs.GetArg("-blockversion", pblock->GetBaseVersion()), nChainId);
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
@ -150,6 +156,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
int64_t nTime1 = GetTimeMicros();
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
nLastBlockWeight = nBlockWeight;
// Create coinbase transaction.
@ -165,7 +172,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
pblocktemplate->vTxFees[0] = -nFees;
LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION);
LogPrintf("CreateNewBlock(): total size: %u block weight: %u txs: %u fees: %ld sigops %d\n", nSerializeSize, GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
@ -212,6 +220,8 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
// - transaction finality (locktime)
// - premature witness (in case segwit transactions are added to mempool before
// segwit activation)
// - serialized size (in case -blockmaxsize is in use)
// - Namecoin maturity conditions
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package)
{
for (CTxMemPool::txiter it : package) {

View File

@ -132,10 +132,12 @@ private:
// Configuration parameters for the block size
bool fIncludeWitness;
unsigned int nBlockMaxWeight;
bool fNeedSizeAccounting;
CFeeRate blockMinFeeRate;
// Information on the current status of the block
uint64_t nBlockWeight;
uint64_t nBlockSize;
uint64_t nBlockTx;
uint64_t nBlockSigOpsCost;
CAmount nFees;
@ -196,5 +198,6 @@ private:
/** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainParams);
#endif // BITCOIN_MINER_H

View File

@ -2180,7 +2180,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId());
for (; pindex; pindex = chainActive.Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
vHeaders.push_back(pindex->GetBlockHeader(chainparams.GetConsensus()));
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
@ -3423,14 +3423,14 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pBestIndex = pindex;
if (fFoundStartingHeader) {
// add this to the headers message
vHeaders.push_back(pindex->GetBlockHeader());
vHeaders.push_back(pindex->GetBlockHeader(consensusParams));
} else if (PeerHasHeader(&state, pindex)) {
continue; // keep looking for the first new block
} else if (pindex->pprev == nullptr || PeerHasHeader(&state, pindex->pprev)) {
// Peer doesn't have this header but they do have the prior one.
// Start sending headers.
fFoundStartingHeader = true;
vHeaders.push_back(pindex->GetBlockHeader());
vHeaders.push_back(pindex->GetBlockHeader(consensusParams));
} else {
// Peer doesn't have this header or the prior one -- nothing will
// connect, so bail out.

View File

@ -16,8 +16,10 @@
class CCoinsViewCache;
class CTxOut;
/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = 3000000;
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000;
/** The maximum weight for transactions we're willing to relay/mine */

View File

@ -5,8 +5,10 @@
#include <pow.h>
#include <auxpow.h>
#include <arith_uint256.h>
#include <chain.h>
#include <dogecoin.h>
#include <primitives/block.h>
#include <uint256.h>
#include <util.h>
@ -16,8 +18,21 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
assert(pindexLast != nullptr);
unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();
// Dogecoin: Special rules for minimum difficulty blocks with Digishield
if (AllowDigishieldMinDifficultyForBlock(pindexLast, pblock, params))
{
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* nTargetSpacing minutes
// then allow mining of a min-difficulty block.
return nProofOfWorkLimit;
}
// Only change once per difficulty adjustment interval
if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0)
bool fNewDifficultyProtocol = (pindexLast->nHeight >= 145000);
const int64_t difficultyAdjustmentInterval = fNewDifficultyProtocol
? 1
: params.DifficultyAdjustmentInterval();
if ((pindexLast->nHeight+1) % difficultyAdjustmentInterval != 0)
{
if (params.fPowAllowMinDifficultyBlocks)
{
@ -40,9 +55,9 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
// Litecoin: This fixes an issue where a 51% attack can change difficulty at will.
// Go back the full period unless it's the first retarget after genesis. Code courtesy of Art Forz
int blockstogoback = params.DifficultyAdjustmentInterval()-1;
if ((pindexLast->nHeight+1) != params.DifficultyAdjustmentInterval())
blockstogoback = params.DifficultyAdjustmentInterval();
int blockstogoback = difficultyAdjustmentInterval-1;
if ((pindexLast->nHeight+1) != difficultyAdjustmentInterval)
blockstogoback = difficultyAdjustmentInterval;
// Go back by what we want to be 14 days worth of blocks
int nHeightFirst = pindexLast->nHeight - blockstogoback;
@ -50,7 +65,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
assert(pindexFirst);
return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
return CalculateDogecoinNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params);
}
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)

View File

@ -6,21 +6,21 @@
#include <primitives/block.h>
#include <hash.h>
#include <crypto/scrypt.h>
#include <tinyformat.h>
#include <utilstrencodings.h>
#include <crypto/common.h>
uint256 CBlockHeader::GetHash() const
void CBlockHeader::SetAuxpow (std::unique_ptr<CAuxPow> apow)
{
return SerializeHash(*this);
}
uint256 CBlockHeader::GetPoWHash() const
{
uint256 thash;
scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash));
return thash;
if (apow != nullptr)
{
auxpow.reset(apow.release());
SetAuxpowFlag(true);
} else
{
auxpow.reset();
SetAuxpowFlag(false);
}
}
std::string CBlock::ToString() const

View File

@ -6,10 +6,14 @@
#ifndef BITCOIN_PRIMITIVES_BLOCK_H
#define BITCOIN_PRIMITIVES_BLOCK_H
#include <auxpow.h>
#include <primitives/transaction.h>
#include <primitives/pureheader.h>
#include <serialize.h>
#include <uint256.h>
#include <memory>
/** Nodes collect new transactions into a block, hash them into a hash tree,
* and scan through nonce values to make the block's hash satisfy proof-of-work
* requirements. When they solve the proof-of-work, they broadcast the block
@ -17,16 +21,12 @@
* 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;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
// auxpow (if this is a merge-minded block)
std::shared_ptr<CAuxPow> auxpow;
CBlockHeader()
{
@ -37,37 +37,29 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
READWRITE(*(CPureBlockHeader*)this);
if (this->IsAuxpow())
{
if (ser_action.ForRead())
auxpow = std::make_shared<CAuxPow>();
assert(auxpow != nullptr);
READWRITE(*auxpow);
} else if (ser_action.ForRead())
auxpow.reset();
}
void SetNull()
{
nVersion = 0;
hashPrevBlock.SetNull();
hashMerkleRoot.SetNull();
nTime = 0;
nBits = 0;
nNonce = 0;
CPureBlockHeader::SetNull();
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.
*/
void SetAuxpow (std::unique_ptr<CAuxPow> apow);
};
@ -115,6 +107,7 @@ public:
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
block.auxpow = auxpow;
return block;
}

View File

@ -0,0 +1,29 @@
// 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 "crypto/scrypt.h"
#include "hash.h"
#include "utilstrencodings.h"
void CPureBlockHeader::SetBaseVersion(int32_t nBaseVersion, int32_t nChainId)
{
assert(nBaseVersion >= 1 && nBaseVersion < VERSION_AUXPOW);
assert(!IsAuxpow());
nVersion = nBaseVersion | (nChainId * VERSION_CHAIN_START);
}
uint256 CPureBlockHeader::GetHash() const
{
return SerializeHash(*this);
}
uint256 CPureBlockHeader::GetPoWHash() const
{
uint256 thash;
scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash));
return thash;
}

157
src/primitives/pureheader.h Normal file
View File

@ -0,0 +1,157 @@
// 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
{
public:
/* Modifiers to the version. */
static const int32_t VERSION_AUXPOW = (1 << 8);
/** Bits above are reserved for the auxpow chain ID. */
static const int32_t VERSION_CHAIN_START = (1 << 16);
// header
int32_t nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
CPureBlockHeader()
{
SetNull();
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->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;
}
/* Below are methods to interpret the version with respect to
auxpow data and chain ID. This used to be in the CBlockVersion
class, but was moved here when we switched back to nVersion being
a pure int member as preparation to undoing the "abuse" and
allowing BIP9 to work. */
/**
* Extract the base version (without modifiers and chain ID).
* @return The base version./
*/
inline int32_t GetBaseVersion() const
{
return GetBaseVersion(nVersion);
}
static inline int32_t GetBaseVersion(int32_t ver)
{
return ver % VERSION_AUXPOW;
}
/**
* Set the base version (apart from chain ID and auxpow flag) to
* the one given. This should only be called when auxpow is not yet
* set, to initialise a block!
* @param nBaseVersion The base version.
* @param nChainId The auxpow chain ID.
*/
void SetBaseVersion(int32_t nBaseVersion, int32_t nChainId);
/**
* Extract the chain ID.
* @return The chain ID encoded in the version.
*/
inline int32_t GetChainId() const
{
return nVersion >> 16;
}
/**
* Set the chain ID. This is used for the test suite.
* @param ch The chain ID to set.
*/
inline void SetChainId(int32_t chainId)
{
nVersion %= VERSION_CHAIN_START;
nVersion |= chainId * VERSION_CHAIN_START;
}
/**
* 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;
}
/**
* Check whether this is a "legacy" block without chain ID.
* @return True iff it is.
*/
inline bool IsLegacy() const
{
return nVersion == 1
// Dogecoin: We have a random v2 block with no AuxPoW, treat as legacy
|| (nVersion == 2 && GetChainId() == 0);
}
};
#endif // BITCOIN_PRIMITIVES_PUREHEADER_H

View File

@ -56,7 +56,7 @@ void EditAddressAndSubmit(
*/
void TestAddAddressesToSendBook()
{
TestChain100Setup test;
TestChain240Setup test;
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("mock", WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);

View File

@ -128,7 +128,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st
// src/qt/test/test_bitcoin-qt -platform cocoa # macOS
void TestGUI()
{
// Set up wallet and chain with 105 blocks (5 mature blocks for spending).
// Set up wallet and chain with 245 blocks (5 mature blocks for spending).
TestChain240Setup test;
for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));

View File

@ -157,9 +157,10 @@ static bool rest_headers(HTTPRequest* req,
}
}
const CChainParams& chainparams = Params();
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
for (const CBlockIndex *pindex : headers) {
ssHeader << pindex->GetBlockHeader();
ssHeader << pindex->GetBlockHeader(chainparams.GetConsensus());
}
switch (rf) {

165
src/rpc/auxpow_miner.cpp Normal file
View File

@ -0,0 +1,165 @@
// Copyright (c) 2018 Daniel Kraft
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <rpc/auxpow_miner.h>
#include <arith_uint256.h>
#include <auxpow.h>
#include <chainparams.h>
#include <net.h>
#include <rpc/protocol.h>
#include <utilstrencodings.h>
#include <utiltime.h>
#include <validation.h>
#include <cassert>
namespace
{
void auxMiningCheck()
{
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);
const auto auxpowStart = Params ().GetConsensus ().nAuxpowStartHeight;
if (chainActive.Height () + 1 < auxpowStart)
throw std::runtime_error ("mining auxblock method is not yet available");
}
}
} // anonymous namespace
const CBlock*
AuxpowMiner::getCurrentBlock (const CScript& scriptPubKey, uint256& target)
{
AssertLockHeld (cs);
{
LOCK (cs_main);
if (pindexPrev != chainActive.Tip ()
|| (mempool.GetTransactionsUpdated () != txUpdatedLast
&& GetTime () - startTime > 60))
{
if (pindexPrev != chainActive.Tip ())
{
/* Clear old blocks since they're obsolete now. */
blocks.clear ();
templates.clear ();
pblockCur = nullptr;
}
/* Create new block with nonce = 0 and extraNonce = 1. */
std::unique_ptr<CBlockTemplate> newBlock
= BlockAssembler (Params ()).CreateNewBlock (scriptPubKey);
if (newBlock == nullptr)
throw JSONRPCError (RPC_OUT_OF_MEMORY, "out of memory");
/* Update state only when CreateNewBlock succeeded. */
txUpdatedLast = mempool.GetTransactionsUpdated ();
pindexPrev = chainActive.Tip ();
startTime = GetTime ();
/* Finalise it by setting the version and building the merkle root. */
IncrementExtraNonce (&newBlock->block, pindexPrev, extraNonce);
newBlock->block.SetAuxpowFlag (true);
/* Save in our map of constructed blocks. */
pblockCur = &newBlock->block;
blocks[pblockCur->GetHash ()] = pblockCur;
templates.push_back (std::move (newBlock));
}
}
/* At this point, pblockCur is always initialised: If we make it here
without creating a new block above, it means that, in particular,
pindexPrev == chainActive.Tip(). But for that to happen, we must
already have created a pblockCur in a previous call, as pindexPrev is
initialised only when pblockCur is. */
assert (pblockCur);
arith_uint256 arithTarget;
bool fNegative, fOverflow;
arithTarget.SetCompact (pblockCur->nBits, &fNegative, &fOverflow);
if (fNegative || fOverflow || arithTarget == 0)
throw std::runtime_error ("invalid difficulty bits in block");
target = ArithToUint256 (arithTarget);
return pblockCur;
}
const CBlock*
AuxpowMiner::lookupSavedBlock (const std::string& hashHex) const
{
AssertLockHeld (cs);
uint256 hash;
hash.SetHex (hashHex);
const auto iter = blocks.find (hash);
if (iter == blocks.end ())
throw JSONRPCError (RPC_INVALID_PARAMETER, "block hash unknown");
return iter->second;
}
UniValue
AuxpowMiner::createAuxBlock (const CScript& scriptPubKey)
{
auxMiningCheck ();
LOCK (cs);
uint256 target;
const CBlock* pblock = getCurrentBlock (scriptPubKey, target);
UniValue result(UniValue::VOBJ);
result.pushKV ("hash", pblock->GetHash ().GetHex ());
result.pushKV ("chainid", pblock->GetChainId ());
result.pushKV ("previousblockhash", pblock->hashPrevBlock.GetHex ());
result.pushKV ("coinbasevalue",
static_cast<int64_t> (pblock->vtx[0]->vout[0].nValue));
result.pushKV ("bits", strprintf ("%08x", pblock->nBits));
result.pushKV ("height", static_cast<int64_t> (pindexPrev->nHeight + 1));
result.pushKV ("target", HexStr (BEGIN (target), END (target)));
return result;
}
bool
AuxpowMiner::submitAuxBlock (const std::string& hashHex,
const std::string& auxpowHex) const
{
auxMiningCheck ();
std::shared_ptr<CBlock> shared_block;
{
LOCK (cs);
const CBlock* pblock = lookupSavedBlock (hashHex);
shared_block = std::make_shared<CBlock> (*pblock);
}
const std::vector<unsigned char> vchAuxPow = ParseHex (auxpowHex);
CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION);
std::unique_ptr<CAuxPow> pow(new CAuxPow ());
ss >> *pow;
shared_block->SetAuxpow (std::move (pow));
assert (shared_block->GetHash ().GetHex () == hashHex);
return ProcessNewBlock (Params (), shared_block, true, nullptr);
}

95
src/rpc/auxpow_miner.h Normal file
View File

@ -0,0 +1,95 @@
// Copyright (c) 2018 Daniel Kraft
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_RPC_AUXPOW_MINER_H
#define BITCOIN_RPC_AUXPOW_MINER_H
#include <miner.h>
#include <script/script.h>
#include <sync.h>
#include <uint256.h>
#include <univalue.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace auxpow_tests
{
class AuxpowMinerForTest;
}
/**
* This class holds "global" state used to construct blocks for the auxpow
* mining RPCs and the map of already constructed blocks to look them up
* in the submitauxblock RPC.
*
* It is used as a singleton that is initialised during startup, taking the
* place of the previously real global and static variables.
*/
class AuxpowMiner
{
private:
/** The lock used for state in this object. */
mutable CCriticalSection cs;
/** All currently "active" block templates. */
std::vector<std::unique_ptr<CBlockTemplate>> templates;
/** Maps block hashes to pointers in vTemplates. Does not own the memory. */
std::map<uint256, const CBlock*> blocks;
/**
* The block we are "currently" working on. This does not own the memory,
* instead, it points into an element of templates.
*/
CBlock* pblockCur = nullptr;
/** The current extra nonce for block creation. */
unsigned extraNonce = 0;
/* Some data about when the current block (pblock) was constructed. */
unsigned txUpdatedLast;
const CBlockIndex* pindexPrev = nullptr;
uint64_t startTime;
/**
* Constructs a new current block if necessary (checking the current state to
* see if "enough changed" for this), and returns a pointer to the block
* that should be returned to a miner for working on at the moment. Also
* fills in the difficulty target value.
*/
const CBlock* getCurrentBlock (const CScript& scriptPubKey, uint256& target);
/**
* Looks up a previously constructed block by its (hex-encoded) hash. If the
* block is found, it is returned. Otherwise, a JSONRPCError is thrown.
*/
const CBlock* lookupSavedBlock (const std::string& hashHex) const;
friend class auxpow_tests::AuxpowMinerForTest;
public:
AuxpowMiner () = default;
/**
* Performs the main work for the "createauxblock" RPC: Construct a new block
* to work on with the given address for the block reward and return the
* necessary information for the miner to construct an auxpow for it.
*/
UniValue createAuxBlock (const CScript& scriptPubKey);
/**
* Performs the main work for the "submitauxblock" RPC: Look up the block
* previously created for the given hash, attach the given auxpow to it
* and try to submit it. Returns true if all was successful and the block
* was accepted.
*/
bool submitAuxBlock (const std::string& hashHex,
const std::string& auxpowHex) const;
};
#endif // BITCOIN_RPC_AUXPOW_MINER_H

View File

@ -80,6 +80,42 @@ double GetDifficulty(const CBlockIndex* blockindex)
return dDiff;
}
UniValue AuxpowToJSON(const CAuxPow& auxpow)
{
UniValue result(UniValue::VOBJ);
{
UniValue tx(UniValue::VOBJ);
tx.pushKV("hex", EncodeHexTx(*auxpow.coinbaseTx.tx));
TxToUniv(*auxpow.coinbaseTx.tx, auxpow.parentBlock.GetHash(), tx);
result.pushKV("tx", tx);
}
result.pushKV("index", auxpow.coinbaseTx.nIndex);
result.pushKV("chainindex", auxpow.nChainIndex);
{
UniValue branch(UniValue::VARR);
for (const auto& node : auxpow.coinbaseTx.vMerkleBranch)
branch.push_back(node.GetHex());
result.pushKV("merklebranch", branch);
}
{
UniValue branch(UniValue::VARR);
for (const auto& node : auxpow.vChainMerkleBranch)
branch.push_back(node.GetHex());
result.pushKV("chainmerklebranch", branch);
}
CDataStream ssParent(SER_NETWORK, PROTOCOL_VERSION);
ssParent << auxpow.parentBlock;
const std::string strHex = HexStr(ssParent.begin(), ssParent.end());
result.pushKV("parentblock", strHex);
return result;
}
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
AssertLockHeld(cs_main);
@ -148,6 +184,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.pushKV("auxpow", AuxpowToJSON(*block.auxpow));
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
CBlockIndex *pnext = chainActive.Next(blockindex);
@ -729,7 +768,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;
}
@ -1270,7 +1309,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
for (int pos = Consensus::DEPLOYMENT_TESTDUMMY + 1; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos));
}
obj.pushKV("softforks", softforks);

View File

@ -16,6 +16,7 @@
#include <net.h>
#include <policy/fees.h>
#include <pow.h>
#include <rpc/auxpow_miner.h>
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/server.h>
@ -26,8 +27,9 @@
#include <validationinterface.h>
#include <warnings.h>
#include <memory>
#include <stdint.h>
#include <string>
#include <utility>
unsigned int ParseConfirmTarget(const UniValue& value)
{
@ -126,14 +128,15 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
LOCK(cs_main);
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetPoWHash(), pblock->nBits, Params().GetConsensus())) {
++pblock->nNonce;
auto& miningHeader = CAuxPow::initAuxPow(*pblock);
while (nMaxTries > 0 && miningHeader.nNonce < nInnerLoopCount && !CheckProofOfWork(miningHeader.GetHash(), pblock->nBits, Params().GetConsensus())) {
++miningHeader.nNonce;
--nMaxTries;
}
if (nMaxTries == 0) {
break;
}
if (pblock->nNonce == nInnerLoopCount) {
if (miningHeader.nNonce == nInnerLoopCount) {
continue;
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
@ -489,11 +492,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
}
const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT];
// If the caller is indicating segwit support, then allow CreateNewBlock()
// to select witness transactions, after segwit activates (otherwise
// don't).
bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end();
bool fSupportsSegwit = setClientRules.find("segwit") != setClientRules.end();
// Update block
static CBlockIndex* pindexPrev;
@ -533,7 +535,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
pblock->nNonce = 0;
// NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration
const bool fPreSegWit = (ThresholdState::ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache));
const bool fPreSegWit = !IsWitnessEnabled(pindexPrev, consensusParams);
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
@ -932,6 +934,68 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
return result;
}
/* ************************************************************************** */
/* Merge mining. */
std::unique_ptr<AuxpowMiner> g_auxpow_miner;
UniValue createauxblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"createauxblock <address>\n"
"\ncreate a new block and return information required to merge-mine it.\n"
"\nArguments:\n"
"1. address (string, required) specify coinbase transaction payout address\n"
"\nResult:\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"
"\nExamples:\n"
+ HelpExampleCli("createauxblock", "\"address\"")
+ HelpExampleRpc("createauxblock", "\"address\"")
);
// Check coinbase payout address
const CTxDestination coinbaseScript
= DecodeDestination(request.params[0].get_str());
if (!IsValidDestination(coinbaseScript)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Error: Invalid coinbase payout address");
}
const CScript scriptPubKey = GetScriptForDestination(coinbaseScript);
return g_auxpow_miner->createAuxBlock(scriptPubKey);
}
UniValue submitauxblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
"submitauxblock <hash> <auxpow>\n"
"\nsubmit a solved auxpow for a previously block created by 'createauxblock'.\n"
"\nArguments:\n"
"1. hash (string, required) hash of the block to submit\n"
"2. auxpow (string, required) serialised auxpow found\n"
"\nResult:\n"
"xxxxx (boolean) whether the submitted block was correct\n"
"\nExamples:\n"
+ HelpExampleCli("submitauxblock", "\"hash\" \"serialised auxpow\"")
+ HelpExampleRpc("submitauxblock", "\"hash\" \"serialised auxpow\"")
);
return g_auxpow_miner->submitAuxBlock(request.params[0].get_str(),
request.params[1].get_str());
}
/* ************************************************************************** */
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@ -940,7 +1004,8 @@ static const CRPCCommand commands[] =
{ "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} },
{ "mining", "getblocktemplate", &getblocktemplate, {"template_request"} },
{ "mining", "submitblock", &submitblock, {"hexdata","dummy"} },
{ "mining", "createauxblock", &createauxblock, {"address"} },
{ "mining", "submitauxblock", &submitauxblock, {"hash", "auxpow"} },
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },

View File

@ -9,10 +9,17 @@
#include <univalue.h>
#include <memory>
class AuxpowMiner;
/** Generate blocks (mine) */
UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript);
/** Check bounds on a command line confirm target */
unsigned int ParseConfirmTarget(const UniValue& value);
/** Singleton instance of the AuxpowMiner, created during startup. */
extern std::unique_ptr<AuxpowMiner> g_auxpow_miner;
#endif

591
src/test/auxpow_tests.cpp Normal file
View File

@ -0,0 +1,591 @@
// Copyright (c) 2014-2018 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <arith_uint256.h>
#include <auxpow.h>
#include <chainparams.h>
#include <coins.h>
#include <consensus/merkle.h>
#include <dogecoin.h>
#include <validation.h>
#include <pow.h>
#include <primitives/block.h>
#include <rpc/auxpow_miner.h>
#include <script/script.h>
#include <utilstrencodings.h>
#include <utiltime.h>
#include <uint256.h>
#include <univalue.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <vector>
/* No space between BOOST_AUTO_TEST_SUITE and '(', so that extraction of
the test-suite name works with grep as done in the Makefile. */
BOOST_AUTO_TEST_SUITE(auxpow_tests)
/* ************************************************************************** */
/**
* Tamper with a uint256 (modify it).
* @param num The number to modify.
*/
static void
tamperWith(uint256& num)
{
arith_uint256 modifiable = UintToArith256(num);
modifiable += 1;
num = ArithToUint256(modifiable);
}
/**
* Helper class that is friend to CAuxPow and makes the internals accessible
* to the test code.
*/
class CAuxPowForTest : public CAuxPow
{
public:
explicit inline CAuxPowForTest (CTransactionRef txIn)
: CAuxPow (txIn)
{}
using CAuxPow::coinbaseTx;
using CAuxPow::vChainMerkleBranch;
using CAuxPow::nChainIndex;
using CAuxPow::parentBlock;
using CAuxPow::CheckMerkleBranch;
};
/**
* Utility class to construct auxpow's and manipulate them. This is used
* to simulate various scenarios.
*/
class CAuxpowBuilder
{
public:
/** The parent block (with coinbase, not just header). */
CBlock parentBlock;
/** The auxpow's merkle branch (connecting it to the coinbase). */
std::vector<uint256> auxpowChainMerkleBranch;
/** The auxpow's merkle tree index. */
int auxpowChainIndex;
/**
* Initialise everything.
* @param baseVersion The parent block's base version to use.
* @param chainId The parent block's chain ID to use.
*/
CAuxpowBuilder(int baseVersion, int chainId);
/**
* Set the coinbase's script.
* @param scr Set it to this script.
*/
void setCoinbase(const CScript& scr);
/**
* Build the auxpow merkle branch. The member variables will be
* set accordingly. This has to be done before constructing the coinbase
* itself (which must contain the root merkle hash). When we have the
* coinbase afterwards, the member variables can be used to initialise
* the CAuxPow object from it.
* @param hashAux The merge-mined chain's block hash.
* @param h Height of the merkle tree to build.
* @param index Index to use in the merkle tree.
* @return The root hash, with reversed endian.
*/
std::vector<unsigned char> buildAuxpowChain(const uint256& hashAux, unsigned h, int index);
/**
* Build the finished CAuxPow object. We assume that the auxpowChain
* member variables are already set. We use the passed in transaction
* as the base. It should (probably) be the parent block's coinbase.
* @param tx The base tx to use.
* @return The constructed CAuxPow object.
*/
CAuxPow get(const CTransactionRef tx) const;
/**
* Build the finished CAuxPow object from the parent block's coinbase.
* @return The constructed CAuxPow object.
*/
inline CAuxPow
get() const
{
assert(!parentBlock.vtx.empty());
return get(parentBlock.vtx[0]);
}
/**
* Returns the finished CAuxPow object and returns it as std::unique_ptr.
*/
inline std::unique_ptr<CAuxPow>
getUnique () const
{
return std::unique_ptr<CAuxPow>(new CAuxPow (get ()));
}
/**
* Build a data vector to be included in the coinbase. It consists
* of the aux hash, the merkle tree size and the nonce. Optionally,
* the header can be added as well.
* @param header Add the header?
* @param hashAux The aux merkle root hash.
* @param h Height of the merkle tree.
* @param nonce The nonce value to use.
* @return The constructed data.
*/
static std::vector<unsigned char> buildCoinbaseData (bool header, const std::vector<unsigned char>& auxRoot,
unsigned h, int nonce);
};
CAuxpowBuilder::CAuxpowBuilder(int baseVersion, int chainId)
: auxpowChainIndex(-1)
{
parentBlock.SetBaseVersion(baseVersion, chainId);
}
void
CAuxpowBuilder::setCoinbase (const CScript& scr)
{
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = scr;
parentBlock.vtx.clear();
parentBlock.vtx.push_back(MakeTransactionRef(std::move(mtx)));
parentBlock.hashMerkleRoot = BlockMerkleRoot(parentBlock);
}
std::vector<unsigned char>
CAuxpowBuilder::buildAuxpowChain(const uint256& hashAux, unsigned h, int index)
{
auxpowChainIndex = index;
/* Just use "something" for the branch. Doesn't really matter. */
auxpowChainMerkleBranch.clear();
for (unsigned i = 0; i < h; ++i)
auxpowChainMerkleBranch.push_back(ArithToUint256(arith_uint256(i)));
const uint256 hash
= CAuxPowForTest::CheckMerkleBranch (hashAux, auxpowChainMerkleBranch,
index);
std::vector<unsigned char> res = ToByteVector(hash);
std::reverse(res.begin(), res.end());
return res;
}
CAuxPow
CAuxpowBuilder::get(const CTransactionRef tx) const
{
LOCK(cs_main);
CAuxPowForTest res(tx);
res.coinbaseTx.hashBlock = parentBlock.GetHash ();
res.coinbaseTx.nIndex = 0;
res.coinbaseTx.vMerkleBranch
= merkle_tests::BlockMerkleBranch (parentBlock, res.coinbaseTx.nIndex);
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_FIXTURE_TEST_CASE (check_auxpow, BasicTestingSetup)
{
const Consensus::Params& params = Params().GetConsensus();
CAuxpowBuilder builder(5, 42);
CAuxPow auxpow;
const uint256 hashAux = ArithToUint256(arith_uint256(12345));
const int32_t ourChainId = params.nAuxpowChainId;
const unsigned height = 30;
const int nonce = 7;
int index;
std::vector<unsigned char> auxRoot, data;
CScript scr;
/* Build a correct auxpow. The height is the maximally allowed one. */
index = CAuxPow::getExpectedIndex(nonce, ourChainId, height);
auxRoot = builder.buildAuxpowChain(hashAux, height, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
scr = (CScript() << 2809 << 2013) + 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.GetHash()) <= target);
if ((ok && nowOk) || (!ok && !nowOk))
break;
++block.nNonce;
}
if (ok)
BOOST_CHECK(CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus()));
else
BOOST_CHECK(!CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus()));
}
BOOST_FIXTURE_TEST_CASE (auxpow_pow, BasicTestingSetup)
{
/* Use regtest parameters to allow mining with easy difficulty. */
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
const arith_uint256 target = (~arith_uint256(0) >> 1);
CBlockHeader block;
block.nBits = target.GetCompact();
/* Verify the block version checks. */
block.nVersion = 1;
mineBlock(block, true);
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
// 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 (builder.getUnique ());
BOOST_CHECK (!CheckAuxPowProofOfWork (block, params));
mineBlock(builder.parentBlock, true, block.nBits);
block.SetAuxpow (builder.getUnique ());
BOOST_CHECK (CheckAuxPowProofOfWork (block, params));
/* Mismatch between auxpow being present and block.nVersion. Note that
block.SetAuxpow sets also the version and that we want to ensure
that the block hash itself doesn't change due to version changes.
This requires some work arounds. */
block.SetAuxpowFlag(false);
const uint256 hashAux = block.GetHash();
auxRoot = builder.buildAuxpowChain(hashAux, height, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder.setCoinbase(CScript() << data);
mineBlock(builder.parentBlock, true, block.nBits);
block.SetAuxpow (builder.getUnique ());
BOOST_CHECK(hashAux != block.GetHash());
block.SetAuxpowFlag(false);
BOOST_CHECK(hashAux == block.GetHash());
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
/* Modifying the block invalidates the PoW. */
block.SetAuxpowFlag(true);
auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder.setCoinbase(CScript() << data);
mineBlock(builder.parentBlock, true, block.nBits);
block.SetAuxpow (builder.getUnique ());
BOOST_CHECK (CheckAuxPowProofOfWork (block, params));
tamperWith(block.hashMerkleRoot);
BOOST_CHECK (!CheckAuxPowProofOfWork (block, params));
}
/* ************************************************************************** */
/**
* Helper class that is friend to AuxpowMiner and makes the tested methods
* accessible to the test code.
*/
class AuxpowMinerForTest : public AuxpowMiner
{
public:
using AuxpowMiner::cs;
using AuxpowMiner::getCurrentBlock;
using AuxpowMiner::lookupSavedBlock;
};
BOOST_FIXTURE_TEST_CASE (auxpow_miner_blockRegeneration, TestChain240Setup)
{
AuxpowMinerForTest miner;
LOCK (miner.cs);
/* We use mocktime so that we can control GetTime() as it is used in the
logic that determines whether or not to reconstruct a block. The "base"
time is set such that the blocks we have from the fixture are fresh. */
const int64_t baseTime = chainActive.Tip ()->GetMedianTimePast () + 1;
SetMockTime (baseTime);
/* Construct a first block. */
CScript scriptPubKey;
uint256 target;
const CBlock* pblock1 = miner.getCurrentBlock (scriptPubKey, target);
BOOST_CHECK (pblock1 != nullptr);
const uint256 hash1 = pblock1->GetHash ();
/* Verify target computation. */
arith_uint256 expected;
expected.SetCompact (pblock1->nBits);
BOOST_CHECK (target == ArithToUint256 (expected));
/* Calling the method again should return the same, cached block a second
time (even if we advance the clock, since there are no new
transactions). */
SetMockTime (baseTime + 240);
const CBlock* pblock = miner.getCurrentBlock (scriptPubKey, target);
BOOST_CHECK (pblock == pblock1 && pblock->GetHash () == hash1);
/* Mine a block, then we should get a new auxpow block constructed. Note that
it can be the same *pointer* if the memory was reused after clearing it,
so we can only verify that the hash is different. */
CreateAndProcessBlock ({}, scriptPubKey);
const CBlock* pblock2 = miner.getCurrentBlock (scriptPubKey, target);
BOOST_CHECK (pblock2 != nullptr);
const uint256 hash2 = pblock2->GetHash ();
BOOST_CHECK (hash2 != hash1);
/* Add a new transaction to the mempool. */
TestMemPoolEntryHelper entry;
CMutableTransaction mtx;
mtx.vout.emplace_back (1234, scriptPubKey);
{
LOCK (mempool.cs);
mempool.addUnchecked (mtx.GetHash (), entry.FromTx (mtx));
}
/* We should still get back the cached block, for now. */
SetMockTime (baseTime + 300);
pblock = miner.getCurrentBlock (scriptPubKey, target);
BOOST_CHECK (pblock == pblock2 && pblock->GetHash () == hash2);
/* With time advanced too far, we get a new block. This time, we should also
definitely get a different pointer, as there is no clearing. The old
blocks are freed only after a new tip is found. */
SetMockTime (baseTime + 301);
const CBlock* pblock3 = miner.getCurrentBlock (scriptPubKey, target);
BOOST_CHECK (pblock3 != pblock2 && pblock3->GetHash () != hash2);
}
BOOST_FIXTURE_TEST_CASE (auxpow_miner_createAndLookupBlock, TestChain240Setup)
{
AuxpowMinerForTest miner;
LOCK (miner.cs);
CScript scriptPubKey;
uint256 target;
const CBlock* pblock = miner.getCurrentBlock (scriptPubKey, target);
BOOST_CHECK (pblock != nullptr);
BOOST_CHECK (miner.lookupSavedBlock (pblock->GetHash ().GetHex ()) == pblock);
BOOST_CHECK_THROW (miner.lookupSavedBlock ("foobar"), UniValue);
}
/* ************************************************************************** */
BOOST_AUTO_TEST_SUITE_END()

View File

@ -21,15 +21,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
namespace
{
//! equality test
bool operator==(const Coin &a, const Coin &b) {
// Empty Coin objects are always equal.
if (a.IsSpent() && b.IsSpent()) return true;
return a.fCoinBase == b.fCoinBase &&
a.nHeight == b.nHeight &&
a.out == b.out;
}
class CCoinsViewTest : public CCoinsView
{
uint256 hashBestBlock_;

View File

@ -83,7 +83,7 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test)
CAmount nSubsidy = GetDogecoinBlockSubsidy(nHeight, params, prevHash);
CAmount nExpectedSubsidy = (500000 >> (nHeight / 100000)) * COIN;
BOOST_CHECK(MoneyRange(nSubsidy));
BOOST_CHECK(nSubsidy == nExpectedSubsidy);
BOOST_CHECK_EQUAL(nSubsidy, nExpectedSubsidy);
nSum += nSubsidy * nStepSize;
}
@ -96,10 +96,99 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test)
// Test reward at 600k+ is constant
CAmount nConstantSubsidy = GetDogecoinBlockSubsidy(600000, params, prevHash);
BOOST_CHECK(nConstantSubsidy == 10000 * COIN);
BOOST_CHECK_EQUAL(nConstantSubsidy, 10000 * COIN);
nConstantSubsidy = GetDogecoinBlockSubsidy(700000, params, prevHash);
BOOST_CHECK(nConstantSubsidy == 10000 * COIN);
BOOST_CHECK_EQUAL(nConstantSubsidy, 10000 * COIN);
}
BOOST_AUTO_TEST_CASE(get_next_work_difficulty_limit)
{
SelectParams(CBaseChainParams::MAIN);
const Consensus::Params& params = Params().GetConsensus();
CBlockIndex pindexLast;
int64_t nLastRetargetTime = 1386474927; // Block # 1
pindexLast.nHeight = 239;
pindexLast.nTime = 1386475638; // Block #239
pindexLast.nBits = 0x1e0ffff0;
BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1e00ffff);
}
BOOST_AUTO_TEST_CASE(get_next_work_pre_digishield)
{
SelectParams(CBaseChainParams::MAIN);
const Consensus::Params& params = Params().GetConsensus();
CBlockIndex pindexLast;
int64_t nLastRetargetTime = 1386942008; // Block 9359
pindexLast.nHeight = 9599;
pindexLast.nTime = 1386954113;
pindexLast.nBits = 0x1c1a1206;
BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1c15ea59);
}
BOOST_AUTO_TEST_CASE(get_next_work_digishield)
{
SelectParams(CBaseChainParams::MAIN);
const Consensus::Params& params = Params().GetConsensus();
CBlockIndex pindexLast;
int64_t nLastRetargetTime = 1395094427;
// First hard-fork at 145,000, which applies to block 145,001 onwards
pindexLast.nHeight = 145000;
pindexLast.nTime = 1395094679;
pindexLast.nBits = 0x1b499dfd;
BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b671062);
}
BOOST_AUTO_TEST_CASE(get_next_work_digishield_modulated_upper)
{
SelectParams(CBaseChainParams::MAIN);
const Consensus::Params& params = Params().GetConsensus();
CBlockIndex pindexLast;
int64_t nLastRetargetTime = 1395100835;
// Test the upper bound on modulated time using mainnet block #145,107
pindexLast.nHeight = 145107;
pindexLast.nTime = 1395101360;
pindexLast.nBits = 0x1b3439cd;
BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b4e56b3);
}
BOOST_AUTO_TEST_CASE(get_next_work_digishield_modulated_lower)
{
SelectParams(CBaseChainParams::MAIN);
const Consensus::Params& params = Params().GetConsensus();
CBlockIndex pindexLast;
int64_t nLastRetargetTime = 1395380517;
// Test the lower bound on modulated time using mainnet block #149,423
pindexLast.nHeight = 149423;
pindexLast.nTime = 1395380447;
pindexLast.nBits = 0x1b446f21;
BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b335358);
}
BOOST_AUTO_TEST_CASE(get_next_work_digishield_rounding)
{
SelectParams(CBaseChainParams::MAIN);
const Consensus::Params& params = Params().GetConsensus();
CBlockIndex pindexLast;
int64_t nLastRetargetTime = 1395094679;
// Test case for correct rounding of modulated time - this depends on
// handling of integer division, and is not obvious from the code
pindexLast.nHeight = 145001;
pindexLast.nTime = 1395094727;
pindexLast.nBits = 0x1b671062;
BOOST_CHECK_EQUAL(CalculateDogecoinNextWorkRequired(&pindexLast, nLastRetargetTime, params), 0x1b6558a4);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -116,7 +116,7 @@ static std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leav
return ret;
}
static std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());

View File

@ -197,3 +197,12 @@ CBlock getBlock13b8a()
stream >> block;
return block;
}
//! equality test
bool operator==(const Coin &a, const Coin &b) {
// Empty Coin objects are always equal.
if (a.IsSpent() && b.IsSpent()) return true;
return a.fCoinBase == b.fCoinBase &&
a.nHeight == b.nHeight &&
a.out == b.out;
}

View File

@ -127,4 +127,12 @@ CBlock getBlock13b8a();
// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_*
std::ostream& operator<<(std::ostream& os, const uint256& num);
/* This is defined in merkle_tests.cpp, but also used by auxpow_tests.cpp. */
namespace merkle_tests {
std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position);
}
// Define == for coin equality (used by multiple tests).
bool operator==(const Coin &a, const Coin &b);
#endif

View File

@ -273,10 +273,11 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
pindexNew->hashBlockPoW = diskindex.hashBlockPoW;
if (!CheckProofOfWork(pindexNew->GetBlockPoWHash(), pindexNew->nBits, consensusParams))
return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
/* Bitcoin checks the PoW here. We don't do this because
the CDiskBlockIndex does not contain the auxpow.
This check isn't important, since the data on disk should
already be valid and can be trusted. */
pcursor->Next();
} else {

View File

@ -6,6 +6,7 @@
#include <validation.h>
#include <arith_uint256.h>
#include <auxpow.h>
#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
@ -1074,7 +1075,11 @@ static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMes
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
/* 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)
{
block.SetNull();
@ -1092,13 +1097,14 @@ 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;
}
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
template<typename T>
static bool ReadBlockOrHeader(T& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
CDiskBlockPos blockPos;
{
@ -1106,7 +1112,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
blockPos = pindex->GetBlockPos();
}
if (!ReadBlockFromDisk(block, blockPos, consensusParams))
if (!ReadBlockOrHeader(block, blockPos, consensusParams))
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
@ -1114,6 +1120,21 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
return ReadBlockOrHeader(block, pos, consensusParams);
}
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
return ReadBlockOrHeader(block, pindex, consensusParams);
}
bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
return ReadBlockOrHeader(block, pindex, consensusParams);
}
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start)
{
CDiskBlockPos hpos = pos;
@ -3080,7 +3101,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;
@ -3231,6 +3252,22 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
assert(pindexPrev != nullptr);
const int nHeight = pindexPrev->nHeight + 1;
// Disallow legacy blocks after merge-mining start.
if (!Params().GetConsensus().AllowLegacyBlocks(nHeight)
&& block.IsLegacy())
return state.DoS(100, error("%s : legacy block after auxpow start",
__func__),
REJECT_INVALID, "late-legacy-block");
// Dogecoin: Disallow AuxPow blocks before it is activated.
// TODO: Remove this test, as checkpoints will enforce this for us now
// NOTE: Previously this had its own fAllowAuxPoW flag, but that's always the opposite of fAllowLegacyBlocks
if (Params().GetConsensus().AllowLegacyBlocks(nHeight)
&& block.IsAuxpow())
return state.DoS(100, error("%s : auxpow blocks are not allowed at height %d",
__func__, pindexPrev->nHeight + 1),
REJECT_INVALID, "early-auxpow-block");
// Check proof of work
const Consensus::Params& consensusParams = params.GetConsensus();
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
@ -3257,10 +3294,10 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
// check for version 2, 3 and 4 upgrades
// Dogecoin: Version 2 enforcement was never used
if((block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion),
strprintf("rejected nVersion=0x%08x block", block.nVersion));
if((block.GetBaseVersion() < 3 && nHeight >= consensusParams.BIP66Height) ||
(block.GetBaseVersion() < 4 && nHeight >= consensusParams.BIP65Height))
return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.GetBaseVersion()),
strprintf("rejected nVersion=0x%08x block", block.GetBaseVersion()));
return true;
}
@ -3276,6 +3313,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
// Start enforcing BIP113 (Median Time Past) using versionbits logic.
// Dogecoin: We probably want to disable this
int nLockTimeFlags = 0;
if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;

View File

@ -149,6 +149,7 @@ extern std::atomic_bool g_is_mempool_loaded;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap& mapBlockIndex;
extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
extern uint64_t nLastBlockWeight;
extern const std::string strMessageMagic;
extern CWaitableCriticalSection g_best_block_mutex;
@ -393,6 +394,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start);
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
/** Functions for validating blocks and updating the block tree */

View File

@ -16,6 +16,7 @@
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <rpc/auxpow_miner.h>
#include <rpc/mining.h>
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
@ -28,7 +29,6 @@
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@ -4759,6 +4759,75 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
return result;
}
UniValue getauxblock(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
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, deprecated\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", "")
);
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
std::shared_ptr<CReserveScript> coinbaseScript;
pwallet->GetScriptForMining(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)");
/* Create a new block */
if (request.params.size() == 0)
return g_auxpow_miner->createAuxBlock(coinbaseScript->reserveScript);
/* Submit a block instead. */
assert(request.params.size() == 2);
bool fAccepted
= g_auxpow_miner->submitAuxBlock(request.params[0].get_str(),
request.params[1].get_str());
if (fAccepted)
coinbaseScript->KeepScript();
return fAccepted;
}
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
@ -4844,6 +4913,7 @@ static const CRPCCommand commands[] =
{ "wallet", "setlabel", &setlabel, {"address","label"} },
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
{ "mining", "getauxblock", &getauxblock, {"hash", "auxpow"} },
};
void RegisterWalletRPCCommands(CRPCTable &t)

View File

@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include <amount.h>
#include <auxpow.h> // contains CBaseMerkleTx
#include <outputtype.h>
#include <policy/feerate.h>
#include <streams.h>
@ -208,58 +209,21 @@ struct COutputEntry
};
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx
class CMerkleTx : public CBaseMerkleTx
{
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
CTransactionRef tx;
uint256 hashBlock;
/* 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;
CMerkleTx()
{
SetTx(MakeTransactionRef());
Init();
}
CMerkleTx() = default;
explicit CMerkleTx(CTransactionRef arg)
{
SetTx(std::move(arg));
Init();
}
: CBaseMerkleTx(arg)
{}
void Init()
{
hashBlock = uint256();
nIndex = -1;
}
void SetTx(CTransactionRef arg)
{
tx = std::move(arg);
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
std::vector<uint256> vMerkleBranch; // For compatibility with older versions.
READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
}
void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock);
void SetMerkleBranch(const CBlockIndex* pindex, int posInBlock);
/**
* Return depth of transaction in blockchain:
@ -274,7 +238,6 @@ public:
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(); }
};

188
test/functional/auxpow_mining.py Executable file
View File

@ -0,0 +1,188 @@
#!/usr/bin/env python3
# Copyright (c) 2014-2018 Daniel Kraft
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# Test the merge-mining RPC interface:
# getauxblock, createauxblock, submitauxblock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from test_framework.auxpow import reverseHex
from test_framework.auxpow_testing import (
computeAuxpow,
getCoinbaseAddr,
mineAuxpowBlockWithMethods,
)
class AuxpowMiningTest (BitcoinTestFramework):
def set_test_params (self):
self.num_nodes = 2
def add_options (self, parser):
parser.add_argument ("--segwit", dest="segwit", default=False,
action="store_true",
help="Test behaviour with SegWit active")
def run_test (self):
# Enable mock time to be out of IBD.
self.enable_mocktime ()
# Activate segwit if requested.
if self.options.segwit:
self.nodes[0].generate (500)
self.sync_all ()
# Test with getauxblock and createauxblock/submitauxblock.
self.test_getauxblock ()
self.test_create_submit_auxblock ()
def test_common (self, create, submit):
"""
Common test code that is shared between the tests for getauxblock and the
createauxblock / submitauxblock method pair.
"""
# Verify data that can be found in another way.
auxblock = create ()
assert_equal (auxblock['chainid'], 1)
assert_equal (auxblock['height'], self.nodes[0].getblockcount () + 1)
assert_equal (auxblock['previousblockhash'],
self.nodes[0].getblockhash (auxblock['height'] - 1))
# Calling again should give the same block.
auxblock2 = create ()
assert_equal (auxblock2, auxblock)
# If we receive a new block, the old hash will be replaced.
self.sync_all ()
self.nodes[1].generate (1)
self.sync_all ()
auxblock2 = create ()
assert auxblock['hash'] != auxblock2['hash']
assert_raises_rpc_error (-8, 'block hash unknown', submit,
auxblock['hash'], "x")
# Invalid format for auxpow.
assert_raises_rpc_error (-1, None, submit,
auxblock2['hash'], "x")
# Invalidate the block again, send a transaction and query for the
# auxblock to solve that contains the transaction.
self.nodes[0].generate (1)
addr = self.nodes[1].getnewaddress ()
txid = self.nodes[0].sendtoaddress (addr, 1)
self.sync_all ()
assert_equal (self.nodes[1].getrawmempool (), [txid])
auxblock = create ()
target = reverseHex (auxblock['_target'])
# Compute invalid auxpow.
apow = computeAuxpow (auxblock['hash'], target, False)
res = submit (auxblock['hash'], apow)
assert not res
# Compute and submit valid auxpow.
apow = computeAuxpow (auxblock['hash'], target, True)
res = submit (auxblock['hash'], apow)
assert res
# Make sure that the block is indeed accepted.
self.sync_all ()
assert_equal (self.nodes[1].getrawmempool (), [])
height = self.nodes[1].getblockcount ()
assert_equal (height, auxblock['height'])
assert_equal (self.nodes[1].getblockhash (height), auxblock['hash'])
# Call getblock and verify the auxpow field.
data = self.nodes[1].getblock (auxblock['hash'])
assert 'auxpow' in data
auxJson = data['auxpow']
assert_equal (auxJson['index'], 0)
assert_equal (auxJson['chainindex'], 0)
assert_equal (auxJson['merklebranch'], [])
assert_equal (auxJson['chainmerklebranch'], [])
assert_equal (auxJson['parentblock'], apow[-160:])
# Also previous blocks should have 'auxpow', since all blocks (also
# those generated by "generate") are merge-mined.
oldHash = self.nodes[1].getblockhash (100)
data = self.nodes[1].getblock (oldHash)
assert 'auxpow' in data
# Check that it paid correctly to the first node.
t = self.nodes[0].listtransactions ("*", 1)
assert_equal (len (t), 1)
t = t[0]
assert_equal (t['category'], "immature")
assert_equal (t['blockhash'], auxblock['hash'])
assert t['generated']
assert_greater_than_or_equal (t['amount'], Decimal ("1"))
assert_equal (t['confirmations'], 1)
# Verify the coinbase script. Ensure that it includes the block height
# to make the coinbase tx unique. The expected block height is around
# 200, so that the serialisation of the CScriptNum ends in an extra 00.
# The vector has length 2, which makes up for 02XX00 as the serialised
# height. Check this. (With segwit, the height is different, so we skip
# this for simplicity.)
if not self.options.segwit:
blk = self.nodes[1].getblock (auxblock['hash'])
tx = self.nodes[1].getrawtransaction (blk['tx'][0], 1)
coinbase = tx['vin'][0]['coinbase']
assert_equal ("02%02x00" % auxblock['height'], coinbase[0 : 6])
def test_getauxblock (self):
"""
Test the getauxblock method.
"""
create = self.nodes[0].getauxblock
submit = self.nodes[0].getauxblock
self.test_common (create, submit)
# Ensure that the payout address is changed from one block to the next.
hash1 = mineAuxpowBlockWithMethods (create, submit)
hash2 = mineAuxpowBlockWithMethods (create, submit)
self.sync_all ()
addr1 = getCoinbaseAddr (self.nodes[1], hash1)
addr2 = getCoinbaseAddr (self.nodes[1], hash2)
assert addr1 != addr2
info = self.nodes[0].getaddressinfo (addr1)
assert info['ismine']
info = self.nodes[0].getaddressinfo (addr2)
assert info['ismine']
def test_create_submit_auxblock (self):
"""
Test the createauxblock / submitauxblock method pair.
"""
# Check for errors with wrong parameters.
assert_raises_rpc_error (-1, None, self.nodes[0].createauxblock)
assert_raises_rpc_error (-5, "Invalid coinbase payout address",
self.nodes[0].createauxblock,
"this_an_invalid_address")
# Fix a coinbase address and construct methods for it.
coinbaseAddr = self.nodes[0].getnewaddress ()
def create ():
return self.nodes[0].createauxblock (coinbaseAddr)
submit = self.nodes[0].submitauxblock
# Run common tests.
self.test_common (create, submit)
# Ensure that the payout address is the one which we specify
hash1 = mineAuxpowBlockWithMethods (create, submit)
hash2 = mineAuxpowBlockWithMethods (create, submit)
self.sync_all ()
addr1 = getCoinbaseAddr (self.nodes[1], hash1)
addr2 = getCoinbaseAddr (self.nodes[1], hash2)
assert_equal (addr1, coinbaseAddr)
assert_equal (addr2, coinbaseAddr)
if __name__ == '__main__':
AuxpowMiningTest ().main ()

View File

@ -14,6 +14,7 @@ from struct import pack, unpack
import http.client
import urllib.parse
from test_framework import auxpow
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@ -175,7 +176,13 @@ class RESTTest (BitcoinTestFramework):
assert_equal(len(json_obj['utxos']), 0)
self.nodes[0].generate(1)
json_request = json_request.rstrip("/")
response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
assert_equal(response.status, 200) #must be a 200 because we are within the limits
self.sync_all()
bb_hash = self.nodes[0].getbestblockhash()
json_obj = self.test_rest_request("/getutxos/{}-{}".format(*spending))
assert_equal(len(json_obj['utxos']), 1)
@ -195,7 +202,7 @@ class RESTTest (BitcoinTestFramework):
long_uri = '/'.join(['{}-{}'.format(txid, n_) for n_ in range(15)])
self.test_rest_request("/getutxos/checkmempool/{}".format(long_uri), http_method='POST', status=200)
self.nodes[0].generate(1) # generate block to not affect upcoming tests
mineAuxpowBlock(self.nodes[0]) # generate block to not affect upcoming tests
self.sync_all()
self.log.info("Test the /block and /headers URIs")
@ -208,9 +215,10 @@ class RESTTest (BitcoinTestFramework):
# Compare with block header
response_header = self.test_rest_request("/headers/1/{}".format(bb_hash), req_type=ReqType.BIN, ret_type=RetType.OBJ)
assert_equal(int(response_header.getheader('content-length')), 80)
headerLen = int(response_header.getheader('content-length'))
assert_greater_than(headerLen, 80)
response_header_bytes = response_header.read()
assert_equal(response_bytes[:80], response_header_bytes)
assert_equal(response_bytes[:headerLen], response_header_bytes)
# Check block hex format
response_hex = self.test_rest_request("/block/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
@ -221,8 +229,10 @@ class RESTTest (BitcoinTestFramework):
# Compare with hex block header
response_header_hex = self.test_rest_request("/headers/1/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
assert_greater_than(int(response_header_hex.getheader('content-length')), 160)
response_header_hex_bytes = response_header_hex.read(160)
assert_equal(binascii.hexlify(response_bytes[:80]), response_header_hex_bytes)
response_header_hex_bytes = response_header_hex.read().strip()
headerLen = len (response_header_hex_bytes)
assert_equal(binascii.hexlify(response_bytes[:headerLen]), response_header_hex_bytes)
assert_equal(encode(response_header_bytes, "hex_codec"), response_header_hex_bytes)
# Check json format
block_json_obj = self.test_rest_request("/block/{}".format(bb_hash))

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python3
# Copyright (c) 2014-2018 Daniel Kraft
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# Basic code for working with auxpow. This is used for the regtests (e.g. from
# auxpow_testing.py), but also for contrib/auxpow/getwork-wrapper.py.
import binascii
import codecs
import hashlib
def constructAuxpow (block):
"""
Starts to construct a minimal auxpow, ready to be mined. Returns the fake
coinbase tx and the unmined parent block header as hex strings.
"""
block = codecs.encode (block, 'ascii')
# Start by building the merge-mining coinbase. The merkle tree
# consists only of the block hash as root.
coinbase = b"fabe" + binascii.hexlify (b"m" * 2)
coinbase += block
coinbase += b"01000000" + (b"00" * 4)
# Construct "vector" of transaction inputs.
vin = b"01"
vin += (b"00" * 32) + (b"ff" * 4)
vin += codecs.encode ("%02x" % (len (coinbase) // 2), "ascii") + coinbase
vin += (b"ff" * 4)
# Build up the full coinbase transaction. It consists only
# of the input and has no outputs.
tx = b"01000000" + vin + b"00" + (b"00" * 4)
txHash = doubleHashHex (tx)
# Construct the parent block header. It need not be valid, just good
# enough for auxpow purposes.
header = b"01000000"
header += b"00" * 32
header += reverseHex (txHash)
header += b"00" * 4
header += b"00" * 4
header += b"00" * 4
return (tx.decode ('ascii'), header.decode ('ascii'))
def finishAuxpow (tx, header):
"""
Constructs the finished auxpow hex string based on the mined header.
"""
blockhash = doubleHashHex (header)
# Build the MerkleTx part of the auxpow.
auxpow = codecs.encode (tx, 'ascii')
auxpow += blockhash
auxpow += b"00"
auxpow += b"00" * 4
# Extend to full auxpow.
auxpow += b"00"
auxpow += b"00" * 4
auxpow += header
return auxpow.decode ("ascii")
def doubleHashHex (data):
"""
Perform Bitcoin's Double-SHA256 hash on the given hex string.
"""
hasher = hashlib.sha256 ()
hasher.update (binascii.unhexlify (data))
data = hasher.digest ()
hasher = hashlib.sha256 ()
hasher.update (data)
return reverseHex (hasher.hexdigest ())
def reverseHex (data):
"""
Flip byte order in the given data (hex string).
"""
b = bytearray (binascii.unhexlify (data))
b.reverse ()
return binascii.hexlify (b)
def getworkByteswap (data):
"""
Run the byte-order swapping step necessary for working with getwork.
"""
data = bytearray (data)
assert len (data) % 4 == 0
for i in range (0, len (data), 4):
data[i], data[i + 3] = data[i + 3], data[i]
data[i + 1], data[i + 2] = data[i + 2], data[i + 1]
return data

View File

@ -0,0 +1,82 @@
#!/usr/bin/env python3
# Copyright (c) 2014-2018 Daniel Kraft
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# Utility routines for auxpow that are needed specifically by the regtests.
# This is mostly about actually *solving* an auxpow block (with regtest
# difficulty) or inspecting the information for verification.
import binascii
from test_framework import auxpow
def computeAuxpow (block, target, ok):
"""
Build an auxpow object (serialised as hex string) that solves
(ok = True) or doesn't solve (ok = False) the block.
"""
(tx, header) = auxpow.constructAuxpow (block)
(header, _) = mineBlock (header, target, ok)
return auxpow.finishAuxpow (tx, header)
def mineAuxpowBlock (node):
"""
Mine an auxpow block on the given RPC connection. This uses the
createauxblock and submitauxblock command pair.
"""
def create ():
addr = node.getnewaddress ()
return node.createauxblock (addr)
return mineAuxpowBlockWithMethods (create, node.submitauxblock)
def mineAuxpowBlockWithMethods (create, submit):
"""
Mine an auxpow block, using the given methods for creation and submission.
"""
auxblock = create ()
target = auxpow.reverseHex (auxblock['_target'])
apow = computeAuxpow (auxblock['hash'], target, True)
res = submit (auxblock['hash'], apow)
assert res
return auxblock['hash']
def getCoinbaseAddr (node, blockHash):
"""
Extract the coinbase tx' payout address for the given block.
"""
blockData = node.getblock (blockHash)
txn = blockData['tx']
assert len (txn) >= 1
txData = node.getrawtransaction (txn[0], 1)
assert len (txData['vout']) >= 1 and len (txData['vin']) == 1
assert 'coinbase' in txData['vin'][0]
addr = txData['vout'][0]['scriptPubKey']['addresses']
assert len (addr) == 1
return addr[0]
def mineBlock (header, target, ok):
"""
Given a block header, update the nonce until it is ok (or not)
for the given target.
"""
data = bytearray (binascii.unhexlify (header))
while True:
assert data[79] < 255
data[79] += 1
hexData = binascii.hexlify (data)
blockhash = auxpow.doubleHashHex (hexData)
if (ok and blockhash < target) or ((not ok) and blockhash > target):
break
return (hexData, blockhash)