Merge AuxPoW support from Namecore

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.
This commit is contained in:
Ross Nicoll 2017-08-13 13:31:12 +01:00
parent d2feb2e6fb
commit bc8cca4896
32 changed files with 1884 additions and 226 deletions

123
qa/rpc-tests/getauxblock.py Executable file
View File

@ -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 ()

View File

@ -8,6 +8,7 @@
#
from test_framework import auxpow
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from struct import *
@ -203,8 +204,10 @@ class RESTTest (BitcoinTestFramework):
response = http_post_call(url.hostname, url.port, '/rest/getutxos'+json_request+self.FORMAT_SEPARATOR+'json', '', True)
assert_equal(response.status, 200) #must be a 200 because we are within the limits
self.nodes[0].generate(1) #generate block to not affect upcoming tests
# Generate a block to not affect upcoming tests.
auxpow.mineAuxpowBlock(self.nodes[0]) #generate
self.sync_all()
bb_hash = self.nodes[0].getbestblockhash()
################
# /rest/block/ #
@ -219,24 +222,26 @@ class RESTTest (BitcoinTestFramework):
# compare with block header
response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", True)
assert_equal(response_header.status, 200)
assert_equal(int(response_header.getheader('content-length')), 80)
headerLen = int(response_header.getheader('content-length'))
assert_greater_than(headerLen, 80)
response_header_str = response_header.read()
assert_equal(response_str[0:80], response_header_str)
assert_equal(response_str[0:headerLen], response_header_str)
# check block hex format
response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
assert_equal(response_hex.status, 200)
assert_greater_than(int(response_hex.getheader('content-length')), 160)
response_hex_str = response_hex.read()
assert_equal(encode(response_str, "hex_codec")[0:160], response_hex_str[0:160])
response_hex_str = response_hex.read().strip()
assert_equal(encode(response_str, "hex_codec"), response_hex_str)
# compare with hex block header
response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", True)
assert_equal(response_header_hex.status, 200)
assert_greater_than(int(response_header_hex.getheader('content-length')), 160)
response_header_hex_str = response_header_hex.read()
assert_equal(response_hex_str[0:160], response_header_hex_str[0:160])
assert_equal(encode(response_header_str, "hex_codec")[0:160], response_header_hex_str[0:160])
response_header_hex_str = response_header_hex.read().strip()
headerLen = len (response_header_hex_str)
assert_equal(response_hex_str[0:headerLen], response_header_hex_str)
assert_equal(encode(response_header_str, "hex_codec"), response_header_hex_str)
# check json format
block_json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')

View File

@ -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)

View File

@ -78,6 +78,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
auxpow.h \
base58.h \
bloom.h \
blockencodings.h \
@ -121,6 +122,8 @@ BITCOIN_CORE_H = \
policy/policy.h \
policy/rbf.h \
pow.h \
primitives/block.h \
primitives/pureheader.h \
protocol.h \
random.h \
reverselock.h \
@ -300,6 +303,8 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
amount.cpp \
arith_uint256.cpp \
auxpow.cpp \
base58.cpp \
chainparams.cpp \
coins.cpp \
@ -310,6 +315,9 @@ libbitcoin_common_a_SOURCES = \
keystore.cpp \
netaddress.cpp \
netbase.cpp \
primitives/block.cpp \
primitives/pureheader.cpp \
primitives/transaction.cpp \
protocol.cpp \
scheduler.cpp \
script/sign.cpp \

View File

@ -82,6 +82,7 @@ BITCOIN_TESTS =\
test/addrman_tests.cpp \
test/amount_tests.cpp \
test/allocator_tests.cpp \
test/auxpow_tests.cpp \
test/base32_tests.cpp \
test/base58_tests.cpp \
test/base64_tests.cpp \

238
src/auxpow.cpp Normal file
View File

@ -0,0 +1,238 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 Vince Durham
// Copyright (c) 2009-2014 The Bitcoin developers
// Copyright (c) 2014-2016 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "auxpow.h"
#include "compat/endian.h"
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.h"
#include "script/script.h"
#include "txmempool.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validation.h"
#include <algorithm>
/* Moved from wallet.cpp. CMerkleTx is necessary for auxpow, independent
of an enabled (or disabled) wallet. Always include the code. */
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
{
// Update the tx's hashBlock
hashBlock = pindex->GetBlockHash();
// set the position of the transaction in the block
nIndex = posInBlock;
}
void CMerkleTx::InitMerkleBranch(const CBlock& block, int posInBlock)
{
hashBlock = block.GetHash();
nIndex = posInBlock;
vMerkleBranch = BlockMerkleBranch(block, nIndex);
}
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
{
if (hashUnset())
return 0;
AssertLockHeld(cs_main);
// Find the block it claims to be in
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex))
return 0;
pindexRet = pindex;
return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
}
int CMerkleTx::GetBlocksToMaturity() const
{
if (!IsCoinBase())
return 0;
return std::max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
}
bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
}
/* ************************************************************************** */
bool
CAuxPow::check(const uint256& hashAuxBlock, int nChainId,
const Consensus::Params& params) const
{
if (nIndex != 0)
return error("AuxPow is not a generate");
if (params.fStrictChainId && parentBlock.GetChainId () == nChainId)
return error("Aux POW parent has our chain ID");
if (vChainMerkleBranch.size() > 30)
return error("Aux POW chain merkle branch too long");
// Check that the chain merkle root is in the coinbase
const uint256 nRootHash
= CheckMerkleBranch(hashAuxBlock, vChainMerkleBranch, nChainIndex);
std::vector<unsigned char> vchRootHash(nRootHash.begin(), nRootHash.end());
std::reverse(vchRootHash.begin(), vchRootHash.end()); // correct endian
// Check that we are in the parent block merkle tree
if (CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex)
!= parentBlock.hashMerkleRoot)
return error("Aux POW merkle root incorrect");
const CScript script = tx->vin[0].scriptSig;
// Check that the same work is not submitted twice to our chain.
//
CScript::const_iterator pcHead =
std::search(script.begin(), script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader));
CScript::const_iterator pc =
std::search(script.begin(), script.end(), vchRootHash.begin(), vchRootHash.end());
if (pc == script.end())
return error("Aux POW missing chain merkle root in parent coinbase");
if (pcHead != script.end())
{
// Enforce only one chain merkle root by checking that a single instance of the merged
// mining header exists just before.
if (script.end() != std::search(pcHead + 1, script.end(), UBEGIN(pchMergedMiningHeader), UEND(pchMergedMiningHeader)))
return error("Multiple merged mining headers in coinbase");
if (pcHead + sizeof(pchMergedMiningHeader) != pc)
return error("Merged mining header is not just before chain merkle root");
}
else
{
// For backward compatibility.
// Enforce only one chain merkle root by checking that it starts early in the coinbase.
// 8-12 bytes are enough to encode extraNonce and nBits.
if (pc - script.begin() > 20)
return error("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase");
}
// Ensure we are at a deterministic point in the merkle leaves by hashing
// a nonce and our chain ID and comparing to the index.
pc += vchRootHash.size();
if (script.end() - pc < 8)
return error("Aux POW missing chain merkle tree size and nonce in parent coinbase");
uint32_t nSize;
memcpy(&nSize, &pc[0], 4);
nSize = le32toh(nSize);
const unsigned merkleHeight = vChainMerkleBranch.size();
if (nSize != (1u << merkleHeight))
return error("Aux POW merkle branch size does not match parent coinbase");
uint32_t nNonce;
memcpy(&nNonce, &pc[4], 4);
nNonce = le32toh (nNonce);
if (nChainIndex != getExpectedIndex (nNonce, nChainId, merkleHeight))
return error("Aux POW wrong index");
return true;
}
int
CAuxPow::getExpectedIndex (uint32_t nNonce, int nChainId, unsigned h)
{
// Choose a pseudo-random slot in the chain merkle tree
// but have it be fixed for a size/nonce/chain combination.
//
// This prevents the same work from being used twice for the
// same chain while reducing the chance that two chains clash
// for the same slot.
/* This computation can overflow the uint32 used. This is not an issue,
though, since we take the mod against a power-of-two in the end anyway.
This also ensures that the computation is, actually, consistent
even if done in 64 bits as it was in the past on some systems.
Note that h is always <= 30 (enforced by the maximum allowed chain
merkle branch length), so that 32 bits are enough for the computation. */
uint32_t rand = nNonce;
rand = rand * 1103515245 + 12345;
rand += nChainId;
rand = rand * 1103515245 + 12345;
return rand % (1 << h);
}
uint256
CAuxPow::CheckMerkleBranch (uint256 hash,
const std::vector<uint256>& vMerkleBranch,
int nIndex)
{
if (nIndex == -1)
return uint256 ();
for (std::vector<uint256>::const_iterator it(vMerkleBranch.begin ());
it != vMerkleBranch.end (); ++it)
{
if (nIndex & 1)
hash = Hash (BEGIN (*it), END (*it), BEGIN (hash), END (hash));
else
hash = Hash (BEGIN (hash), END (hash), BEGIN (*it), END (*it));
nIndex >>= 1;
}
return hash;
}
void
CAuxPow::initAuxPow (CBlockHeader& header)
{
/* Set auxpow flag right now, since we take the block hash below. */
header.SetAuxpowFlag(true);
/* Build a minimal coinbase script input for merge-mining. */
const uint256 blockHash = header.GetHash ();
std::vector<unsigned char> inputData(blockHash.begin (), blockHash.end ());
std::reverse (inputData.begin (), inputData.end ());
inputData.push_back (1);
inputData.insert (inputData.end (), 7, 0);
/* Fake a parent-block coinbase with just the required input
script and no outputs. */
CMutableTransaction coinbase;
coinbase.vin.resize(1);
coinbase.vin[0].prevout.SetNull();
coinbase.vin[0].scriptSig = (CScript () << inputData);
assert (coinbase.vout.empty());
CTransactionRef coinbaseRef = MakeTransactionRef(coinbase);
/* Build a fake parent block with the coinbase. */
CBlock parent;
parent.nVersion = 1;
parent.vtx.resize(1);
parent.vtx[0] = coinbaseRef;
parent.hashMerkleRoot = BlockMerkleRoot(parent);
/* Construct the auxpow object. */
header.SetAuxpow(new CAuxPow(coinbaseRef));
assert (header.auxpow->vChainMerkleBranch.empty());
header.auxpow->nChainIndex = 0;
assert (header.auxpow->vMerkleBranch.empty());
header.auxpow->nIndex = 0;
header.auxpow->parentBlock = parent;
}

