aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax K <[email protected]>2019-07-14 19:35:30 +0200
committerGitHub <[email protected]>2019-07-14 19:35:30 +0200
commitcee13699a5676355487f8eb2d91985f63438eae4 (patch)
treecf12be6180f950a25ee2ee7f3f2126542835d6e3
parentCorrect build and test net seed (diff)
parentHandle legacy v2 block at #66064 (diff)
downloaddiscoin-cee13699a5676355487f8eb2d91985f63438eae4.tar.xz
discoin-cee13699a5676355487f8eb2d91985f63438eae4.zip
Merge pull request #1546 from rnicoll/1.17-auxpow1.17-dev
1.17 AuxPoW support
-rw-r--r--build-aux/m4/bitcoin_qt.m48
-rw-r--r--depends/packages/bdb.mk11
-rw-r--r--depends/packages/openssl.mk4
-rw-r--r--depends/packages/qt.mk2
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/auxpow.cpp198
-rw-r--r--src/auxpow.h204
-rw-r--r--src/chain.cpp28
-rw-r--r--src/chain.h31
-rw-r--r--src/chainparams.cpp13
-rw-r--r--src/chainparams.h1
-rw-r--r--src/consensus/params.h18
-rw-r--r--src/dogecoin.cpp120
-rw-r--r--src/dogecoin.h13
-rw-r--r--src/init.cpp8
-rw-r--r--src/miner.cpp16
-rw-r--r--src/miner.h3
-rw-r--r--src/net_processing.cpp6
-rw-r--r--src/policy/policy.h4
-rw-r--r--src/pow.cpp25
-rw-r--r--src/primitives/block.cpp20
-rw-r--r--src/primitives/block.h59
-rw-r--r--src/primitives/pureheader.cpp29
-rw-r--r--src/primitives/pureheader.h157
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/rest.cpp3
-rw-r--r--src/rpc/auxpow_miner.cpp165
-rw-r--r--src/rpc/auxpow_miner.h95
-rw-r--r--src/rpc/blockchain.cpp43
-rw-r--r--src/rpc/mining.cpp81
-rw-r--r--src/rpc/mining.h7
-rw-r--r--src/test/auxpow_tests.cpp591
-rw-r--r--src/test/coins_tests.cpp9
-rw-r--r--src/test/dogecoin_tests.cpp95
-rw-r--r--src/test/merkle_tests.cpp2
-rw-r--r--src/test/test_bitcoin.cpp9
-rw-r--r--src/test/test_bitcoin.h8
-rw-r--r--src/txdb.cpp7
-rw-r--r--src/validation.cpp56
-rw-r--r--src/validation.h2
-rw-r--r--src/wallet/rpcwallet.cpp72
-rw-r--r--src/wallet/wallet.h49
-rwxr-xr-xtest/functional/auxpow_mining.py188
-rwxr-xr-xtest/functional/interface_rest.py20
-rw-r--r--test/functional/test_framework/auxpow.py104
-rw-r--r--test/functional/test_framework/auxpow_testing.py82
48 files changed, 2499 insertions, 178 deletions
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index 05df8621d..eb9ccbf61 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -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"
diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk
index 6c9876c2c..0b7142d9a 100644
--- a/depends/packages/bdb.mk
+++ b/depends/packages/bdb.mk
@@ -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
diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk
index db47113b2..e258bc222 100644
--- a/depends/packages/openssl.mk
+++ b/depends/packages/openssl.mk
@@ -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)"
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index 113b8dbc3..7a404e10f 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -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)
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c57e6fc8..1653af522 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 6f309f3d6..96fe2d4e6 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -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 \
diff --git a/src/auxpow.cpp b/src/auxpow.cpp
new file mode 100644
index 000000000..208a54bec
--- /dev/null
+++ b/src/auxpow.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;
+}
diff --git a/src/auxpow.h b/src/auxpow.h
new file mode 100644
index 000000000..a3280a19e
--- /dev/null
+++ b/src/auxpow.h
@@ -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
diff --git a/src/chain.cpp b/src/chain.cpp
index d462f94ab..5cb09f6fc 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -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
diff --git a/src/chain.h b/src/chain.h
index 34879d576..ca10f4449 100644
--- a/src/chain.h
+++ b/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
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 830dd2f91..ca0ed3982 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -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;
diff --git a/src/chainparams.h b/src/chainparams.h
index 722e52ff4..d95c471a3 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -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() {}
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 6c3a201f4..ff90b1304 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -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
diff --git a/src/dogecoin.cpp b/src/dogecoin.cpp
index 77a8a0093..396c4cbc4 100644
--- a/src/dogecoin.cpp
+++ b/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;
diff --git a/src/dogecoin.h b/src/dogecoin.h
index d834372a0..403673da5 100644
--- a/src/dogecoin.h
+++ b/src/dogecoin.h
@@ -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);
diff --git a/src/init.cpp b/src/init.cpp
index 88bfbcff0..9f9e47e78 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -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
diff --git a/src/miner.cpp b/src/miner.cpp
index 04476b037..04f43792b 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -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) {
diff --git a/src/miner.h b/src/miner.h
index 8cdcf7133..a2d6b6b5f 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -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
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 582d9fcad..bf6cb77c1 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -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.
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 3d47ac126..7a45ec0f6 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -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 */
diff --git a/src/pow.cpp b/src/pow.cpp
index ab305649a..09e38cef0 100644
--- a/src/pow.cpp
+++ b/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)
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index c61dc5fb5..f272347a3 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -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
diff --git a/src/primitives/block.h b/src/primitives/block.h
index 9bc06ad6b..1c3243bb3 100644
--- a/src/primitives/block.h
+++ b/src/primitives/block.h
@@ -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;
- }
-
- bool IsNull() const
- {
- return (nBits == 0);
+ CPureBlockHeader::SetNull();
+ auxpow.reset();
}
- 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;
}
diff --git a/src/primitives/pureheader.cpp b/src/primitives/pureheader.cpp
new file mode 100644
index 000000000..2dc33d738
--- /dev/null
+++ b/src/primitives/pureheader.cpp
@@ -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;
+}
diff --git a/src/primitives/pureheader.h b/src/primitives/pureheader.h
new file mode 100644
index 000000000..34373bbb7
--- /dev/null
+++ b/src/primitives/pureheader.h
@@ -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
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 352584604..6760b0786 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -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);
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index a5b9dd722..61c35b531 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -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()));
diff --git a/src/rest.cpp b/src/rest.cpp
index b81a6f9c5..56ce83098 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -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) {
diff --git a/src/rpc/auxpow_miner.cpp b/src/rpc/auxpow_miner.cpp
new file mode 100644
index 000000000..3d6e93bac
--- /dev/null
+++ b/src/rpc/auxpow_miner.cpp
@@ -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);
+}
diff --git a/src/rpc/auxpow_miner.h b/src/rpc/auxpow_miner.h
new file mode 100644
index 000000000..159495730
--- /dev/null
+++ b/src/rpc/auxpow_miner.h
@@ -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
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 948553d9a..3309a9701 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -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);
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 0622e3b72..75c71a323 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -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"} },
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
index 8d4627315..759d35e64 100644
--- a/src/rpc/mining.h
+++ b/src/rpc/mining.h
@@ -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
diff --git a/src/test/auxpow_tests.cpp b/src/test/auxpow_tests.cpp
new file mode 100644
index 000000000..6447ef9fd
--- /dev/null
+++ b/src/test/auxpow_tests.cpp
@@ -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()
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 63b91b74c..0eb5b90da 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -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_;
diff --git a/src/test/dogecoin_tests.cpp b/src/test/dogecoin_tests.cpp
index f1d60f709..b5d7716d5 100644
--- a/src/test/dogecoin_tests.cpp
+++ b/src/test/dogecoin_tests.cpp
@@ -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()
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index 5e55ad662..5e88e8fee 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -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());
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 4e88bd4c4..6dd0d8b22 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -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;
+}
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 0643e6e04..82bf98da6 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -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
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 998cf7264..2e9cc3d87 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -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 {
diff --git a/src/validation.cpp b/src/validation.cpp
index c14db2454..0086d3911 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -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;
diff --git a/src/validation.h b/src/validation.h
index 4965ff152..acfbadd18 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -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 */
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 193da7655..ce534a905 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -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)
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 7b791ae41..ae5de3e85 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -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();
- }
-
- 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);
- }
+ : CBaseMerkleTx(arg)
+ {}
- 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(); }
};
diff --git a/test/functional/auxpow_mining.py b/test/functional/auxpow_mining.py
new file mode 100755
index 000000000..bb4568772
--- /dev/null
+++ b/test/functional/auxpow_mining.py
@@ -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 ()
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index afa9de580..2ae7b7c94 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -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))
diff --git a/test/functional/test_framework/auxpow.py b/test/functional/test_framework/auxpow.py
new file mode 100644
index 000000000..e145e7ce7
--- /dev/null
+++ b/test/functional/test_framework/auxpow.py
@@ -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
diff --git a/test/functional/test_framework/auxpow_testing.py b/test/functional/test_framework/auxpow_testing.py
new file mode 100644
index 000000000..f1ef056b8
--- /dev/null
+++ b/test/functional/test_framework/auxpow_testing.py
@@ -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)