Browse Source

Merge pull request #1816

b867e40 CreateNewBlock: Stick height in coinbase so we pass template sanity check (Luke Dashjr)
60755db submitblock: Check for duplicate submissions explicitly (Luke Dashjr)
bc6cb41 QA RPC tests: Add tests block block proposals (Luke Dashjr)
9765a50 Implement BIP 23 Block Proposal (Luke Dashjr)
3dcbb9b Abstract DecodeHexBlk and BIP22ValidationResult functions out of submitblock (Luke Dashjr)
132ea9b miner_tests: Disable checkpoints so they don't fail the subsidy-change test (Luke Dashjr)
df08a62 TestBlockValidity function for CBlock proposals (used by CreateNewBlock) (Luke Dashjr)
4ea1be7 CreateNewBlock and miner_tests: Also check generated template is valid by CheckBlockHeader, ContextualCheckBlockHeader, CheckBlock, and ContextualCheckBlock (Luke Dashjr)
a48f2d6 Abstract context-dependent block checking from acceptance (Luke Dashjr)
0.10
Wladimir J. van der Laan 10 years ago
parent
commit
f24bcce2ac
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 6
      qa/rpc-tests/getblocktemplate_longpoll.py
  2. 182
      qa/rpc-tests/getblocktemplate_proposals.py
  3. 2
      src/core_io.h
  4. 18
      src/core_read.cpp
  5. 136
      src/main.cpp
  6. 9
      src/main.h
  7. 19
      src/miner.cpp
  8. 96
      src/rpcmining.cpp
  9. 2
      src/test/miner_tests.cpp

6
qa/rpc-tests/getblocktemplate.py → qa/rpc-tests/getblocktemplate_longpoll.py

@ -3,8 +3,6 @@
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
# Exercise the listtransactions API
from test_framework import BitcoinTestFramework from test_framework import BitcoinTestFramework
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from util import * from util import *
@ -46,7 +44,7 @@ class LongpollThread(threading.Thread):
def run(self): def run(self):
self.node.getblocktemplate({'longpollid':self.longpollid}) self.node.getblocktemplate({'longpollid':self.longpollid})
class GetBlockTemplateTest(BitcoinTestFramework): class GetBlockTemplateLPTest(BitcoinTestFramework):
''' '''
Test longpolling with getblocktemplate. Test longpolling with getblocktemplate.
''' '''
@ -90,5 +88,5 @@ class GetBlockTemplateTest(BitcoinTestFramework):
assert(not thr.is_alive()) assert(not thr.is_alive())
if __name__ == '__main__': if __name__ == '__main__':
GetBlockTemplateTest().main() GetBlockTemplateLPTest().main()

182
qa/rpc-tests/getblocktemplate_proposals.py

@ -0,0 +1,182 @@
#!/usr/bin/env python
# Copyright (c) 2014 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from test_framework import BitcoinTestFramework
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
from util import *
from binascii import a2b_hex, b2a_hex
from hashlib import sha256
from struct import pack
def check_array_result(object_array, to_match, expected):
"""
Pass in array of JSON objects, a dictionary with key/value pairs
to match against, and another dictionary with expected key/value
pairs.
"""
num_matched = 0
for item in object_array:
all_match = True
for key,value in to_match.items():
if item[key] != value:
all_match = False
if not all_match:
continue
for key,value in expected.items():
if item[key] != value:
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
num_matched = num_matched+1
if num_matched == 0:
raise AssertionError("No objects matched %s"%(str(to_match)))
def b2x(b):
return b2a_hex(b).decode('ascii')
# NOTE: This does not work for signed numbers (set the high bit) or zero (use b'\0')
def encodeUNum(n):
s = bytearray(b'\1')
while n > 127:
s[0] += 1
s.append(n % 256)
n //= 256
s.append(n)
return bytes(s)
def varlenEncode(n):
if n < 0xfd:
return pack('<B', n)
if n <= 0xffff:
return b'\xfd' + pack('<H', n)
if n <= 0xffffffff:
return b'\xfe' + pack('<L', n)
return b'\xff' + pack('<Q', n)
def dblsha(b):
return sha256(sha256(b).digest()).digest()
def genmrklroot(leaflist):
cur = leaflist
while len(cur) > 1:
n = []
if len(cur) & 1:
cur.append(cur[-1])
for i in range(0, len(cur), 2):
n.append(dblsha(cur[i] + cur[i+1]))
cur = n
return cur[0]
def template_to_bytes(tmpl, txlist):
blkver = pack('<L', tmpl['version'])
mrklroot = genmrklroot(list(dblsha(a) for a in txlist))
timestamp = pack('<L', tmpl['curtime'])
nonce = b'\0\0\0\0'
blk = blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce
blk += varlenEncode(len(txlist))
for tx in txlist:
blk += tx
return blk
def template_to_hex(tmpl, txlist):
return b2x(template_to_bytes(tmpl, txlist))
def assert_template(node, tmpl, txlist, expect):
rsp = node.getblocktemplate({'data':template_to_hex(tmpl, txlist),'mode':'proposal'})
if rsp != expect:
raise AssertionError('unexpected: %s' % (rsp,))
class GetBlockTemplateProposalTest(BitcoinTestFramework):
'''
Test block proposals with getblocktemplate.
'''
def run_test(self):
node = self.nodes[0]
tmpl = node.getblocktemplate()
if 'coinbasetxn' not in tmpl:
rawcoinbase = encodeUNum(tmpl['height'])
rawcoinbase += b'\x01-'
hexcoinbase = b2x(rawcoinbase)
hexoutval = b2x(pack('<Q', tmpl['coinbasevalue']))
tmpl['coinbasetxn'] = {'data': '01000000' + '01' + '0000000000000000000000000000000000000000000000000000000000000000ffffffff' + ('%02x' % (len(rawcoinbase),)) + hexcoinbase + 'fffffffe' + '01' + hexoutval + '00' + '00000000'}
txlist = list(bytearray(a2b_hex(a['data'])) for a in (tmpl['coinbasetxn'],) + tuple(tmpl['transactions']))
# Test 0: Capability advertised
assert('proposal' in tmpl['capabilities'])
# NOTE: This test currently FAILS (regtest mode doesn't enforce block height in coinbase)
## Test 1: Bad height in coinbase
#txlist[0][4+1+36+1+1] += 1
#assert_template(node, tmpl, txlist, 'FIXME')
#txlist[0][4+1+36+1+1] -= 1
# Test 2: Bad input hash for gen tx
txlist[0][4+1] += 1
assert_template(node, tmpl, txlist, 'bad-cb-missing')
txlist[0][4+1] -= 1
# Test 3: Truncated final tx
lastbyte = txlist[-1].pop()
try:
assert_template(node, tmpl, txlist, 'n/a')
except JSONRPCException:
pass # Expected
txlist[-1].append(lastbyte)
# Test 4: Add an invalid tx to the end (duplicate of gen tx)
txlist.append(txlist[0])
assert_template(node, tmpl, txlist, 'bad-txns-duplicate')
txlist.pop()
# Test 5: Add an invalid tx to the end (non-duplicate)
txlist.append(bytearray(txlist[0]))
txlist[-1][4+1] = b'\xff'
assert_template(node, tmpl, txlist, 'bad-txns-inputs-missingorspent')
txlist.pop()
# Test 6: Future tx lock time
txlist[0][-4:] = b'\xff\xff\xff\xff'
assert_template(node, tmpl, txlist, 'bad-txns-nonfinal')
txlist[0][-4:] = b'\0\0\0\0'
# Test 7: Bad tx count
txlist.append(b'')
try:
assert_template(node, tmpl, txlist, 'n/a')
except JSONRPCException:
pass # Expected
txlist.pop()
# Test 8: Bad bits
realbits = tmpl['bits']
tmpl['bits'] = '1c0000ff' # impossible in the real world
assert_template(node, tmpl, txlist, 'bad-diffbits')
tmpl['bits'] = realbits
# Test 9: Bad merkle root
rawtmpl = template_to_bytes(tmpl, txlist)
rawtmpl[4+32] = (rawtmpl[4+32] + 1) % 0x100
rsp = node.getblocktemplate({'data':b2x(rawtmpl),'mode':'proposal'})
if rsp != 'bad-txnmrklroot':
raise AssertionError('unexpected: %s' % (rsp,))
# Test 10: Bad timestamps
realtime = tmpl['curtime']
tmpl['curtime'] = 0x7fffffff
assert_template(node, tmpl, txlist, 'time-too-new')
tmpl['curtime'] = 0
assert_template(node, tmpl, txlist, 'time-too-old')
tmpl['curtime'] = realtime
# Test 11: Valid block
assert_template(node, tmpl, txlist, None)
# Test 12: Orphan block
tmpl['previousblockhash'] = 'ff00' * 16
assert_template(node, tmpl, txlist, 'inconclusive-not-best-prevblk')
if __name__ == '__main__':
GetBlockTemplateProposalTest().main()

2
src/core_io.h

@ -8,6 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
class CBlock;
class CScript; class CScript;
class CTransaction; class CTransaction;
class uint256; class uint256;
@ -16,6 +17,7 @@ class UniValue;
// core_read.cpp // core_read.cpp
extern CScript ParseScript(std::string s); extern CScript ParseScript(std::string s);
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName); extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName); extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);

18
src/core_read.cpp

@ -4,6 +4,7 @@
#include "core_io.h" #include "core_io.h"
#include "core/block.h"
#include "core/transaction.h" #include "core/transaction.h"
#include "script/script.h" #include "script/script.h"
#include "serialize.h" #include "serialize.h"
@ -108,6 +109,23 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx)
return true; return true;
} }
bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
{
if (!IsHex(strHexBlk))
return false;
std::vector<unsigned char> blockData(ParseHex(strHexBlk));
CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION);
try {
ssBlock >> block;
}
catch (const std::exception &) {
return false;
}
return true;
}
uint256 ParseHashUV(const UniValue& v, const string& strName) uint256 ParseHashUV(const UniValue& v, const string& strName)
{ {
string strHex; string strHex;

136
src/main.cpp

@ -1593,7 +1593,7 @@ static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0; static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0; static int64_t nTimeTotal = 0;
bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
// Check it again in case a previous version let a bad block in // Check it again in case a previous version let a bad block in
@ -2336,35 +2336,15 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
return true; return true;
} }
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev)
{ {
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash(); uint256 hash = block.GetHash();
BlockMap::iterator miSelf = mapBlockIndex.find(hash); if (hash == Params().HashGenesisBlock())
CBlockIndex *pindex = NULL;
if (miSelf != mapBlockIndex.end()) {
// Block header is already known.
pindex = miSelf->second;
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK)
return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate");
return true; return true;
}
if (!CheckBlockHeader(block, state)) assert(pindexPrev);
return false;
// Get prev block index int nHeight = pindexPrev->nHeight+1;
CBlockIndex* pindexPrev = NULL;
int nHeight = 0;
if (hash != Params().HashGenesisBlock()) {
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk");
pindexPrev = (*mi).second;
nHeight = pindexPrev->nHeight+1;
// Check proof of work // Check proof of work
if ((!Params().SkipProofOfWorkCheck()) && if ((!Params().SkipProofOfWorkCheck()) &&
@ -2394,8 +2374,67 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc
return state.Invalid(error("%s : rejected nVersion=1 block", __func__), return state.Invalid(error("%s : rejected nVersion=1 block", __func__),
REJECT_OBSOLETE, "bad-version"); REJECT_OBSOLETE, "bad-version");
} }
return true;
} }
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) {
return state.DoS(10, error("%s : contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal");
}
// 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 &&
CBlockIndex::IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority()))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) {
return state.DoS(100, error("%s : block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height");
}
}
return true;
}
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator miSelf = mapBlockIndex.find(hash);
CBlockIndex *pindex = NULL;
if (miSelf != mapBlockIndex.end()) {
// Block header is already known.
pindex = miSelf->second;
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK)
return state.Invalid(error("%s : block is marked invalid", __func__), 0, "duplicate");
return true;
}
if (!CheckBlockHeader(block, state))
return false;
// Get prev block index
CBlockIndex* pindexPrev = NULL;
if (hash != Params().HashGenesisBlock()) {
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk");
pindexPrev = (*mi).second;
}
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
return false;
if (pindex == NULL) if (pindex == NULL)
pindex = AddToBlockIndex(block); pindex = AddToBlockIndex(block);
@ -2420,7 +2459,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
return true; return true;
} }
if (!CheckBlock(block, state)) { if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
if (state.IsInvalid() && !state.CorruptionPossible()) { if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID; pindex->nStatus |= BLOCK_FAILED_VALID;
} }
@ -2429,27 +2468,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
int nHeight = pindex->nHeight; int nHeight = pindex->nHeight;
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) {
pindex->nStatus |= BLOCK_FAILED_VALID;
return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"),
REJECT_INVALID, "bad-txns-nonfinal");
}
// 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 &&
CBlockIndex::IsSuperMajority(2, pindex->pprev, Params().EnforceBlockUpgradeMajority()))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) {
pindex->nStatus |= BLOCK_FAILED_VALID;
return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), REJECT_INVALID, "bad-cb-height");
}
}
// Write block to history file // Write block to history file
try { try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
@ -2560,6 +2578,30 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis
return true; return true;
} }
bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
{
AssertLockHeld(cs_main);
assert(pindexPrev == chainActive.Tip());
CCoinsViewCache viewNew(pcoinsTip);
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
return false;
if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot))
return false;
if (!ContextualCheckBlock(block, state, pindexPrev))
return false;
if (!ConnectBlock(block, state, &indexDummy, viewNew, true))
return false;
assert(state.IsValid());
return true;
}