226
src/auxpow.h Normal file
View File

@ -0,0 +1,226 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
// Copyright (c) 2014-2016 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_AUXPOW_H
#define BITCOIN_AUXPOW_H
#include "consensus/params.h"
#include "consensus/validation.h"
#include "primitives/pureheader.h"
#include "primitives/transaction.h"
#include "serialize.h"
#include "uint256.h"
#include <vector>
class CBlock;
class CBlockHeader;
class CBlockIndex;
/** Header for merge-mining data in the coinbase. */
static const unsigned char pchMergedMiningHeader[] = { 0xfa, 0xbe, 'm', 'm' };
/* Because it is needed for auxpow, the definition of CMerkleTx is moved
here from wallet.h. */
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx
{
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
CTransactionRef tx;
uint256 hashBlock;
// Dogecoin TODO: Is this used? If not remove. If it is, I don't think it's actually set
// anywhere. Check with Namecore
std::vector<uint256> vMerkleBranch;
/* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
* block in the chain we know this or any in-wallet dependency conflicts
* with. Older clients interpret nIndex == -1 as unconfirmed for backward
* compatibility.
*/
int nIndex;
CMerkleTx()
{
SetTx(MakeTransactionRef());
Init();
}
CMerkleTx(CTransactionRef arg)
{
SetTx(std::move(arg));
Init();
}
/** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
* TODO: adapt callers and remove this operator. */
operator const CTransaction&() const { return *tx; }
void Init()
{
hashBlock = uint256();
nIndex = -1;
}
void SetTx(CTransactionRef arg)
{
tx = std::move(arg);
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
}
/**
* Actually compute the Merkle branch. This is used for unit tests when
* constructing an auxpow. It is not needed for actual production, since
* we do not care in the Namecoin client how the auxpow is constructed
* by a miner.
*/
void InitMerkleBranch(const CBlock& block, int posInBlock);
void SetMerkleBranch(const CBlockIndex* pindex, int posInBlock);
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
int GetBlocksToMaturity() const;
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
};
/**
* Data for the merge-mining auxpow. This is a merkle tx (the parent block's
* coinbase tx) that can be verified to be in the parent block, and this
* transaction's input (the coinbase script) contains the reference
* to the actual merge-mined block.
*/
class CAuxPow : public CMerkleTx
{
/* Public for the unit tests. */
public:
/** The merkle branch connecting the aux block to our coinbase. */
std::vector<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(CTransactionRef txIn)
: CMerkleTx(txIn)
{
}
inline CAuxPow()
: CMerkleTx()
{
}
ADD_SERIALIZE_METHODS;
template<typename Stream, typename Operation>
inline void
SerializationOp (Stream& s, Operation ser_action)
{
READWRITE (*static_cast<CMerkleTx*> (this));
READWRITE (vChainMerkleBranch);
READWRITE (nChainIndex);
READWRITE (parentBlock);
}
/**
* Check the auxpow, given the merge-mined block's hash and our chain ID.
* Note that this does not verify the actual PoW on the parent block! It
* just confirms that all the merkle branches are valid.
* @param hashAuxBlock Hash of the merge-mined block.
* @param nChainId The auxpow chain ID of the block to check.
* @param params Consensus parameters.
* @return True if the auxpow is valid.
*/
bool check(const uint256& hashAuxBlock, int nChainId, const Consensus::Params& params) const;
/**
* Get the parent block's hash. This is used to verify that it
* satisfies the PoW requirement.
* @return The parent block hash.
*/
inline uint256
getParentBlockPoWHash() const
{
return parentBlock.GetPoWHash ();
}
/**
* Return parent block. This is only used for the temporary parentblock
* auxpow version check.
* @return The parent block.
*/
/* FIXME: Remove after the hardfork. */
inline const CPureBlockHeader&
getParentBlock() const
{
return parentBlock;
}
/**
* Calculate the expected index in the merkle tree. This is also used
* for the test-suite.
* @param nNonce The coinbase's nonce value.
* @param nChainId The chain ID.
* @param h The merkle block height.
* @return The expected index for the aux hash.
*/
static int getExpectedIndex(uint32_t nNonce, int nChainId, unsigned h);
/**
* Check a merkle branch. This used to be in CBlock, but was removed
* upstream. Thus include it here now.
*/
static uint256 CheckMerkleBranch(uint256 hash,
const std::vector<uint256>& vMerkleBranch,
int nIndex);
/**
* Initialise the auxpow of the given block header. This constructs
* a minimal CAuxPow object with a minimal parent block and sets
* it on the block header. The auxpow is not necessarily valid, but
* can be "mined" to make it valid.
* @param header The header to set the auxpow on.
*/
static void initAuxPow(CBlockHeader& header);
};
#endif // BITCOIN_AUXPOW_H

