diff options
| -rwxr-xr-x | qa/rpc-tests/getauxblock.py | 123 | ||||
| -rwxr-xr-x | qa/rpc-tests/rest.py | 20 | ||||
| -rw-r--r-- | qa/rpc-tests/test_framework/auxpow.py | 111 | ||||
| -rw-r--r-- | src/Makefile.am | 4 | ||||
| -rw-r--r-- | src/Makefile.test.include | 1 | ||||
| -rw-r--r-- | src/auxpow.cpp | 192 | ||||
| -rw-r--r-- | src/auxpow.h | 185 | ||||
| -rw-r--r-- | src/chain.cpp | 27 | ||||
| -rw-r--r-- | src/chain.h | 17 | ||||
| -rw-r--r-- | src/chainparams.cpp | 11 | ||||
| -rw-r--r-- | src/chainparams.h | 1 | ||||
| -rw-r--r-- | src/consensus/params.h | 17 | ||||
| -rw-r--r-- | src/init.cpp | 3 | ||||
| -rw-r--r-- | src/main.cpp | 95 | ||||
| -rw-r--r-- | src/main.h | 10 | ||||
| -rw-r--r-- | src/miner.cpp | 9 | ||||
| -rw-r--r-- | src/miner.h | 3 | ||||
| -rw-r--r-- | src/primitives/block.cpp | 22 | ||||
| -rw-r--r-- | src/primitives/block.h | 58 | ||||
| -rw-r--r-- | src/primitives/pureheader.cpp | 31 | ||||
| -rw-r--r-- | src/primitives/pureheader.h | 197 | ||||
| -rw-r--r-- | src/rpcblockchain.cpp | 34 | ||||
| -rw-r--r-- | src/rpcmining.cpp | 179 | ||||
| -rw-r--r-- | src/rpcserver.cpp | 3 | ||||
| -rw-r--r-- | src/rpcserver.h | 1 | ||||
| -rw-r--r-- | src/test/auxpow_tests.cpp | 442 | ||||
| -rw-r--r-- | src/test/miner_tests.cpp | 2 | ||||
| -rw-r--r-- | src/txdb.cpp | 6 | ||||
| -rw-r--r-- | src/wallet/wallet.cpp | 85 | ||||
| -rw-r--r-- | src/wallet/wallet.h | 60 |
30 files changed, 1720 insertions, 229 deletions
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 6c51b2fcd..f5eda9968 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -14,6 +14,7 @@ from struct import * import binascii import json import StringIO +import auxpow try: import http.client as httplib @@ -57,6 +58,9 @@ class RESTTest (BitcoinTestFramework): self.sync_all() def run_test(self): + auxpow.mineAuxpowBlock(self.nodes[0]) + self.sync_all() + url = urlparse.urlparse(self.nodes[0].url) print "Mining blocks..." @@ -215,24 +219,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(response_str.encode("hex")[0:160], response_hex_str[0:160]) + response_hex_str = response_hex.read().strip() + assert_equal(response_str.encode("hex"), 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(response_header_str.encode("hex")[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(response_header_str.encode("hex"), response_header_hex_str) # check json format 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 d0d5137b7..7d92f0dfb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,6 +72,7 @@ endif BITCOIN_CORE_H = \ addrman.h \ alert.h \ + auxpow.h \ amount.h \ arith_uint256.h \ base58.h \ @@ -115,6 +116,7 @@ BITCOIN_CORE_H = \ policy/fees.h \ pow.h \ primitives/block.h \ + primitives/pureheader.h \ primitives/transaction.h \ protocol.h \ pubkey.h \ @@ -250,6 +252,7 @@ libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES) libbitcoin_common_a_SOURCES = \ amount.cpp \ arith_uint256.cpp \ + auxpow.cpp \ base58.cpp \ chainparams.cpp \ coins.cpp \ @@ -263,6 +266,7 @@ libbitcoin_common_a_SOURCES = \ keystore.cpp \ netbase.cpp \ primitives/block.cpp \ + primitives/pureheader.cpp \ primitives/transaction.cpp \ protocol.cpp \ pubkey.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index f45364e7f..e75a0a0d8 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -36,6 +36,7 @@ BITCOIN_TESTS =\ test/bignum.h \ test/alert_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..3c350a67a --- /dev/null +++ b/src/auxpow.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 Vince Durham +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 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 "consensus/validation.h" +#include "main.h" +#include "script/script.h" +#include "util.h" + +#include <algorithm> + +/* Moved from wallet.cpp. CMerkleTx is necessary for auxpow, independent + of an enabled (or disabled) wallet. Always include the code. */ + +int CMerkleTx::SetMerkleBranch(const CBlock& block) +{ + AssertLockHeld(cs_main); + CBlock blockTmp; + + // Update the tx's hashBlock + hashBlock = block.GetHash(); + + // Locate the transaction + for (nIndex = 0; nIndex < (int)block.vtx.size(); nIndex++) + if (block.vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == (int)block.vtx.size()) { + vMerkleBranch.clear(); + nIndex = -1; + LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n"); + return 0; + } + + // Fill in merkle branch + vMerkleBranch = block.GetMerkleBranch(nIndex); + + // Is the tx in a block that's in the main chain + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + const CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + return chainActive.Height() - pindex->nHeight + 1; +} + +int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex*& pindexRet) const +{ + if (hashBlock.IsNull() || nIndex == -1) + 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; + + // Make sure the merkle branch connects to this block + if (!fMerkleVerified) { + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + return 0; + fMerkleVerified = true; + } + + pindexRet = pindex; + return chainActive.Height() - pindex->nHeight + 1; +} + +int CMerkleTx::GetDepthInMainChain(const CBlockIndex*& pindexRet) const +{ + AssertLockHeld(cs_main); + int nResult = GetDepthInMainChainINTERNAL(pindexRet); + if (nResult == 0 && !mempool.exists(GetHash())) + return -1; // Not in chain, not in mempool + + return nResult; +} + +int CMerkleTx::GetBlocksToMaturity() const +{ + if (!IsCoinBase()) + return 0; + return std::max(0, (COINBASE_MATURITY + 1) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) +{ + CValidationState state; + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee); +} + +/* ************************************************************************** */ + +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.nVersion.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 = CBlock::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 (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != parentBlock.hashMerkleRoot) + return error("Aux POW merkle root incorrect"); + + const CScript script = 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"); + + int nSize; + memcpy(&nSize, &pc[0], 4); + const unsigned merkleHeight = vChainMerkleBranch.size(); + if (nSize != (1 << merkleHeight)) + return error("Aux POW merkle branch size does not match parent coinbase"); + + int nNonce; + memcpy(&nNonce, &pc[4], 4); + + if (nChainIndex != getExpectedIndex(nNonce, nChainId, merkleHeight)) + return error("Aux POW wrong index"); + + return true; +} + +int +CAuxPow::getExpectedIndex(int 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. + + unsigned rand = nNonce; + rand = rand * 1103515245 + 12345; + rand += nChainId; + rand = rand * 1103515245 + 12345; + + return rand % (1 << h); +} diff --git a/src/auxpow.h b/src/auxpow.h new file mode 100644 index 000000000..26cc05f98 --- /dev/null +++ b/src/auxpow.h @@ -0,0 +1,185 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 Daniel Kraft +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_AUXPOW_H +#define BITCOIN_AUXPOW_H + +#include "consensus/params.h" +#include "primitives/pureheader.h" +#include "primitives/transaction.h" +#include "serialize.h" +#include "uint256.h" + +#include <vector> + +class CBlock; +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 : public CTransaction +{ +private: + int GetDepthInMainChainINTERNAL(const CBlockIndex*& pindexRet) const; + +public: + uint256 hashBlock; + std::vector<uint256> vMerkleBranch; + int nIndex; + + // memory only + mutable bool fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = uint256(); + nIndex = -1; + fMerkleVerified = false; + } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(*(CTransaction*)this); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + } + + int SetMerkleBranch(const CBlock& block); + + + /** + * Return depth of transaction in blockchain: + * -1 : not in blockchain, and not in memory pool (conflicted transaction) + * 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 GetDepthInMainChainINTERNAL(pindexRet) > 0; + } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(bool fLimitFree = true, bool fRejectAbsurdFee = true); +}; + +/** + * Data for the merge-mining auxpow. This is a merkle tx (the parent block's + * coinbase tx) that can be verified to be in the parent block, and this + * transaction's input (the coinbase script) contains the reference + * to the actual merge-mined block. + */ +class CAuxPow : public CMerkleTx +{ + /* Public for the unit tests. */ +public: + /** The merkle branch connecting the aux block to our coinbase. */ + std::vector<uint256> vChainMerkleBranch; + + /** Merkle tree index of the aux block header in the coinbase. */ + int nChainIndex; + + /** Parent block header (on which the real PoW is done). */ + CPureBlockHeader parentBlock; + +public: + /* Prevent accidental conversion. */ + inline explicit CAuxPow(const CTransaction& txIn) + : CMerkleTx(txIn) + { + } + + inline CAuxPow() + : CMerkleTx() + { + } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void + SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(*static_cast<CMerkleTx*>(this)); + nVersion = this->nVersion; + + 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 + getParentBlockHash() const + { + return parentBlock.GetHash(); + } + + /** + * 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(int nNonce, int nChainId, unsigned h); +}; + +#endif // BITCOIN_AUXPOW_H diff --git a/src/chain.cpp b/src/chain.cpp index 719256106..e76073674 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -5,8 +5,35 @@ #include "chain.h" +#include "main.h" + using namespace std; +/* Moved here from the header, because we need auxpow and the logic + becomes more involved. */ +CBlockHeader CBlockIndex::GetBlockHeader() 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 (nVersion.IsAuxpow()) + { + ReadBlockHeaderFromDisk(block, this); + 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 01be2d6e5..c735f6a70 100644 --- a/src/chain.h +++ b/src/chain.h @@ -139,7 +139,7 @@ public: unsigned int nStatus; //! block header - int nVersion; + CBlockVersion nVersion; uint256 hashMerkleRoot; unsigned int nTime; unsigned int nBits; @@ -163,7 +163,7 @@ public: nStatus = 0; nSequenceId = 0; - nVersion = 0; + nVersion.SetNull(); hashMerkleRoot = uint256(); nTime = 0; nBits = 0; @@ -204,18 +204,7 @@ 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; uint256 GetBlockHash() const { diff --git a/src/chainparams.cpp b/src/chainparams.cpp index cabff98c6..b573aa80a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -39,6 +39,10 @@ public: consensus.nPowTargetTimespan = 4 * 60 * 60; // pre-digishield: 4 hours consensus.nPowTargetSpacing = 60; // 1 minute consensus.fPowAllowMinDifficultyBlocks = false; + consensus.nAuxpowChainId = 0x0001; + consensus.nAuxpowStartHeight = 19200; + consensus.fStrictChainId = true; + consensus.nLegacyBlocksBefore = 19200; /** * 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 @@ -74,7 +78,7 @@ public: genesis.vtx.push_back(txNew); genesis.hashPrevBlock.SetNull(); genesis.hashMerkleRoot = genesis.BuildMerkleTree(); - genesis.nVersion = 1; + genesis.nVersion.SetGenesisVersion(1); genesis.nTime = 1386325540; genesis.nBits = 0x1e0ffff0; genesis.nNonce = 99943; @@ -148,6 +152,9 @@ public: consensus.nMajorityRejectBlockOutdated = 75; consensus.nMajorityWindow = 100; consensus.fPowAllowMinDifficultyBlocks = true; + consensus.nAuxpowStartHeight = 0; + consensus.fStrictChainId = false; + consensus.nLegacyBlocksBefore = -1; pchMessageStart[0] = 0xfc; pchMessageStart[1] = 0xc1; @@ -209,6 +216,8 @@ public: consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityWindow = 1000; consensus.powLimit = uint256S("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1; + 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 8044b553e..b9d1c1758 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -71,6 +71,7 @@ public: const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const Checkpoints::CCheckpointData& Checkpoints() const { return checkpointData; } + protected: CChainParams() {} diff --git a/src/consensus/params.h b/src/consensus/params.h index c480a1cce..873a93d78 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -25,6 +25,23 @@ struct Params { int64_t nPowTargetSpacing; int64_t nPowTargetTimespan; int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } + /** Auxpow parameters */ + int32_t nAuxpowChainId; + int nAuxpowStartHeight; + bool fStrictChainId; + int nLegacyBlocksBefore; // -1 for "always allow" + + /** + * Check whether or not to allow legacy blocks at the given height. + * @param nHeight Height of the block to check. + * @return True if it is allowed to have a legacy version. + */ + bool AllowLegacyBlocks(unsigned nHeight) const + { + if (nLegacyBlocksBefore < 0) + return true; + return static_cast<int> (nHeight) < nLegacyBlocksBefore; + } }; } // namespace Consensus diff --git a/src/init.cpp b/src/init.cpp index 987b527ae..7bb7b183f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -154,6 +154,7 @@ void Shutdown() mempool.AddTransactionsUpdated(1); StopRPCThreads(); #ifdef ENABLE_WALLET + ShutdownRPCMining(); if (pwalletMain) pwalletMain->Flush(false); GenerateBitcoins(false, NULL, 0); @@ -1431,6 +1432,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Generate coins in the background if (pwalletMain) GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1)); + + InitRPCMining (); #endif // ********************************************************* Step 11: finished diff --git a/src/main.cpp b/src/main.cpp index 91d89cea7..4046330e9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include "addrman.h" #include "alert.h" #include "arith_uint256.h" +#include "auxpow.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -1138,6 +1139,51 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // +bool CheckProofOfWork(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.nVersion.IsLegacy() && params.fStrictChainId + && block.nVersion.GetChainId() != params.nAuxpowChainId) + return error("%s : block does not have our chain ID" + " (got %d, expected %d, full nVersion %d)", + __func__, block.nVersion.GetChainId(), + params.nAuxpowChainId, block.nVersion.GetFullVersion()); + + /* If there is no auxpow, just check the block hash. */ + if (!block.auxpow) + { + if (block.nVersion.IsAuxpow()) + return error("%s : no auxpow on block with auxpow version", + __func__); + + if (!CheckProofOfWork(block.GetHash(), block.nBits, params)) + return error("%s : non-AUX proof of work failed", __func__); + + return true; + } + + /* We have auxpow. Check it. */ + + if (!block.nVersion.IsAuxpow()) + return error("%s : auxpow on block with non-auxpow version", __func__); + + /* Temporary check: Disallow parent blocks with auxpow version. This is + for compatibility with the old client. */ + /* FIXME: Remove this check with a hardfork later on. */ + if (block.auxpow->getParentBlock().nVersion.IsAuxpow()) + return error("%s : auxpow parent block has auxpow version", __func__); + + if (!block.auxpow->check(block.GetHash(), block.nVersion.GetChainId(), params)) + return error("%s : AUX POW is not valid", __func__); + if (!CheckProofOfWork(block.auxpow->getParentBlockHash(), block.nBits, params)) + return error("%s : AUX proof of work failed", __func__); + + return true; +} + bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append @@ -1159,7 +1205,11 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M return true; } -bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) +/* Generic implementation of block reading that can handle + both a block and its header. */ + +template<typename T> +static bool ReadBlockOrHeader(T& block, const CDiskBlockPos& pos) { block.SetNull(); @@ -1177,15 +1227,16 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) } // Check the header - if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, Params().GetConsensus())) + if (!CheckProofOfWork(block, Params().GetConsensus())) return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); return true; } -bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) +template<typename T> +static bool ReadBlockOrHeader(T& block, const CBlockIndex* pindex) { - if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) + if (!ReadBlockOrHeader(block, pindex->GetBlockPos())) return false; if (block.GetHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", @@ -1193,6 +1244,21 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return true; } +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) +{ + return ReadBlockOrHeader(block, pos); +} + +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) +{ + return ReadBlockOrHeader(block, pindex); +} + +bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex) +{ + return ReadBlockOrHeader(block, pindex); +} + CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; @@ -1829,7 +1895,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: - if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) { + if (block.nVersion.GetBaseVersion() >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) { flags |= SCRIPT_VERIFY_DERSIG; } @@ -2091,7 +2157,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { const CBlockIndex* pindex = chainActive.Tip(); for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION) + if (pindex->nVersion.GetBaseVersion() > CBlock::CURRENT_VERSION) ++nUpgraded; pindex = pindex->pprev; } @@ -2646,7 +2712,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) { // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(), block.nBits, Params().GetConsensus())) + if (fCheckPOW && !CheckProofOfWork(block, Params().GetConsensus())) return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), REJECT_INVALID, "high-hash"); @@ -2730,6 +2796,13 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta int nHeight = pindexPrev->nHeight+1; + // Disallow legacy blocks after merge-mining start. + if (!Params().GetConsensus().AllowLegacyBlocks(nHeight) + && block.nVersion.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, error("%s: incorrect proof of work", __func__), @@ -2754,12 +2827,12 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta } // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) + if (block.nVersion.GetBaseVersion() < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s: rejected nVersion=1 block", __func__), REJECT_OBSOLETE, "bad-version"); // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) + if (block.nVersion.GetBaseVersion() < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s : rejected nVersion=2 block", __func__), REJECT_OBSOLETE, "bad-version"); @@ -2779,7 +2852,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)) + if (block.nVersion.GetBaseVersion() >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)) { CScript expect = CScript() << nHeight; if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || @@ -2897,7 +2970,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned unsigned int nFound = 0; for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++) { - if (pstart->nVersion >= minVersion) + if (pstart->nVersion.GetBaseVersion() >= minVersion) ++nFound; pstart = pstart->pprev; } diff --git a/src/main.h b/src/main.h index 58c717c27..2cb3752ef 100644 --- a/src/main.h +++ b/src/main.h @@ -378,8 +378,8 @@ public: /** Functions for disk access for blocks */ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); -bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); +bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex); /** Functions for validating blocks and updating the block tree */ @@ -404,6 +404,14 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +/** + * 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 CheckProofOfWork(const CBlockHeader& block, const Consensus::Params& params); + /** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp); bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL); diff --git a/src/miner.cpp b/src/miner.cpp index 3e06e877c..a6ed42d00 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -101,10 +101,13 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience + /* Initialise the block version. */ + pblock->nVersion.SetBaseVersion(CBlockHeader::CURRENT_VERSION); + // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (Params().MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + pblock->nVersion.SetBaseVersion(GetArg("-blockversion", pblock->nVersion.GetBaseVersion())); // Create coinbase tx CMutableTransaction txNew; @@ -345,7 +348,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return pblocktemplate.release(); } -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce) { // Update nExtraNonce static uint256 hashPrevBlock; @@ -417,7 +420,7 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey) return CreateNewBlock(scriptPubKey); } -static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) +bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { LogPrintf("%s\n", pblock->ToString()); LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue)); diff --git a/src/miner.h b/src/miner.h index 96a6b70ec..46bed944d 100644 --- a/src/miner.h +++ b/src/miner.h @@ -29,7 +29,8 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads); CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey); /** Modify the extranonce in a block */ -void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce); void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); +bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); #endif // BITCOIN_MINER_H diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 7d2bc26dd..7bd64d77c 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); + nVersion.SetAuxpow(true); + } else + { + auxpow.reset(); + nVersion.SetAuxpow(false); + } } uint256 CBlock::BuildMerkleTree(bool* fMutated) const @@ -122,7 +122,7 @@ std::string CBlock::ToString() const std::stringstream s; s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n", GetHash().ToString(), - nVersion, + nVersion.GetFullVersion(), hashPrevBlock.ToString(), hashMerkleRoot.ToString(), nTime, nBits, nNonce, diff --git a/src/primitives/block.h b/src/primitives/block.h index 21eb82273..ef2b19010 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -6,10 +6,14 @@ #ifndef BITCOIN_PRIMITIVES_BLOCK_H #define BITCOIN_PRIMITIVES_BLOCK_H +#include "auxpow.h" #include "primitives/transaction.h" +#include "primitives/pureheader.h" #include "serialize.h" #include "uint256.h" +#include <boost/shared_ptr.hpp> + /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work * requirements. When they solve the proof-of-work, they broadcast the block @@ -17,17 +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 - static const int32_t CURRENT_VERSION=3; - int32_t nVersion; - uint256 hashPrevBlock; - uint256 hashMerkleRoot; - uint32_t nTime; - uint32_t nBits; - uint32_t nNonce; + // auxpow (if this is a merge-minded block) + boost::shared_ptr<CAuxPow> auxpow; CBlockHeader() { @@ -38,38 +36,35 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(hashPrevBlock); - READWRITE(hashMerkleRoot); - READWRITE(nTime); - READWRITE(nBits); - READWRITE(nNonce); + READWRITE(*(CPureBlockHeader*)this); + nVersion = this->nVersion.GetBaseVersion(); + + if (this->nVersion.IsAuxpow()) { + if (ser_action.ForRead()) + auxpow.reset(new CAuxPow()); + assert(auxpow); + READWRITE(*auxpow); + } else if (ser_action.ForRead()) + auxpow.reset(); } void SetNull() { - nVersion = CBlockHeader::CURRENT_VERSION; - hashPrevBlock.SetNull(); - hashMerkleRoot.SetNull(); - nTime = 0; - nBits = 0; - nNonce = 0; - } - - bool IsNull() const - { - return (nBits == 0); + CPureBlockHeader::SetNull(); + auxpow.reset(); } - uint256 GetHash() const; - - uint256 GetPoWHash() const; - int64_t GetBlockTime() const { return (int64_t)nTime; } + + /** + * Set the block's auxpow (or unset it). This takes care of updating + * the version accordingly. + * @param apow Pointer to the auxpow to use or NULL. + */ + void SetAuxpow(CAuxPow* apow); }; @@ -117,6 +112,7 @@ public: block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; + block.auxpow = auxpow; return block; } diff --git a/src/primitives/pureheader.cpp b/src/primitives/pureheader.cpp new file mode 100644 index 000000000..c90d5fd8e --- /dev/null +++ b/src/primitives/pureheader.cpp @@ -0,0 +1,31 @@ +// 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 CBlockVersion::SetBaseVersion(int32_t nBaseVersion) +{ + assert(nBaseVersion >= 1 && nBaseVersion < VERSION_AUXPOW); + assert(!IsAuxpow()); + const int32_t nChainId = Params().GetConsensus().nAuxpowChainId; + 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..449ebbff0 --- /dev/null +++ b/src/primitives/pureheader.h @@ -0,0 +1,197 @@ +// 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" + +/** + * Encapsulate a block version. This takes care of building it up + * from a base version, the modifier flags (like auxpow) and + * also the auxpow chain ID. + */ +class CBlockVersion +{ +private: + /* 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); + + /** The version as integer. Should not be accessed directly. */ + int32_t nVersion; + +public: + inline CBlockVersion() + { + SetNull(); + } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(this->nVersion); + } + + inline void SetNull() + { + nVersion = 0; + } + + /** + * Extract the base version (without modifiers and chain ID). + * @return The base version./ + */ + inline int32_t GetBaseVersion() const + { + return nVersion % 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. + */ + void SetBaseVersion(int32_t nBaseVersion); + + /** + * Extract the chain ID. + * @return The chain ID encoded in the version. + */ + inline int32_t GetChainId() const + { + return nVersion / VERSION_CHAIN_START; + } + + /** + * 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; + } + + /** + * Extract the full version. Used for RPC results and debug prints. + * @return The full version. + */ + inline int32_t GetFullVersion() const + { + return nVersion; + } + + /** + * Set the genesis block version. This must be a literal write + * through, to get the correct historic version. + * @param nGenesisVersion The version to set. + */ + inline void SetGenesisVersion(int32_t nGenesisVersion) + { + nVersion = nGenesisVersion; + } + + /** + * Check if the auxpow flag is set in the version. + * @return True iff this block version is marked as auxpow. + */ + inline bool IsAuxpow() const + { + return nVersion & VERSION_AUXPOW; + } + + /** + * Set the auxpow flag. This is used for testing. + * @param auxpow Whether to mark auxpow as true. + */ + inline void SetAuxpow(bool auxpow) + { + if (auxpow) + nVersion |= VERSION_AUXPOW; + else + nVersion &= ~VERSION_AUXPOW; + } + + /** + * Check whether this is a "legacy" block without chain ID. + * @return True iff it is. + */ + inline bool IsLegacy() const + { + return nVersion == 1; + } +}; + +/** + * 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: + // header + static const int32_t CURRENT_VERSION = 3; + CBlockVersion nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + uint32_t nTime; + uint32_t nBits; + uint32_t nNonce; + + CPureBlockHeader() + { + SetNull(); + } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(this->nVersion); + nVersion = this->nVersion.GetBaseVersion(); + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(nTime); + READWRITE(nBits); + READWRITE(nNonce); + } + + void SetNull() + { + nVersion.SetNull(); + 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; + } +}; + +#endif // BITCOIN_PRIMITIVES_PUREHEADER_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 79528db2f..3d4cfcd21 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -5,6 +5,7 @@ #include "checkpoints.h" #include "consensus/validation.h" +#include "core_io.h" #include "main.h" #include "primitives/transaction.h" #include "rpcserver.h" @@ -52,6 +53,34 @@ double GetDifficulty(const CBlockIndex* blockindex) return dDiff; } +static Object AuxpowToJSON(const CAuxPow& auxpow) +{ + Object tx; + tx.push_back(Pair("hex", EncodeHexTx(auxpow))); + TxToJSON(auxpow, auxpow.parentBlock.GetHash(), tx); + + Object result; + result.push_back(Pair("tx", tx)); + result.push_back(Pair("index", auxpow.nIndex)); + result.push_back(Pair("chainindex", auxpow.nChainIndex)); + + Array branch; + BOOST_FOREACH(const uint256& node, auxpow.vMerkleBranch) + branch.push_back(node.GetHex()); + result.push_back(Pair("merklebranch", branch)); + + branch.clear(); + 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; +} Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) { @@ -64,7 +93,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDe result.push_back(Pair("confirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); + result.push_back(Pair("version", block.nVersion.GetFullVersion())); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); Array txs; BOOST_FOREACH(const CTransaction&tx, block.vtx) @@ -85,6 +114,9 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDe 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); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 528d5406f..3639ac931 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -30,6 +30,30 @@ using namespace json_spirit; using namespace std; +#ifdef ENABLE_WALLET +// Key used by getwork miners. +// Allocated in InitRPCMining, free'd in ShutdownRPCMining +static CReserveKey* pminingKey = NULL; + +void InitRPCMining() +{ + if (!pwalletMain) + return; + + // getwork/getblocktemplate mining rewards paid here: + pminingKey = new CReserveKey(pwalletMain); +} + +void ShutdownRPCMining() +{ + if (!pminingKey) + return; + + delete pminingKey; + pminingKey = NULL; +} +#endif // ENABLE_WALLET + /** * Return average network hashes per second based on the last 'lookup' blocks, * or from the last difficulty change if 'lookup' is nonpositive. @@ -569,7 +593,7 @@ Value getblocktemplate(const Array& params, bool fHelp) Object result; result.push_back(Pair("capabilities", aCaps)); - result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("version", pblock->nVersion.GetFullVersion())); result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("transactions", transactions)); result.push_back(Pair("coinbaseaux", aux)); @@ -726,3 +750,156 @@ Value estimatepriority(const Array& params, bool fHelp) return mempool.estimatePriority(nBlocks); } + +/* ************************************************************************** */ +/* Merge mining. */ + +#ifdef ENABLE_WALLET +Value getauxblock(const Array& params, bool fHelp) +{ + if (fHelp || (params.size() != 0 && params.size() != 2)) + throw std::runtime_error( + "getauxblock (hash auxpow)\n" + "\nCreate or submit a merge-mined block.\n" + "\nWithout arguments, create a new block and return information\n" + "required to merge-mine it. With arguments, submit a solved\n" + "auxpow for a previously returned block.\n" + "\nArguments:\n" + "1. \"hash\" (string, optional) hash of the block to submit\n" + "2. \"auxpow\" (string, optional) serialised auxpow found\n" + "\nResult (without arguments):\n" + "{\n" + " \"hash\" (string) hash of the created block\n" + " \"chainid\" (numeric) chain ID for this block\n" + " \"previousblockhash\" (string) hash of the previous block\n" + " \"coinbasevalue\" (numeric) value of the block's coinbase\n" + " \"bits\" (string) compressed target of the block\n" + " \"height\" (numeric) height of the block\n" + " \"_target\" (string) target in reversed byte order, deprecated\n" + "}\n" + "\nResult (with arguments):\n" + "xxxxx (boolean) whether the submitted block was correct\n" + "\nExamples:\n" + + HelpExampleCli("getauxblock", "") + + HelpExampleCli("getauxblock", "\"hash\" \"serialised auxpow\"") + + HelpExampleRpc("getauxblock", "") + ); + + if (pwalletMain == NULL) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + assert (pminingKey); + + if (vNodes.empty() && !Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, + "Namecoin is not connected!"); + + if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, + "Namecoin 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("getauxblock method is not yet available"); + } + + /* The variables below are used to keep track of created and not yet + submitted auxpow blocks. Lock them, just in case. In principle + there's only one RPC thread, so it should be fine without locking + as well. But it cannot hurt to be safe. */ + static CCriticalSection cs_auxblockCache; + LOCK(cs_auxblockCache); + static std::map<uint256, CBlock*> mapNewBlock; + static std::vector<CBlockTemplate*> vNewBlockTemplate; + + /* Create a new block? */ + if (params.size() == 0) + { + static unsigned nTransactionsUpdatedLast; + static const CBlockIndex* pindexPrev = NULL; + static uint64_t nStart; + static CBlockTemplate* pblocktemplate; + static unsigned nExtraNonce = 0; + + // Update block + { + LOCK(cs_main); + if (pindexPrev != chainActive.Tip() + || (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast + && GetTime() - nStart > 60)) + { + if (pindexPrev != chainActive.Tip()) + { + // Deallocate old blocks since they're obsolete now + mapNewBlock.clear(); + BOOST_FOREACH(CBlockTemplate* pbt, vNewBlockTemplate) + delete pbt; + vNewBlockTemplate.clear(); + } + + // Create new block with nonce = 0 and extraNonce = 1 + pblocktemplate = CreateNewBlockWithKey(*pminingKey); + if (!pblocktemplate) + 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 + CBlock* pblock = &pblocktemplate->block; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + pblock->nVersion.SetAuxpow(true); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + // Save + mapNewBlock[pblock->GetHash()] = pblock; + vNewBlockTemplate.push_back(pblocktemplate); + } + } + + const CBlock& block = pblocktemplate->block; + + arith_uint256 target; + bool fNegative, fOverflow; + target.SetCompact(block.nBits, &fNegative, &fOverflow); + if (fNegative || fOverflow || target == 0) + throw std::runtime_error("invalid difficulty bits in block"); + + json_spirit::Object result; + result.push_back(Pair("hash", block.GetHash().GetHex())); + result.push_back(Pair("chainid", block.nVersion.GetChainId())); + result.push_back(Pair("previousblockhash", block.hashPrevBlock.GetHex())); + result.push_back(Pair("coinbasevalue", (int64_t)block.vtx[0].vout[0].nValue)); + result.push_back(Pair("bits", strprintf("%08x", block.nBits))); + result.push_back(Pair("height", static_cast<int64_t> (pindexPrev->nHeight + 1))); + result.push_back(Pair("_target", HexStr(BEGIN(target), END(target)))); + + return result; + } + + /* Submit a block instead. Note that this need not lock cs_main, + since ProcessBlockFound below locks it instead. */ + + assert(params.size() == 2); + uint256 hash; + hash.SetHex(params[0].get_str()); + + const std::map<uint256, CBlock*>::iterator mit = mapNewBlock.find(hash); + if (mit == mapNewBlock.end()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "block hash unknown"); + CBlock& block = *mit->second; + + const std::vector<unsigned char> vchAuxPow = ParseHex(params[1].get_str()); + CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION); + CAuxPow pow; + ss >> pow; + block.SetAuxpow(new CAuxPow(pow)); + assert(block.GetHash() == hash); + + return ProcessBlockFound(&block, *pwalletMain, *pminingKey); +} +#endif // ENABLE_WALLET diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 3f74517a6..f5902d2e5 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -304,6 +304,9 @@ static const CRPCCommand vRPCCommands[] = { "mining", "getnetworkhashps", &getnetworkhashps, true }, { "mining", "prioritisetransaction", &prioritisetransaction, true }, { "mining", "submitblock", &submitblock, true }, +#ifdef ENABLE_WALLET + { "mining", "getauxblock", &getauxblock, true }, +#endif // ENABLE_WALLET #ifdef ENABLE_WALLET /* Coin generation */ diff --git a/src/rpcserver.h b/src/rpcserver.h index 30a5b28db..94f666a3b 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -169,6 +169,7 @@ extern json_spirit::Value getblocktemplate(const json_spirit::Array& params, boo extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value estimatefee(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value estimatepriority(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getauxblock(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp extern json_spirit::Value getaccountaddress(const json_spirit::Array& params, bool fHelp); diff --git a/src/test/auxpow_tests.cpp b/src/test/auxpow_tests.cpp new file mode 100644 index 000000000..1630d000d --- /dev/null +++ b/src/test/auxpow_tests.cpp @@ -0,0 +1,442 @@ +// 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 "main.h" +#include "uint256.h" +#include "primitives/block.h" +#include "script/script.h" + +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +#include <algorithm> +#include <vector> + +BOOST_FIXTURE_TEST_SUITE(auxpow_tests, BasicTestingSetup) + +/* ************************************************************************** */ + +/** + * Tamper with a uint256 (modify it). + * @param num The number to modify. + */ +static void +tamperWith(uint256& num) +{ + arith_uint256 modifiable = UintToArith256(num); + modifiable += 1; + num = ArithToUint256(modifiable); +} + +/** + * Utility class to construct auxpow's and manipulate them. This is used + * to simulate various scenarios. + */ +class CAuxpowBuilder +{ +public: + /** The parent block (with coinbase, not just header). */ + CBlock parentBlock; + + /** The auxpow's merkle branch (connecting it to the coinbase). */ + std::vector<uint256> auxpowChainMerkleBranch; + /** The auxpow's merkle tree index. */ + int auxpowChainIndex; + + /** + * Initialise everything. + * @param baseVersion The parent block's base version to use. + * @param chainId The parent block's chain ID to use. + */ + CAuxpowBuilder(int baseVersion, int chainId); + + /** + * Set the coinbase's script. + * @param scr Set it to this script. + */ + void setCoinbase(const CScript& scr); + + /** + * Build the auxpow merkle branch. The member variables will be + * set accordingly. This has to be done before constructing the coinbase + * itself (which must contain the root merkle hash). When we have the + * coinbase afterwards, the member variables can be used to initialise + * the CAuxPow object from it. + * @param hashAux The merge-mined chain's block hash. + * @param h Height of the merkle tree to build. + * @param index Index to use in the merkle tree. + * @return The root hash, with reversed endian. + */ + std::vector<unsigned char> buildAuxpowChain(const uint256& hashAux, unsigned h, int index); + + /** + * Build the finished CAuxPow object. We assume that the auxpowChain + * member variables are already set. We use the passed in transaction + * as the base. It should (probably) be the parent block's coinbase. + * @param tx The base tx to use. + * @return The constructed CAuxPow object. + */ + CAuxPow get(const CTransaction& tx) const; + + /** + * Build the finished CAuxPow object from the parent block's coinbase. + * @return The constructed CAuxPow object. + */ + inline CAuxPow + get() const + { + assert(!parentBlock.vtx.empty()); + return get(parentBlock.vtx[0]); + } + + /** + * Build a data vector to be included in the coinbase. It consists + * of the aux hash, the merkle tree size and the nonce. Optionally, + * the header can be added as well. + * @param header Add the header? + * @param hashAux The aux merkle root hash. + * @param h Height of the merkle tree. + * @param nonce The nonce value to use. + * @return The constructed data. + */ + static std::vector<unsigned char> buildCoinbaseData(bool header, const std::vector<unsigned char>& auxRoot, unsigned h, int nonce); +}; + +CAuxpowBuilder::CAuxpowBuilder(int baseVersion, int chainId) + : auxpowChainIndex(-1) +{ + parentBlock.nVersion.SetBaseVersion(baseVersion); + parentBlock.nVersion.SetChainId(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(mtx); + parentBlock.hashMerkleRoot = parentBlock.BuildMerkleTree(); +} + +std::vector<unsigned char> +CAuxpowBuilder::buildAuxpowChain(const uint256& hashAux, unsigned h, int index) +{ + auxpowChainIndex = index; + + /* Just use "something" for the branch. Doesn't really matter. */ + auxpowChainMerkleBranch.clear(); + for (unsigned i = 0; i < h; ++i) + auxpowChainMerkleBranch.push_back(ArithToUint256(arith_uint256(i))); + + const uint256 hash = CBlock::CheckMerkleBranch(hashAux, auxpowChainMerkleBranch, index); + + std::vector<unsigned char> res = ToByteVector(hash); + std::reverse(res.begin(), res.end()); + + return res; +} + +CAuxPow +CAuxpowBuilder::get(const CTransaction& tx) const +{ + LOCK(cs_main); + CAuxPow res(tx); + res.SetMerkleBranch(parentBlock); + + res.vChainMerkleBranch = auxpowChainMerkleBranch; + res.nChainIndex = auxpowChainIndex; + res.parentBlock = parentBlock; + + return res; +} + +std::vector<unsigned char> +CAuxpowBuilder::buildCoinbaseData(bool header, const std::vector<unsigned char>& auxRoot, unsigned h, int nonce) +{ + std::vector<unsigned char> res; + + if (header) + res.insert(res.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)); + res.insert(res.end(), auxRoot.begin(), auxRoot.end()); + + const int size = (1 << h); + res.insert(res.end(), UBEGIN(size), UEND(size)); + res.insert(res.end(), UBEGIN(nonce), UEND(nonce)); + + return res; +} + +/* ************************************************************************** */ + +BOOST_AUTO_TEST_CASE(check_auxpow) +{ + const Consensus::Params& params = Params().GetConsensus(); + CAuxpowBuilder builder(5, 42); + CAuxPow auxpow; + + const uint256 hashAux = ArithToUint256(arith_uint256(12345)); + const int32_t ourChainId = params.nAuxpowChainId; + const unsigned height = 30; + const int nonce = 7; + int index; + + std::vector<unsigned char> auxRoot, data; + CScript scr; + + /* Build a correct auxpow. The height is the maximally allowed one. */ + index = CAuxPow::getExpectedIndex(nonce, ourChainId, height); + auxRoot = builder.buildAuxpowChain(hashAux, height, index); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS; + scr = (scr << OP_2 << data); + builder.setCoinbase(scr); + BOOST_CHECK(builder.get().check(hashAux, ourChainId, params)); + + /* Check that the auxpow is invalid if we change either the aux block's + hash or the chain ID. */ + uint256 modifiedAux(hashAux); + tamperWith(modifiedAux); + BOOST_CHECK(!builder.get().check(modifiedAux, ourChainId, params)); + BOOST_CHECK(!builder.get().check(hashAux, ourChainId + 1, params)); + + /* Non-coinbase parent tx should fail. Note that we can't just copy + the coinbase literally, as we have to get a tx with different hash. */ + const CTransaction oldCoinbase = builder.parentBlock.vtx[0]; + builder.setCoinbase(scr << 5); + builder.parentBlock.vtx.push_back(oldCoinbase); + builder.parentBlock.hashMerkleRoot = builder.parentBlock.BuildMerkleTree(); + 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.nVersion.SetChainId(100); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + builder2.parentBlock.nVersion.SetChainId(ourChainId); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + /* Disallow too long merkle branches. */ + builder2 = builder; + index = CAuxPow::getExpectedIndex(nonce, ourChainId, height + 1); + auxRoot = builder2.buildAuxpowChain(hashAux, height + 1, index); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height + 1, nonce); + scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS; + scr = (scr << OP_2 << data); + builder2.setCoinbase(scr); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + /* Verify that we compare correctly to the parent block's merkle root. */ + builder2 = builder; + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + tamperWith(builder2.parentBlock.hashMerkleRoot); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + /* Build a non-header legacy version and check that it is also accepted. */ + builder2 = builder; + index = CAuxPow::getExpectedIndex(nonce, ourChainId, height); + auxRoot = builder2.buildAuxpowChain(hashAux, height, index); + data = CAuxpowBuilder::buildCoinbaseData(false, auxRoot, height, nonce); + scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS; + scr = (scr << OP_2 << data); + builder2.setCoinbase(scr); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + + /* However, various attempts at smuggling two roots in should be detected. */ + + const std::vector<unsigned char> wrongAuxRoot = builder2.buildAuxpowChain(modifiedAux, height, index); + std::vector<unsigned char> data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot, height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data2 = CAuxpowBuilder::buildCoinbaseData(true, wrongAuxRoot, height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot, height, nonce); + builder2.setCoinbase(CScript() << data << data2); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + builder2.setCoinbase(CScript() << data2 << data); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + + /* Verify that the appended nonce/size values are checked correctly. */ + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); + + data.pop_back(); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height - 1, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce + 3); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + /* Put the aux hash in an invalid merkle tree position. */ + + auxRoot = builder.buildAuxpowChain(hashAux, height, index + 1); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params)); + + auxRoot = builder.buildAuxpowChain(hashAux, height, index); + data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce); + builder2.setCoinbase(CScript() << data); + BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params)); +} + +/* ************************************************************************** */ + +/** + * Mine a block (assuming minimal difficulty) that either matches + * or doesn't match the difficulty target specified in the block header. + * @param block The block to mine (by updating nonce). + * @param ok Whether the block should be ok for PoW. + * @param nBits Use this as difficulty if specified. + */ +static void +mineBlock(CBlockHeader& block, bool ok, int nBits = -1) +{ + if (nBits == -1) + nBits = block.nBits; + + arith_uint256 target; + target.SetCompact(nBits); + + block.nNonce = 0; + while (true) { + const bool nowOk = (UintToArith256(block.GetHash()) <= target); + if ((ok && nowOk) || (!ok && !nowOk)) + break; + + ++block.nNonce; + } + + if (ok) + BOOST_CHECK(CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus())); + else + BOOST_CHECK(!CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus())); +} + +BOOST_AUTO_TEST_CASE(auxpow_pow) +{ + /* Use regtest parameters to allow mining with easy difficulty. */ + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + + const arith_uint256 target = (~arith_uint256(0) >> 1); + CBlockHeader block; + block.nBits = target.GetCompact(); + + /* Verify the block version checks. */ + + block.nVersion.SetGenesisVersion(1); + mineBlock(block, true); + BOOST_CHECK(CheckProofOfWork(block, params)); + + block.nVersion.SetGenesisVersion(2); + mineBlock(block, true); + BOOST_CHECK(!CheckProofOfWork(block, params)); + + block.nVersion.SetBaseVersion(2); + block.nVersion.SetChainId(params.nAuxpowChainId); + mineBlock(block, true); + BOOST_CHECK(CheckProofOfWork(block, params)); + + block.nVersion.SetChainId(params.nAuxpowChainId + 1); + mineBlock(block, true); + BOOST_CHECK(!CheckProofOfWork(block, params)); + + /* Check the case when the block does not have auxpow (this is true + right now). */ + + block.nVersion.SetChainId(params.nAuxpowChainId); + block.nVersion.SetAuxpow(true); + mineBlock(block, true); + BOOST_CHECK(!CheckProofOfWork(block, params)); + + block.nVersion.SetAuxpow(false); + mineBlock(block, true); + BOOST_CHECK(CheckProofOfWork(block, params)); + mineBlock(block, false); + BOOST_CHECK(!CheckProofOfWork(block, params)); + + /* ****************************************** */ + /* Check the case that the block has auxpow. */ + + CAuxpowBuilder builder(5, 42); + CAuxPow auxpow; + const int32_t ourChainId = params.nAuxpowChainId; + const unsigned height = 3; + const int nonce = 7; + const int index = CAuxPow::getExpectedIndex(nonce, ourChainId, height); + std::vector<unsigned char> auxRoot, data; + + /* Valid auxpow, PoW check of parent block. */ + block.nVersion.SetAuxpow(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(!CheckProofOfWork(block, params)); + mineBlock(builder.parentBlock, true, block.nBits); + block.SetAuxpow(new CAuxPow(builder.get())); + BOOST_CHECK(CheckProofOfWork(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.nVersion.SetAuxpow(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.nVersion.SetAuxpow(false); + BOOST_CHECK(hashAux == block.GetHash()); + BOOST_CHECK(!CheckProofOfWork(block, params)); + + /* Modifying the block invalidates the PoW. */ + block.nVersion.SetAuxpow(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(CheckProofOfWork(block, params)); + tamperWith(block.hashMerkleRoot); + BOOST_CHECK(!CheckProofOfWork(block, params)); +} + +/* ************************************************************************** */ + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index a4fffdcf8..e8fbaad2b 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CBlock *pblock = &pblocktemplate->block; // pointer for convenience - pblock->nVersion = 1; + pblock->nVersion.SetGenesisVersion(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: diff --git a/src/txdb.cpp b/src/txdb.cpp index df9ff8d8c..bdc67a049 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -225,8 +225,10 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - if (!CheckProofOfWork(pindexNew->GetBlockHash(), 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/wallet/wallet.cpp b/src/wallet/wallet.cpp index 36baf930a..c4562badd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2672,88 +2672,3 @@ CWalletKey::CWalletKey(int64_t nExpires) nTimeCreated = (nExpires ? GetTime() : 0); nTimeExpires = nExpires; } - -int CMerkleTx::SetMerkleBranch(const CBlock& block) -{ - AssertLockHeld(cs_main); - CBlock blockTmp; - - // Update the tx's hashBlock - hashBlock = block.GetHash(); - - // Locate the transaction - for (nIndex = 0; nIndex < (int)block.vtx.size(); nIndex++) - if (block.vtx[nIndex] == *(CTransaction*)this) - break; - if (nIndex == (int)block.vtx.size()) - { - vMerkleBranch.clear(); - nIndex = -1; - LogPrintf("ERROR: SetMerkleBranch(): couldn't find tx in block\n"); - return 0; - } - - // Fill in merkle branch - vMerkleBranch = block.GetMerkleBranch(nIndex); - - // Is the tx in a block that's in the main chain - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - const CBlockIndex* pindex = (*mi).second; - if (!pindex || !chainActive.Contains(pindex)) - return 0; - - return chainActive.Height() - pindex->nHeight + 1; -} - -int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const -{ - if (hashBlock.IsNull() || nIndex == -1) - 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; - - // Make sure the merkle branch connects to this block - if (!fMerkleVerified) - { - if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) - return 0; - fMerkleVerified = true; - } - - pindexRet = pindex; - return chainActive.Height() - pindex->nHeight + 1; -} - -int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const -{ - AssertLockHeld(cs_main); - int nResult = GetDepthInMainChainINTERNAL(pindexRet); - if (nResult == 0 && !mempool.exists(GetHash())) - return -1; // Not in chain, not in mempool - - return nResult; -} - -int CMerkleTx::GetBlocksToMaturity() const -{ - if (!IsCoinBase()) - return 0; - return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain()); -} - - -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) -{ - CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee); -} - diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b0da92cfd..e1ce6f795 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 "key.h" #include "keystore.h" #include "primitives/block.h" @@ -144,65 +145,6 @@ struct COutputEntry int vout; }; -/** A transaction with a merkle branch linking it to the block chain. */ -class CMerkleTx : public CTransaction -{ -private: - int GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const; - -public: - uint256 hashBlock; - std::vector<uint256> vMerkleBranch; - int nIndex; - - // memory only - mutable bool fMerkleVerified; - - - CMerkleTx() - { - Init(); - } - - CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) - { - Init(); - } - - void Init() - { - hashBlock = uint256(); - nIndex = -1; - fMerkleVerified = false; - } - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(*(CTransaction*)this); - nVersion = this->nVersion; - READWRITE(hashBlock); - READWRITE(vMerkleBranch); - READWRITE(nIndex); - } - - int SetMerkleBranch(const CBlock& block); - - - /** - * Return depth of transaction in blockchain: - * -1 : not in blockchain, and not in memory pool (conflicted transaction) - * 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 GetDepthInMainChainINTERNAL(pindexRet) > 0; } - int GetBlocksToMaturity() const; - bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectAbsurdFee=true); -}; - /** * 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. |