9
src/main.h

@ -457,12 +457,19 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
// Apply the effects of this block (with given index) on the UTXO set represented by coins // Apply the effects of this block (with given index) on the UTXO set represented by coins
bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
// Context-independent validity checks // Context-independent validity checks
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true); bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
// Context-dependent validity checks
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev);
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev);
// 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);
// Store block on disk // Store block on disk
// if dbp is provided, the file is known to already reside on disk // if dbp is provided, the file is known to already reside on disk
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL); bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL);

19
src/miner.cpp

@ -134,6 +134,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{ {
LOCK2(cs_main, mempool.cs); LOCK2(cs_main, mempool.cs);
CBlockIndex* pindexPrev = chainActive.Tip(); CBlockIndex* pindexPrev = chainActive.Tip();
const int nHeight = pindexPrev->nHeight + 1;
CCoinsViewCache view(pcoinsTip); CCoinsViewCache view(pcoinsTip);
// Priority order to process transactions // Priority order to process transactions
@ -148,7 +149,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
mi != mempool.mapTx.end(); ++mi) mi != mempool.mapTx.end(); ++mi)
{ {
const CTransaction& tx = mi->second.GetTx(); const CTransaction& tx = mi->second.GetTx();
if (tx.IsCoinBase() || !IsFinalTx(tx, pindexPrev->nHeight + 1)) if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight))
continue; continue;
COrphan* porphan = NULL; COrphan* porphan = NULL;
@ -191,7 +192,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
CAmount nValueIn = coins->vout[txin.prevout.n].nValue; CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
nTotalIn += nValueIn; nTotalIn += nValueIn;
int nConf = pindexPrev->nHeight - coins->nHeight + 1; int nConf = nHeight - coins->nHeight;
dPriority += (double)nValueIn * nConf; dPriority += (double)nValueIn * nConf;
} }
@ -279,7 +280,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
continue; continue;
CTxUndo txundo; CTxUndo txundo;
UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1); UpdateCoins(tx, state, view, txundo, nHeight);
// Added // Added
pblock->vtx.push_back(tx); pblock->vtx.push_back(tx);
@ -319,8 +320,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);
// Compute final coinbase transaction. // Compute final coinbase transaction.
txNew.vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); txNew.vout[0].nValue = GetBlockValue(nHeight, nFees);
txNew.vin[0].scriptSig = CScript() << OP_0 << OP_0; txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
pblock->vtx[0] = txNew; pblock->vtx[0] = txNew;
pblocktemplate->vTxFees[0] = -nFees; pblocktemplate->vTxFees[0] = -nFees;
@ -331,13 +332,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
pblock->nNonce = 0; pblock->nNonce = 0;
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
CBlockIndex indexDummy(*pblock);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
CCoinsViewCache viewNew(pcoinsTip);
CValidationState state; CValidationState state;
if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true)) if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))
throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); throw std::runtime_error("CreateNewBlock() : TestBlockValidity failed");
} }
return pblocktemplate.release(); return pblocktemplate.release();

96
src/rpcmining.cpp