View File

@ -4,6 +4,34 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chain.h"
#include "validation.h"
using namespace std;
/* Moved here from the header, because we need auxpow and the logic
becomes more involved. */
CBlockHeader CBlockIndex::GetBlockHeader(const Consensus::Params& consensusParams) const
{
CBlockHeader block;
/* The CBlockIndex object's block header is missing the auxpow.
So if this is an auxpow block, read it from disk instead. We only
have to read the actual *header*, not the full block. */
if (block.IsAuxpow())
{
ReadBlockHeaderFromDisk(block, this, consensusParams);
return block;
}
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
/**
* CChain implementation

View File

@ -8,6 +8,7 @@
#include "arith_uint256.h"
#include "primitives/block.h"
#include "primitives/pureheader.h"
#include "pow.h"
#include "tinyformat.h"
#include "uint256.h"
@ -199,9 +200,6 @@ public:
unsigned int nBits;
unsigned int nNonce;
// Dogecoin: Keep the Scrypt hash as well as SHA256
uint256 hashBlockPoW;
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
int32_t nSequenceId;
@ -224,12 +222,11 @@ public:
nSequenceId = 0;
nTimeMax = 0;
nVersion = 0;
nVersion = 0;
hashMerkleRoot = uint256();
nTime = 0;
nBits = 0;
nNonce = 0;
hashBlockPoW = uint256();
}
CBlockIndex()
@ -246,7 +243,6 @@ public:
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
hashBlockPoW = block.GetPoWHash();
}
CDiskBlockPos GetBlockPos() const {
@ -267,29 +263,13 @@ public:
return ret;
}
CBlockHeader GetBlockHeader() const
{
CBlockHeader block;
block.nVersion = nVersion;
if (pprev)
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
return block;
}
CBlockHeader GetBlockHeader(const Consensus::Params& consensusParams) const;
uint256 GetBlockHash() const
{
return *phashBlock;
}
uint256 GetBlockPoWHash() const
{
return hashBlockPoW;
}
int64_t GetBlockTime() const
{
return (int64_t)nTime;
@ -353,6 +333,24 @@ public:
//! Efficiently find an ancestor of this block.
CBlockIndex* GetAncestor(int height);
const CBlockIndex* GetAncestor(int height) const;
/**
* Extract the chain ID.
* @return The chain ID encoded in the version.
*/
inline int32_t GetChainId() const
{
return nVersion >> 16;
}
/**
* Check if the auxpow flag is set in the version.
* @return True if this block version is marked as auxpow.
*/
inline bool IsAuxpow() const
{
return nVersion & CPureBlockHeader::VERSION_AUXPOW;
}
};
arith_uint256 GetBlockProof(const CBlockIndex& block);
@ -398,7 +396,6 @@ public:
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
READWRITE(hashBlockPoW);
}
uint256 GetBlockHash() const

View File

@ -106,7 +106,11 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0xca5eb72f1e0d160f1481f74d56d7cc4a27d91aa585ba012da8018a5fe934d61b"); // 1,600,000
/**
consensus.nAuxpowChainId = 0x0062; // 98 - Josh Wise!
consensus.nAuxpowStartHeight = 371337;
consensus.fStrictChainId = true;
consensus.nLegacyBlocksBefore = 371337;
/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
* a large 32-bit integer with any alignment.
@ -119,6 +123,7 @@ public:
nPruneAfterHeight = 100000;
genesis = CreateGenesisBlock(1386325540, 99943, 0x1e0ffff0, 1, 88 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
assert(consensus.hashGenesisBlock == uint256S("0x1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691"));
assert(genesis.hashMerkleRoot == uint256S("0x5b2a3f53f605d62c53e62932dac6925e3d74afa5a4b459745c36d42d0ed26a69"));
@ -213,6 +218,10 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x6943eaeaba98dc7d09f7e73398daccb4abcabb18b66c8c875e52b07638d93951"); // 900,000
consensus.nAuxpowStartHeight = 158100;
consensus.fStrictChainId = false;
consensus.nLegacyBlocksBefore = -1;
pchMessageStart[0] = 0xfc;
pchMessageStart[1] = 0xc1;
pchMessageStart[2] = 0xb7;
@ -300,6 +309,8 @@ public:
// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x00");
consensus.fStrictChainId = true;
consensus.nLegacyBlocksBefore = 0;
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
pchMessageStart[2] = 0xb5;

View File

@ -77,6 +77,7 @@ public:
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
protected:
CChainParams() {}

View File

@ -63,6 +63,24 @@ struct Params {
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
uint256 nMinimumChainWork;
uint256 defaultAssumeValid;
/** Auxpow parameters */
int32_t nAuxpowChainId;
int nAuxpowStartHeight;
bool fStrictChainId;
int nLegacyBlocksBefore; // -1 for "always allow"
/**
* Check whether or not to allow legacy blocks at the given height.
* @param nHeight Height of the block to check.
* @return True if it is allowed to have a legacy version.
*/
bool AllowLegacyBlocks(unsigned nHeight) const
{
if (nLegacyBlocksBefore < 0)
return true;
return static_cast<int> (nHeight) < nLegacyBlocksBefore;
}
};
} // namespace Consensus

View File

@ -21,9 +21,9 @@ unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, in
int nHeight = pindexLast->nHeight + 1;
bool fNewDifficultyProtocol = (nHeight >= 145000);
// bool fNewDifficultyProtocol = (nHeight >= params.GetDigiShieldForkBlock());
const int64_t retargetTimespan = fNewDifficultyProtocol
? 60 // params.DigiShieldTargetTimespan()
: params.nPowTargetTimespan;
const int64_t retargetTimespan = fNewDifficultyProtocol ? 60 // params.DigiShieldTargetTimespan()
:
params.nPowTargetTimespan;
const int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
int64_t nModulatedTimespan = nActualTimespan;
@ -75,6 +75,44 @@ unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, in
return bnNew.GetCompact();
}
bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params)
{
/* Except for legacy blocks with full version 1, ensure that
the chain ID is correct. Legacy blocks are not allowed since
the merge-mining start, which is checked in AcceptBlockHeader
where the height is known. */
if (!block.IsLegacy() && params.fStrictChainId && block.GetChainId() != params.nAuxpowChainId)
return error("%s : block does not have our chain ID"
" (got %d, expected %d, full nVersion %d)",
__func__, block.GetChainId(),
params.nAuxpowChainId, block.nVersion);
/* If there is no auxpow, just check the block hash. */
if (!block.auxpow)
{
if (block.IsAuxpow())
return error("%s : no auxpow on block with auxpow version",
__func__);
if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, params))
return error("%s : non-AUX proof of work failed", __func__);
return true;
}
/* We have auxpow. Check it. */
if (!block.IsAuxpow())
return error("%s : auxpow on block with non-auxpow version", __func__);
if (!block.auxpow->check(block.GetHash(), block.GetChainId(), params))
return error("%s : AUX POW is not valid", __func__);
if (!CheckProofOfWork(block.auxpow->getParentBlockPoWHash(), block.nBits, params))
return error("%s : AUX proof of work failed", __func__);
return true;
}
CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash)
{
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;

View File

@ -2,9 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "amount.h"
#include "chain.h"
#include "chainparams.h"
#include "amount.h"
CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusParams, uint256 prevHash);
unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, int64_t nLastRetargetTime, const Consensus::Params& params);
/**
* Check proof-of-work of a block header, taking auxpow into account.
* @param block The block header.
* @param params Consensus parameters.
* @return True iff the PoW is correct.
*/
bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params);

View File

@ -200,6 +200,7 @@ void Shutdown()
StopRPC();
StopHTTPServer();
#ifdef ENABLE_WALLET
// Dogecoin 1.14 TODO: ShutdownRPCMining();
if (pwalletMain)
pwalletMain->Flush(false);
#endif
@ -1652,6 +1653,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// ********************************************************* Step 12: finished
// Dogecoin: Do we need to do any RPC mining init here?
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));

View File

@ -149,11 +149,12 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
CBlockIndex* pindexPrev = chainActive.Tip();
nHeight = pindexPrev->nHeight + 1;
pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
const int32_t nChainId = chainparams.GetConsensus ().nAuxpowChainId;
pblock->SetBaseVersion(ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()), nChainId);
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
pblock->SetBaseVersion(GetArg("-blockversion", pblock->GetBaseVersion()), nChainId);
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();

View File

@ -1748,7 +1748,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->id);
for (; pindex; pindex = chainActive.Next(pindex))
{
vHeaders.push_back(pindex->GetBlockHeader());
vHeaders.push_back(pindex->GetBlockHeader(chainparams.GetConsensus()));
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}
@ -2942,14 +2942,14 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
pBestIndex = pindex;
if (fFoundStartingHeader) {
// add this to the headers message
vHeaders.push_back(pindex->GetBlockHeader());
vHeaders.push_back(pindex->GetBlockHeader(consensusParams));
} else if (PeerHasHeader(&state, pindex)) {
continue; // keep looking for the first new block
} else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) {
// Peer doesn't have this header but they do have the prior one.
// Start sending headers.
fFoundStartingHeader = true;
vHeaders.push_back(pindex->GetBlockHeader());
vHeaders.push_back(pindex->GetBlockHeader(consensusParams));
} else {
// Peer doesn't have this header or the prior one -- nothing will
// connect, so bail out.

View File

@ -5,6 +5,7 @@
#include "pow.h"
#include "auxpow.h"
#include "arith_uint256.h"
#include "chain.h"
#include "dogecoin.h"

View File

@ -6,21 +6,21 @@
#include "primitives/block.h"
#include "hash.h"
#include "crypto/scrypt.h"
#include "tinyformat.h"
#include "utilstrencodings.h"
#include "crypto/common.h"
uint256 CBlockHeader::GetHash() const
void CBlockHeader::SetAuxpow (CAuxPow* apow)
{
return SerializeHash(*this);
}
uint256 CBlockHeader::GetPoWHash() const
{
uint256 thash;
scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash));
return thash;
if (apow)
{
auxpow.reset(apow);
SetAuxpowFlag(true);
} else
{
auxpow.reset();
SetAuxpowFlag(false);
}
}
std::string CBlock::ToString() const

View File

@ -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,16 +21,11 @@
* in the block is a special one that creates a new coin owned by the creator
* of the block.
*/
class CBlockHeader
class CBlockHeader : public CPureBlockHeader
{
public:
// header
int32_t nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
// auxpow (if this is a merge-minded block)
boost::shared_ptr<CAuxPow> auxpow;
CBlockHeader()
{
@ -37,37 +36,30 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
READWRITE(*(CPureBlockHeader*)this);
if (this->IsAuxpow())
{
if (ser_action.ForRead())
auxpow.reset(new CAuxPow());
assert(auxpow);
READWRITE(*auxpow);
} else if (ser_action.ForRead())
auxpow.reset();
}
void SetNull()
{
nVersion = 0;
hashPrevBlock.SetNull();
hashMerkleRoot.SetNull();
nTime = 0;
nBits = 0;
nNonce = 0;
CPureBlockHeader::SetNull();
auxpow.reset();
}
bool IsNull() const
{
return (nBits == 0);
}
uint256 GetHash() const;
uint256 GetPoWHash() const;
int64_t GetBlockTime() const
{
return (int64_t)nTime;
}
/**
* Set the block's auxpow (or unset it). This takes care of updating
* the version accordingly.
* @param apow Pointer to the auxpow to use or NULL.
*/
void SetAuxpow(CAuxPow* apow);
};
@ -115,9 +107,18 @@ public:
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
block.auxpow = auxpow;
return block;
}
// Build the in-memory merkle tree for this block and return the merkle root.
// If non-NULL, *mutated is set to whether mutation was detected in the merkle
// tree (a duplication of transactions in the block leading to an identical
// merkle root).
uint256 BuildMerkleTree(bool* mutated = NULL) const;
std::vector<uint256> GetMerkleBranch(int nIndex) const;
static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);
std::string ToString() const;
};

View File

@ -0,0 +1,30 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "primitives/pureheader.h"
#include "chainparams.h"
#include "crypto/scrypt.h"
#include "hash.h"
#include "utilstrencodings.h"
void CPureBlockHeader::SetBaseVersion(int32_t nBaseVersion, int32_t nChainId)
{
assert(nBaseVersion >= 1 && nBaseVersion < VERSION_AUXPOW);
assert(!IsAuxpow());
nVersion = nBaseVersion | (nChainId * VERSION_CHAIN_START);
}
uint256 CPureBlockHeader::GetHash() const
{
return SerializeHash(*this);
}
uint256 CPureBlockHeader::GetPoWHash() const
{
uint256 thash;
scrypt_1024_1_1_256(BEGIN(nVersion), BEGIN(thash));
return thash;
}

156
src/primitives/pureheader.h Normal file
View File

@ -0,0 +1,156 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PRIMITIVES_PUREHEADER_H
#define BITCOIN_PRIMITIVES_PUREHEADER_H
#include "serialize.h"
#include "uint256.h"
/**
* A block header without auxpow information. This "intermediate step"
* in constructing the full header is useful, because it breaks the cyclic
* dependency between auxpow (referencing a parent block header) and
* the block header (referencing an auxpow). The parent block header
* does not have auxpow itself, so it is a pure header.
*/
class CPureBlockHeader
{
public:
/* Modifiers to the version. */
static const int32_t VERSION_AUXPOW = (1 << 8);
/** Bits above are reserved for the auxpow chain ID. */
static const int32_t VERSION_CHAIN_START = (1 << 16);
// header
int32_t nVersion;
uint256 hashPrevBlock;
uint256 hashMerkleRoot;
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
CPureBlockHeader()
{
SetNull();
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(this->nVersion);
READWRITE(hashPrevBlock);
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);
}
void SetNull()
{
nVersion = 0;
hashPrevBlock.SetNull();
hashMerkleRoot.SetNull();
nTime = 0;
nBits = 0;
nNonce = 0;
}
bool IsNull() const
{
return (nBits == 0);
}
uint256 GetHash() const;
uint256 GetPoWHash() const;
int64_t GetBlockTime() const
{
return (int64_t)nTime;
}
/* Below are methods to interpret the version with respect to
auxpow data and chain ID. This used to be in the CBlockVersion
class, but was moved here when we switched back to nVersion being
a pure int member as preparation to undoing the "abuse" and
allowing BIP9 to work. */
/**
* Extract the base version (without modifiers and chain ID).
* @return The base version./
*/
inline int32_t GetBaseVersion() const
{
return GetBaseVersion(nVersion);
}
static inline int32_t GetBaseVersion(int32_t ver)
{
return ver % VERSION_AUXPOW;
}
/**
* Set the base version (apart from chain ID and auxpow flag) to
* the one given. This should only be called when auxpow is not yet
* set, to initialise a block!
* @param nBaseVersion The base version.
* @param nChainId The auxpow chain ID.
*/
void SetBaseVersion(int32_t nBaseVersion, int32_t nChainId);
/**
* Extract the chain ID.
* @return The chain ID encoded in the version.
*/
inline int32_t GetChainId() const
{
return nVersion >> 16;
}
/**
* Set the chain ID. This is used for the test suite.
* @param ch The chain ID to set.
*/
inline void SetChainId(int32_t chainId)
{
nVersion %= VERSION_CHAIN_START;
nVersion |= chainId * VERSION_CHAIN_START;
}
/**
* Check if the auxpow flag is set in the version.
* @return True if this block version is marked as auxpow.
*/
inline bool IsAuxpow() const
{
return nVersion & VERSION_AUXPOW;
}
/**
* Set the auxpow flag. This is used for testing.
* @param auxpow Whether to mark auxpow as true.
*/
inline void SetAuxpowFlag(bool auxpow)
{
if (auxpow)
nVersion |= VERSION_AUXPOW;
else
nVersion &= ~VERSION_AUXPOW;
}
/**
* Check whether this is a "legacy" block without chain ID.
* @return True if it is.
*/
inline bool IsLegacy() const
{
return nVersion == 1;
}
};
#endif // BITCOIN_PRIMITIVES_PUREHEADER_H

View File

@ -160,8 +160,9 @@ static bool rest_headers(HTTPRequest* req,
}
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
const CChainParams& chainparams = Params();
BOOST_FOREACH(const CBlockIndex *pindex, headers) {
ssHeader << pindex->GetBlockHeader();
ssHeader << pindex->GetBlockHeader(chainparams.GetConsensus());
}
switch (rf) {

View File

@ -9,6 +9,7 @@
#include "checkpoints.h"
#include "coins.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "validation.h"
#include "policy/policy.h"
#include "primitives/transaction.h"
@ -74,6 +75,42 @@ double GetDifficulty(const CBlockIndex* blockindex)
return dDiff;
}
UniValue AuxpowToJSON(const CAuxPow& auxpow)
{
UniValue result(UniValue::VOBJ);
{
UniValue tx(UniValue::VOBJ);
tx.push_back(Pair("hex", EncodeHexTx(auxpow)));
TxToJSON(auxpow, auxpow.parentBlock.GetHash(), tx);
result.push_back(Pair("tx", tx));
}
result.push_back(Pair("index", auxpow.nIndex));
result.push_back(Pair("chainindex", auxpow.nChainIndex));
{
UniValue branch(UniValue::VARR);
BOOST_FOREACH(const uint256& node, auxpow.vMerkleBranch)
branch.push_back(node.GetHex());
result.push_back(Pair("merklebranch", branch));
}
{
UniValue branch(UniValue::VARR);
BOOST_FOREACH(const uint256& node, auxpow.vChainMerkleBranch)
branch.push_back(node.GetHex());
result.push_back(Pair("chainmerklebranch", branch));
}
CDataStream ssParent(SER_NETWORK, PROTOCOL_VERSION);
ssParent << auxpow.parentBlock;
const std::string strHex = HexStr(ssParent.begin(), ssParent.end());
result.push_back(Pair("parentblock", strHex));
return result;
}
UniValue blockheaderToJSON(const CBlockIndex* blockindex)
{
UniValue result(UniValue::VOBJ);
@ -138,6 +175,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("difficulty", GetDifficulty(blockindex)));
result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex()));
if (block.auxpow)
result.push_back(Pair("auxpow", AuxpowToJSON(*block.auxpow)));
if (blockindex->pprev)
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
CBlockIndex *pnext = chainActive.Next(blockindex);
@ -680,7 +720,7 @@ UniValue getblockheader(const JSONRPCRequest& request)
if (!fVerbose)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << pblockindex->GetBlockHeader();
ssBlock << pblockindex->GetBlockHeader(Params().GetConsensus());
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
return strHex;
}

View File

