From a48f2d6ddd8a438a126bfb7940d993f2da598476 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 19 Oct 2014 23:09:50 +0000 Subject: [PATCH 1/9] Abstract context-dependent block checking from acceptance --- src/main.cpp | 124 +++++++++++++++++++++++++++++---------------------- src/main.h | 4 ++ 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 82d52913a0..2781c6f3fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2334,6 +2334,73 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return true; } +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +{ + uint256 hash = block.GetHash(); + if (hash == Params().HashGenesisBlock()) + return true; + + assert(pindexPrev); + + int nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if ((!Params().SkipProofOfWorkCheck()) && + (block.nBits != GetNextWorkRequired(pindexPrev, &block))) + return state.DoS(100, error("%s : incorrect proof of work", __func__), + REJECT_INVALID, "bad-diffbits"); + + // Check timestamp against prev + if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return state.Invalid(error("%s : block's timestamp is too early", __func__), + REJECT_INVALID, "time-too-old"); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(nHeight, hash)) + return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), + REJECT_CHECKPOINT, "checkpoint mismatch"); + + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); + + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 2 && + CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) + { + return state.Invalid(error("%s : rejected nVersion=1 block", __func__), + 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); @@ -2353,44 +2420,16 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc // Get prev block index 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 - if ((!Params().SkipProofOfWorkCheck()) && - (block.nBits != GetNextWorkRequired(pindexPrev, &block))) - return state.DoS(100, error("%s : incorrect proof of work", __func__), - REJECT_INVALID, "bad-diffbits"); - - // Check timestamp against prev - if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("%s : block's timestamp is too early", __func__), - REJECT_INVALID, "time-too-old"); - - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), - REJECT_CHECKPOINT, "checkpoint mismatch"); - - // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); - - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && - CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) - { - return state.Invalid(error("%s : rejected nVersion=1 block", __func__), - REJECT_OBSOLETE, "bad-version"); - } } + if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + return false; + if (pindex == NULL) pindex = AddToBlockIndex(block); @@ -2415,7 +2454,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return true; } - if (!CheckBlock(block, state)) { + if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; } @@ -2424,27 +2463,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, 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 try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); diff --git a/src/main.h b/src/main.h index 1bb0919817..6e684be8c0 100644 --- a/src/main.h +++ b/src/main.h @@ -463,6 +463,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = 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); + // Store block 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); From 4ea1be7fb84a397222754473c2bc315e3665ff18 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 16 Oct 2014 03:50:33 +0000 Subject: [PATCH 2/9] CreateNewBlock and miner_tests: Also check generated template is valid by CheckBlockHeader, ContextualCheckBlockHeader, CheckBlock, and ContextualCheckBlock --- src/miner.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/miner.cpp b/src/miner.cpp index b5bfa9c7be..d7ecd5e40e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -326,8 +326,17 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) indexDummy.nHeight = pindexPrev->nHeight + 1; CCoinsViewCache viewNew(pcoinsTip); CValidationState state; + // NOTE: CheckBlockHeader is called by CheckBlock + if (!ContextualCheckBlockHeader(*pblock, state, pindexPrev)) + throw std::runtime_error("CreateNewBlock() : ContextualCheckBlockHeader failed"); + if (!CheckBlock(*pblock, state, false, false)) + throw std::runtime_error("CreateNewBlock() : CheckBlock failed"); + if (!ContextualCheckBlock(*pblock, state, pindexPrev)) + throw std::runtime_error("CreateNewBlock() : ContextualCheckBlock failed"); if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); + if (!state.IsValid()) + throw std::runtime_error("CreateNewBlock() : State is not valid"); } return pblocktemplate.release(); From df08a626e0440457ae0d1966439fd956c27ae2fe Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 20 Oct 2014 02:10:03 +0000 Subject: [PATCH 3/9] TestBlockValidity function for CBlock proposals (used by CreateNewBlock) --- src/main.cpp | 26 +++++++++++++++++++++++++- src/main.h | 5 ++++- src/miner.cpp | 17 ++--------------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2781c6f3fb..fda71a365d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1593,7 +1593,7 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 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); // Check it again in case a previous version let a bad block in @@ -2573,6 +2573,30 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis 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; +} + diff --git a/src/main.h b/src/main.h index 6e684be8c0..b49f0a06eb 100644 --- a/src/main.h +++ b/src/main.h @@ -457,7 +457,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); 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 -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 bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); @@ -467,6 +467,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = t 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 // if dbp is provided, the file is known to already reside on disk bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL); diff --git a/src/miner.cpp b/src/miner.cpp index d7ecd5e40e..200498d109 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -321,22 +321,9 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) pblock->nNonce = 0; pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); - CBlockIndex indexDummy(*pblock); - indexDummy.pprev = pindexPrev; - indexDummy.nHeight = pindexPrev->nHeight + 1; - CCoinsViewCache viewNew(pcoinsTip); CValidationState state; - // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(*pblock, state, pindexPrev)) - throw std::runtime_error("CreateNewBlock() : ContextualCheckBlockHeader failed"); - if (!CheckBlock(*pblock, state, false, false)) - throw std::runtime_error("CreateNewBlock() : CheckBlock failed"); - if (!ContextualCheckBlock(*pblock, state, pindexPrev)) - throw std::runtime_error("CreateNewBlock() : ContextualCheckBlock failed"); - if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true)) - throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); - if (!state.IsValid()) - throw std::runtime_error("CreateNewBlock() : State is not valid"); + if (!TestBlockValidity(state, *pblock, pindexPrev, false, false)) + throw std::runtime_error("CreateNewBlock() : TestBlockValidity failed"); } return pblocktemplate.release(); From 132ea9b48f65dcb4784a7e9688f3b194d5578c80 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 30 Oct 2014 04:26:31 +0000 Subject: [PATCH 4/9] miner_tests: Disable checkpoints so they don't fail the subsidy-change test --- src/test/miner_tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 032ae983ce..1caee13c33 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -56,6 +56,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) uint256 hash; LOCK(cs_main); + Checkpoints::fEnabled = false; // Simple block creation, nothing special yet: BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); @@ -258,6 +259,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_FOREACH(CTransaction *tx, txFirst) delete tx; + Checkpoints::fEnabled = true; } BOOST_AUTO_TEST_SUITE_END() From 3dcbb9b6b488f077d4ab7e4296dffbf3aea4a0fb Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 30 Oct 2014 02:56:33 +0000 Subject: [PATCH 5/9] Abstract DecodeHexBlk and BIP22ValidationResult functions out of submitblock --- src/core_io.h | 2 ++ src/core_read.cpp | 18 ++++++++++++++++++ src/rpcmining.cpp | 42 +++++++++++++++++++++--------------------- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/core_io.h b/src/core_io.h index 94848f1c3d..8777aa3b85 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -8,6 +8,7 @@ #include #include +class CBlock; class CScript; class CTransaction; class uint256; @@ -16,6 +17,7 @@ class UniValue; // core_read.cpp extern CScript ParseScript(std::string s); 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 std::vector ParseHexUV(const UniValue& v, const std::string& strName); diff --git a/src/core_read.cpp b/src/core_read.cpp index d39bc9a780..42e2f8d200 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -4,6 +4,7 @@ #include "core_io.h" +#include "core/block.h" #include "core/transaction.h" #include "script/script.h" #include "serialize.h" @@ -108,6 +109,23 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx) return true; } +bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) +{ + if (!IsHex(strHexBlk)) + return false; + + std::vector 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) { string strHex; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 879a504115..577f377796 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -283,6 +283,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) { if (fHelp || params.size() > 1) @@ -566,15 +585,9 @@ Value submitblock(const Array& params, bool fHelp) + HelpExampleRpc("submitblock", "\"mydata\"") ); - vector blockData(ParseHex(params[0].get_str())); - CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); CBlock pblock; - try { - ssBlock >> pblock; - } - catch (const std::exception &) { + if (!DecodeHexBlk(pblock, params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); - } CValidationState state; submitblock_StateCatcher sc(pblock.GetHash()); @@ -587,20 +600,7 @@ Value submitblock(const Array& params, bool fHelp) return "inconclusive"; state = sc.state; } - if (state.IsError()) - { - 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; + return BIP22ValidationResult(state); } Value estimatefee(const Array& params, bool fHelp) From 9765a50cbdbcb45cc87c51e301663c2b29ff1bf8 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 10 Sep 2012 02:55:03 +0000 Subject: [PATCH 6/9] Implement BIP 23 Block Proposal --- src/rpcmining.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 577f377796..0d49fb34d2 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -379,6 +379,36 @@ Value getblocktemplate(const Array& params, bool fHelp) else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); 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") @@ -481,6 +511,8 @@ Value getblocktemplate(const Array& params, bool fHelp) UpdateTime(pblock, pindexPrev); pblock->nNonce = 0; + static const Array aCaps = boost::assign::list_of("proposal"); + Array transactions; map setTxIndex; int i = 0; @@ -527,6 +559,7 @@ Value getblocktemplate(const Array& params, bool fHelp) } Object result; + result.push_back(Pair("capabilities", aCaps)); result.push_back(Pair("version", pblock->nVersion)); result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("transactions", transactions)); From bc6cb4177b143bf1c3d0fad065d3a4de6df97ef9 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 17 Nov 2014 18:47:40 +0000 Subject: [PATCH 7/9] QA RPC tests: Add tests block block proposals --- ...mplate.py => getblocktemplate_longpoll.py} | 6 +- qa/rpc-tests/getblocktemplate_proposals.py | 182 ++++++++++++++++++ 2 files changed, 184 insertions(+), 4 deletions(-) rename qa/rpc-tests/{getblocktemplate.py => getblocktemplate_longpoll.py} (96%) create mode 100755 qa/rpc-tests/getblocktemplate_proposals.py diff --git a/qa/rpc-tests/getblocktemplate.py b/qa/rpc-tests/getblocktemplate_longpoll.py similarity index 96% rename from qa/rpc-tests/getblocktemplate.py rename to qa/rpc-tests/getblocktemplate_longpoll.py index 500662bf87..263a5f6d59 100755 --- a/qa/rpc-tests/getblocktemplate.py +++ b/qa/rpc-tests/getblocktemplate_longpoll.py @@ -3,8 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# Exercise the listtransactions API - from test_framework import BitcoinTestFramework from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException from util import * @@ -46,7 +44,7 @@ class LongpollThread(threading.Thread): def run(self): self.node.getblocktemplate({'longpollid':self.longpollid}) -class GetBlockTemplateTest(BitcoinTestFramework): +class GetBlockTemplateLPTest(BitcoinTestFramework): ''' Test longpolling with getblocktemplate. ''' @@ -90,5 +88,5 @@ class GetBlockTemplateTest(BitcoinTestFramework): assert(not thr.is_alive()) if __name__ == '__main__': - GetBlockTemplateTest().main() + GetBlockTemplateLPTest().main() diff --git a/qa/rpc-tests/getblocktemplate_proposals.py b/qa/rpc-tests/getblocktemplate_proposals.py new file mode 100755 index 0000000000..0f7859584a --- /dev/null +++ b/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(' 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(' Date: Tue, 18 Nov 2014 19:09:20 +0000 Subject: [PATCH 8/9] submitblock: Check for duplicate submissions explicitly --- src/rpcmining.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 0d49fb34d2..7a2b6c7789 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -618,15 +618,32 @@ Value submitblock(const Array& params, bool fHelp) + HelpExampleRpc("submitblock", "\"mydata\"") ); - CBlock pblock; - if (!DecodeHexBlk(pblock, params[0].get_str())) + CBlock block; + if (!DecodeHexBlk(block, params[0].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"; + // Otherwise, we might only have the header - process the block before returning + } + CValidationState state; - submitblock_StateCatcher sc(pblock.GetHash()); + submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, NULL, &pblock); + bool fAccepted = ProcessNewBlock(state, NULL, &block); UnregisterValidationInterface(&sc); + if (mi != mapBlockIndex.end()) + { + if (fAccepted && !sc.found) + return "duplicate-inconclusive"; + return "duplicate"; + } if (fAccepted) { if (!sc.found) From b867e409e5dd34b84eb9d6d0d8f257dbb19b986d Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 20 Nov 2014 02:23:20 +0000 Subject: [PATCH 9/9] CreateNewBlock: Stick height in coinbase so we pass template sanity check --- src/miner.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index 200498d109..5ac4b05af8 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -124,6 +124,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) { LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); + const int nHeight = pindexPrev->nHeight + 1; CCoinsViewCache view(pcoinsTip); // Priority order to process transactions @@ -138,7 +139,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) mi != mempool.mapTx.end(); ++mi) { const CTransaction& tx = mi->second.GetTx(); - if (tx.IsCoinBase() || !IsFinalTx(tx, pindexPrev->nHeight + 1)) + if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight)) continue; COrphan* porphan = NULL; @@ -181,7 +182,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) CAmount nValueIn = coins->vout[txin.prevout.n].nValue; nTotalIn += nValueIn; - int nConf = pindexPrev->nHeight - coins->nHeight + 1; + int nConf = nHeight - coins->nHeight; dPriority += (double)nValueIn * nConf; } @@ -269,7 +270,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) continue; CTxUndo txundo; - UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1); + UpdateCoins(tx, state, view, txundo, nHeight); // Added pblock->vtx.push_back(tx); @@ -309,8 +310,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); // Compute final coinbase transaction. - txNew.vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); - txNew.vin[0].scriptSig = CScript() << OP_0 << OP_0; + txNew.vout[0].nValue = GetBlockValue(nHeight, nFees); + txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees;