diff options
| author | Daniel Kraft <[email protected]> | 2014-11-12 13:56:22 +0100 |
|---|---|---|
| committer | Ross Nicoll <[email protected]> | 2015-07-05 18:45:24 +0100 |
| commit | eabf633f13bef26d5775f00b9c5c59d72ad232cf (patch) | |
| tree | aadb413abd944ed8b996ef9d65e8e26b20ac6249 | |
| parent | [tests] fix partition alerts for Dogecoin (diff) | |
| download | discoin-eabf633f13bef26d5775f00b9c5c59d72ad232cf.tar.xz discoin-eabf633f13bef26d5775f00b9c5c59d72ad232cf.zip | |
Merge AuxPoW support from Namecore
Includes the following commits:
commit 2ea1b5cd8cf2bdd08e43ae39fb76352eebe14895
commit f2a8220c34275f022f02d81e9e84d4cec33bd51c
commit 84b8b56113e6057b0253475b83e797dc1fed2eed
commit 8471d5db221f145f5a40022ed1897c1b996b628e
commit 2f125ad0a67e1b8f71b18a14a3b41d7e577391d1
commit e01dbc3608703b4cb4e9882738125f2b7816cdb8
commit f0421312631cd44669f9f84d339a0c470b4423b9
commit 1fd522db5dfddfd0e1b0c794b82fae2cc7bdb099
commit 71f63ad99f70ff6461c795fd728aea16aa1008f8
commit a7c44d98a8ded4df0e8455c4c5629b1a5b303bbf
commit e1d7b4fc15addf3dfeb3853fa66230a8bdacd75f
commit d016f2fa02572fd340129176b942b3f19bd5260b
commit f4716e5a168a697afce854a37350fdae0988bdd8
commit 03a575e3b13bf06cbb0a007d6672b05d2085c26e
commit d7ea37444bd9e9ac461a3dda0b16afefd160b062
commit db22affa01e050ff847a12e20c83b88952d80b59
commit 9b7e14986655f914b2d0903ca1d79367c92c6c7e
commit e21034f9c124893d5b9631e9ca231b0b9ede3d52
Changes are as below:
Wrap CBlockHeader::nVersion into a new class (CBlockVersion). This allows to take care of interpreting the field into a base version, auxpow flag and the chain ID.
Update getauxblock.py for new 'generate' RPC call.
Add 'auxpow' to block JSON.
Accept auxpow as PoW verification.
Add unit tests for auxpow verification.
Add check for memory-layout of CBlockVersion.
Weaken auxpow chain ID checks for the testnet.
Allow Params() to overrule when to check the auxpow chain ID and for legacy blocks. Use this to disable the checks on testnet.
Introduce CPureBlockHeader.
Split the block header part that is used by auxpow and the "real" block header (that uses auxpow) to resolve the cyclic dependency between the two.
Differentiate between uint256 and arith_uint256.
This change was done upstream, modify the auxpow code.
Add missing lock in auxpow_tests.
Fix REST header check for auxpow headers.
Those can be longer, thus take that into account. Also perform the check actually on an auxpow header.
Correctly set the coinbase for getauxblock results.
Call IncrementExtraNonce in getauxblock so that the coinbase is actually initialised with the stuff it should be. (BIP30 block height and COINBASE_FLAGS.)
Implement getauxblock plus regression test.
Turn auxpow test into FIXTURE test.
This allows using of the Params() calls.
Move CMerkleTx code to auxpow.cpp.
Otherwise we get linker errors when building without wallet.
Fix rebase with BIP66.
Update the code to handle BIP66's nVersion=3.
Enforce that auxpow parent blocks have no auxpow block version.
This is for compatibility with namecoind. See also https://github.com/namecoin/namecoin/pull/199.
Move auxpow-related parameters to Consensus::Params.
| -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. |