diff --git a/qa/rpc-tests/getauxblock.py b/qa/rpc-tests/getauxblock.py new file mode 100755 index 000000000..dfb57be8d --- /dev/null +++ b/qa/rpc-tests/getauxblock.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# Copyright (c) 2014-2015 Daniel Kraft +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Test the "getauxblock" merge-mining RPC interface. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +from test_framework import auxpow + +class GetAuxBlockTest (BitcoinTestFramework): + + def run_test (self): + BitcoinTestFramework.run_test (self) + + # Generate a block so that we are not "downloading blocks". + self.nodes[0].generate (1) + + # Compare basic data of getauxblock to getblocktemplate. + auxblock = self.nodes[0].getauxblock () + blocktemplate = self.nodes[0].getblocktemplate () + assert_equal (auxblock['coinbasevalue'], blocktemplate['coinbasevalue']) + assert_equal (auxblock['bits'], blocktemplate['bits']) + assert_equal (auxblock['height'], blocktemplate['height']) + assert_equal (auxblock['previousblockhash'], blocktemplate['previousblockhash']) + + # Compare target and take byte order into account. + target = auxblock['_target'] + reversedTarget = auxpow.reverseHex (target) + assert_equal (reversedTarget, blocktemplate['target']) + + # Verify data that can be found in another way. + assert_equal (auxblock['chainid'], 1) + assert_equal (auxblock['height'], self.nodes[0].getblockcount () + 1) + assert_equal (auxblock['previousblockhash'], self.nodes[0].getblockhash (auxblock['height'] - 1)) + + # Calling again should give the same block. + auxblock2 = self.nodes[0].getauxblock () + assert_equal (auxblock2, auxblock) + + # If we receive a new block, the old hash will be replaced. + self.sync_all () + self.nodes[1].generate (1) + self.sync_all () + auxblock2 = self.nodes[0].getauxblock () + assert auxblock['hash'] != auxblock2['hash'] + try: + self.nodes[0].getauxblock (auxblock['hash'], "x") + raise AssertionError ("invalid block hash accepted") + except JSONRPCException as exc: + assert_equal (exc.error['code'], -8) + + # Invalid format for auxpow. + try: + self.nodes[0].getauxblock (auxblock2['hash'], "x") + raise AssertionError ("malformed auxpow accepted") + except JSONRPCException as exc: + assert_equal (exc.error['code'], -1) + + # Invalidate the block again, send a transaction and query for the + # auxblock to solve that contains the transaction. + self.nodes[0].generate (1) + addr = self.nodes[1].getnewaddress () + txid = self.nodes[0].sendtoaddress (addr, 1) + self.sync_all () + assert_equal (self.nodes[1].getrawmempool (), [txid]) + auxblock = self.nodes[0].getauxblock () + blocktemplate = self.nodes[0].getblocktemplate () + target = blocktemplate['target'] + + # Compute invalid auxpow. + apow = auxpow.computeAuxpow (auxblock['hash'], target, False) + res = self.nodes[0].getauxblock (auxblock['hash'], apow) + assert not res + + # Compute and submit valid auxpow. + apow = auxpow.computeAuxpow (auxblock['hash'], target, True) + res = self.nodes[0].getauxblock (auxblock['hash'], apow) + assert res + + # Make sure that the block is indeed accepted. + self.sync_all () + assert_equal (self.nodes[1].getrawmempool (), []) + height = self.nodes[1].getblockcount () + assert_equal (height, auxblock['height']) + assert_equal (self.nodes[1].getblockhash (height), auxblock['hash']) + + # Call getblock and verify the auxpow field. + data = self.nodes[1].getblock (auxblock['hash']) + assert 'auxpow' in data + auxJson = data['auxpow'] + assert_equal (auxJson['index'], 0) + assert_equal (auxJson['parentblock'], apow[-160:]) + + # Check that previous blocks don't have 'auxpow' in their getblock JSON. + oldHash = self.nodes[1].getblockhash (100) + data = self.nodes[1].getblock (oldHash) + assert 'auxpow' not in data + + # Check that it paid correctly to the first node. + t = self.nodes[0].listtransactions ("", 1) + assert_equal (len (t), 1) + t = t[0] + assert_equal (t['category'], "immature") + assert_equal (t['blockhash'], auxblock['hash']) + assert t['generated'] + assert t['amount'] >= Decimal ("25") + assert_equal (t['confirmations'], 1) + + # Verify the coinbase script. Ensure that it includes the block height + # to make the coinbase tx unique. The expected block height is around + # 200, so that the serialisation of the CScriptNum ends in an extra 00. + # The vector has length 2, which makes up for 02XX00 as the serialised + # height. Check this. + blk = self.nodes[1].getblock (auxblock['hash']) + tx = self.nodes[1].getrawtransaction (blk['tx'][0], 1) + coinbase = tx['vin'][0]['coinbase'] + assert_equal ("02%02x00" % auxblock['height'], coinbase[0 : 6]) + +if __name__ == '__main__': + GetAuxBlockTest ().main () diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index b769cd71f..99a1c49b0 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -8,6 +8,7 @@ # +from test_framework import auxpow from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from struct import * @@ -203,8 +204,10 @@ class RESTTest (BitcoinTestFramework): response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True) assert_equal(response.status, 200) #must be a 200 because we are within the limits - self.nodes[0].generate(1) #generate block to not affect upcoming tests + # Generate a block to not affect upcoming tests. + auxpow.mineAuxpowBlock(self.nodes[0]) #generate self.sync_all() + bb_hash = self.nodes[0].getbestblockhash() ################ # /rest/block/ # @@ -219,24 +222,26 @@ class RESTTest (BitcoinTestFramework): # compare with block header response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True) assert_equal(response_header.status, 200) - assert_equal(int(response_header.getheader('content-length')), 80) + headerLen = int(response_header.getheader('content-length')) + assert_greater_than(headerLen, 80) response_header_str = response_header.read() - assert_equal(response_str[0:80], response_header_str) + assert_equal(response_str[0:headerLen], response_header_str) # check block hex format response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True) assert_equal(response_hex.status, 200) assert_greater_than(int(response_hex.getheader('content-length')), 160) - response_hex_str = response_hex.read() - assert_equal(encode(response_str, "hex_codec")[0:160], response_hex_str[0:160]) + response_hex_str = response_hex.read().strip() + assert_equal(encode(response_str, "hex_codec"), response_hex_str) # compare with hex block header response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True) assert_equal(response_header_hex.status, 200) assert_greater_than(int(response_header_hex.getheader('content-length')), 160) - response_header_hex_str = response_header_hex.read() - assert_equal(response_hex_str[0:160], response_header_hex_str[0:160]) - assert_equal(encode(response_header_str, "hex_codec")[0:160], response_header_hex_str[0:160]) + response_header_hex_str = response_header_hex.read().strip() + headerLen = len (response_header_hex_str) + assert_equal(response_hex_str[0:headerLen], response_header_hex_str) + assert_equal(encode(response_header_str, "hex_codec"), response_header_hex_str) # check json format block_json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json') diff --git a/qa/rpc-tests/test_framework/auxpow.py b/qa/rpc-tests/test_framework/auxpow.py new file mode 100644 index 000000000..7027a712b --- /dev/null +++ b/qa/rpc-tests/test_framework/auxpow.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# Copyright (c) 2014 Daniel Kraft +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# General code for auxpow testing. This includes routines to +# solve an auxpow and to generate auxpow blocks. + +import binascii +import hashlib + +def computeAuxpow (block, target, ok): + """ + Build an auxpow object (serialised as hex string) that solves + (ok = True) or doesn't solve (ok = False) the block. + """ + + # Start by building the merge-mining coinbase. The merkle tree + # consists only of the block hash as root. + coinbase = "fabe" + binascii.hexlify ("m" * 2) + coinbase += block + coinbase += "01000000" + ("00" * 4) + + # Construct "vector" of transaction inputs. + vin = "01" + vin += ("00" * 32) + ("ff" * 4) + vin += ("%02x" % (len (coinbase) / 2)) + coinbase + vin += ("ff" * 4) + + # Build up the full coinbase transaction. It consists only + # of the input and has no outputs. + tx = "01000000" + vin + "00" + ("00" * 4) + txHash = doubleHashHex (tx) + + # Construct the parent block header. It need not be valid, just good + # enough for auxpow purposes. + header = "01000000" + header += "00" * 32 + header += reverseHex (txHash) + header += "00" * 4 + header += "00" * 4 + header += "00" * 4 + + # Mine the block. + (header, blockhash) = mineBlock (header, target, ok) + + # Build the MerkleTx part of the auxpow. + auxpow = tx + auxpow += blockhash + auxpow += "00" + auxpow += "00" * 4 + + # Extend to full auxpow. + auxpow += "00" + auxpow += "00" * 4 + auxpow += header + + return auxpow + +def mineAuxpowBlock (node): + """ + Mine an auxpow block on the given RPC connection. + """ + + auxblock = node.getauxblock () + target = reverseHex (auxblock['_target']) + apow = computeAuxpow (auxblock['hash'], target, True) + res = node.getauxblock (auxblock['hash'], apow) + assert res + +def mineBlock (header, target, ok): + """ + Given a block header, update the nonce until it is ok (or not) + for the given target. + """ + + data = bytearray (binascii.unhexlify (header)) + while True: + assert data[79] < 255 + data[79] += 1 + hexData = binascii.hexlify (data) + + blockhash = doubleHashHex (hexData) + if (ok and blockhash < target) or ((not ok) and blockhash > target): + break + + return (hexData, blockhash) + +def doubleHashHex (data): + """ + Perform Bitcoin's Double-SHA256 hash on the given hex string. + """ + + hasher = hashlib.sha256 () + hasher.update (binascii.unhexlify (data)) + data = hasher.digest () + + hasher = hashlib.sha256 () + hasher.update (data) + + return reverseHex (hasher.hexdigest ()) + +def reverseHex (data): + """ + Flip byte order in the given data (hex string). + """ + + b = bytearray (binascii.unhexlify (data)) + b.reverse () + + return binascii.hexlify (b) diff --git a/src/Makefile.am b/src/Makefile.am index 1cc8a0055..c78cc83ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,6 +78,7 @@ endif BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ + auxpow.h \ base58.h \ bloom.h \ blockencodings.h \ @@ -121,6 +122,8 @@ BITCOIN_CORE_H = \ policy/policy.h \ policy/rbf.h \ pow.h \ + primitives/block.h \ + primitives/pureheader.h \ protocol.h \ random.h \ reverselock.h \ @@ -300,6 +303,8 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ amount.cpp \ + arith_uint256.cpp \ + auxpow.cpp \ base58.cpp \ chainparams.cpp \ coins.cpp \ @@ -310,6 +315,9 @@ libbitcoin_common_a_SOURCES = \ keystore.cpp \ netaddress.cpp \ netbase.cpp \ + primitives/block.cpp \ + primitives/pureheader.cpp \ + primitives/transaction.cpp \ protocol.cpp \ scheduler.cpp \ script/sign.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 957b6729b..c0304337a 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -82,6 +82,7 @@ BITCOIN_TESTS =\ test/addrman_tests.cpp \ test/amount_tests.cpp \ test/allocator_tests.cpp \ + test/auxpow_tests.cpp \ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ diff --git a/src/auxpow.cpp b/src/auxpow.cpp new file mode 100644 index 000000000..46fe6f650 --- /dev/null +++ b/src/auxpow.cpp @@ -0,0 +1,238 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 Vince Durham +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2016 Daniel Kraft +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include "auxpow.h" + +#include "compat/endian.h" +#include "consensus/consensus.h" +#include "consensus/merkle.h" +#include "consensus/validation.h" +#include "hash.h" +#include "script/script.h" +#include "txmempool.h" +#include "util.h" +#include "utilstrencodings.h" +#include "validation.h" + +#include + +/* Moved from wallet.cpp. CMerkleTx is necessary for auxpow, independent + of an enabled (or disabled) wallet. Always include the code. */ + +const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); + +void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) +{ + // Update the tx's hashBlock + hashBlock = pindex->GetBlockHash(); + + // set the position of the transaction in the block + nIndex = posInBlock; +} + +void CMerkleTx::InitMerkleBranch(const CBlock& block, int posInBlock) +{ + hashBlock = block.GetHash(); + nIndex = posInBlock; + vMerkleBranch = BlockMerkleBranch(block, nIndex); +} + +int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const +{ + if (hashUnset()) + return 0; + + AssertLockHeld(cs_main); + + // Find the block it claims to be in + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + pindexRet = pindex; + return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); +} + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return std::max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) +{ + return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee); +} + +/* ************************************************************************** */ + +bool +CAuxPow::check(const uint256& hashAuxBlock, int nChainId, + const Consensus::Params& params) const +{ + if (nIndex != 0) + return error("AuxPow is not a generate"); + + if (params.fStrictChainId && parentBlock.GetChainId () == nChainId) + return error("Aux POW parent has our chain ID"); + + if (vChainMerkleBranch.size() > 30) + return error("Aux POW chain merkle branch too long"); + + // Check that the chain merkle root is in the coinbase + const uint256 nRootHash + = CheckMerkleBranch(hashAuxBlock, vChainMerkleBranch, nChainIndex); + std::vector vchRootHash(nRootHash.begin(), nRootHash.end()); + std::reverse(vchRootHash.begin(), vchRootHash.end()); // correct endian + + // Check that we are in the parent block merkle tree + if (CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) + != parentBlock.hashMerkleRoot) + return error("Aux POW merkle root incorrect"); + + const CScript script = tx->vin[0].scriptSig; + + // Check that the same work is not submitted twice to our chain. + // + + CScript::const_iterator pcHead = + std::search(script.begin(), script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)); + + CScript::const_iterator pc = + std::search(script.begin(), script.end(), vchRootHash.begin(), vchRootHash.end()); + + if (pc == script.end()) + return error("Aux POW missing chain merkle root in parent coinbase"); + + if (pcHead != script.end()) + { + // Enforce only one chain merkle root by checking that a single instance of the merged + // mining header exists just before. + if (script.end() != std::search(pcHead + 1, script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader))) + return error("Multiple merged mining headers in coinbase"); + if (pcHead + sizeof(pchMergedMiningHeader) != pc) + return error("Merged mining header is not just before chain merkle root"); + } + else + { + // For backward compatibility. + // Enforce only one chain merkle root by checking that it starts early in the coinbase. + // 8-12 bytes are enough to encode extraNonce and nBits. + if (pc - script.begin() > 20) + return error("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase"); + } + + + // Ensure we are at a deterministic point in the merkle leaves by hashing + // a nonce and our chain ID and comparing to the index. + pc += vchRootHash.size(); + if (script.end() - pc < 8) + return error("Aux POW missing chain merkle tree size and nonce in parent coinbase"); + + uint32_t nSize; + memcpy(&nSize, &pc[0], 4); + nSize = le32toh(nSize); + const unsigned merkleHeight = vChainMerkleBranch.size(); + if (nSize != (1u << merkleHeight)) + return error("Aux POW merkle branch size does not match parent coinbase"); + + uint32_t nNonce; + memcpy(&nNonce, &pc[4], 4); + nNonce = le32toh (nNonce); + if (nChainIndex != getExpectedIndex (nNonce, nChainId, merkleHeight)) + return error("Aux POW wrong index"); + + return true; +} + +int +CAuxPow::getExpectedIndex (uint32_t nNonce, int nChainId, unsigned h) +{ + // Choose a pseudo-random slot in the chain merkle tree + // but have it be fixed for a size/nonce/chain combination. + // + // This prevents the same work from being used twice for the + // same chain while reducing the chance that two chains clash + // for the same slot. + + /* This computation can overflow the uint32 used. This is not an issue, + though, since we take the mod against a power-of-two in the end anyway. + This also ensures that the computation is, actually, consistent + even if done in 64 bits as it was in the past on some systems. + + Note that h is always <= 30 (enforced by the maximum allowed chain + merkle branch length), so that 32 bits are enough for the computation. */ + + uint32_t rand = nNonce; + rand = rand * 1103515245 + 12345; + rand += nChainId; + rand = rand * 1103515245 + 12345; + + return rand % (1 << h); +} + +uint256 +CAuxPow::CheckMerkleBranch (uint256 hash, + const std::vector& vMerkleBranch, + int nIndex) +{ + if (nIndex == -1) + return uint256 (); + for (std::vector::const_iterator it(vMerkleBranch.begin ()); + it != vMerkleBranch.end (); ++it) + { + if (nIndex & 1) + hash = Hash (BEGIN (*it), END (*it), BEGIN (hash), END (hash)); + else + hash = Hash (BEGIN (hash), END (hash), BEGIN (*it), END (*it)); + nIndex >>= 1; + } + return hash; +} + +void +CAuxPow::initAuxPow (CBlockHeader& header) +{ + /* Set auxpow flag right now, since we take the block hash below. */ + header.SetAuxpowFlag(true); + + /* Build a minimal coinbase script input for merge-mining. */ + const uint256 blockHash = header.GetHash (); + std::vector inputData(blockHash.begin (), blockHash.end ()); + std::reverse (inputData.begin (), inputData.end ()); + inputData.push_back (1); + inputData.insert (inputData.end (), 7, 0); + + /* Fake a parent-block coinbase with just the required input + script and no outputs. */ + CMutableTransaction coinbase; + coinbase.vin.resize(1); + coinbase.vin[0].prevout.SetNull(); + coinbase.vin[0].scriptSig = (CScript () << inputData); + assert (coinbase.vout.empty()); + CTransactionRef coinbaseRef = MakeTransactionRef(coinbase); + + /* Build a fake parent block with the coinbase. */ + CBlock parent; + parent.nVersion = 1; + parent.vtx.resize(1); + parent.vtx[0] = coinbaseRef; + parent.hashMerkleRoot = BlockMerkleRoot(parent); + + /* Construct the auxpow object. */ + header.SetAuxpow(new CAuxPow(coinbaseRef)); + assert (header.auxpow->vChainMerkleBranch.empty()); + header.auxpow->nChainIndex = 0; + assert (header.auxpow->vMerkleBranch.empty()); + header.auxpow->nIndex = 0; + header.auxpow->parentBlock = parent; +} diff --git a/src/auxpow.h b/src/auxpow.h new file mode 100644 index 000000000..06c13da0c --- /dev/null +++ b/src/auxpow.h @@ -0,0 +1,226 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2016 Daniel Kraft +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_AUXPOW_H +#define BITCOIN_AUXPOW_H + +#include "consensus/params.h" +#include "consensus/validation.h" +#include "primitives/pureheader.h" +#include "primitives/transaction.h" +#include "serialize.h" +#include "uint256.h" + +#include + +class CBlock; +class CBlockHeader; +class CBlockIndex; + +/** Header for merge-mining data in the coinbase. */ +static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' }; + +/* Because it is needed for auxpow, the definition of CMerkleTx is moved + here from wallet.h. */ + +/** A transaction with a merkle branch linking it to the block chain. */ +class CMerkleTx +{ +private: + /** Constant used in hashBlock to indicate tx has been abandoned */ + static const uint256 ABANDON_HASH; + +public: + CTransactionRef tx; + uint256 hashBlock; + // Dogecoin TODO: Is this used? If not remove. If it is, I don't think it's actually set + // anywhere. Check with Namecore + std::vector vMerkleBranch; + + /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest + * block in the chain we know this or any in-wallet dependency conflicts + * with. Older clients interpret nIndex == -1 as unconfirmed for backward + * compatibility. + */ + int nIndex; + + CMerkleTx() + { + SetTx(MakeTransactionRef()); + Init(); + } + + CMerkleTx(CTransactionRef arg) + { + SetTx(std::move(arg)); + Init(); + } + + /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected. + * TODO: adapt callers and remove this operator. */ + operator const CTransaction&() const { return *tx; } + + void Init() + { + hashBlock = uint256(); + nIndex = -1; + } + + void SetTx(CTransactionRef arg) + { + tx = std::move(arg); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(tx); + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + } + + /** + * Actually compute the Merkle branch. This is used for unit tests when + * constructing an auxpow. It is not needed for actual production, since + * we do not care in the Namecoin client how the auxpow is constructed + * by a miner. + */ + void InitMerkleBranch(const CBlock& block, int posInBlock); + + void SetMerkleBranch(const CBlockIndex* pindex, int posInBlock); + + /** + * Return depth of transaction in blockchain: + * <0 : conflicts with a transaction this deep in the blockchain + * 0 : in memory pool, waiting to be included in a block + * >=1 : this many blocks deep in the main chain + */ + int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; + int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } + bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; } + int GetBlocksToMaturity() const; + /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ + bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state); + bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } + bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } + void setAbandoned() { hashBlock = ABANDON_HASH; } + + const uint256& GetHash() const { return tx->GetHash(); } + bool IsCoinBase() const { return tx->IsCoinBase(); } +}; + +/** + * Data for the merge-mining auxpow. This is a merkle tx (the parent block's + * coinbase tx) that can be verified to be in the parent block, and this + * transaction's input (the coinbase script) contains the reference + * to the actual merge-mined block. + */ +class CAuxPow : public CMerkleTx +{ + +/* Public for the unit tests. */ +public: + + /** The merkle branch connecting the aux block to our coinbase. */ + std::vector vChainMerkleBranch; + + /** Merkle tree index of the aux block header in the coinbase. */ + int nChainIndex; + + /** Parent block header (on which the real PoW is done). */ + CPureBlockHeader parentBlock; + +public: + + /* Prevent accidental conversion. */ + inline explicit CAuxPow(CTransactionRef txIn) + : CMerkleTx(txIn) + { + } + + inline CAuxPow() + : CMerkleTx() + { + } + + ADD_SERIALIZE_METHODS; + + template + inline void + SerializationOp (Stream& s, Operation ser_action) + { + READWRITE (*static_cast (this)); + READWRITE (vChainMerkleBranch); + READWRITE (nChainIndex); + READWRITE (parentBlock); + } + + /** + * Check the auxpow, given the merge-mined block's hash and our chain ID. + * Note that this does not verify the actual PoW on the parent block! It + * just confirms that all the merkle branches are valid. + * @param hashAuxBlock Hash of the merge-mined block. + * @param nChainId The auxpow chain ID of the block to check. + * @param params Consensus parameters. + * @return True if the auxpow is valid. + */ + bool check(const uint256& hashAuxBlock, int nChainId, const Consensus::Params& params) const; + + /** + * Get the parent block's hash. This is used to verify that it + * satisfies the PoW requirement. + * @return The parent block hash. + */ + inline uint256 + getParentBlockPoWHash() const + { + return parentBlock.GetPoWHash (); + } + + /** + * Return parent block. This is only used for the temporary parentblock + * auxpow version check. + * @return The parent block. + */ + /* FIXME: Remove after the hardfork. */ + inline const CPureBlockHeader& + getParentBlock() const + { + return parentBlock; + } + + /** + * Calculate the expected index in the merkle tree. This is also used + * for the test-suite. + * @param nNonce The coinbase's nonce value. + * @param nChainId The chain ID. + * @param h The merkle block height. + * @return The expected index for the aux hash. + */ + static int getExpectedIndex(uint32_t nNonce, int nChainId, unsigned h); + + /** + * Check a merkle branch. This used to be in CBlock, but was removed + * upstream. Thus include it here now. + */ + static uint256 CheckMerkleBranch(uint256 hash, + const std::vector& vMerkleBranch, + int nIndex); + + /** + * Initialise the auxpow of the given block header. This constructs + * a minimal CAuxPow object with a minimal parent block and sets + * it on the block header. The auxpow is not necessarily valid, but + * can be "mined" to make it valid. + * @param header The header to set the auxpow on. + */ + static void initAuxPow(CBlockHeader& header); + +}; + +#endif // BITCOIN_AUXPOW_H diff --git a/src/chain.cpp b/src/chain.cpp index a5b369c4f..e42d6497f 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -4,6 +4,34 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "chain.h" +#include "validation.h" + +using namespace std; + +/* Moved here from the header, because we need auxpow and the logic + becomes more involved. */ +CBlockHeader CBlockIndex::GetBlockHeader(const Consensus::Params& consensusParams) const +{ + CBlockHeader block; + + /* The CBlockIndex object's block header is missing the auxpow. + So if this is an auxpow block, read it from disk instead. We only + have to read the actual *header*, not the full block. */ + if (block.IsAuxpow()) + { + ReadBlockHeaderFromDisk(block, this, consensusParams); + return block; + } + + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; +} /** * CChain implementation diff --git a/src/chain.h b/src/chain.h index 76d13bd0a..d9a4bdd69 100644 --- a/src/chain.h +++ b/src/chain.h @@ -8,6 +8,7 @@ #include "arith_uint256.h" #include "primitives/block.h" +#include "primitives/pureheader.h" #include "pow.h" #include "tinyformat.h" #include "uint256.h" @@ -199,9 +200,6 @@ public: unsigned int nBits; unsigned int nNonce; - // Dogecoin: Keep the Scrypt hash as well as SHA256 - uint256 hashBlockPoW; - //! (memory only) Sequential id assigned to distinguish order in which blocks are received. int32_t nSequenceId; @@ -224,12 +222,11 @@ public: nSequenceId = 0; nTimeMax = 0; - nVersion = 0; + nVersion = 0; hashMerkleRoot = uint256(); nTime = 0; nBits = 0; nNonce = 0; - hashBlockPoW = uint256(); } CBlockIndex() @@ -246,7 +243,6 @@ public: nTime = block.nTime; nBits = block.nBits; nNonce = block.nNonce; - hashBlockPoW = block.GetPoWHash(); } CDiskBlockPos GetBlockPos() const { @@ -267,29 +263,13 @@ public: return ret; } - CBlockHeader GetBlockHeader() const - { - CBlockHeader block; - block.nVersion = nVersion; - if (pprev) - block.hashPrevBlock = pprev->GetBlockHash(); - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block; - } + CBlockHeader GetBlockHeader(const Consensus::Params& consensusParams) const; uint256 GetBlockHash() const { return *phashBlock; } - uint256 GetBlockPoWHash() const - { - return hashBlockPoW; - } - int64_t GetBlockTime() const { return (int64_t)nTime; @@ -353,6 +333,24 @@ public: //! Efficiently find an ancestor of this block. CBlockIndex* GetAncestor(int height); const CBlockIndex* GetAncestor(int height) const; + + /** + * Extract the chain ID. + * @return The chain ID encoded in the version. + */ + inline int32_t GetChainId() const + { + return nVersion >> 16; + } + + /** + * Check if the auxpow flag is set in the version. + * @return True if this block version is marked as auxpow. + */ + inline bool IsAuxpow() const + { + return nVersion & CPureBlockHeader::VERSION_AUXPOW; + } }; arith_uint256 GetBlockProof(const CBlockIndex& block); @@ -398,7 +396,6 @@ public: READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); - READWRITE(hashBlockPoW); } uint256 GetBlockHash() const diff --git a/src/chainparams.cpp b/src/chainparams.cpp index f1e4fe049..729ab4fde 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -106,7 +106,11 @@ public: // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0xca5eb72f1e0d160f1481f74d56d7cc4a27d91aa585ba012da8018a5fe934d61b"); // 1,600,000 - /** + consensus.nAuxpowChainId = 0x0062; // 98 - Josh Wise! + consensus.nAuxpowStartHeight = 371337; + consensus.fStrictChainId = true; + consensus.nLegacyBlocksBefore = 371337; + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce * a large 32-bit integer with any alignment. @@ -119,6 +123,7 @@ public: nPruneAfterHeight = 100000; genesis = CreateGenesisBlock(1386325540, 99943, 0x1e0ffff0, 1, 88 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("0x1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691")); assert(genesis.hashMerkleRoot == uint256S("0x5b2a3f53f605d62c53e62932dac6925e3d74afa5a4b459745c36d42d0ed26a69")); @@ -213,6 +218,10 @@ public: // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x6943eaeaba98dc7d09f7e73398daccb4abcabb18b66c8c875e52b07638d93951"); // 900,000 + consensus.nAuxpowStartHeight = 158100; + consensus.fStrictChainId = false; + consensus.nLegacyBlocksBefore = -1; + pchMessageStart[0] = 0xfc; pchMessageStart[1] = 0xc1; pchMessageStart[2] = 0xb7; @@ -300,6 +309,8 @@ public: // By default assume that the signatures in ancestors of this block are valid. consensus.defaultAssumeValid = uint256S("0x00"); + consensus.fStrictChainId = true; + consensus.nLegacyBlocksBefore = 0; pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; pchMessageStart[2] = 0xb5; diff --git a/src/chainparams.h b/src/chainparams.h index db524e8f8..22843b886 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -77,6 +77,7 @@ public: const std::vector& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } const ChainTxData& TxData() const { return chainTxData; } + protected: CChainParams() {} diff --git a/src/consensus/params.h b/src/consensus/params.h index 6240e8285..55a7b27b6 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -63,6 +63,24 @@ struct Params { int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; uint256 defaultAssumeValid; + + /** Auxpow parameters */ + int32_t nAuxpowChainId; + int nAuxpowStartHeight; + bool fStrictChainId; + int nLegacyBlocksBefore; // -1 for "always allow" + + /** + * Check whether or not to allow legacy blocks at the given height. + * @param nHeight Height of the block to check. + * @return True if it is allowed to have a legacy version. + */ + bool AllowLegacyBlocks(unsigned nHeight) const + { + if (nLegacyBlocksBefore < 0) + return true; + return static_cast (nHeight) < nLegacyBlocksBefore; + } }; } // namespace Consensus diff --git a/src/dogecoin.cpp b/src/dogecoin.cpp index e946acad6..8ac2782ba 100644 --- a/src/dogecoin.cpp +++ b/src/dogecoin.cpp @@ -21,9 +21,9 @@ unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, in int nHeight = pindexLast->nHeight + 1; bool fNewDifficultyProtocol = (nHeight >= 145000); // bool fNewDifficultyProtocol = (nHeight >= params.GetDigiShieldForkBlock()); - const int64_t retargetTimespan = fNewDifficultyProtocol - ? 60 // params.DigiShieldTargetTimespan() - : params.nPowTargetTimespan; + const int64_t retargetTimespan = fNewDifficultyProtocol ? 60 // params.DigiShieldTargetTimespan() + : + params.nPowTargetTimespan; const int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime; int64_t nModulatedTimespan = nActualTimespan; @@ -75,6 +75,44 @@ unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, in return bnNew.GetCompact(); } +bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params) +{ + /* Except for legacy blocks with full version 1, ensure that + the chain ID is correct. Legacy blocks are not allowed since + the merge-mining start, which is checked in AcceptBlockHeader + where the height is known. */ + if (!block.IsLegacy() && params.fStrictChainId && block.GetChainId() != params.nAuxpowChainId) + return error("%s : block does not have our chain ID" + " (got %d, expected %d, full nVersion %d)", + __func__, block.GetChainId(), + params.nAuxpowChainId, block.nVersion); + + /* If there is no auxpow, just check the block hash. */ + if (!block.auxpow) + { + if (block.IsAuxpow()) + return error("%s : no auxpow on block with auxpow version", + __func__); + + if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, params)) + return error("%s : non-AUX proof of work failed", __func__); + + return true; + } + + /* We have auxpow. Check it. */ + + if (!block.IsAuxpow()) + return error("%s : auxpow on block with non-auxpow version", __func__); + + if (!block.auxpow->check(block.GetHash(), block.GetChainId(), params)) + return error("%s : AUX POW is not valid", __func__); + if (!CheckProofOfWork(block.auxpow->getParentBlockPoWHash(), block.nBits, params)) + return error("%s : AUX proof of work failed", __func__); + + return true; +} + CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash) { int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; diff --git a/src/dogecoin.h b/src/dogecoin.h index cedbfac78..edc07b5f5 100644 --- a/src/dogecoin.h +++ b/src/dogecoin.h @@ -2,9 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "amount.h" #include "chain.h" #include "chainparams.h" -#include "amount.h" CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash); unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, int64_t nLastRetargetTime, const Consensus::Params& params); + +/** + * Check proof-of-work of a block header, taking auxpow into account. + * @param block The block header. + * @param params Consensus parameters. + * @return True iff the PoW is correct. + */ +bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params); diff --git a/src/init.cpp b/src/init.cpp index 4623bac53..6c39b1496 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -200,6 +200,7 @@ void Shutdown() StopRPC(); StopHTTPServer(); #ifdef ENABLE_WALLET + // Dogecoin 1.14 TODO: ShutdownRPCMining(); if (pwalletMain) pwalletMain->Flush(false); #endif @@ -1652,6 +1653,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 12: finished + // Dogecoin: Do we need to do any RPC mining init here? + SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); diff --git a/src/miner.cpp b/src/miner.cpp index 2c50fd94e..bac62821b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -149,11 +149,12 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc CBlockIndex* pindexPrev = chainActive.Tip(); nHeight = pindexPrev->nHeight + 1; - pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + const int32_t nChainId = chainparams.GetConsensus ().nAuxpowChainId; + pblock->SetBaseVersion(ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()), nChainId); // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + pblock->SetBaseVersion(GetArg("-blockversion", pblock->GetBaseVersion()), nChainId); pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 91cc92725..cf6a82805 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1748,7 +1748,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { - vHeaders.push_back(pindex->GetBlockHeader()); + vHeaders.push_back(pindex->GetBlockHeader(chainparams.GetConsensus())); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } @@ -2942,14 +2942,14 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr pBestIndex = pindex; if (fFoundStartingHeader) { // add this to the headers message - vHeaders.push_back(pindex->GetBlockHeader()); + vHeaders.push_back(pindex->GetBlockHeader(consensusParams)); } else if (PeerHasHeader(&state, pindex)) { continue; // keep looking for the first new block } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) { // Peer doesn't have this header but they do have the prior one. // Start sending headers. fFoundStartingHeader = true; - vHeaders.push_back(pindex->GetBlockHeader()); + vHeaders.push_back(pindex->GetBlockHeader(consensusParams)); } else { // Peer doesn't have this header or the prior one -- nothing will // connect, so bail out. diff --git a/src/pow.cpp b/src/pow.cpp index 64628b73f..a8d0efdab 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -5,6 +5,7 @@ #include "pow.h" +#include "auxpow.h" #include "arith_uint256.h" #include "chain.h" #include "dogecoin.h" diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 4cfc576f0..1df912d35 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -6,21 +6,21 @@ #include "primitives/block.h" #include "hash.h" -#include "crypto/scrypt.h" #include "tinyformat.h" #include "utilstrencodings.h" #include "crypto/common.h" -uint256 CBlockHeader::GetHash() const +void CBlockHeader::SetAuxpow (CAuxPow* apow) { - return SerializeHash(*this); -} - -uint256 CBlockHeader::GetPoWHash() const -{ - uint256 thash; - scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash)); - return thash; + if (apow) + { + auxpow.reset(apow); + SetAuxpowFlag(true); + } else + { + auxpow.reset(); + SetAuxpowFlag(false); + } } std::string CBlock::ToString() const diff --git a/src/primitives/block.h b/src/primitives/block.h index 8b2f1de89..dcde500ab 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -6,10 +6,14 @@ #ifndef BITCOIN_PRIMITIVES_BLOCK_H #define BITCOIN_PRIMITIVES_BLOCK_H +#include "auxpow.h" #include "primitives/transaction.h" +#include "primitives/pureheader.h" #include "serialize.h" #include "uint256.h" +#include + /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block @@ -17,16 +21,11 @@ * in the block is a special one that creates a new coin owned by the creator * of the block. */ -class CBlockHeader +class CBlockHeader : public CPureBlockHeader { public: - // header - int32_t nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - uint32_t nTime; - uint32_t nBits; - uint32_t nNonce; + // auxpow (if this is a merge-minded block) + boost::shared_ptr auxpow; CBlockHeader() { @@ -37,37 +36,30 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); - READWRITE(hashPrevBlock); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); + READWRITE(*(CPureBlockHeader*)this); + + if (this->IsAuxpow()) + { + if (ser_action.ForRead()) + auxpow.reset(new CAuxPow()); + assert(auxpow); + READWRITE(*auxpow); + } else if (ser_action.ForRead()) + auxpow.reset(); } void SetNull() { - nVersion = 0; - hashPrevBlock.SetNull(); - hashMerkleRoot.SetNull(); - nTime = 0; - nBits = 0; - nNonce = 0; + CPureBlockHeader::SetNull(); + auxpow.reset(); } - bool IsNull() const - { - return (nBits == 0); - } - - uint256 GetHash() const; - - uint256 GetPoWHash() const; - - int64_t GetBlockTime() const - { - return (int64_t)nTime; - } + /** + * Set the block's auxpow (or unset it). This takes care of updating + * the version accordingly. + * @param apow Pointer to the auxpow to use or NULL. + */ + void SetAuxpow(CAuxPow* apow); }; @@ -115,9 +107,18 @@ public: block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; + block.auxpow = auxpow; return block; } + // Build the in-memory merkle tree for this block and return the merkle root. + // If non-NULL, *mutated is set to whether mutation was detected in the merkle + // tree (a duplication of transactions in the block leading to an identical + // merkle root). + uint256 BuildMerkleTree(bool* mutated = NULL) const; + + std::vector GetMerkleBranch(int nIndex) const; + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex); std::string ToString() const; }; diff --git a/src/primitives/pureheader.cpp b/src/primitives/pureheader.cpp new file mode 100644 index 000000000..9a339bf1f --- /dev/null +++ b/src/primitives/pureheader.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "primitives/pureheader.h" + +#include "chainparams.h" +#include "crypto/scrypt.h" +#include "hash.h" +#include "utilstrencodings.h" + +void CPureBlockHeader::SetBaseVersion(int32_t nBaseVersion, int32_t nChainId) +{ + assert(nBaseVersion >= 1 && nBaseVersion < VERSION_AUXPOW); + assert(!IsAuxpow()); + nVersion = nBaseVersion | (nChainId * VERSION_CHAIN_START); +} + +uint256 CPureBlockHeader::GetHash() const +{ + return SerializeHash(*this); +} + +uint256 CPureBlockHeader::GetPoWHash() const +{ + uint256 thash; + scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash)); + return thash; +} diff --git a/src/primitives/pureheader.h b/src/primitives/pureheader.h new file mode 100644 index 000000000..0443a530d --- /dev/null +++ b/src/primitives/pureheader.h @@ -0,0 +1,156 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_PRIMITIVES_PUREHEADER_H +#define BITCOIN_PRIMITIVES_PUREHEADER_H + +#include "serialize.h" +#include "uint256.h" + +/** + * A block header without auxpow information. This "intermediate step" + * in constructing the full header is useful, because it breaks the cyclic + * dependency between auxpow (referencing a parent block header) and + * the block header (referencing an auxpow). The parent block header + * does not have auxpow itself, so it is a pure header. + */ +class CPureBlockHeader +{ +public: + /* Modifiers to the version. */ + static const int32_t VERSION_AUXPOW = (1 << 8); + + /** Bits above are reserved for the auxpow chain ID. */ + static const int32_t VERSION_CHAIN_START = (1 << 16); + + // header + int32_t nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + uint32_t nTime; + uint32_t nBits; + uint32_t nNonce; + + CPureBlockHeader() + { + SetNull(); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(this->nVersion); + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + } + + void SetNull() + { + nVersion = 0; + hashPrevBlock.SetNull(); + hashMerkleRoot.SetNull(); + nTime = 0; + nBits = 0; + nNonce = 0; + } + + bool IsNull() const + { + return (nBits == 0); + } + + uint256 GetHash() const; + + uint256 GetPoWHash() const; + + int64_t GetBlockTime() const + { + return (int64_t)nTime; + } + + /* Below are methods to interpret the version with respect to + auxpow data and chain ID. This used to be in the CBlockVersion + class, but was moved here when we switched back to nVersion being + a pure int member as preparation to undoing the "abuse" and + allowing BIP9 to work. */ + + /** + * Extract the base version (without modifiers and chain ID). + * @return The base version./ + */ + inline int32_t GetBaseVersion() const + { + return GetBaseVersion(nVersion); + } + static inline int32_t GetBaseVersion(int32_t ver) + { + return ver % VERSION_AUXPOW; + } + + /** + * Set the base version (apart from chain ID and auxpow flag) to + * the one given. This should only be called when auxpow is not yet + * set, to initialise a block! + * @param nBaseVersion The base version. + * @param nChainId The auxpow chain ID. + */ + void SetBaseVersion(int32_t nBaseVersion, int32_t nChainId); + + /** + * Extract the chain ID. + * @return The chain ID encoded in the version. + */ + inline int32_t GetChainId() const + { + return nVersion >> 16; + } + + /** + * Set the chain ID. This is used for the test suite. + * @param ch The chain ID to set. + */ + inline void SetChainId(int32_t chainId) + { + nVersion %= VERSION_CHAIN_START; + nVersion |= chainId * VERSION_CHAIN_START; + } + + /** + * Check if the auxpow flag is set in the version. + * @return True if this block version is marked as auxpow. + */ + inline bool IsAuxpow() const + { + return nVersion & VERSION_AUXPOW; + } + + /** + * Set the auxpow flag. This is used for testing. + * @param auxpow Whether to mark auxpow as true. + */ + inline void SetAuxpowFlag(bool auxpow) + { + if (auxpow) + nVersion |= VERSION_AUXPOW; + else + nVersion &= ~VERSION_AUXPOW; + } + + /** + * Check whether this is a "legacy" block without chain ID. + * @return True if it is. + */ + inline bool IsLegacy() const + { + return nVersion == 1; + } +}; + +#endif // BITCOIN_PRIMITIVES_PUREHEADER_H diff --git a/src/rest.cpp b/src/rest.cpp index 54eefcafe..e9d0a8cef 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -160,8 +160,9 @@ static bool rest_headers(HTTPRequest* req, } CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); + const CChainParams& chainparams = Params(); BOOST_FOREACH(const CBlockIndex *pindex, headers) { - ssHeader << pindex->GetBlockHeader(); + ssHeader << pindex->GetBlockHeader(chainparams.GetConsensus()); } switch (rf) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index fd8f52a5c..92601a5ec 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -9,6 +9,7 @@ #include "checkpoints.h" #include "coins.h" #include "consensus/validation.h" +#include "core_io.h" #include "validation.h" #include "policy/policy.h" #include "primitives/transaction.h" @@ -74,6 +75,42 @@ double GetDifficulty(const CBlockIndex* blockindex) return dDiff; } +UniValue AuxpowToJSON(const CAuxPow& auxpow) +{ + UniValue result(UniValue::VOBJ); + + { + UniValue tx(UniValue::VOBJ); + tx.push_back(Pair("hex", EncodeHexTx(auxpow))); + TxToJSON(auxpow, auxpow.parentBlock.GetHash(), tx); + result.push_back(Pair("tx", tx)); + } + + result.push_back(Pair("index", auxpow.nIndex)); + result.push_back(Pair("chainindex", auxpow.nChainIndex)); + + { + UniValue branch(UniValue::VARR); + BOOST_FOREACH(const uint256& node, auxpow.vMerkleBranch) + branch.push_back(node.GetHex()); + result.push_back(Pair("merklebranch", branch)); + } + + { + UniValue branch(UniValue::VARR); + BOOST_FOREACH(const uint256& node, auxpow.vChainMerkleBranch) + branch.push_back(node.GetHex()); + result.push_back(Pair("chainmerklebranch", branch)); + } + + CDataStream ssParent(SER_NETWORK, PROTOCOL_VERSION); + ssParent << auxpow.parentBlock; + const std::string strHex = HexStr(ssParent.begin(), ssParent.end()); + result.push_back(Pair("parentblock", strHex)); + + return result; +} + UniValue blockheaderToJSON(const CBlockIndex* blockindex) { UniValue result(UniValue::VOBJ); @@ -138,6 +175,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + if (block.auxpow) + result.push_back(Pair("auxpow", AuxpowToJSON(*block.auxpow))); + if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); CBlockIndex *pnext = chainActive.Next(blockindex); @@ -680,7 +720,7 @@ UniValue getblockheader(const JSONRPCRequest& request) if (!fVerbose) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); - ssBlock << pblockindex->GetBlockHeader(); + ssBlock << pblockindex->GetBlockHeader(Params().GetConsensus()); std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); return strHex; } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 33e234a95..70192686b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -121,14 +121,16 @@ UniValue generateBlocks(boost::shared_ptr coinbaseScript, int nG LOCK(cs_main); IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } - while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { - ++pblock->nNonce; + CAuxPow::initAuxPow(*pblock); + CPureBlockHeader& miningHeader = pblock->auxpow->parentBlock; + while (nMaxTries > 0 && miningHeader.nNonce < nInnerLoopCount && !CheckProofOfWork(miningHeader.GetHash(), pblock->nBits, Params().GetConsensus())) { + ++miningHeader.nNonce; --nMaxTries; } if (nMaxTries == 0) { break; } - if (pblock->nNonce == nInnerLoopCount) { + if (miningHeader.nNonce == nInnerLoopCount) { continue; } std::shared_ptr shared_pblock = std::make_shared(*pblock); @@ -925,6 +927,262 @@ UniValue estimatesmartpriority(const JSONRPCRequest& request) return result; } +/* ************************************************************************** */ +/* Merge mining. */ + +/** + * The variables below are used to keep track of created and not yet + * submitted auxpow blocks. Lock them to be sure even for multiple + * RPC threads running in parallel. + */ +static CCriticalSection cs_auxblockCache; +static std::map mapNewBlock; +static std::vector> vNewBlockTemplate; + +static +void AuxMiningCheck() +{ + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, + "Error: Peer-to-peer functionality missing or disabled"); + + if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 + && !Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, + "Dogecoin is not connected!"); + + if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, + "Dogecoin is downloading blocks..."); + + /* This should never fail, since the chain is already + past the point of merge-mining start. Check nevertheless. */ + { + LOCK(cs_main); + if (chainActive.Height() + 1 < Params().GetConsensus().nAuxpowStartHeight) + throw std::runtime_error("mining auxblock method is not yet available"); + } +} + +static +UniValue AuxMiningCreateBlock(const CScript& scriptPubKey) +{ + AuxMiningCheck(); + + LOCK(cs_auxblockCache); + + static unsigned nTransactionsUpdatedLast; + static const CBlockIndex* pindexPrev = nullptr; + static uint64_t nStart; + static CBlock* pblock = nullptr; + static unsigned nExtraNonce = 0; + + // Update block + { + LOCK(cs_main); + if (pindexPrev != chainActive.Tip() + || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast + && GetTime() - nStart > 60)) + { + if (pindexPrev != chainActive.Tip()) + { + // Clear old blocks since they're obsolete now. + mapNewBlock.clear(); + vNewBlockTemplate.clear(); + pblock = nullptr; + } + + // Create new block with nonce = 0 and extraNonce = 1 + std::unique_ptr newBlock + = BlockAssembler(Params()).CreateNewBlock(scriptPubKey); + if (!newBlock) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory"); + + // Update state only when CreateNewBlock succeeded + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + pindexPrev = chainActive.Tip(); + nStart = GetTime(); + + // Finalise it by setting the version and building the merkle root + IncrementExtraNonce(&newBlock->block, pindexPrev, nExtraNonce); + newBlock->block.SetAuxpowFlag(true); + + // Save + pblock = &newBlock->block; + mapNewBlock[pblock->GetHash()] = pblock; + vNewBlockTemplate.push_back(std::move(newBlock)); + } + } + + // At this point, pblock is always initialised: If we make it here + // without creating a new block above, it means that, in particular, + // pindexPrev == chainActive.Tip(). But for that to happen, we must + // already have created a pblock in a previous call, as pindexPrev is + // initialised only when pblock is. + assert(pblock); + + arith_uint256 target; + bool fNegative, fOverflow; + target.SetCompact(pblock->nBits, &fNegative, &fOverflow); + if (fNegative || fOverflow || target == 0) + throw std::runtime_error("invalid difficulty bits in block"); + + UniValue result(UniValue::VOBJ); + result.pushKV("hash", pblock->GetHash().GetHex()); + result.pushKV("chainid", pblock->GetChainId()); + result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); + result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue); + result.pushKV("bits", strprintf("%08x", pblock->nBits)); + result.pushKV("height", static_cast (pindexPrev->nHeight + 1)); + result.pushKV("_target", HexStr(BEGIN(target), END(target))); + + return result; +} + +static +bool AuxMiningSubmitBlock(const std::string& hashHex, + const std::string& auxpowHex) +{ + AuxMiningCheck(); + + LOCK(cs_auxblockCache); + + uint256 hash; + hash.SetHex(hashHex); + + const std::map::iterator mit = mapNewBlock.find(hash); + if (mit == mapNewBlock.end()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown"); + CBlock& block = *mit->second; + + const std::vector vchAuxPow = ParseHex(auxpowHex); + CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION); + CAuxPow pow; + ss >> pow; + block.SetAuxpow(new CAuxPow(pow)); + assert(block.GetHash() == hash); + + submitblock_StateCatcher sc(block.GetHash()); + RegisterValidationInterface(&sc); + std::shared_ptr shared_block + = std::make_shared(block); + bool fAccepted = ProcessNewBlock(Params(), shared_block, true, nullptr); + UnregisterValidationInterface(&sc); + + return fAccepted; +} + +UniValue getauxblock(const JSONRPCRequest& request) +{ + if (request.fHelp + || (request.params.size() != 0 && request.params.size() != 2)) + throw std::runtime_error( + "getauxblock (hash auxpow)\n" + "\nCreate or submit a merge-mined block.\n" + "\nWithout arguments, create a new block and return information\n" + "required to merge-mine it. With arguments, submit a solved\n" + "auxpow for a previously returned block.\n" + "\nArguments:\n" + "1. hash (string, optional) hash of the block to submit\n" + "2. auxpow (string, optional) serialised auxpow found\n" + "\nResult (without arguments):\n" + "{\n" + " \"hash\" (string) hash of the created block\n" + " \"chainid\" (numeric) chain ID for this block\n" + " \"previousblockhash\" (string) hash of the previous block\n" + " \"coinbasevalue\" (numeric) value of the block's coinbase\n" + " \"bits\" (string) compressed target of the block\n" + " \"height\" (numeric) height of the block\n" + " \"_target\" (string) target in reversed byte order, deprecated\n" + "}\n" + "\nResult (with arguments):\n" + "xxxxx (boolean) whether the submitted block was correct\n" + "\nExamples:\n" + + HelpExampleCli("getauxblock", "") + + HelpExampleCli("getauxblock", "\"hash\" \"serialised auxpow\"") + + HelpExampleRpc("getauxblock", "") + ); + + boost::shared_ptr coinbaseScript; + GetMainSignals().ScriptForMining(coinbaseScript); + + // If the keypool is exhausted, no script is returned at all. Catch this. + if (!coinbaseScript) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + //throw an error if no script was provided + if (!coinbaseScript->reserveScript.size()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)"); + + /* Create a new block */ + if (request.params.size() == 0) + return AuxMiningCreateBlock(coinbaseScript->reserveScript); + + /* Submit a block instead. Note that this need not lock cs_main, + since ProcessNewBlock below locks it instead. */ + assert(request.params.size() == 2); + bool fAccepted = AuxMiningSubmitBlock(request.params[0].get_str(), + request.params[1].get_str()); + if (fAccepted) + coinbaseScript->KeepScript(); + + return fAccepted; +} + +UniValue createauxblock(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "createauxblock
\n" + "\ncreate a new block and return information required to merge-mine it.\n" + "\nArguments:\n" + "1. address (string, required) specify coinbase transaction payout address\n" + "\nResult:\n" + "{\n" + " \"hash\" (string) hash of the created block\n" + " \"chainid\" (numeric) chain ID for this block\n" + " \"previousblockhash\" (string) hash of the previous block\n" + " \"coinbasevalue\" (numeric) value of the block's coinbase\n" + " \"bits\" (string) compressed target of the block\n" + " \"height\" (numeric) height of the block\n" + " \"_target\" (string) target in reversed byte order, deprecated\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("createauxblock", "\"address\"") + + HelpExampleRpc("createauxblock", "\"address\"") + ); + + // Check coinbase payout address + CBitcoinAddress coinbaseAddress(request.params[0].get_str()); + if (!coinbaseAddress.IsValid()) + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Invalid coinbase payout address"); + const CScript scriptPubKey + = GetScriptForDestination(coinbaseAddress.Get()); + + return AuxMiningCreateBlock(scriptPubKey); +} + +UniValue submitauxblock(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 2) + throw std::runtime_error( + "submitauxblock \n" + "\nsubmit a solved auxpow for a previously block created by 'createauxblock'.\n" + "\nArguments:\n" + "1. hash (string, required) hash of the block to submit\n" + "2. auxpow (string, required) serialised auxpow found\n" + "\nResult:\n" + "xxxxx (boolean) whether the submitted block was correct\n" + "\nExamples:\n" + + HelpExampleCli("submitauxblock", "\"hash\" \"serialised auxpow\"") + + HelpExampleRpc("submitauxblock", "\"hash\" \"serialised auxpow\"") + ); + + return AuxMiningSubmitBlock(request.params[0].get_str(), + request.params[1].get_str()); +} + static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- @@ -933,6 +1191,9 @@ static const CRPCCommand commands[] = { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","priority_delta","fee_delta"} }, { "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} }, { "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} }, + { "mining", "getauxblock", &getauxblock, true, {"hash", "auxpow"} }, + { "mining", "createauxblock", &createauxblock, true, {"address"} }, + { "mining", "submitauxblock", &submitauxblock, true, {"hash", "auxpow"} }, { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} }, diff --git a/src/test/auxpow_tests.cpp b/src/test/auxpow_tests.cpp new file mode 100644 index 000000000..eaad49eef --- /dev/null +++ b/src/test/auxpow_tests.cpp @@ -0,0 +1,444 @@ +// Copyright (c) 2014-2015 Daniel Kraft +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "auxpow.h" +#include "chainparams.h" +#include "coins.h" +#include "consensus/merkle.h" +#include "dogecoin.h" +#include "primitives/block.h" +#include "script/script.h" +#include "uint256.h" +#include "utilstrencodings.h" +#include "validation.h" + +#include "test/test_bitcoin.h" + +#include + +#include +#include + +BOOST_FIXTURE_TEST_SUITE(auxpow_tests, BasicTestingSetup) + +/* ************************************************************************** */ + +/** + * Tamper with a uint256 (modify it). + * @param num The number to modify. + */ +static void +tamperWith(uint256& num) +{ + arith_uint256 modifiable = UintToArith256(num); + modifiable += 1; + num = ArithToUint256(modifiable); +} + +/** + * Utility class to construct auxpow's and manipulate them. This is used + * to simulate various scenarios. + */ +class CAuxpowBuilder +{ +public: + /** The parent block (with coinbase, not just header). */ + CBlock parentBlock; + + /** The auxpow's merkle branch (connecting it to the coinbase). */ + std::vector 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 buildAuxpowChain(const uint256& hashAux, unsigned h, int index); + + /** + * Build the finished CAuxPow object. We assume that the auxpowChain + * member variables are already set. We use the passed in transaction + * as the base. It should (probably) be the parent block's coinbase. + * @param tx The base tx to use. + * @return The constructed CAuxPow object. + */ + CAuxPow get(const CTransactionRef tx) const; + + /** + * Build the finished CAuxPow object from the parent block's coinbase. + * @return The constructed CAuxPow object. + */ + inline CAuxPow + get() const + { + assert(!parentBlock.vtx.empty()); + return get(parentBlock.vtx[0]); + } + + /** + * Build a data vector to be included in the coinbase. It consists + * of the aux hash, the merkle tree size and the nonce. Optionally, + * the header can be added as well. + * @param header Add the header? + * @param hashAux The aux merkle root hash. + * @param h Height of the merkle tree. + * @param nonce The nonce value to use. + * @return The constructed data. + */ + static std::vector buildCoinbaseData(bool header, const std::vector& 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 +CAuxpowBuilder::buildAuxpowChain(const uint256& hashAux, unsigned h, int index) +{ + auxpowChainIndex = index; + + /* Just use "something" for the branch. Doesn't really matter. */ + auxpowChainMerkleBranch.clear(); + for (unsigned i = 0; i < h; ++i) + auxpowChainMerkleBranch.push_back(ArithToUint256(arith_uint256(i))); + + const uint256 hash = CAuxPow::CheckMerkleBranch(hashAux, auxpowChainMerkleBranch, index); + + std::vector res = ToByteVector(hash); + std::reverse(res.begin(), res.end()); + + return res; +} + +CAuxPow +CAuxpowBuilder::get(const CTransactionRef tx) const +{ + LOCK(cs_main); + CAuxPow res(tx); + res.InitMerkleBranch(parentBlock, 0); + + res.vChainMerkleBranch = auxpowChainMerkleBranch; + res.nChainIndex = auxpowChainIndex; + res.parentBlock = parentBlock; + + return res; +} + +std::vector +CAuxpowBuilder::buildCoinbaseData(bool header, const std::vector& auxRoot, unsigned h, int nonce) +{ + std::vector res; + + if (header) + res.insert(res.end(), UBEGIN(pchMergedMiningHeader), + UEND(pchMergedMiningHeader)); + res.insert(res.end(), auxRoot.begin(), auxRoot.end()); + + const int size = (1 << h); + res.insert(res.end(), UBEGIN(size), UEND(size)); + res.insert(res.end(), UBEGIN(nonce), UEND(nonce)); + + return res; +} + +/* ************************************************************************** */ + +BOOST_AUTO_TEST_CASE(check_auxpow) +{ + const Consensus::Params& params = Params().GetConsensus(); + CAuxpowBuilder builder(5, 42); + CAuxPow auxpow; + + const uint256 hashAux = ArithToUint256(arith_uint256(12345)); + const int32_t ourChainId = params.nAuxpowChainId; + const unsigned height = 30; + const int nonce = 7; + int index; + + std::vector 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 wrongAuxRoot = builder2.buildAuxpowChain(modifiedAux, height, index); + std::vector data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot, height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data2 = CAuxpowBuilder::buildCoinbaseData(true, wrongAuxRoot, height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot, + height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + + /* Verify that the appended nonce/size values are checked correctly. */ + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + + data.pop_back(); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height - 1, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce + 3); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + /* Put the aux hash in an invalid merkle tree position. */ + + auxRoot = builder.buildAuxpowChain(hashAux, height, index + 1); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + auxRoot = builder.buildAuxpowChain(hashAux, height, index); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); +} + +/* ************************************************************************** */ + +/** + * Mine a block (assuming minimal difficulty) that either matches + * or doesn't match the difficulty target specified in the block header. + * @param block The block to mine (by updating nonce). + * @param ok Whether the block should be ok for PoW. + * @param nBits Use this as difficulty if specified. + */ +static void +mineBlock(CBlockHeader& block, bool ok, int nBits = -1) +{ + if (nBits == -1) + nBits = block.nBits; + + arith_uint256 target; + target.SetCompact(nBits); + + block.nNonce = 0; + while (true) { + const bool nowOk = (UintToArith256(block.GetHash()) <= target); + if ((ok && nowOk) || (!ok && !nowOk)) + break; + + ++block.nNonce; + } + + if (ok) + BOOST_CHECK(CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus())); + else + BOOST_CHECK(!CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus())); +} + +BOOST_AUTO_TEST_CASE(auxpow_pow) +{ + /* Use regtest parameters to allow mining with easy difficulty. */ + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + + const arith_uint256 target = (~arith_uint256(0) >> 1); + CBlockHeader block; + block.nBits = target.GetCompact(); + + /* Verify the block version checks. */ + + block.nVersion = 1; + mineBlock(block, true); + BOOST_CHECK(CheckAuxPowProofOfWork(block, params)); + + block.nVersion = 2; + mineBlock(block, true); + BOOST_CHECK(!CheckAuxPowProofOfWork(block, params)); + + block.SetBaseVersion(2, params.nAuxpowChainId); + mineBlock(block, true); + BOOST_CHECK(CheckAuxPowProofOfWork(block, params)); + + block.SetChainId(params.nAuxpowChainId + 1); + mineBlock(block, true); + BOOST_CHECK(!CheckAuxPowProofOfWork(block, params)); + + /* Check the case when the block does not have auxpow (this is true + right now). */ + + block.SetChainId(params.nAuxpowChainId); + block.SetAuxpowFlag(true); + mineBlock(block, true); + BOOST_CHECK(!CheckAuxPowProofOfWork(block, params)); + + block.SetAuxpowFlag(false); + mineBlock(block, true); + BOOST_CHECK(CheckAuxPowProofOfWork(block, params)); + mineBlock(block, false); + BOOST_CHECK(!CheckAuxPowProofOfWork(block, params)); + + /* ****************************************** */ + /* Check the case that the block has auxpow. */ + + CAuxpowBuilder builder(5, 42); + CAuxPow auxpow; + const int32_t ourChainId = params.nAuxpowChainId; + const unsigned height = 3; + const int nonce = 7; + const int index = CAuxPow::getExpectedIndex(nonce, ourChainId, height); + std::vector auxRoot, data; + + /* Valid auxpow, PoW check of parent block. */ + block.SetAuxpowFlag(true); + auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder.setCoinbase(CScript() << data); + mineBlock(builder.parentBlock, false, block.nBits); + block.SetAuxpow(new CAuxPow(builder.get())); + BOOST_CHECK(!CheckAuxPowProofOfWork(block, params)); + mineBlock(builder.parentBlock, true, block.nBits); + block.SetAuxpow(new CAuxPow(builder.get())); + BOOST_CHECK(CheckAuxPowProofOfWork(block, params)); + + /* Mismatch between auxpow being present and block.nVersion. Note that + block.SetAuxpow sets also the version and that we want to ensure + that the block hash itself doesn't change due to version changes. + This requires some work arounds. */ + block.SetAuxpowFlag(false); + const uint256 hashAux = block.GetHash(); + auxRoot = builder.buildAuxpowChain(hashAux, height, index); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder.setCoinbase(CScript() << data); + mineBlock(builder.parentBlock, true, block.nBits); + block.SetAuxpow(new CAuxPow(builder.get())); + BOOST_CHECK(hashAux != block.GetHash()); + block.SetAuxpowFlag(false); + BOOST_CHECK(hashAux == block.GetHash()); + BOOST_CHECK(!CheckAuxPowProofOfWork(block, params)); + + /* Modifying the block invalidates the PoW. */ + block.SetAuxpowFlag(true); + auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder.setCoinbase(CScript() << data); + mineBlock(builder.parentBlock, true, block.nBits); + block.SetAuxpow(new CAuxPow(builder.get())); + BOOST_CHECK(CheckAuxPowProofOfWork(block, params)); + tamperWith(block.hashMerkleRoot); + BOOST_CHECK(!CheckAuxPowProofOfWork(block, params)); +} + +/* ************************************************************************** */ + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 0b96d7507..a9ce7d1ca 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -210,9 +210,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; - // Replaced chainActive.Tip()->GetMedianTimePast()+1 with an actual 60 second block - // interval because median([1,2,2,3,3,3,4,4,4,4]) will eventually be problematic re: - // block timing. Tests should be more stable than that. pblock->nTime = chainActive.Tip()->GetBlockTime() + 60; CMutableTransaction txCoinbase(*pblock->vtx[0]); txCoinbase.nVersion = 1; diff --git a/src/txdb.cpp b/src/txdb.cpp index 0f11b5f0c..b27ad8010 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -200,10 +200,11 @@ bool CBlockTreeDB::LoadBlockIndexGuts(boost::functionnNonce = diskindex.nNonce; pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - pindexNew->hashBlockPoW = diskindex.hashBlockPoW; - if (!CheckProofOfWork(pindexNew->GetBlockPoWHash(), pindexNew->nBits, Params().GetConsensus())) - return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); + /* Bitcoin checks the PoW here. We don't do this because + the CDiskBlockIndex does not contain the auxpow. + This check isn't important, since the data on disk should + already be valid and can be trusted. */ pcursor->Next(); } else { diff --git a/src/validation.cpp b/src/validation.cpp index ff5644ff8..eb02a1dd2 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1130,7 +1130,11 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea return true; } -bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) +/* Generic implementation of block reading that can handle + both a block and its header. */ + +template +static bool ReadBlockOrHeader(T& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) { block.SetNull(); @@ -1148,22 +1152,38 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus: } // Check the header - if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, consensusParams)) + if (!CheckAuxPowProofOfWork(block, consensusParams)) return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); return true; } -bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) +template +static bool ReadBlockOrHeader(T& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) { - if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams)) + if (!ReadBlockOrHeader(block, pindex->GetBlockPos(), consensusParams)) return false; if (block.GetHash() != pindex->GetBlockHash()) - return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", + return error("ReadBlockOrHeader(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", pindex->ToString(), pindex->GetBlockPos().ToString()); return true; } +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) +{ + return ReadBlockOrHeader(block, pos, consensusParams); +} + +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + return ReadBlockOrHeader(block, pindex, consensusParams); +} + +bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + return ReadBlockOrHeader(block, pindex, consensusParams); +} + CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; @@ -2982,6 +3002,14 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + + // Disallow legacy blocks after merge-mining start. + if (!Params().GetConsensus().AllowLegacyBlocks(nHeight) + && block.IsLegacy()) + return state.DoS(100, error("%s : legacy block after auxpow start", + __func__), + REJECT_INVALID, "late-legacy-block"); + // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); @@ -2997,10 +3025,10 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: // check for version 2, 3 and 4 upgrades // Dogecoin: Version 2 enforcement was never used - if((block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || - (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) - return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), - strprintf("rejected nVersion=0x%08x block", block.nVersion)); + if((block.GetBaseVersion() < 3 && nHeight >= consensusParams.BIP66Height) || + (block.GetBaseVersion() < 4 && nHeight >= consensusParams.BIP65Height)) + return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.GetBaseVersion()), + strprintf("rejected nVersion=0x%08x block", block.GetBaseVersion())); return true; } diff --git a/src/validation.h b/src/validation.h index 9c606f241..bd7d4b9a4 100644 --- a/src/validation.h +++ b/src/validation.h @@ -472,6 +472,7 @@ public: bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); +bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); /** Functions for validating blocks and updating the block tree */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2697a3769..983aff862 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -57,8 +57,6 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); */ CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); -const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); - /** @defgroup mapWallet * * @{ @@ -3934,44 +3932,3 @@ CWalletKey::CWalletKey(int64_t nExpires) nTimeCreated = (nExpires ? GetTime() : 0); nTimeExpires = nExpires; } - -void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock) -{ - // Update the tx's hashBlock - hashBlock = pindex->GetBlockHash(); - - // set the position of the transaction in the block - nIndex = posInBlock; -} - -int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const -{ - if (hashUnset()) - return 0; - - AssertLockHeld(cs_main); - - // Find the block it claims to be in - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; - if (!pindex || !chainActive.Contains(pindex)) - return 0; - - pindexRet = pindex; - return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); -} - -int CMerkleTx::GetBlocksToMaturity() const -{ - if (!IsCoinBase()) - return 0; - return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); -} - - -bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) -{ - return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee); -} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 98e4fb87b..8cf4a83ae 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -7,6 +7,7 @@ #define BITCOIN_WALLET_WALLET_H #include "amount.h" +#include "auxpow.h" #include "streams.h" #include "tinyformat.h" #include "ui_interface.h" @@ -168,84 +169,6 @@ struct COutputEntry int vout; }; -/** A transaction with a merkle branch linking it to the block chain. */ -class CMerkleTx -{ -private: - /** Constant used in hashBlock to indicate tx has been abandoned */ - static const uint256 ABANDON_HASH; - -public: - CTransactionRef tx; - uint256 hashBlock; - - /* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest - * block in the chain we know this or any in-wallet dependency conflicts - * with. Older clients interpret nIndex == -1 as unconfirmed for backward - * compatibility. - */ - int nIndex; - - CMerkleTx() - { - SetTx(MakeTransactionRef()); - Init(); - } - - CMerkleTx(CTransactionRef arg) - { - SetTx(std::move(arg)); - Init(); - } - - /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected. - * TODO: adapt callers and remove this operator. */ - operator const CTransaction&() const { return *tx; } - - void Init() - { - hashBlock = uint256(); - nIndex = -1; - } - - void SetTx(CTransactionRef arg) - { - tx = std::move(arg); - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - std::vector vMerkleBranch; // For compatibility with older versions. - READWRITE(tx); - READWRITE(hashBlock); - READWRITE(vMerkleBranch); - READWRITE(nIndex); - } - - void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock); - - /** - * Return depth of transaction in blockchain: - * <0 : conflicts with a transaction this deep in the blockchain - * 0 : in memory pool, waiting to be included in a block - * >=1 : this many blocks deep in the main chain - */ - int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; - int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } - bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; } - int GetBlocksToMaturity() const; - /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ - bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state); - bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } - bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } - void setAbandoned() { hashBlock = ABANDON_HASH; } - - const uint256& GetHash() const { return tx->GetHash(); } - bool IsCoinBase() const { return tx->IsCoinBase(); } -}; - /** * A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain.