aboutsummaryrefslogtreecommitdiff
path: root/src/auxpow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/auxpow.cpp')
-rw-r--r--src/auxpow.cpp198
1 files changed, 198 insertions, 0 deletions
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;
+}