diff options
| author | Max K <[email protected]> | 2019-07-14 19:35:30 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2019-07-14 19:35:30 +0200 |
| commit | cee13699a5676355487f8eb2d91985f63438eae4 (patch) | |
| tree | cf12be6180f950a25ee2ee7f3f2126542835d6e3 /src | |
| parent | Correct build and test net seed (diff) | |
| parent | Handle legacy v2 block at #66064 (diff) | |
| download | discoin-1.17-dev.tar.xz discoin-1.17-dev.zip | |
Merge pull request #1546 from rnicoll/1.17-auxpow1.17-dev
1.17 AuxPoW support
Diffstat (limited to 'src')
40 files changed, 2095 insertions, 163 deletions
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(); } }; |