@ -121,14 +121,16 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
LOCK(cs_main);
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
++pblock->nNonce;
CAuxPow::initAuxPow(*pblock);
CPureBlockHeader& miningHeader = pblock->auxpow->parentBlock;
while (nMaxTries > 0 && miningHeader.nNonce < nInnerLoopCount && !CheckProofOfWork(miningHeader.GetHash(), pblock->nBits, Params().GetConsensus())) {
++miningHeader.nNonce;
--nMaxTries;
}
if (nMaxTries == 0) {
break;
}
if (pblock->nNonce == nInnerLoopCount) {
if (miningHeader.nNonce == nInnerLoopCount) {
continue;
}
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
@ -925,6 +927,262 @@ UniValue estimatesmartpriority(const JSONRPCRequest& request)
return result;
}
/* ************************************************************************** */
/* Merge mining. */
/**
* The variables below are used to keep track of created and not yet
* submitted auxpow blocks. Lock them to be sure even for multiple
* RPC threads running in parallel.
*/
static CCriticalSection cs_auxblockCache;
static std::map<uint256, CBlock*> mapNewBlock;
static std::vector<std::unique_ptr<CBlockTemplate>> vNewBlockTemplate;
static
void AuxMiningCheck()
{
if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0
&& !Params().MineBlocksOnDemand())
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED,
"Dogecoin is not connected!");
if (IsInitialBlockDownload() && !Params().MineBlocksOnDemand())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
"Dogecoin is downloading blocks...");
/* This should never fail, since the chain is already
past the point of merge-mining start. Check nevertheless. */
{
LOCK(cs_main);
if (chainActive.Height() + 1 < Params().GetConsensus().nAuxpowStartHeight)
throw std::runtime_error("mining auxblock method is not yet available");
}
}
static
UniValue AuxMiningCreateBlock(const CScript& scriptPubKey)
{
AuxMiningCheck();
LOCK(cs_auxblockCache);
static unsigned nTransactionsUpdatedLast;
static const CBlockIndex* pindexPrev = nullptr;
static uint64_t nStart;
static CBlock* pblock = nullptr;
static unsigned nExtraNonce = 0;
// Update block
{
LOCK(cs_main);
if (pindexPrev != chainActive.Tip()
|| (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast
&& GetTime() - nStart > 60))
{
if (pindexPrev != chainActive.Tip())
{
// Clear old blocks since they're obsolete now.
mapNewBlock.clear();
vNewBlockTemplate.clear();
pblock = nullptr;
}
// Create new block with nonce = 0 and extraNonce = 1
std::unique_ptr<CBlockTemplate> newBlock
= BlockAssembler(Params()).CreateNewBlock(scriptPubKey);
if (!newBlock)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "out of memory");
// Update state only when CreateNewBlock succeeded
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
pindexPrev = chainActive.Tip();
nStart = GetTime();
// Finalise it by setting the version and building the merkle root
IncrementExtraNonce(&newBlock->block, pindexPrev, nExtraNonce);
newBlock->block.SetAuxpowFlag(true);
// Save
pblock = &newBlock->block;
mapNewBlock[pblock->GetHash()] = pblock;
vNewBlockTemplate.push_back(std::move(newBlock));
}
}
// At this point, pblock is always initialised: If we make it here
// without creating a new block above, it means that, in particular,
// pindexPrev == chainActive.Tip(). But for that to happen, we must
// already have created a pblock in a previous call, as pindexPrev is
// initialised only when pblock is.
assert(pblock);
arith_uint256 target;
bool fNegative, fOverflow;
target.SetCompact(pblock->nBits, &fNegative, &fOverflow);
if (fNegative || fOverflow || target == 0)
throw std::runtime_error("invalid difficulty bits in block");
UniValue result(UniValue::VOBJ);
result.pushKV("hash", pblock->GetHash().GetHex());
result.pushKV("chainid", pblock->GetChainId());
result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
result.pushKV("bits", strprintf("%08x", pblock->nBits));
result.pushKV("height", static_cast<int64_t> (pindexPrev->nHeight + 1));
result.pushKV("_target", HexStr(BEGIN(target), END(target)));
return result;
}
static
bool AuxMiningSubmitBlock(const std::string& hashHex,
const std::string& auxpowHex)
{
AuxMiningCheck();
LOCK(cs_auxblockCache);
uint256 hash;
hash.SetHex(hashHex);
const std::map<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(auxpowHex);
CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION);
CAuxPow pow;
ss >> pow;
block.SetAuxpow(new CAuxPow(pow));
assert(block.GetHash() == hash);
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
std::shared_ptr<const CBlock> shared_block
= std::make_shared<const CBlock>(block);
bool fAccepted = ProcessNewBlock(Params(), shared_block, true, nullptr);
UnregisterValidationInterface(&sc);
return fAccepted;
}
UniValue getauxblock(const JSONRPCRequest& request)
{
if (request.fHelp
|| (request.params.size() != 0 && request.params.size() != 2))
throw std::runtime_error(
"getauxblock (hash auxpow)\n"
"\nCreate or submit a merge-mined block.\n"
"\nWithout arguments, create a new block and return information\n"
"required to merge-mine it. With arguments, submit a solved\n"
"auxpow for a previously returned block.\n"
"\nArguments:\n"
"1. hash (string, optional) hash of the block to submit\n"
"2. auxpow (string, optional) serialised auxpow found\n"
"\nResult (without arguments):\n"
"{\n"
" \"hash\" (string) hash of the created block\n"
" \"chainid\" (numeric) chain ID for this block\n"
" \"previousblockhash\" (string) hash of the previous block\n"
" \"coinbasevalue\" (numeric) value of the block's coinbase\n"
" \"bits\" (string) compressed target of the block\n"
" \"height\" (numeric) height of the block\n"
" \"_target\" (string) target in reversed byte order, deprecated\n"
"}\n"
"\nResult (with arguments):\n"
"xxxxx (boolean) whether the submitted block was correct\n"
"\nExamples:\n"
+ HelpExampleCli("getauxblock", "")
+ HelpExampleCli("getauxblock", "\"hash\" \"serialised auxpow\"")
+ HelpExampleRpc("getauxblock", "")
);
boost::shared_ptr<CReserveScript> coinbaseScript;
GetMainSignals().ScriptForMining(coinbaseScript);
// If the keypool is exhausted, no script is returned at all. Catch this.
if (!coinbaseScript)
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
//throw an error if no script was provided
if (!coinbaseScript->reserveScript.size())
throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet)");
/* Create a new block */
if (request.params.size() == 0)
return AuxMiningCreateBlock(coinbaseScript->reserveScript);
/* Submit a block instead. Note that this need not lock cs_main,
since ProcessNewBlock below locks it instead. */
assert(request.params.size() == 2);
bool fAccepted = AuxMiningSubmitBlock(request.params[0].get_str(),
request.params[1].get_str());
if (fAccepted)
coinbaseScript->KeepScript();
return fAccepted;
}
UniValue createauxblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"createauxblock <address>\n"
"\ncreate a new block and return information required to merge-mine it.\n"
"\nArguments:\n"
"1. address (string, required) specify coinbase transaction payout address\n"
"\nResult:\n"
"{\n"
" \"hash\" (string) hash of the created block\n"
" \"chainid\" (numeric) chain ID for this block\n"
" \"previousblockhash\" (string) hash of the previous block\n"
" \"coinbasevalue\" (numeric) value of the block's coinbase\n"
" \"bits\" (string) compressed target of the block\n"
" \"height\" (numeric) height of the block\n"
" \"_target\" (string) target in reversed byte order, deprecated\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("createauxblock", "\"address\"")
+ HelpExampleRpc("createauxblock", "\"address\"")
);
// Check coinbase payout address
CBitcoinAddress coinbaseAddress(request.params[0].get_str());
if (!coinbaseAddress.IsValid())
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid coinbase payout address");
const CScript scriptPubKey
= GetScriptForDestination(coinbaseAddress.Get());
return AuxMiningCreateBlock(scriptPubKey);
}
UniValue submitauxblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
"submitauxblock <hash> <auxpow>\n"
"\nsubmit a solved auxpow for a previously block created by 'createauxblock'.\n"
"\nArguments:\n"
"1. hash (string, required) hash of the block to submit\n"
"2. auxpow (string, required) serialised auxpow found\n"
"\nResult:\n"
"xxxxx (boolean) whether the submitted block was correct\n"
"\nExamples:\n"
+ HelpExampleCli("submitauxblock", "\"hash\" \"serialised auxpow\"")
+ HelpExampleRpc("submitauxblock", "\"hash\" \"serialised auxpow\"")
);
return AuxMiningSubmitBlock(request.params[0].get_str(),
request.params[1].get_str());
}
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
@ -933,6 +1191,9 @@ static const CRPCCommand commands[] =
{ "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","priority_delta","fee_delta"} },
{ "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} },
{ "mining", "submitblock", &submitblock, true, {"hexdata","parameters"} },
{ "mining", "getauxblock", &getauxblock, true, {"hash", "auxpow"} },
{ "mining", "createauxblock", &createauxblock, true, {"address"} },
{ "mining", "submitauxblock", &submitauxblock, true, {"hash", "auxpow"} },
{ "generating", "generate", &generate, true, {"nblocks","maxtries"} },
{ "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} },

444
src/test/auxpow_tests.cpp Normal file
View File

@ -0,0 +1,444 @@
// Copyright (c) 2014-2015 Daniel Kraft
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "auxpow.h"
#include "chainparams.h"
#include "coins.h"
#include "consensus/merkle.h"
#include "dogecoin.h"
#include "primitives/block.h"
#include "script/script.h"
#include "uint256.h"
#include "utilstrencodings.h"
#include "validation.h"
#include "test/test_bitcoin.h"
#include <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 CTransactionRef tx) const;
/**
* Build the finished CAuxPow object from the parent block's coinbase.
* @return The constructed CAuxPow object.
*/
inline CAuxPow
get() const
{
assert(!parentBlock.vtx.empty());
return get(parentBlock.vtx[0]);
}
/**
* Build a data vector to be included in the coinbase. It consists
* of the aux hash, the merkle tree size and the nonce. Optionally,
* the header can be added as well.
* @param header Add the header?
* @param hashAux The aux merkle root hash.
* @param h Height of the merkle tree.
* @param nonce The nonce value to use.
* @return The constructed data.
*/
static std::vector<unsigned char> buildCoinbaseData(bool header, const std::vector<unsigned char>& auxRoot, unsigned h, int nonce);
};
CAuxpowBuilder::CAuxpowBuilder(int baseVersion, int chainId)
: auxpowChainIndex(-1)
{
parentBlock.SetBaseVersion(baseVersion, chainId);
}
void CAuxpowBuilder::setCoinbase(const CScript& scr)
{
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = scr;
parentBlock.vtx.clear();
parentBlock.vtx.push_back(MakeTransactionRef(std::move(mtx)));
parentBlock.hashMerkleRoot = BlockMerkleRoot(parentBlock);
}
std::vector<unsigned char>
CAuxpowBuilder::buildAuxpowChain(const uint256& hashAux, unsigned h, int index)
{
auxpowChainIndex = index;
/* Just use "something" for the branch. Doesn't really matter. */
auxpowChainMerkleBranch.clear();
for (unsigned i = 0; i < h; ++i)
auxpowChainMerkleBranch.push_back(ArithToUint256(arith_uint256(i)));
const uint256 hash = CAuxPow::CheckMerkleBranch(hashAux, auxpowChainMerkleBranch, index);
std::vector<unsigned char> res = ToByteVector(hash);
std::reverse(res.begin(), res.end());
return res;
}
CAuxPow
CAuxpowBuilder::get(const CTransactionRef tx) const
{
LOCK(cs_main);
CAuxPow res(tx);
res.InitMerkleBranch(parentBlock, 0);
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 CTransactionRef oldCoinbase = builder.parentBlock.vtx[0];
builder.setCoinbase(scr << 5);
builder.parentBlock.vtx.push_back(oldCoinbase);
builder.parentBlock.hashMerkleRoot = BlockMerkleRoot(builder.parentBlock);
auxpow = builder.get(builder.parentBlock.vtx[0]);
BOOST_CHECK(auxpow.check(hashAux, ourChainId, params));
auxpow = builder.get(builder.parentBlock.vtx[1]);
BOOST_CHECK(!auxpow.check(hashAux, ourChainId, params));
/* The parent chain can't have the same chain ID. */
CAuxpowBuilder builder2(builder);
builder2.parentBlock.SetChainId(100);
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
builder2.parentBlock.SetChainId(ourChainId);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
/* Disallow too long merkle branches. */
builder2 = builder;
index = CAuxPow::getExpectedIndex(nonce, ourChainId, height + 1);
auxRoot = builder2.buildAuxpowChain(hashAux, height + 1, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height + 1, nonce);
scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS;
scr = (scr << OP_2 << data);
builder2.setCoinbase(scr);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
/* Verify that we compare correctly to the parent block's merkle root. */
builder2 = builder;
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
tamperWith(builder2.parentBlock.hashMerkleRoot);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
/* Build a non-header legacy version and check that it is also accepted. */
builder2 = builder;
index = CAuxPow::getExpectedIndex(nonce, ourChainId, height);
auxRoot = builder2.buildAuxpowChain(hashAux, height, index);
data = CAuxpowBuilder::buildCoinbaseData(false, auxRoot, height, nonce);
scr = (CScript() << 2809 << 2013) + COINBASE_FLAGS;
scr = (scr << OP_2 << data);
builder2.setCoinbase(scr);
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
/* However, various attempts at smuggling two roots in should be detected. */
const std::vector<unsigned char> wrongAuxRoot = builder2.buildAuxpowChain(modifiedAux, height, index);
std::vector<unsigned char> data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot, height, nonce);
builder2.setCoinbase(CScript() << data << data2);
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
builder2.setCoinbase(CScript() << data2 << data);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
data2 = CAuxpowBuilder::buildCoinbaseData(true, wrongAuxRoot, height, nonce);
builder2.setCoinbase(CScript() << data << data2);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
builder2.setCoinbase(CScript() << data2 << data);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder2.setCoinbase(CScript() << data << data2);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
builder2.setCoinbase(CScript() << data2 << data);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
data2 = CAuxpowBuilder::buildCoinbaseData(false, wrongAuxRoot,
height, nonce);
builder2.setCoinbase(CScript() << data << data2);
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
builder2.setCoinbase(CScript() << data2 << data);
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
/* Verify that the appended nonce/size values are checked correctly. */
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder2.setCoinbase(CScript() << data);
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
data.pop_back();
builder2.setCoinbase(CScript() << data);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height - 1, nonce);
builder2.setCoinbase(CScript() << data);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce + 3);
builder2.setCoinbase(CScript() << data);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
/* Put the aux hash in an invalid merkle tree position. */
auxRoot = builder.buildAuxpowChain(hashAux, height, index + 1);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder2.setCoinbase(CScript() << data);
BOOST_CHECK(!builder2.get().check(hashAux, ourChainId, params));
auxRoot = builder.buildAuxpowChain(hashAux, height, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder2.setCoinbase(CScript() << data);
BOOST_CHECK(builder2.get().check(hashAux, ourChainId, params));
}
/* ************************************************************************** */
/**
* Mine a block (assuming minimal difficulty) that either matches
* or doesn't match the difficulty target specified in the block header.
* @param block The block to mine (by updating nonce).
* @param ok Whether the block should be ok for PoW.
* @param nBits Use this as difficulty if specified.
*/
static void
mineBlock(CBlockHeader& block, bool ok, int nBits = -1)
{
if (nBits == -1)
nBits = block.nBits;
arith_uint256 target;
target.SetCompact(nBits);
block.nNonce = 0;
while (true) {
const bool nowOk = (UintToArith256(block.GetHash()) <= target);
if ((ok && nowOk) || (!ok && !nowOk))
break;
++block.nNonce;
}
if (ok)
BOOST_CHECK(CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus()));
else
BOOST_CHECK(!CheckProofOfWork(block.GetHash(), nBits, Params().GetConsensus()));
}
BOOST_AUTO_TEST_CASE(auxpow_pow)
{
/* Use regtest parameters to allow mining with easy difficulty. */
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
const arith_uint256 target = (~arith_uint256(0) >> 1);
CBlockHeader block;
block.nBits = target.GetCompact();
/* Verify the block version checks. */
block.nVersion = 1;
mineBlock(block, true);
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
block.nVersion = 2;
mineBlock(block, true);
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
block.SetBaseVersion(2, params.nAuxpowChainId);
mineBlock(block, true);
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
block.SetChainId(params.nAuxpowChainId + 1);
mineBlock(block, true);
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
/* Check the case when the block does not have auxpow (this is true
right now). */
block.SetChainId(params.nAuxpowChainId);
block.SetAuxpowFlag(true);
mineBlock(block, true);
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
block.SetAuxpowFlag(false);
mineBlock(block, true);
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
mineBlock(block, false);
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
/* ****************************************** */
/* Check the case that the block has auxpow. */
CAuxpowBuilder builder(5, 42);
CAuxPow auxpow;
const int32_t ourChainId = params.nAuxpowChainId;
const unsigned height = 3;
const int nonce = 7;
const int index = CAuxPow::getExpectedIndex(nonce, ourChainId, height);
std::vector<unsigned char> auxRoot, data;
/* Valid auxpow, PoW check of parent block. */
block.SetAuxpowFlag(true);
auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder.setCoinbase(CScript() << data);
mineBlock(builder.parentBlock, false, block.nBits);
block.SetAuxpow(new CAuxPow(builder.get()));
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
mineBlock(builder.parentBlock, true, block.nBits);
block.SetAuxpow(new CAuxPow(builder.get()));
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
/* Mismatch between auxpow being present and block.nVersion. Note that
block.SetAuxpow sets also the version and that we want to ensure
that the block hash itself doesn't change due to version changes.
This requires some work arounds. */
block.SetAuxpowFlag(false);
const uint256 hashAux = block.GetHash();
auxRoot = builder.buildAuxpowChain(hashAux, height, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder.setCoinbase(CScript() << data);
mineBlock(builder.parentBlock, true, block.nBits);
block.SetAuxpow(new CAuxPow(builder.get()));
BOOST_CHECK(hashAux != block.GetHash());
block.SetAuxpowFlag(false);
BOOST_CHECK(hashAux == block.GetHash());
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
/* Modifying the block invalidates the PoW. */
block.SetAuxpowFlag(true);
auxRoot = builder.buildAuxpowChain(block.GetHash(), height, index);
data = CAuxpowBuilder::buildCoinbaseData(true, auxRoot, height, nonce);
builder.setCoinbase(CScript() << data);
mineBlock(builder.parentBlock, true, block.nBits);
block.SetAuxpow(new CAuxPow(builder.get()));
BOOST_CHECK(CheckAuxPowProofOfWork(block, params));
tamperWith(block.hashMerkleRoot);
BOOST_CHECK(!CheckAuxPowProofOfWork(block, params));
}
/* ************************************************************************** */
BOOST_AUTO_TEST_SUITE_END()

View File

@ -210,9 +210,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
pblock->nVersion = 1;
// Replaced chainActive.Tip()->GetMedianTimePast()+1 with an actual 60 second block
// interval because median([1,2,2,3,3,3,4,4,4,4]) will eventually be problematic re:
// block timing. Tests should be more stable than that.
pblock->nTime = chainActive.Tip()->GetBlockTime() + 60;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;

View File

@ -200,10 +200,11 @@ bool CBlockTreeDB::LoadBlockIndexGuts(boost::function<CBlockIndex*(const uint256
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
pindexNew->hashBlockPoW = diskindex.hashBlockPoW;
if (!CheckProofOfWork(pindexNew->GetBlockPoWHash(), pindexNew->nBits, Params().GetConsensus()))
return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());
/* Bitcoin checks the PoW here. We don't do this because
the CDiskBlockIndex does not contain the auxpow.
This check isn't important, since the data on disk should
already be valid and can be trusted. */
pcursor->Next();
} else {

View File

@ -1130,7 +1130,11 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
/* Generic implementation of block reading that can handle
both a block and its header. */
template<typename T>
static bool ReadBlockOrHeader(T& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
block.SetNull();
@ -1148,22 +1152,38 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:
}
// Check the header
if (!CheckProofOfWork(block.GetPoWHash(), block.nBits, consensusParams))
if (!CheckAuxPowProofOfWork(block, consensusParams))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
template<typename T>
static bool ReadBlockOrHeader(T& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams))
if (!ReadBlockOrHeader(block, pindex->GetBlockPos(), consensusParams))
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
return error("ReadBlockOrHeader(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
pindex->ToString(), pindex->GetBlockPos().ToString());
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
return ReadBlockOrHeader(block, pos, consensusParams);
}
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
return ReadBlockOrHeader(block, pindex, consensusParams);
}
bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
return ReadBlockOrHeader(block, pindex, consensusParams);
}
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
@ -2982,6 +3002,14 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
// Disallow legacy blocks after merge-mining start.
if (!Params().GetConsensus().AllowLegacyBlocks(nHeight)
&& block.IsLegacy())
return state.DoS(100, error("%s : legacy block after auxpow start",
__func__),
REJECT_INVALID, "late-legacy-block");
// Check proof of work
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
@ -2997,10 +3025,10 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
// check for version 2, 3 and 4 upgrades
// Dogecoin: Version 2 enforcement was never used
if((block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion),
strprintf("rejected nVersion=0x%08x block", block.nVersion));
if((block.GetBaseVersion() < 3 && nHeight >= consensusParams.BIP66Height) ||
(block.GetBaseVersion() < 4 && nHeight >= consensusParams.BIP65Height))
return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.GetBaseVersion()),
strprintf("rejected nVersion=0x%08x block", block.GetBaseVersion()));
return true;
}

