aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/auxpow.cpp238
-rw-r--r--src/auxpow.h226
-rw-r--r--src/chain.cpp28
-rw-r--r--src/chain.h45
-rw-r--r--src/chainparams.cpp13
-rw-r--r--src/chainparams.h1
-rw-r--r--src/consensus/params.h18
-rw-r--r--src/dogecoin.cpp44
-rw-r--r--src/dogecoin.h10
-rw-r--r--src/init.cpp3
-rw-r--r--src/miner.cpp5
-rw-r--r--src/net_processing.cpp6
-rw-r--r--src/pow.cpp1
-rw-r--r--src/primitives/block.cpp20
-rw-r--r--src/primitives/block.h67
-rw-r--r--src/primitives/pureheader.cpp30
-rw-r--r--src/primitives/pureheader.h156
-rw-r--r--src/rest.cpp3
-rw-r--r--src/rpc/blockchain.cpp42
-rw-r--r--src/rpc/mining.cpp267
-rw-r--r--src/test/auxpow_tests.cpp444
-rw-r--r--src/test/miner_tests.cpp3
-rw-r--r--src/txdb.cpp7
-rw-r--r--src/validation.cpp46
-rw-r--r--src/validation.h1
-rw-r--r--src/wallet/wallet.cpp43
-rw-r--r--src/wallet/wallet.h79
29 files changed, 1637 insertions, 218 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 1cc8a0055..c78cc83ef 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -78,6 +78,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
+ auxpow.h \
base58.h \
bloom.h \
blockencodings.h \
@@ -121,6 +122,8 @@ BITCOIN_CORE_H = \
policy/policy.h \
policy/rbf.h \
pow.h \
+ primitives/block.h \
+ primitives/pureheader.h \
protocol.h \
random.h \
reverselock.h \
@@ -300,6 +303,8 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
amount.cpp \
+ arith_uint256.cpp \
+ auxpow.cpp \
base58.cpp \
chainparams.cpp \
coins.cpp \
@@ -310,6 +315,9 @@ libbitcoin_common_a_SOURCES = \
keystore.cpp \
netaddress.cpp \
netbase.cpp \
+ primitives/block.cpp \
+ primitives/pureheader.cpp \
+ primitives/transaction.cpp \
protocol.cpp \
scheduler.cpp \
script/sign.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 957b6729b..c0304337a 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -82,6 +82,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..46fe6f650
--- /dev/null
+++ b/src/auxpow.cpp
@@ -0,0 +1,238 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2011 Vince Durham
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Copyright (c) 2014-2016 Daniel Kraft
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+
+#include "auxpow.h"
+
+#include "compat/endian.h"
+#include "consensus/consensus.h"
+#include "consensus/merkle.h"
+#include "consensus/validation.h"
+#include "hash.h"
+#include "script/script.h"
+#include "txmempool.h"
+#include "util.h"
+#include "utilstrencodings.h"
+#include "validation.h"
+
+#include <algorithm>
+
+/* Moved from wallet.cpp. CMerkleTx is necessary for auxpow, independent
+ of an enabled (or disabled) wallet. Always include the code. */
+
+const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
+
+void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
+{
+ // Update the tx's hashBlock
+ hashBlock = pindex->GetBlockHash();
+
+ // set the position of the transaction in the block
+ nIndex = posInBlock;
+}
+
+void CMerkleTx::InitMerkleBranch(const CBlock& block, int posInBlock)
+{
+ hashBlock = block.GetHash();
+ nIndex = posInBlock;
+ vMerkleBranch = BlockMerkleBranch(block, nIndex);
+}
+
+int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
+{
+ if (hashUnset())
+ return 0;
+
+ AssertLockHeld(cs_main);
+
+ // Find the block it claims to be in
+ BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
+ if (mi == mapBlockIndex.end())
+ return 0;
+ CBlockIndex* pindex = (*mi).second;
+ if (!pindex || !chainActive.Contains(pindex))
+ return 0;
+
+ pindexRet = pindex;
+ return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
+}
+
+int CMerkleTx::GetBlocksToMaturity() const
+{
+ if (!IsCoinBase())
+ return 0;
+ return std::max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
+}
+
+
+bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
+{
+ return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
+}
+
+/* ************************************************************************** */
+
+bool
+CAuxPow::check(const uint256& hashAuxBlock, int nChainId,
+ const Consensus::Params& params) const
+{
+ if (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(GetHash(), vMerkleBranch, nIndex)
+ != parentBlock.hashMerkleRoot)
+ return error("Aux POW merkle root incorrect");
+
+ const CScript script = tx->vin[0].scriptSig;
+
+ // Check that the same work is not submitted twice to our chain.
+ //
+
+ CScript::const_iterator pcHead =
+ std::search(script.begin(), script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader));
+
+ CScript::const_iterator pc =
+ std::search(script.begin(), script.end(), vchRootHash.begin(), vchRootHash.end());
+
+ if (pc == script.end())
+ return error("Aux POW missing chain merkle root in parent coinbase");
+
+ if (pcHead != script.end())
+ {
+ // Enforce only one chain merkle root by checking that a single instance of the merged
+ // mining header exists just before.
+ if (script.end() != std::search(pcHead + 1, script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)))
+ return error("Multiple merged mining headers in coinbase");
+ if (pcHead + sizeof(pchMergedMiningHeader) != pc)
+ return error("Merged mining header is not just before chain merkle root");
+ }
+ else
+ {
+ // For backward compatibility.
+ // Enforce only one chain merkle root by checking that it starts early in the coinbase.
+ // 8-12 bytes are enough to encode extraNonce and nBits.
+ if (pc - script.begin() > 20)
+ return error("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase");
+ }
+
+
+ // Ensure we are at a deterministic point in the merkle leaves by hashing
+ // a nonce and our chain ID and comparing to the index.
+ pc += vchRootHash.size();
+ if (script.end() - pc < 8)
+ return error("Aux POW missing chain merkle tree size and nonce in parent coinbase");
+
+ uint32_t nSize;
+ memcpy(&nSize, &pc[0], 4);
+ nSize = le32toh(nSize);
+ const unsigned merkleHeight = vChainMerkleBranch.size();
+ if (nSize != (1u << merkleHeight))
+ return error("Aux POW merkle branch size does not match parent coinbase");
+
+ uint32_t nNonce;
+ memcpy(&nNonce, &pc[4], 4);
+ nNonce = le32toh (nNonce);
+ if (nChainIndex != getExpectedIndex (nNonce, nChainId, merkleHeight))
+ return error("Aux POW wrong index");
+
+ return true;
+}
+
+int
+CAuxPow::getExpectedIndex (uint32_t nNonce, int nChainId, unsigned h)
+{
+ // Choose a pseudo-random slot in the chain merkle tree
+ // but have it be fixed for a size/nonce/chain combination.
+ //
+ // This prevents the same work from being used twice for the
+ // same chain while reducing the chance that two chains clash
+ // for the same slot.
+
+ /* This computation can overflow the uint32 used. This is not an issue,
+ though, since we take the mod against a power-of-two in the end anyway.
+ This also ensures that the computation is, actually, consistent
+ even if done in 64 bits as it was in the past on some systems.
+
+ Note that h is always <= 30 (enforced by the maximum allowed chain
+ merkle branch length), so that 32 bits are enough for the computation. */
+
+ uint32_t rand = nNonce;
+ rand = rand * 1103515245 + 12345;
+ rand += nChainId;
+ rand = rand * 1103515245 + 12345;
+
+ return rand % (1 << h);
+}
+
+uint256
+CAuxPow::CheckMerkleBranch (uint256 hash,
+ const std::vector<uint256>& vMerkleBranch,
+ int nIndex)
+{
+ if (nIndex == -1)
+ return uint256 ();
+ for (std::vector<uint256>::const_iterator it(vMerkleBranch.begin ());
+ it != vMerkleBranch.end (); ++it)
+ {
+ if (nIndex & 1)
+ hash = Hash (BEGIN (*it), END (*it), BEGIN (hash), END (hash));
+ else
+ hash = Hash (BEGIN (hash), END (hash), BEGIN (*it), END (*it));
+ nIndex >>= 1;
+ }
+ return hash;
+}
+
+void
+CAuxPow::initAuxPow (CBlockHeader& header)
+{
+ /* Set auxpow flag right now, since we take the block hash below. */
+ header.SetAuxpowFlag(true);
+
+ /* Build a minimal coinbase script input for merge-mining. */
+ const uint256 blockHash = header.GetHash ();
+ std::vector<unsigned char> inputData(blockHash.begin (), blockHash.end ());
+ std::reverse (inputData.begin (), inputData.end ());
+ inputData.push_back (1);
+ inputData.insert (inputData.end (), 7, 0);
+
+ /* Fake a parent-block coinbase with just the required input
+ script and no outputs. */
+ CMutableTransaction coinbase;
+ coinbase.vin.resize(1);
+ coinbase.vin[0].prevout.SetNull();
+ coinbase.vin[0].scriptSig = (CScript () << inputData);
+ assert (coinbase.vout.empty());
+ CTransactionRef coinbaseRef = MakeTransactionRef(coinbase);
+
+ /* Build a fake parent block with the coinbase. */
+ CBlock parent;
+ parent.nVersion = 1;
+ parent.vtx.resize(1);
+ parent.vtx[0] = coinbaseRef;
+ parent.hashMerkleRoot = BlockMerkleRoot(parent);
+
+ /* Construct the auxpow object. */
+ header.SetAuxpow(new CAuxPow(coinbaseRef));
+ assert (header.auxpow->vChainMerkleBranch.empty());
+ header.auxpow->nChainIndex = 0;
+ assert (header.auxpow->vMerkleBranch.empty());
+ header.auxpow->nIndex = 0;
+ header.auxpow->parentBlock = parent;
+}
diff --git a/src/auxpow.h b/src/auxpow.h
new file mode 100644
index 000000000..06c13da0c
--- /dev/null
+++ b/src/auxpow.h
@@ -0,0 +1,226 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Copyright (c) 2014-2016 Daniel Kraft
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_AUXPOW_H
+#define BITCOIN_AUXPOW_H
+
+#include "consensus/params.h"
+#include "consensus/validation.h"
+#include "primitives/pureheader.h"
+#include "primitives/transaction.h"
+#include "serialize.h"
+#include "uint256.h"
+
+#include <vector>
+
+class CBlock;
+class CBlockHeader;
+class CBlockIndex;
+
+/** Header for merge-mining data in the coinbase. */
+static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' };
+
+/* Because it is needed for auxpow, the definition of CMerkleTx is moved
+ here from wallet.h. */
+
+/** A transaction with a merkle branch linking it to the block chain. */
+class CMerkleTx
+{
+private:
+ /** Constant used in hashBlock to indicate tx has been abandoned */
+ static const uint256 ABANDON_HASH;
+
+public:
+ CTransactionRef tx;
+ uint256 hashBlock;
+ // Dogecoin TODO: Is this used? If not remove. If it is, I don't think it's actually set
+ // anywhere. Check with Namecore
+ 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;
+
+ CMerkleTx()
+ {
+ SetTx(MakeTransactionRef());
+ Init();
+ }
+
+ CMerkleTx(CTransactionRef arg)
+ {
+ SetTx(std::move(arg));
+ Init();
+ }
+
+ /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
+ * TODO: adapt callers and remove this operator. */
+ operator const CTransaction&() const { return *tx; }
+
+ void 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);
+ }
+
+ /**
+ * Actually compute the Merkle branch. This is used for unit tests when
+ * constructing an auxpow. It is not needed for actual production, since
+ * we do not care in the Namecoin client how the auxpow is constructed
+ * by a miner.
+ */
+ void InitMerkleBranch(const CBlock& block, int posInBlock);
+
+ void SetMerkleBranch(const CBlockIndex* pindex, int posInBlock);
+
+ /**
+ * Return depth of transaction in blockchain:
+ * <0 : conflicts with a transaction this deep in the blockchain
+ * 0 : in memory pool, waiting to be included in a block
+ * >=1 : this many blocks deep in the main chain
+ */
+ int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
+ int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
+ bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
+ int GetBlocksToMaturity() const;
+ /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
+ bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
+ bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
+ bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
+ void setAbandoned() { hashBlock = ABANDON_HASH; }
+
+ const uint256& GetHash() const { return tx->GetHash(); }
+ bool IsCoinBase() const { return tx->IsCoinBase(); }
+};
+
+/**
+ * Data for the merge-mining auxpow. This is a merkle tx (the parent block's
+ * coinbase tx) that can be verified to be in the parent block, and this
+ * transaction's input (the coinbase script) contains the reference
+ * to the actual merge-mined block.
+ */
+class CAuxPow : public CMerkleTx
+{
+
+/* Public for the unit tests. */
+public:
+
+ /** The merkle branch connecting the aux block to our coinbase. */
+ std::vector<uint256> vChainMerkleBranch;
+
+ /** Merkle tree index of the aux block header in the coinbase. */
+ int nChainIndex;
+
+ /** Parent block header (on which the real PoW is done). */
+ CPureBlockHeader parentBlock;
+
+public:
+
+ /* Prevent accidental conversion. */
+ inline explicit CAuxPow(CTransactionRef txIn)
+ : CMerkleTx(txIn)
+ {
+ }
+
+ inline CAuxPow()
+ : CMerkleTx()
+ {
+ }
+
+ ADD_SERIALIZE_METHODS;
+
+ template<typename Stream, typename Operation>
+ inline void
+ SerializationOp (Stream& s, Operation ser_action)
+ {
+ READWRITE (*static_cast<CMerkleTx*> (this));
+ READWRITE (vChainMerkleBranch);
+ READWRITE (nChainIndex);
+ READWRITE (parentBlock);
+ }
+
+ /**
+ * Check the auxpow, given the merge-mined block's hash and our chain ID.
+ * Note that this does not verify the actual PoW on the parent block! It
+ * just confirms that all the merkle branches are valid.
+ * @param hashAuxBlock Hash of the merge-mined block.
+ * @param nChainId The auxpow chain ID of the block to check.
+ * @param params Consensus parameters.
+ * @return True if the auxpow is valid.
+ */
+ bool check(const uint256& hashAuxBlock, int nChainId, const Consensus::Params& params) const;
+
+ /**
+ * Get the parent block's hash. This is used to verify that it
+ * satisfies the PoW requirement.
+ * @return The parent block hash.
+ */
+ inline uint256
+ getParentBlockPoWHash() const
+ {
+ return parentBlock.GetPoWHash ();
+ }
+
+ /**
+ * 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);
+
+ /**
+ * Check a merkle branch. This used to be in CBlock, but was removed
+ * upstream. Thus include it here now.
+ */
+ static uint256 CheckMerkleBranch(uint256 hash,
+ const std::vector<uint256>& vMerkleBranch,
+ int nIndex);
+
+ /**
+ * Initialise the auxpow of the given block header. This constructs
+ * a minimal CAuxPow object with a minimal parent block and sets
+ * it on the block header. The auxpow is not necessarily valid, but
+ * can be "mined" to make it valid.
+ * @param header The header to set the auxpow on.
+ */
+ static void initAuxPow(CBlockHeader& header);
+
+};
+
+#endif // BITCOIN_AUXPOW_H
diff --git a/src/chain.cpp b/src/chain.cpp
index a5b369c4f..e42d6497f 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 76d13bd0a..d9a4bdd69 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -8,6 +8,7 @@
#include "arith_uint256.h"
#include "primitives/block.h"
+#include "primitives/pureheader.h"
#include "pow.h"
#include "tinyformat.h"
#include "uint256.h"
@@ -199,9 +200,6 @@ public:
unsigned int nBits;
unsigned int 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;
@@ -224,12 +222,11 @@ public:
nSequenceId = 0;
nTimeMax = 0;
- nVersion = 0;
+ nVersion = 0;
hashMerkleRoot = uint256();
nTime = 0;
nBits = 0;
nNonce = 0;
- hashBlockPoW = uint256();
}
CBlockIndex()
@@ -246,7 +243,6 @@ public:
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
- hashBlockPoW = block.GetPoWHash();
}
CDiskBlockPos GetBlockPos() const {
@@ -267,29 +263,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;
@@ -353,6 +333,24 @@ public:
//! Efficiently find an ancestor of this block.
CBlockIndex* GetAncestor(int height);
const CBlockIndex* GetAncestor(int height) const;
+
+ /**
+ * Extract the chain ID.
+ * @return The chain ID encoded in the version.
+ */
+ inline int32_t GetChainId() const
+ {
+ return nVersion >> 16;
+ }
+
+ /**
+ * Check if the auxpow flag is set in the version.
+ * @return True if this block version is marked as auxpow.
+ */
+ inline bool IsAuxpow() const
+ {
+ return nVersion & CPureBlockHeader::VERSION_AUXPOW;
+ }
};
arith_uint256 GetBlockProof(const CBlockIndex& block);
@@ -398,7 +396,6 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
- READWRITE(hashBlockPoW);
}
uint256 GetBlockHash() const
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index f1e4fe049..729ab4fde 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -106,7 +106,11 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0xca5eb72f1e0d160f1481f74d56d7cc4a27d91aa585ba012da8018a5fe934d61b"); // 1,600,000
- /**
+ 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.
@@ -119,6 +123,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"));
@@ -213,6 +218,10 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x6943eaeaba98dc7d09f7e73398daccb4abcabb18b66c8c875e52b07638d93951"); // 900,000
+ consensus.nAuxpowStartHeight = 158100;
+ consensus.fStrictChainId = false;
+ consensus.nLegacyBlocksBefore = -1;
+
pchMessageStart[0] = 0xfc;
pchMessageStart[1] = 0xc1;
pchMessageStart[2] = 0xb7;
@@ -300,6 +309,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 db524e8f8..22843b886 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -77,6 +77,7 @@ public:
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
+
protected:
CChainParams() {}
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 6240e8285..55a7b27b6 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -63,6 +63,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 e946acad6..8ac2782ba 100644
--- a/src/dogecoin.cpp
+++ b/src/dogecoin.cpp
@@ -21,9 +21,9 @@ unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, in
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 retargetTimespan = fNewDifficultyProtocol ? 60 // params.DigiShieldTargetTimespan()
+ :
+ params.nPowTargetTimespan;
const int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
int64_t nModulatedTimespan = nActualTimespan;
@@ -75,6 +75,44 @@ unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, in
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 cedbfac78..edc07b5f5 100644
--- a/src/dogecoin.h
+++ b/src/dogecoin.h
@@ -2,9 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include "amount.h"
#include "chain.h"
#include "chainparams.h"
-#include "amount.h"
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 4623bac53..6c39b1496 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -200,6 +200,7 @@ void Shutdown()
StopRPC();
StopHTTPServer();
#ifdef ENABLE_WALLET
+ // Dogecoin 1.14 TODO: ShutdownRPCMining();
if (pwalletMain)
pwalletMain->Flush(false);
#endif
@@ -1652,6 +1653,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 12: finished
+ // Dogecoin: Do we need to do any RPC mining init here?
+
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));
diff --git a/src/miner.cpp b/src/miner.cpp
index 2c50fd94e..bac62821b 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -149,11 +149,12 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
CBlockIndex* pindexPrev = chainActive.Tip();
nHeight = pindexPrev->nHeight + 1;
- pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
+ const int32_t nChainId = chainparams.GetConsensus ().nAuxpowChainId;
+ pblock->SetBaseVersion(ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()), nChainId);
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
- pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
+ pblock->SetBaseVersion(GetArg("-blockversion", pblock->GetBaseVersion()), nChainId);
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 91cc92725..cf6a82805 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1748,7 +1748,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id);
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;
}
@@ -2942,14 +2942,14 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
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 == NULL || 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/pow.cpp b/src/pow.cpp
index 64628b73f..a8d0efdab 100644
--- a/src/pow.cpp
+++ b/src/pow.cpp
@@ -5,6 +5,7 @@
#include "pow.h"
+#include "auxpow.h"
#include "arith_uint256.h"
#include "chain.h"
#include "dogecoin.h"
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index 4cfc576f0..1df912d35 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 (CAuxPow* apow)
{
- return SerializeHash(*this);
-}
-
-uint256 CBlockHeader::GetPoWHash() const
-{
- uint256 thash;
- scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash));
- return thash;
+ if (apow)
+ {
+ auxpow.reset(apow);
+ 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 8b2f1de89..dcde500ab 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 <boost/shared_ptr.hpp>
+
/** 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,11 @@
* 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)
+ boost::shared_ptr<CAuxPow> auxpow;
CBlockHeader()
{
@@ -37,37 +36,30 @@ 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.reset(new CAuxPow());
+ assert(auxpow);
+ 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.
+ * @param apow Pointer to the auxpow to use or NULL.
+ */
+ void SetAuxpow(CAuxPow* apow);
};
@@ -115,9 +107,18 @@ public:
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
+ block.auxpow = auxpow;
return block;
}
+ // Build the in-memory merkle tree for this block and return the merkle root.
+ // If non-NULL, *mutated is set to whether mutation was detected in the merkle
+ // tree (a duplication of transactions in the block leading to an identical
+ // merkle root).
+ uint256 BuildMerkleTree(bool* mutated = NULL) const;
+
+ std::vector<uint256> GetMerkleBranch(int nIndex) const;
+ static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);
std::string ToString() const;
};
diff --git a/src/primitives/pureheader.cpp b/src/primitives/pureheader.cpp
new file mode 100644
index 000000000..9a339bf1f
--- /dev/null
+++ b/src/primitives/pureheader.cpp
@@ -0,0 +1,30 @@
+// 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 "chainparams.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..0443a530d
--- /dev/null
+++ b/src/primitives/pureheader.h
@@ -0,0 +1,156 @@
+// 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 if 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 if it is.
+ */
+ inline bool IsLegacy() const
+ {
+ return nVersion == 1;
+ }
+};
+
+#endif // BITCOIN_PRIMITIVES_PUREHEADER_H
diff --git a/src/rest.cpp b/src/rest.cpp
index 54eefcafe..e9d0a8cef 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -160,8 +160,9 @@ static bool rest_headers(HTTPRequest* req,
}
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
+ const CChainParams& chainparams = Params();
BOOST_FOREACH(const CBlockIndex *pindex, headers) {
- ssHeader << pindex->GetBlockHeader();
+ ssHeader << pindex->GetBlockHeader(chainparams.GetConsensus());
}
switch (rf) {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index fd8f52a5c..92601a5ec 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -9,6 +9,7 @@
#include "checkpoints.h"
#include "coins.h"
#include "consensus/validation.h"
+#include "core_io.h"
#include "validation.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
@@ -74,6 +75,42 @@ double GetDifficulty(const CBlockIndex* blockindex)
return dDiff;
}
+UniValue AuxpowToJSON(const CAuxPow& auxpow)
+{
+ UniValue result(UniValue::VOBJ);
+
+ {
+ UniValue tx(UniValue::VOBJ);
+ tx.push_back(Pair("hex", EncodeHexTx(auxpow)));
+ TxToJSON(auxpow, auxpow.parentBlock.GetHash(), tx);
+ result.push_back(Pair("tx", tx));
+ }
+
+ result.push_back(Pair("index", auxpow.nIndex));
+ result.push_back(Pair("chainindex", auxpow.nChainIndex));
+
+ {
+ UniValue branch(UniValue::VARR);
+ BOOST_FOREACH(const uint256& node, auxpow.vMerkleBranch)
+ branch.push_back(node.GetHex());
+ result.push_back(Pair("merklebranch", branch));
+ }
+
+ {
+ UniValue branch(UniValue::VARR);
+ BOOST_FOREACH(const uint256& node, auxpow.vChainMerkleBranch)
+ branch.push_back(node.GetHex());
+ result.push_back(Pair("chainmerklebranch", branch));
+ }
+
+ CDataStream ssParent(SER_NETWORK, PROTOCOL_VERSION);
+ ssParent << auxpow.parentBlock;
+ const std::string strHex = HexStr(ssParent.begin(), ssParent.end());
+ result.push_back(Pair("parentblock", strHex));
+
+ return result;
+}
+
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
UniValue result(UniValue::VOBJ);
@@ -138,6 +175,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
+ if (block.auxpow)
+ result.push_back(Pair("auxpow", AuxpowToJSON(*block.auxpow)));
+
if (blockindex->pprev)
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
CBlockIndex *pnext = chainActive.Next(blockindex);
@@ -680,7 +720,7 @@ 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;
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 33e234a95..70192686b 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -121,14 +121,16 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
LOCK(cs_main);
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
}
- while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
- ++pblock->nNonce;
+ CAuxPow::initAuxPow(*pblock);
+ CPureBlockHeader& miningHeader = pblock->auxpow->parentBlock;
+ 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);
@@ -925,6 +927,262 @@ UniValue estimatesmartpriority(const JSONRPCRequest& request)
return result;
}
+/* ************************************************************************** */
+/* Merge mining. */
+
+/**
+ * The variables below are used to keep track of created and not yet
+ * submitted auxpow blocks. Lock them to be sure even for multiple
+ * RPC threads running in parallel.
+ */
+static CCriticalSection cs_auxblockCache;
+static std::map<uint256, CBlock*> mapNewBlock;
+static std::vector<std::unique_ptr<CBlockTemplate>> vNewBlockTemplate;
+
+static
+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);
+ if (chainActive.Height() + 1 < Params().GetConsensus().nAuxpowStartHeight)
+ throw std::runtime_error("mining auxblock method is not yet available");
+ }
+}
+
+static
+UniValue AuxMiningCreateBlock(const CScript& scriptPubKey)
+{
+ AuxMiningCheck();
+
+ LOCK(cs_auxblockCache);
+
+ static unsigned nTransactionsUpdatedLast;
+ static const CBlockIndex* pindexPrev = nullptr;
+ static uint64_t nStart;
+ static CBlock* pblock = nullptr;
+ static unsigned nExtraNonce = 0;
+
+ // Update block
+ {
+ LOCK(cs_main);
+ if (pindexPrev != chainActive.Tip()
+ || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast
+ && GetTime() - nStart > 60))
+ {
+ if (pindexPrev != chainActive.Tip())
+ {
+ // Clear old blocks since they're obsolete now.
+ mapNewBlock.clear();
+ vNewBlockTemplate.clear();
+ pblock = nullptr;
+ }
+
+ // Create new block with nonce = 0 and extraNonce = 1
+ std::unique_ptr<CBlockTemplate> newBlock
+ = BlockAssembler(Params()).CreateNewBlock(scriptPubKey);
+ if (!newBlock)
+ throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory");
+
+ // Update state only when CreateNewBlock succeeded
+ nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
+ pindexPrev = chainActive.Tip();
+ nStart = GetTime();
+
+ // Finalise it by setting the version and building the merkle root
+ IncrementExtraNonce(&newBlock->block, pindexPrev, nExtraNonce);
+ newBlock->block.SetAuxpowFlag(true);
+
+ // Save
+ pblock = &newBlock->block;
+ mapNewBlock[pblock->GetHash()] = pblock;
+ vNewBlockTemplate.push_back(std::move(newBlock));
+ }
+ }
+
+ // At this point, pblock 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 pblock in a previous call, as pindexPrev is
+ // initialised only when pblock is.
+ assert(pblock);
+
+ arith_uint256 target;
+ bool fNegative, fOverflow;
+ target.SetCompact(pblock->nBits, &fNegative, &fOverflow);
+ if (fNegative || fOverflow || target == 0)
+ throw std::runtime_error("invalid difficulty bits in block");
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("hash", pblock->GetHash().GetHex());
+ result.pushKV("chainid", pblock->GetChainId());
+ result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
+ result.pushKV("coinbasevalue", (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;
+}
+
+static
+bool AuxMiningSubmitBlock(const std::string& hashHex,
+ const std::string& auxpowHex)
+{
+ AuxMiningCheck();
+
+ LOCK(cs_auxblockCache);
+
+ uint256 hash;
+ hash.SetHex(hashHex);
+
+ const std::map<uint256, CBlock*>::iterator mit = mapNewBlock.find(hash);
+ if (mit == mapNewBlock.end())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown");
+ CBlock& block = *mit->second;
+
+ const std::vector<unsigned char> vchAuxPow = ParseHex(auxpowHex);
+ CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION);
+ CAuxPow pow;
+ ss >> pow;
+ block.SetAuxpow(new CAuxPow(pow));
+ assert(block.GetHash() == hash);
+
+ submitblock_StateCatcher sc(block.GetHash());
+ RegisterValidationInterface(&sc);
+ std::shared_ptr<const CBlock> shared_block
+ = std::make_shared<const CBlock>(block);
+ bool fAccepted = ProcessNewBlock(Params(), shared_block, true, nullptr);
+ UnregisterValidationInterface(&sc);
+
+ return fAccepted;
+}
+
+UniValue getauxblock(const JSONRPCRequest& request)
+{
+ if (request.fHelp
+ || (request.params.size() != 0 && request.params.size() != 2))
+ throw std::runtime_error(
+ "getauxblock (hash auxpow)\n"
+ "\nCreate or submit a merge-mined block.\n"
+ "\nWithout arguments, create a new block and return information\n"
+ "required to merge-mine it. With arguments, submit a solved\n"
+ "auxpow for a previously returned block.\n"
+ "\nArguments:\n"
+ "1. hash (string, optional) hash of the block to submit\n"
+ "2. auxpow (string, optional) serialised auxpow found\n"
+ "\nResult (without arguments):\n"
+ "{\n"
+ " \"hash\" (string) hash of the created block\n"
+ " \"chainid\" (numeric) chain ID for this block\n"
+ " \"previousblockhash\" (string) hash of the previous block\n"
+ " \"coinbasevalue\" (numeric) value of the block's coinbase\n"
+ " \"bits\" (string) compressed target of the block\n"
+ " \"height\" (numeric) height of the block\n"
+ " \"_target\" (string) target in reversed byte order, 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", "")
+ );
+
+ boost::shared_ptr<CReserveScript> coinbaseScript;
+ GetMainSignals().ScriptForMining(coinbaseScript);
+
+ // If the keypool is exhausted, no script is returned at all. Catch this.
+ if (!coinbaseScript)
+ throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
+
+ //throw an error if no script was provided
+ if (!coinbaseScript->reserveScript.size())
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)");
+
+ /* Create a new block */
+ if (request.params.size() == 0)
+ return AuxMiningCreateBlock(coinbaseScript->reserveScript);
+
+ /* Submit a block instead. Note that this need not lock cs_main,
+ since ProcessNewBlock below locks it instead. */
+ assert(request.params.size() == 2);
+ bool fAccepted = AuxMiningSubmitBlock(request.params[0].get_str(),
+ request.params[1].get_str());
+ if (fAccepted)
+ coinbaseScript->KeepScript();
+
+ return fAccepted;
+}
+
+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, deprecated\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("createauxblock", "\"address\"")
+ + HelpExampleRpc("createauxblock", "\"address\"")
+ );
+
+ // Check coinbase payout address
+ CBitcoinAddress coinbaseAddress(request.params[0].get_str());
+ if (!coinbaseAddress.IsValid())
+ throw JSONRPCError(RPC_INVALID_PARAMETER,
+ "Invalid coinbase payout address");
+ const CScript scriptPubKey
+ = GetScriptForDestination(coinbaseAddress.Get());
+
+ return AuxMiningCreateBlock(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 AuxMiningSubmitBlock(request.params[0].get_str(),
+ request.params[1].get_str());
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
@@ -933,6 +1191,9 @@ static const CRPCCommand commands[] =
{ "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","priority_delta","fee_delta"} },
{ "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} },
{ "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} },
+ { "mining", "getauxblock", &getauxblock, true, {"hash", "auxpow"} },
+ { "mining", "createauxblock", &createauxblock, true, {"address"} },
+ { "mining", "submitauxblock", &submitauxblock, true, {"hash", "auxpow"} },
{ "generating", "generate", &generate, true, {"nblocks","maxtries"} },
{ "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} },
diff --git a/src/test/auxpow_tests.cpp b/src/test/auxpow_tests.cpp
new file mode 100644
index 000000000..eaad49eef
--- /dev/null
+++ b/src/test/auxpow_tests.cpp
@@ -0,0 +1,444 @@
+// Copyright (c) 2014-2015 Daniel Kraft
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "auxpow.h"
+#include "chainparams.h"
+#include "coins.h"
+#include "consensus/merkle.h"
+#include "dogecoin.h"
+#include "primitives/block.h"
+#include "script/script.h"
+#include "uint256.h"
+#include "utilstrencodings.h"
+#include "validation.h"
+
+#include "test/test_bitcoin.h"
+
+#include <boost/test/unit_test.hpp>
+
+#include <algorithm>
+#include <vector>
+
+BOOST_FIXTURE_TEST_SUITE(auxpow_tests, BasicTestingSetup)
+
+/* ************************************************************************** */
+
+/**
+ * Tamper with a uint256 (modify it).
+ * @param num The number to modify.
+ */
+static void
+tamperWith(uint256& num)
+{
+ arith_uint256 modifiable = UintToArith256(num);
+ modifiable += 1;
+ num = ArithToUint256(modifiable);
+}
+
+/**
+ * Utility class to construct auxpow's and manipulate them. This is used
+ * to simulate various scenarios.
+ */
+class CAuxpowBuilder
+{
+public:
+ /** The parent block (with coinbase, not just header). */
+ CBlock parentBlock;
+
+ /** The auxpow's merkle branch (connecting it to the coinbase). */
+ std::vector<uint256> auxpowChainMerkleBranch;
+ /** The auxpow's merkle tree index. */
+ int auxpowChainIndex;
+
+ /**
+ * Initialise everything.
+ * @param baseVersion The parent block's base version to use.
+ * @param chainId The parent block's chain ID to use.
+ */
+ CAuxpowBuilder(int baseVersion, int chainId);
+
+ /**
+ * Set the coinbase's script.
+ * @param scr Set it to this script.
+ */
+ void setCoinbase(const CScript& scr);
+
+ /**
+ * Build the auxpow merkle branch. The member variables will be
+ * set accordingly. This has to be done before constructing the coinbase
+ * itself (which must contain the root merkle hash). When we have the
+ * coinbase afterwards, the member variables can be used to initialise
+ * the CAuxPow object from it.
+ * @param hashAux The merge-mined chain's block hash.
+ * @param h Height of the merkle tree to build.
+ * @param index Index to use in the merkle tree.
+ * @return The root hash, with reversed endian.
+ */
+ std::vector<unsigned char> buildAuxpowChain(const uint256& hashAux, unsigned h, int index);
+
+ /**
+ * Build the finished CAuxPow object. We assume that the auxpowChain
+ * member variables are already set. We use the passed in transaction
+ * as the base. It should (probably) be the parent block's coinbase.
+ * @param tx The base tx to use.
+ * @return The constructed CAuxPow object.
+ */
+ CAuxPow get(const CTransactionRef tx) const;
+
+ /**
+ * Build the finished CAuxPow object from the parent block's coinbase.
+ * @return The constructed CAuxPow object.
+ */
+ inline CAuxPow
+ get() const
+ {
+ assert(!parentBlock.vtx.empty());
+ return get(parentBlock.vtx[0]);
+ }
+
+ /**
+ * Build a data vector to be included in the coinbase. It consists
+ * of the aux hash, the merkle tree size and the nonce. Optionally,
+ * the header can be added as well.
+ * @param header Add the header?
+ * @param hashAux The aux merkle root hash.
+ * @param h Height of the merkle tree.
+ * @param nonce The nonce value to use.
+ * @return The constructed data.
+ */
+ static std::vector<unsigned char> buildCoinbaseData(bool header, const std::vector<unsigned char>& auxRoot, unsigned h, int nonce);
+};
+
+CAuxpowBuilder::CAuxpowBuilder(int baseVersion, int chainId)
+ : auxpowChainIndex(-1)
+{
+ parentBlock.SetBaseVersion(baseVersion, chainId);
+}
+
+void CAuxpowBuilder::setCoinbase(const CScript& scr)
+{
+ CMutableTransaction mtx;
+ mtx.vin.resize(1);
+ mtx.vin[0].prevout.SetNull();
+ mtx.vin[0].scriptSig = scr;
+
+ parentBlock.vtx.clear();
+ parentBlock.vtx.push_back(MakeTransactionRef(std::move(mtx)));
+ parentBlock.hashMerkleRoot = BlockMerkleRoot(parentBlock);
+}
+
+std::vector<unsigned char>
+CAuxpowBuilder::buildAuxpowChain(const uint256& hashAux, unsigned h, int index)
+{
+ auxpowChainIndex = index;
+
+ /* Just use "something" for the branch. Doesn't really matter. */
+ auxpowChainMerkleBranch.clear();
+ for (unsigned i = 0; i < h; ++i)
+ auxpowChainMerkleBranch.push_back(ArithToUint256(arith_uint256(i)));
+
+ const uint256 hash = CAuxPow::CheckMerkleBranch(hashAux, auxpowChainMerkleBranch, index);
+
+ std::vector<unsigned char> res = ToByteVector(hash);
+ std::reverse(res.begin(), res.end());
+
+ return res;
+}
+
+CAuxPow
+CAuxpowBuilder::get(const CTransactionRef tx) const
+{
+ LOCK(cs_main);
+ CAuxPow res(tx);
+ res.InitMerkleBranch(parentBlock, 0);
+
+ res.vChainMerkleBranch = auxpowChainMerkleBranch;
+ res.nChainIndex = auxpowChainIndex;
+ res.parentBlock = parentBlock;
+
+ return res;
+}
+
+std::vector<unsigned char>
+CAuxpowBuilder::buildCoinbaseData(bool header, const std::vector<unsigned char>& auxRoot, unsigned h, int nonce)
+{
+ std::vector<unsigned char> res;
+
+ if (header)
+ res.insert(res.end(), UBEGIN(pchMergedMiningHeader),
+ UEND(pchMergedMiningHeader));
+ res.insert(res.end(), auxRoot.begin(), auxRoot.end());
+
+ const int size = (1 << h);
+ res.insert(res.end(), UBEGIN(size), UEND(size));
+ res.insert(res.end(), UBEGIN(nonce), UEND(nonce));
+
+ return res;
+}
+
+/* ************************************************************************** */
+
+BOOST_AUTO_TEST_CASE(check_auxpow)
+{
+ const Consensus::Params& params = Params().GetConsensus();
+ CAuxpowBuilder builder(5, 42);
+ CAuxPow auxpow;
+
+ const uint256 hashAux = ArithToUint256(arith_uint256(12345));
+ const int32_t ourChainId = params.nAuxpowChainId;
+ const unsigned height = 30;
+ const int nonce = 7;
+ int index;
+
+ std::vector<unsigned char> auxRoot, data;
+ CScript scr;
+
+ /* Build a correct auxpow. The height is the maximally allowed one. */
+ index = CAuxPow::getExpectedIndex(nonce, ourChainId, height);
+ auxRoot = builder.buildAuxpowChain(hashAux, height, index);
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS;
+ scr = (scr << OP_2 << data);
+ builder.setCoinbase(scr);
+ BOOST_CHECK(builder.get().check(hashAux, ourChainId, params));
+
+ /* Check that the auxpow is invalid if we change either the aux block's
+ hash or the chain ID. */
+ uint256 modifiedAux(hashAux);
+ tamperWith(modifiedAux);
+ BOOST_CHECK(!builder.get().check(modifiedAux, ourChainId, params));
+ BOOST_CHECK(!builder.get().check(hashAux, ourChainId + 1, params));
+
+ /* Non-coinbase parent tx should fail. Note that we can't just copy
+ the coinbase literally, as we have to get a tx with different hash. */
+ const CTransactionRef oldCoinbase = builder.parentBlock.vtx[0];
+ builder.setCoinbase(scr << 5);
+ builder.parentBlock.vtx.push_back(oldCoinbase);
+ builder.parentBlock.hashMerkleRoot = BlockMerkleRoot(builder.parentBlock);
+ auxpow = builder.get(builder.parentBlock.vtx[0]);
+ BOOST_CHECK(auxpow.check(hashAux, ourChainId, params));
+ auxpow = builder.get(builder.parentBlock.vtx[1]);
+ BOOST_CHECK(!auxpow.check(hashAux, ourChainId, params));
+
+ /* The parent chain can't have the same chain ID. */
+ CAuxpowBuilder builder2(builder);
+ builder2.parentBlock.SetChainId(100);
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+ builder2.parentBlock.SetChainId(ourChainId);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ /* Disallow too long merkle branches. */
+ builder2 = builder;
+ index = CAuxPow::getExpectedIndex(nonce, ourChainId, height + 1);
+ auxRoot = builder2.buildAuxpowChain(hashAux, height + 1, index);
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height + 1, nonce);
+ scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS;
+ scr = (scr << OP_2 << data);
+ builder2.setCoinbase(scr);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ /* Verify that we compare correctly to the parent block's merkle root. */
+ builder2 = builder;
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+ tamperWith(builder2.parentBlock.hashMerkleRoot);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ /* Build a non-header legacy version and check that it is also accepted. */
+ builder2 = builder;
+ index = CAuxPow::getExpectedIndex(nonce, ourChainId, height);
+ auxRoot = builder2.buildAuxpowChain(hashAux, height, index);
+ data = CAuxpowBuilder::buildCoinbaseData(false, auxRoot, height, nonce);
+ scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS;
+ scr = (scr << OP_2 << data);
+ builder2.setCoinbase(scr);
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+
+ /* However, various attempts at smuggling two roots in should be detected. */
+
+ const std::vector<unsigned char> wrongAuxRoot = builder2.buildAuxpowChain(modifiedAux, height, index);
+ std::vector<unsigned char> data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot, height, nonce);
+ builder2.setCoinbase(CScript() << data << data2);
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+ builder2.setCoinbase(CScript() << data2 << data);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ data2 = CAuxpowBuilder::buildCoinbaseData(true, wrongAuxRoot, height, nonce);
+ builder2.setCoinbase(CScript() << data << data2);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+ builder2.setCoinbase(CScript() << data2 << data);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ builder2.setCoinbase(CScript() << data << data2);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+ builder2.setCoinbase(CScript() << data2 << data);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot,
+ height, nonce);
+ builder2.setCoinbase(CScript() << data << data2);
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+ builder2.setCoinbase(CScript() << data2 << data);
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+
+ /* Verify that the appended nonce/size values are checked correctly. */
+
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ builder2.setCoinbase(CScript() << data);
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+
+ data.pop_back();
+ builder2.setCoinbase(CScript() << data);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height - 1, nonce);
+ builder2.setCoinbase(CScript() << data);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce + 3);
+ builder2.setCoinbase(CScript() << data);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ /* Put the aux hash in an invalid merkle tree position. */
+
+ auxRoot = builder.buildAuxpowChain(hashAux, height, index + 1);
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ builder2.setCoinbase(CScript() << data);
+ BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
+
+ auxRoot = builder.buildAuxpowChain(hashAux, height, index);
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ builder2.setCoinbase(CScript() << data);
+ BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
+}
+
+/* ************************************************************************** */
+
+/**
+ * Mine a block (assuming minimal difficulty) that either matches
+ * or doesn't match the difficulty target specified in the block header.
+ * @param block The block to mine (by updating nonce).
+ * @param ok Whether the block should be ok for PoW.
+ * @param nBits Use this as difficulty if specified.
+ */
+static void
+mineBlock(CBlockHeader& block, bool ok, int nBits = -1)
+{
+ if (nBits == -1)
+ nBits = block.nBits;
+
+ arith_uint256 target;
+ target.SetCompact(nBits);
+
+ block.nNonce = 0;
+ while (true) {
+ const bool nowOk = (UintToArith256(block.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_AUTO_TEST_CASE(auxpow_pow)
+{
+ /* 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));
+
+ block.nVersion = 2;
+ mineBlock(block, true);
+ BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
+
+ block.SetBaseVersion(2, params.nAuxpowChainId);
+ mineBlock(block, true);
+ BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
+
+ block.SetChainId(params.nAuxpowChainId + 1);
+ mineBlock(block, true);
+ BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
+
+ /* Check the case when the block does not have auxpow (this is true
+ right now). */
+
+ block.SetChainId(params.nAuxpowChainId);
+ block.SetAuxpowFlag(true);
+ mineBlock(block, true);
+ BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
+
+ block.SetAuxpowFlag(false);
+ mineBlock(block, true);
+ BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
+ mineBlock(block, false);
+ BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
+
+ /* ****************************************** */
+ /* Check the case that the block has auxpow. */
+
+ CAuxpowBuilder builder(5, 42);
+ CAuxPow auxpow;
+ const int32_t ourChainId = params.nAuxpowChainId;
+ const unsigned height = 3;
+ const int nonce = 7;
+ const int index = CAuxPow::getExpectedIndex(nonce, ourChainId, height);
+ std::vector<unsigned char> auxRoot, data;
+
+ /* Valid auxpow, PoW check of parent block. */
+ block.SetAuxpowFlag(true);
+ auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index);
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ builder.setCoinbase(CScript() << data);
+ mineBlock(builder.parentBlock, false, block.nBits);
+ block.SetAuxpow(new CAuxPow(builder.get()));
+ BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
+ mineBlock(builder.parentBlock, true, block.nBits);
+ block.SetAuxpow(new CAuxPow(builder.get()));
+ BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
+
+ /* Mismatch between auxpow being present and block.nVersion. Note that
+ block.SetAuxpow sets also the version and that we want to ensure
+ that the block hash itself doesn't change due to version changes.
+ This requires some work arounds. */
+ block.SetAuxpowFlag(false);
+ const uint256 hashAux = block.GetHash();
+ auxRoot = builder.buildAuxpowChain(hashAux, height, index);
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ builder.setCoinbase(CScript() << data);
+ mineBlock(builder.parentBlock, true, block.nBits);
+ block.SetAuxpow(new CAuxPow(builder.get()));
+ BOOST_CHECK(hashAux != block.GetHash());
+ block.SetAuxpowFlag(false);
+ BOOST_CHECK(hashAux == block.GetHash());
+ BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
+
+ /* Modifying the block invalidates the PoW. */
+ block.SetAuxpowFlag(true);
+ auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index);
+ data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
+ builder.setCoinbase(CScript() << data);
+ mineBlock(builder.parentBlock, true, block.nBits);
+ block.SetAuxpow(new CAuxPow(builder.get()));
+ BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
+ tamperWith(block.hashMerkleRoot);
+ BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
+}
+
+/* ************************************************************************** */
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 0b96d7507..a9ce7d1ca 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -210,9 +210,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
pblock->nVersion = 1;
- // Replaced chainActive.Tip()->GetMedianTimePast()+1 with an actual 60 second block
- // interval because median([1,2,2,3,3,3,4,4,4,4]) will eventually be problematic re:
- // block timing. Tests should be more stable than that.
pblock->nTime = chainActive.Tip()->GetBlockTime() + 60;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 0f11b5f0c..b27ad8010 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -200,10 +200,11 @@ bool CBlockTreeDB::LoadBlockIndexGuts(boost::function<CBlockIndex*(const uint256
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
- pindexNew->hashBlockPoW = diskindex.hashBlockPoW;
- if (!CheckProofOfWork(pindexNew->GetBlockPoWHash(), pindexNew->nBits, Params().GetConsensus()))
- return error("LoadBlockIndex(): CheckProofOfWork failed: %s", 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 ff5644ff8..eb02a1dd2 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1130,7 +1130,11 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea
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();
@@ -1148,22 +1152,38 @@ 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)
{
- if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams))
+ if (!ReadBlockOrHeader(block, pindex->GetBlockPos(), consensusParams))
return false;
if (block.GetHash() != pindex->GetBlockHash())
- return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
+ return error("ReadBlockOrHeader(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
pindex->ToString(), pindex->GetBlockPos().ToString());
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);
+}
+
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
@@ -2982,6 +3002,14 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
const int nHeight = pindexPrev == NULL ? 0 : 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");
+
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
@@ -2997,10 +3025,10 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
// 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;
}
diff --git a/src/validation.h b/src/validation.h
index 9c606f241..bd7d4b9a4 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -472,6 +472,7 @@ public:
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
+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/wallet.cpp b/src/wallet/wallet.cpp
index 2697a3769..983aff862 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -57,8 +57,6 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
-const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
-
/** @defgroup mapWallet
*
* @{
@@ -3934,44 +3932,3 @@ CWalletKey::CWalletKey(int64_t nExpires)
nTimeCreated = (nExpires ? GetTime() : 0);
nTimeExpires = nExpires;
}
-
-void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
-{
- // Update the tx's hashBlock
- hashBlock = pindex->GetBlockHash();
-
- // set the position of the transaction in the block
- nIndex = posInBlock;
-}
-
-int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
-{
- if (hashUnset())
- return 0;
-
- AssertLockHeld(cs_main);
-
- // Find the block it claims to be in
- BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
- if (mi == mapBlockIndex.end())
- return 0;
- CBlockIndex* pindex = (*mi).second;
- if (!pindex || !chainActive.Contains(pindex))
- return 0;
-
- pindexRet = pindex;
- return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
-}
-
-int CMerkleTx::GetBlocksToMaturity() const
-{
- if (!IsCoinBase())
- return 0;
- return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
-}
-
-
-bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
-{
- return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
-}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 98e4fb87b..8cf4a83ae 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"
#include "streams.h"
#include "tinyformat.h"
#include "ui_interface.h"
@@ -168,84 +169,6 @@ struct COutputEntry
int vout;
};
-/** A transaction with a merkle branch linking it to the block chain. */
-class CMerkleTx
-{
-private:
- /** Constant used in hashBlock to indicate tx has been abandoned */
- static const uint256 ABANDON_HASH;
-
-public:
- CTransactionRef tx;
- uint256 hashBlock;
-
- /* 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(CTransactionRef arg)
- {
- SetTx(std::move(arg));
- Init();
- }
-
- /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
- * TODO: adapt callers and remove this operator. */
- operator const CTransaction&() const { return *tx; }
-
- void Init()
- {
- hashBlock = uint256();
- nIndex = -1;
- }
-
- void SetTx(CTransactionRef arg)
- {
- tx = std::move(arg);
- }
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- std::vector<uint256> vMerkleBranch; // For compatibility with older versions.
- READWRITE(tx);
- READWRITE(hashBlock);
- READWRITE(vMerkleBranch);
- READWRITE(nIndex);
- }
-
- void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock);
-
- /**
- * Return depth of transaction in blockchain:
- * <0 : conflicts with a transaction this deep in the blockchain
- * 0 : in memory pool, waiting to be included in a block
- * >=1 : this many blocks deep in the main chain
- */
- int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
- int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
- bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
- int GetBlocksToMaturity() const;
- /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
- bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
- bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
- void setAbandoned() { hashBlock = ABANDON_HASH; }
-
- const uint256& GetHash() const { return tx->GetHash(); }
- bool IsCoinBase() const { return tx->IsCoinBase(); }
-};
-
/**
* A transaction with a bunch of additional info that only the owner cares about.
* It includes any unrecorded transactions needed to link it back to the block chain.