commit
cee13699a5
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
31
src/chain.h
31
src/chain.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
120
src/dogecoin.cpp
120
src/dogecoin.cpp
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
25
src/pow.cpp
25
src/pow.cpp
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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"} },
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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_;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -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 ()
|
|
@ -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))
|
||||
|
|
|
@ -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
|
|
@ -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)
|
Loading…
Reference in New Issue