View File

@ -472,6 +472,7 @@ public:
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
/** Functions for validating blocks and updating the block tree */

View File

@ -57,8 +57,6 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
*
* @{
@ -3934,44 +3932,3 @@ CWalletKey::CWalletKey(int64_t nExpires)
nTimeCreated = (nExpires ? GetTime() : 0);
nTimeExpires = nExpires;
}
void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
{
// Update the tx's hashBlock
hashBlock = pindex->GetBlockHash();
// set the position of the transaction in the block
nIndex = posInBlock;
}
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
{
if (hashUnset())
return 0;
AssertLockHeld(cs_main);
// Find the block it claims to be in
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex))
return 0;
pindexRet = pindex;
return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
}
int CMerkleTx::GetBlocksToMaturity() const
{
if (!IsCoinBase())
return 0;
return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
}
bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
}

View File

@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include "amount.h"
#include "auxpow.h"
#include "streams.h"
#include "tinyformat.h"
#include "ui_interface.h"
@ -168,84 +169,6 @@ struct COutputEntry
int vout;
};
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx
{
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
CTransactionRef tx;
uint256 hashBlock;
/* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
* block in the chain we know this or any in-wallet dependency conflicts
* with. Older clients interpret nIndex == -1 as unconfirmed for backward
* compatibility.
*/
int nIndex;
CMerkleTx()
{
SetTx(MakeTransactionRef());
Init();
}
CMerkleTx(CTransactionRef arg)
{
SetTx(std::move(arg));
Init();
}
/** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected.
* TODO: adapt callers and remove this operator. */
operator const CTransaction&() const { return *tx; }
void Init()
{
hashBlock = uint256();
nIndex = -1;
}
void SetTx(CTransactionRef arg)
{
tx = std::move(arg);
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
std::vector<uint256> vMerkleBranch; // For compatibility with older versions.
READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
}
void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock);
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; }
int GetBlocksToMaturity() const;
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state);
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
};
/**
* A transaction with a bunch of additional info that only the owner cares about.
* It includes any unrecorded transactions needed to link it back to the block chain.