@ -299,6 +299,25 @@ Value prioritisetransaction(const Array& params, bool fHelp)
} }
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller
static Value BIP22ValidationResult(const CValidationState& state)
{
if (state.IsValid())
return Value::null;
std::string strRejectReason = state.GetRejectReason();
if (state.IsError())
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
if (state.IsInvalid())
{
if (strRejectReason.empty())
return "rejected";
return strRejectReason;
}
// Should be impossible
return "valid?";
}
Value getblocktemplate(const Array& params, bool fHelp) Value getblocktemplate(const Array& params, bool fHelp)
{ {
if (fHelp || params.size() > 1) if (fHelp || params.size() > 1)
@ -376,6 +395,36 @@ Value getblocktemplate(const Array& params, bool fHelp)
else else
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
lpval = find_value(oparam, "longpollid"); lpval = find_value(oparam, "longpollid");
if (strMode == "proposal")
{
const Value& dataval = find_value(oparam, "data");
if (dataval.type() != str_type)
throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal");
CBlock block;
if (!DecodeHexBlk(block, dataval.get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
uint256 hash = block.GetHash();
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
return "duplicate";
if (pindex->nStatus & BLOCK_FAILED_MASK)
return "duplicate-invalid";
return "duplicate-inconclusive";
}
CBlockIndex* const pindexPrev = chainActive.Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
CValidationState state;
TestBlockValidity(state, block, pindexPrev, false, true);
return BIP22ValidationResult(state);
}
} }
if (strMode != "template") if (strMode != "template")
@ -478,6 +527,8 @@ Value getblocktemplate(const Array& params, bool fHelp)
UpdateTime(pblock, pindexPrev); UpdateTime(pblock, pindexPrev);
pblock->nNonce = 0; pblock->nNonce = 0;
static const Array aCaps = boost::assign::list_of("proposal");
Array transactions; Array transactions;
map<uint256, int64_t> setTxIndex; map<uint256, int64_t> setTxIndex;
int i = 0; int i = 0;
@ -524,6 +575,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
} }
Object result; Object result;
result.push_back(Pair("capabilities", aCaps));
result.push_back(Pair("version", pblock->nVersion)); result.push_back(Pair("version", pblock->nVersion));
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("transactions", transactions)); result.push_back(Pair("transactions", transactions));
@ -582,41 +634,39 @@ Value submitblock(const Array& params, bool fHelp)
+ HelpExampleRpc("submitblock", "\"mydata\"") + HelpExampleRpc("submitblock", "\"mydata\"")
); );
vector<unsigned char> blockData(ParseHex(params[0].get_str())); CBlock block;
CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); if (!DecodeHexBlk(block, params[0].get_str()))
CBlock pblock;
try {
ssBlock >> pblock;
}
catch (const std::exception &) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
uint256 hash = block.GetHash();
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
return "duplicate";
if (pindex->nStatus & BLOCK_FAILED_MASK)
return "duplicate-invalid";
// Otherwise, we might only have the header - process the block before returning
} }
CValidationState state; CValidationState state;
submitblock_StateCatcher sc(pblock.GetHash()); submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc); RegisterValidationInterface(&sc);
bool fAccepted = ProcessNewBlock(state, NULL, &pblock); bool fAccepted = ProcessNewBlock(state, NULL, &block);
UnregisterValidationInterface(&sc); UnregisterValidationInterface(&sc);
if (mi != mapBlockIndex.end())
{
if (fAccepted && !sc.found)
return "duplicate-inconclusive";
return "duplicate";
}
if (fAccepted) if (fAccepted)
{ {
if (!sc.found) if (!sc.found)
return "inconclusive"; return "inconclusive";
state = sc.state; state = sc.state;
} }
if (state.IsError()) return BIP22ValidationResult(state);
{
std::string strRejectReason = state.GetRejectReason();
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
}
if (state.IsInvalid())
{
std::string strRejectReason = state.GetRejectReason();
if (strRejectReason.empty())
return "rejected";
return strRejectReason;
}
return Value::null;
} }
Value estimatefee(const Array& params, bool fHelp) Value estimatefee(const Array& params, bool fHelp)

2
src/test/miner_tests.cpp

@ -57,6 +57,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
uint256 hash; uint256 hash;
LOCK(cs_main); LOCK(cs_main);
Checkpoints::fEnabled = false;
// Simple block creation, nothing special yet: // Simple block creation, nothing special yet:
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
@ -259,6 +260,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_FOREACH(CTransaction *tx, txFirst) BOOST_FOREACH(CTransaction *tx, txFirst)
delete tx; delete tx;
Checkpoints::fEnabled = true;
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save