diff --git a/src/Makefile.am b/src/Makefile.am index 3c056386fac7b..e156219eea6c9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -75,6 +75,7 @@ BITCOIN_CORE_H = \ addrman.h \ base58.h \ bloom.h \ + blockchain.h \ chain.h \ chainparams.h \ chainparamsbase.h \ @@ -163,6 +164,7 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_server_a_SOURCES = \ addrman.cpp \ bloom.cpp \ + blockchain.cpp \ chain.cpp \ checkpoints.cpp \ httprpc.cpp \ diff --git a/src/blockchain.cpp b/src/blockchain.cpp new file mode 100644 index 0000000000000..f495429046ca0 --- /dev/null +++ b/src/blockchain.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "addrman.h" +#include "arith_uint256.h" +#include "blockchain.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "checkqueue.h" +#include "consensus/consensus.h" +#include "consensus/merkle.h" +#include "consensus/validation.h" +#include "hash.h" +#include "init.h" +#include "merkleblock.h" +#include "net.h" +#include "policy/fees.h" +#include "policy/policy.h" +#include "pow.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "random.h" +#include "script/script.h" +#include "script/sigcache.h" +#include "script/standard.h" +#include "tinyformat.h" +#include "txdb.h" +#include "txmempool.h" +#include "ui_interface.h" +#include "undo.h" +#include "util.h" +#include "utilmoneystr.h" +#include "utilstrencodings.h" +#include "validationinterface.h" +#include "versionbits.h" + +#include + +#include +#include +#include +#include +#include + +bool CBlockchain::CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) { + return CheckBlockHeader(Params(), block, state, GetAdjustedTime(), fCheckPOW); +} + +bool CBlockchain::CheckBlockHeader(const CChainParams chainParams, const CBlockHeader& block, CValidationState& state, int64_t nAdjustedTime, bool fCheckPOW) { + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, chainParams.GetConsensus())) + return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); + + // Check timestamp + if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60) + return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); + + return true; +} + +bool CBlockchain::CheckTransaction(const CTransaction& tx, CValidationState &state) +{ + // Basic checks that don't depend on any context + if (tx.vin.empty()) + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); + if (tx.vout.empty()) + return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); + // Size limits + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); + + // Check for negative or overflow output values + CAmount nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + if (txout.nValue < 0) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); + if (txout.nValue > MAX_MONEY) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); + nValueOut += txout.nValue; + if (!MoneyRange(nValueOut)) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); + } + + // Check for duplicate inputs + std::set vInOutPoints; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (vInOutPoints.count(txin.prevout)) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); + vInOutPoints.insert(txin.prevout); + } + + if (tx.IsCoinBase()) + { + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) + return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); + } + else + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (txin.prevout.IsNull()) + return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); + } + + return true; +} diff --git a/src/blockchain.h b/src/blockchain.h new file mode 100644 index 0000000000000..09f18aea4b21e --- /dev/null +++ b/src/blockchain.h @@ -0,0 +1,22 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_BLOCKCHAIN_H +#define BITCOIN_BLOCKCHAIN_H + +#include "chainparams.h" +#include "consensus/validation.h" +#include "primitives/block.h" + +class CBlockchain { +protected: + static bool CheckBlockHeader(const CChainParams chainParams, const CBlockHeader& block, CValidationState& state, int64_t nAdjustedTime, bool fCheckPOW = true); +public: + /** Context-independent validity checks */ + static bool CheckTransaction(const CTransaction& tx, CValidationState& state); + static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); +}; + +#endif // BITCOIN_BLOCKCHAIN_H diff --git a/src/main.cpp b/src/main.cpp index 9ba90b4ead2b3..585d2ad6d22fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "addrman.h" #include "arith_uint256.h" +#include "blockchain.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -936,54 +937,6 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in -bool CheckTransaction(const CTransaction& tx, CValidationState &state) -{ - // Basic checks that don't depend on any context - if (tx.vin.empty()) - return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); - if (tx.vout.empty()) - return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); - // Size limits - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); - - // Check for negative or overflow output values - CAmount nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - if (txout.nValue < 0) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); - if (txout.nValue > MAX_MONEY) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); - nValueOut += txout.nValue; - if (!MoneyRange(nValueOut)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); - } - - // Check for duplicate inputs - set vInOutPoints; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); - vInOutPoints.insert(txin.prevout); - } - - if (tx.IsCoinBase()) - { - if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); - } - else - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (txin.prevout.IsNull()) - return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); - } - - return true; -} - void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { int expired = pool.Expire(GetTime() - age); if (expired != 0) @@ -1013,7 +966,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (pfMissingInputs) *pfMissingInputs = false; - if (!CheckTransaction(tx, state)) + if (!CBlockchain::CheckTransaction(tx, state)) return false; // state filled in by CheckTransaction // Coinbase is only valid in a block, not as a loose transaction @@ -3234,19 +3187,6 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) -{ - // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) - return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); - - // Check timestamp - if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); - - return true; -} - bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context. @@ -3256,7 +3196,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, fCheckPOW)) + if (!CBlockchain::CheckBlockHeader(block, state, fCheckPOW)) return false; // Check the merkle root. @@ -3290,7 +3230,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!CheckTransaction(tx, state)) + if (!CBlockchain::CheckTransaction(tx, state)) return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage())); @@ -3397,7 +3337,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return true; } - if (!CheckBlockHeader(block, state)) + if (!CBlockchain::CheckBlockHeader(block, state)) return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Get prev block index diff --git a/src/main.h b/src/main.h index f287171f141ba..040655b578bf9 100644 --- a/src/main.h +++ b/src/main.h @@ -340,9 +340,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi /** Apply the effects of this transaction on the UTXO set represented by view */ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); -/** Context-independent validity checks */ -bool CheckTransaction(const CTransaction& tx, CValidationState& state); - /** * Check if transaction is final and can be included in a block with the * specified height and time. Consensus critical. @@ -425,7 +422,6 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus /** Functions for validating blocks and updating the block tree */ /** Context-independent validity checks */ -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. diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 04c6fa9625caa..de215930356e7 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "blockchain.h" #include "consensus/validation.h" #include "data/sighash.json.h" #include "hash.h" -#include "main.h" // For CheckTransaction #include "random.h" #include "script/interpreter.h" #include "script/script.h" @@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); + BOOST_CHECK_MESSAGE(CBlockchain::CheckTransaction(tx, state), strTest); BOOST_CHECK(state.IsValid()); std::vector raw = ParseHex(raw_script); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index d9195bf345ca4..be5d5800dcdeb 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -6,12 +6,13 @@ #include "data/tx_valid.json.h" #include "test/test_bitcoin.h" +#include "blockchain.h" #include "clientversion.h" #include "consensus/validation.h" #include "core_io.h" #include "key.h" #include "keystore.h" -#include "main.h" // For CheckTransaction +#include "main.h" // For cs_main, minRelayTxFee, and DEFAULT_MIN_RELAY_TX_FEE #include "policy/policy.h" #include "script/script.h" #include "script/script_error.h" @@ -138,7 +139,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); + BOOST_CHECK_MESSAGE(CBlockchain::CheckTransaction(tx, state), strTest); BOOST_CHECK(state.IsValid()); for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -213,7 +214,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) stream >> tx; CValidationState state; - fValid = CheckTransaction(tx, state) && state.IsValid(); + fValid = CBlockchain::CheckTransaction(tx, state) && state.IsValid(); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { @@ -242,11 +243,11 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CMutableTransaction tx; stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid."); + BOOST_CHECK_MESSAGE(CBlockchain::CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail tx.vin.push_back(tx.vin[0]); - BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); + BOOST_CHECK_MESSAGE(!CBlockchain::CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); } // diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 48579b2821e8e..76838f77d1608 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -6,8 +6,8 @@ #include "wallet/walletdb.h" #include "base58.h" +#include "blockchain.h" #include "consensus/validation.h" -#include "main.h" // For CheckTransaction #include "protocol.h" #include "serialize.h" #include "sync.h" @@ -374,7 +374,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletTx wtx; ssValue >> wtx; CValidationState state; - if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid())) + if (!(CBlockchain::CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid())) return false; // Undo serialize changes in 31600