From 05df3fc68d68e87415ed9e534db3ea3160dc3092 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 8 Jan 2013 04:17:15 -0800 Subject: [PATCH] Removed AcceptToMemoryPool method from CTransaction. This method belongs to the mempool instance. Removed AreInputsStandard from CTransaction, made it a regular function in main. Moved CTransaction::GetOutputFor to CCoinsViewCache. Moved GetLegacySigOpCount and GetP2SHSigOpCount out of CTransaction into regular functions in main. Moved GetValueIn and HaveInputs from CTransaction into CCoinsViewCache. Moved AllowFree, ClientCheckInputs, CheckInputs, UpdateCoins, and CheckTransaction out of CTransaction and into main. Moved IsStandard and IsFinal out of CTransaction and put them in main as IsStandardTx and IsFinalTx. Moved GetValueOut out of CTransaction into main. Moved CTxIn, CTxOut, and CTransaction into core. Added minimum fee parameter to CTxOut::IsDust() temporarily until CTransaction is moved to core.h so that CTxOut needn't know about CTransaction. --- src/core.cpp | 3 +- src/core.h | 277 +++++++++++++++++++++- src/init.cpp | 1 + src/main.cpp | 276 +++++++++++----------- src/main.h | 406 ++++++--------------------------- src/qt/transactiondesc.cpp | 4 +- src/qt/transactionrecord.cpp | 4 +- src/rpcrawtransaction.cpp | 2 +- src/rpcwallet.cpp | 10 +- src/script.h | 2 +- src/test/data/tx_invalid.json | 2 +- src/test/data/tx_valid.json | 2 +- src/test/script_P2SH_tests.cpp | 18 +- src/test/transaction_tests.cpp | 24 +- src/wallet.cpp | 14 +- src/wallet.h | 4 +- src/walletdb.cpp | 2 +- 17 files changed, 527 insertions(+), 524 deletions(-) diff --git a/src/core.cpp b/src/core.cpp index cb1053b69..b12c90efe 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -3,4 +3,5 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "core.h" \ No newline at end of file +#include "core.h" + diff --git a/src/core.h b/src/core.h index bd1ddbf5a..21ca6618a 100644 --- a/src/core.h +++ b/src/core.h @@ -8,6 +8,7 @@ #include "uint256.h" #include "serialize.h" #include "util.h" +#include "script.h" #include @@ -65,4 +66,278 @@ public: bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); } }; -#endif \ No newline at end of file +/** An input of a transaction. It contains the location of the previous + * transaction's output that it claims and a signature that matches the + * output's public key. + */ +class CTxIn +{ +public: + COutPoint prevout; + CScript scriptSig; + unsigned int nSequence; + + CTxIn() + { + nSequence = std::numeric_limits::max(); + } + + explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = prevoutIn; + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) + { + prevout = COutPoint(hashPrevTx, nOut); + scriptSig = scriptSigIn; + nSequence = nSequenceIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(prevout); + READWRITE(scriptSig); + READWRITE(nSequence); + ) + + bool IsFinal() const + { + return (nSequence == std::numeric_limits::max()); + } + + friend bool operator==(const CTxIn& a, const CTxIn& b) + { + return (a.prevout == b.prevout && + a.scriptSig == b.scriptSig && + a.nSequence == b.nSequence); + } + + friend bool operator!=(const CTxIn& a, const CTxIn& b) + { + return !(a == b); + } + + std::string ToString() const + { + std::string str; + str += "CTxIn("; + str += prevout.ToString(); + if (prevout.IsNull()) + str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); + else + str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); + if (nSequence != std::numeric_limits::max()) + str += strprintf(", nSequence=%u", nSequence); + str += ")"; + return str; + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + + + +/** An output of a transaction. It contains the public key that the next input + * must be able to sign with to claim it. + */ +class CTxOut +{ +public: + int64 nValue; + CScript scriptPubKey; + + CTxOut() + { + SetNull(); + } + + CTxOut(int64 nValueIn, CScript scriptPubKeyIn) + { + nValue = nValueIn; + scriptPubKey = scriptPubKeyIn; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nValue); + READWRITE(scriptPubKey); + ) + + void SetNull() + { + nValue = -1; + scriptPubKey.clear(); + } + + bool IsNull() const + { + return (nValue == -1); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsDust(int64 nMinRelayTxFee) const + { + // "Dust" is defined in terms of CTransaction::nMinRelayTxFee, + // which has units satoshis-per-kilobyte. + // If you'd pay more than 1/3 in fees + // to spend something, then we consider it dust. + // A typical txout is 33 bytes big, and will + // need a CTxIn of at least 148 bytes to spend, + // so dust is a txout less than 54 uBTC + // (5430 satoshis) with default nMinRelayTxFee + return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < nMinRelayTxFee); + } + + friend bool operator==(const CTxOut& a, const CTxOut& b) + { + return (a.nValue == b.nValue && + a.scriptPubKey == b.scriptPubKey); + } + + friend bool operator!=(const CTxOut& a, const CTxOut& b) + { + return !(a == b); + } + + std::string ToString() const + { + if (scriptPubKey.size() < 6) + return "CTxOut(error)"; + return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); + } + + void print() const + { + printf("%s\n", ToString().c_str()); + } +}; + + +/** The basic transaction that is broadcasted on the network and contained in + * blocks. A transaction can contain multiple inputs and outputs. + */ +class CTransaction +{ +public: + static int64 nMinTxFee; + static int64 nMinRelayTxFee; + static const int CURRENT_VERSION=1; + int nVersion; + std::vector vin; + std::vector vout; + unsigned int nLockTime; + + CTransaction() + { + SetNull(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(vin); + READWRITE(vout); + READWRITE(nLockTime); + ) + + void SetNull() + { + nVersion = CTransaction::CURRENT_VERSION; + vin.clear(); + vout.clear(); + nLockTime = 0; + } + + bool IsNull() const + { + return (vin.empty() && vout.empty()); + } + + uint256 GetHash() const + { + return SerializeHash(*this); + } + + bool IsNewerThan(const CTransaction& old) const + { + if (vin.size() != old.vin.size()) + return false; + for (unsigned int i = 0; i < vin.size(); i++) + if (vin[i].prevout != old.vin[i].prevout) + return false; + + bool fNewer = false; + unsigned int nLowest = std::numeric_limits::max(); + for (unsigned int i = 0; i < vin.size(); i++) + { + if (vin[i].nSequence != old.vin[i].nSequence) + { + if (vin[i].nSequence <= nLowest) + { + fNewer = false; + nLowest = vin[i].nSequence; + } + if (old.vin[i].nSequence < nLowest) + { + fNewer = true; + nLowest = old.vin[i].nSequence; + } + } + } + return fNewer; + } + + bool IsCoinBase() const + { + return (vin.size() == 1 && vin[0].prevout.IsNull()); + } + + friend bool operator==(const CTransaction& a, const CTransaction& b) + { + return (a.nVersion == b.nVersion && + a.vin == b.vin && + a.vout == b.vout && + a.nLockTime == b.nLockTime); + } + + friend bool operator!=(const CTransaction& a, const CTransaction& b) + { + return !(a == b); + } + + + std::string ToString() const + { + std::string str; + str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n", + GetHash().ToString().substr(0,10).c_str(), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (unsigned int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; + } + + void print() const + { + printf("%s", ToString().c_str()); + } +}; + +#endif diff --git a/src/init.cpp b/src/init.cpp index 1eee4d247..a1dbfd2a5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -5,6 +5,7 @@ #include "init.h" #include "main.h" +#include "core.h" #include "txdb.h" #include "walletdb.h" #include "bitcoinrpc.h" diff --git a/src/main.cpp b/src/main.cpp index 5a1345d41..28b32101d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -357,41 +357,23 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) -////////////////////////////////////////////////////////////////////////////// -// -// CTransaction / CTxOut -// - -bool CTxOut::IsDust() const +bool IsStandardTx(const CTransaction& tx) { - // "Dust" is defined in terms of CTransaction::nMinRelayTxFee, - // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees - // to spend something, then we consider it dust. - // A typical txout is 33 bytes big, and will - // need a CTxIn of at least 148 bytes to spend, - // so dust is a txout less than 54 uBTC - // (5430 satoshis) with default nMinRelayTxFee - return ((nValue*1000)/(3*((int)GetSerializeSize(SER_DISK,0)+148)) < CTransaction::nMinRelayTxFee); -} - -bool CTransaction::IsStandard() const -{ - if (nVersion > CTransaction::CURRENT_VERSION) + if (tx.nVersion > CTransaction::CURRENT_VERSION) return false; - if (!IsFinal()) + if (!IsFinalTx(tx)) return false; // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. - unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); if (sz >= MAX_STANDARD_TX_SIZE) return false; - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 @@ -401,15 +383,47 @@ bool CTransaction::IsStandard() const if (!txin.scriptSig.IsPushOnly()) return false; } - BOOST_FOREACH(const CTxOut& txout, vout) { + BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (!::IsStandard(txout.scriptPubKey)) return false; - if (txout.IsDust()) + if (txout.IsDust(CTransaction::nMinRelayTxFee)) return false; } return true; } +bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64 nBlockTime) +{ + // Time based nLockTime implemented in 0.1.6 + if (tx.nLockTime == 0) + return true; + if (nBlockHeight == 0) + nBlockHeight = nBestHeight; + if (nBlockTime == 0) + nBlockTime = GetAdjustedTime(); + if ((int64)tx.nLockTime < ((int64)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) + return true; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (!txin.IsFinal()) + return false; + return true; +} + +/** Amount of bitcoins spent by the transaction. + @return sum of all outputs (note: does not include fees) + */ +int64 GetValueOut(const CTransaction& tx) +{ + int64 nValueOut = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nValueOut += txout.nValue; + if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) + throw std::runtime_error("GetValueOut() : value out of range"); + } + return nValueOut; +} + // // Check transaction inputs, and make sure any // pay-to-script-hash transactions are evaluating IsStandard scripts @@ -421,14 +435,14 @@ bool CTransaction::IsStandard() const // expensive-to-check-upon-redemption script like: // DUP CHECKSIG DROP ... repeated 100 times... OP_1 // -bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const +bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs) { - if (IsCoinBase()) + if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally - for (unsigned int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxOut& prev = GetOutputFor(vin[i], mapInputs); + const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); vector > vSolutions; txnouttype whichType; @@ -446,7 +460,7 @@ bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const // beside "push data" in the scriptSig the // IsStandard() call returns false vector > stack; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0)) + if (!EvalScript(stack, tx.vin[i].scriptSig, tx, i, false, 0)) return false; if (whichType == TX_SCRIPTHASH) @@ -475,20 +489,34 @@ bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const return true; } -unsigned int CTransaction::GetLegacySigOpCount() const +unsigned int GetLegacySigOpCount(const CTransaction& tx) { unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { nSigOps += txin.scriptSig.GetSigOpCount(false); } - BOOST_FOREACH(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, tx.vout) { nSigOps += txout.scriptPubKey.GetSigOpCount(false); } return nSigOps; } +unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& inputs) +{ + if (tx.IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); + } + return nSigOps; +} int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { @@ -543,25 +571,25 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -bool CTransaction::CheckTransaction(CValidationState &state) const +bool CheckTransaction(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context - if (vin.empty()) - return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); - if (vout.empty()) - return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + if (tx.vin.empty()) + return state.DoS(10, error("CheckTransaction() : vin empty")); + if (tx.vout.empty()) + return state.DoS(10, error("CheckTransaction() : vout empty")); // Size limits - if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) + BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + return state.DoS(100, error("CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + return state.DoS(100, error("CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); @@ -569,23 +597,23 @@ bool CTransaction::CheckTransaction(CValidationState &state) const // Check for duplicate inputs set vInOutPoints; - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); vInOutPoints.insert(txin.prevout); } - if (IsCoinBase()) + if (tx.IsCoinBase()) { - if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) + return state.DoS(100, error("CheckTransaction() : coinbase script size")); } else { - BOOST_FOREACH(const CTxIn& txin, vin) + BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + return state.DoS(10, error("CheckTransaction() : prevout is null")); } return true; @@ -657,7 +685,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn if (pfMissingInputs) *pfMissingInputs = false; - if (!tx.CheckTransaction(state)) + if (!CheckTransaction(tx, state)) return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction @@ -669,7 +697,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !tx.IsStandard()) + if (!fTestNet && !IsStandardTx(tx)) return error("CTxMemPool::accept() : nonstandard transaction type"); // is it already in the memory pool? @@ -694,7 +722,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn if (i != 0) return false; ptxOld = mapNextTx[outpoint].ptx; - if (ptxOld->IsFinal()) + if (IsFinalTx(*ptxOld)) return false; if (!tx.IsNewerThan(*ptxOld)) return false; @@ -734,7 +762,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn } // are the actual inputs available? - if (!tx.HaveInputs(view)) + if (!view.HaveInputs(tx)) return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); // Bring the best block into scope @@ -745,14 +773,14 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn } // Check for non-standard pay-to-script-hash in inputs - if (!tx.AreInputsStandard(view) && !fTestNet) + if (!AreInputsStandard(tx, view) && !fTestNet) return error("CTxMemPool::accept() : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64 nFees = tx.GetValueIn(view)-tx.GetValueOut(); + int64 nFees = view.GetValueIn(tx)-GetValueOut(tx); unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block @@ -787,7 +815,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) + if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); } @@ -816,14 +844,6 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckIn return true; } -bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) -{ - try { - return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); - } catch(std::runtime_error &e) { - return state.Abort(_("System error: ") + e.what()); - } -} bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) { @@ -936,7 +956,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) { CValidationState state; - return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); + return mempool.accept(state, *this, fCheckInputs, fLimitFree, NULL); } @@ -1299,45 +1319,30 @@ void CBlockHeader::UpdateTime(const CBlockIndex* pindexPrev) -const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view) +const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) { - const CCoins &coins = view.GetCoins(input.prevout.hash); + const CCoins &coins = GetCoins(input.prevout.hash); assert(coins.IsAvailable(input.prevout.n)); return coins.vout[input.prevout.n]; } -int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const +int64 CCoinsViewCache::GetValueIn(const CTransaction& tx) { - if (IsCoinBase()) + if (tx.IsCoinBase()) return 0; int64 nResult = 0; - for (unsigned int i = 0; i < vin.size(); i++) - nResult += GetOutputFor(vin[i], inputs).nValue; + for (unsigned int i = 0; i < tx.vin.size(); i++) + nResult += GetOutputFor(tx.vin[i]).nValue; return nResult; } -unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const -{ - if (IsCoinBase()) - return 0; - - unsigned int nSigOps = 0; - for (unsigned int i = 0; i < vin.size(); i++) - { - const CTxOut &prevout = GetOutputFor(vin[i], inputs); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); - } - return nSigOps; -} - -void CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) { // mark inputs spent - if (!IsCoinBase()) { - BOOST_FOREACH(const CTxIn &txin, vin) { + if (!tx.IsCoinBase()) { + BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoins &coins = inputs.GetCoins(txin.prevout.hash); CTxInUndo undo; assert(coins.Spend(txin.prevout, undo)); @@ -1346,23 +1351,23 @@ void CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, } // add outputs - assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); + assert(inputs.SetCoins(txhash, CCoins(tx, nHeight))); } -bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const +bool CCoinsViewCache::HaveInputs(const CTransaction& tx) { - if (!IsCoinBase()) { + if (!tx.IsCoinBase()) { // first check whether information about the prevout hash is available - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; - if (!inputs.HaveCoins(prevout.hash)) + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + if (!HaveCoins(prevout.hash)) return false; } // then check whether the actual outputs are available - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; - const CCoins &coins = inputs.GetCoins(prevout.hash); + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins &coins = GetCoins(prevout.hash); if (!coins.IsAvailable(prevout.n)) return false; } @@ -1382,26 +1387,26 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); } -bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) const +bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector *pvChecks) { - if (!IsCoinBase()) + if (!tx.IsCoinBase()) { if (pvChecks) - pvChecks->reserve(vin.size()); + pvChecks->reserve(tx.vin.size()); // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. - if (!HaveInputs(inputs)) - return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().c_str())); + if (!inputs.HaveInputs(tx)) + return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString().c_str())); // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; int64 nValueIn = 0; int64 nFees = 0; - for (unsigned int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; + const COutPoint &prevout = tx.vin[i].prevout; const CCoins &coins = inputs.GetCoins(prevout.hash); // If prev is coinbase, check that it's matured @@ -1417,13 +1422,13 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, } - if (nValueIn < GetValueOut()) - return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().c_str())); + if (nValueIn < GetValueOut(tx)) + return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str())); // Tally transaction fees - int64 nTxFee = nValueIn - GetValueOut(); + int64 nTxFee = nValueIn - GetValueOut(tx); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().c_str())); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str())); nFees += nTxFee; if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs() : nFees out of range")); @@ -1436,12 +1441,12 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. if (fScriptChecks) { - for (unsigned int i = 0; i < vin.size(); i++) { - const COutPoint &prevout = vin[i].prevout; + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; const CCoins &coins = inputs.GetCoins(prevout.hash); // Verify signature - CScriptCheck check(coins, *this, i, flags, 0); + CScriptCheck check(coins, tx, i, flags, 0); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1449,7 +1454,7 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, if (flags & SCRIPT_VERIFY_STRICTENC) { // For now, check whether the failure was caused by non-canonical // encodings or not; if so, don't trigger DoS protection. - CScriptCheck check(coins, *this, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); + CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); if (check()) return state.Invalid(); } @@ -1464,7 +1469,6 @@ bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, - bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) { assert(pindex == view.GetBestBlock()); @@ -1644,13 +1648,13 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi const CTransaction &tx = vtx[i]; nInputs += tx.vin.size(); - nSigOps += tx.GetLegacySigOpCount(); + nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock() : too many sigops")); if (!tx.IsCoinBase()) { - if (!tx.HaveInputs(view)) + if (!view.HaveInputs(tx)) return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); if (fStrictPayToScriptHash) @@ -1658,21 +1662,21 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. - nSigOps += tx.GetP2SHSigOpCount(view); + nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock() : too many sigops")); } - nFees += tx.GetValueIn(view)-tx.GetValueOut(); + nFees += view.GetValueIn(tx)-GetValueOut(tx); std::vector vChecks; - if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) + if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } CTxUndo txundo; - tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i)); + UpdateCoins(tx, state, view, txundo, pindex->nHeight, GetTxHash(i)); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); @@ -1683,8 +1687,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi if (fBenchmark) printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees))); + if (GetValueOut(vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(vtx[0]), GetBlockValue(pindex->nHeight, nFees))); if (!control.Wait()) return state.DoS(100, false); @@ -1846,7 +1850,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) BOOST_FOREACH(CTransaction& tx, vResurrect) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - tx.AcceptToMemoryPool(stateDummy, true, false); + mempool.accept(stateDummy, tx, true, false, NULL); } // Delete redundant memory transactions that are in the connected branch @@ -2076,7 +2080,7 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.CheckTransaction(state)) + if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); // Build the merkle tree already. We need it anyway later, and it makes the @@ -2096,7 +2100,7 @@ bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerk unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) { - nSigOps += tx.GetLegacySigOpCount(); + nSigOps += GetLegacySigOpCount(tx); } if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); @@ -2135,7 +2139,7 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.IsFinal(nHeight, GetBlockTime())) + if (!IsFinalTx(tx, nHeight, GetBlockTime())) return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint @@ -3488,7 +3492,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool fMissingInputs = false; CValidationState state; - if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) + if (mempool.accept(state, tx, true, true, &fMissingInputs)) { RelayTransaction(tx, inv.hash, vMsg); mapAlreadyAskedFor.erase(inv); @@ -3511,7 +3515,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned) CValidationState stateDummy; - if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) + if (mempool.accept(stateDummy, tx, true, true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().c_str()); RelayTransaction(tx, inv.hash, vMsg); @@ -4194,7 +4198,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { CTransaction& tx = (*mi).second; - if (tx.IsCoinBase() || !tx.IsFinal()) + if (tx.IsCoinBase() || !IsFinalTx(tx)) continue; COrphan* porphan = NULL; @@ -4249,7 +4253,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) // This is a more accurate fee-per-kilobyte than is used by the client code, because the // client code rounds up the size to the nearest 1K. That's good, because it gives an // incentive to create smaller transactions. - double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); + double dFeePerKb = double(nTotalIn-GetValueOut(tx)) / (double(nTxSize)/1000.0); if (porphan) { @@ -4285,7 +4289,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) continue; // Legacy limits on sigOps: - unsigned int nTxSigOps = tx.GetLegacySigOpCount(); + unsigned int nTxSigOps = GetLegacySigOpCount(tx); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; @@ -4303,22 +4307,22 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); } - if (!tx.HaveInputs(view)) + if (!view.HaveInputs(tx)) continue; - int64 nTxFees = tx.GetValueIn(view)-tx.GetValueOut(); + int64 nTxFees = view.GetValueIn(tx)-GetValueOut(tx); - nTxSigOps += tx.GetP2SHSigOpCount(view); + nTxSigOps += GetP2SHSigOpCount(tx, view); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; CValidationState state; - if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) + if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; uint256 hash = tx.GetHash(); - tx.UpdateCoins(state, view, txundo, pindexPrev->nHeight+1, hash); + UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash); // Added pblock->vtx.push_back(tx); @@ -4366,7 +4370,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; - pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount(); + pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; diff --git a/src/main.h b/src/main.h index c3c7ee396..7069d4bab 100644 --- a/src/main.h +++ b/src/main.h @@ -254,155 +254,6 @@ struct CDiskTxPos : public CDiskBlockPos - -/** An input of a transaction. It contains the location of the previous - * transaction's output that it claims and a signature that matches the - * output's public key. - */ -class CTxIn -{ -public: - COutPoint prevout; - CScript scriptSig; - unsigned int nSequence; - - CTxIn() - { - nSequence = std::numeric_limits::max(); - } - - explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = prevoutIn; - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits::max()) - { - prevout = COutPoint(hashPrevTx, nOut); - scriptSig = scriptSigIn; - nSequence = nSequenceIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(prevout); - READWRITE(scriptSig); - READWRITE(nSequence); - ) - - bool IsFinal() const - { - return (nSequence == std::numeric_limits::max()); - } - - friend bool operator==(const CTxIn& a, const CTxIn& b) - { - return (a.prevout == b.prevout && - a.scriptSig == b.scriptSig && - a.nSequence == b.nSequence); - } - - friend bool operator!=(const CTxIn& a, const CTxIn& b) - { - return !(a == b); - } - - std::string ToString() const - { - std::string str; - str += "CTxIn("; - str += prevout.ToString(); - if (prevout.IsNull()) - str += strprintf(", coinbase %s", HexStr(scriptSig).c_str()); - else - str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str()); - if (nSequence != std::numeric_limits::max()) - str += strprintf(", nSequence=%u", nSequence); - str += ")"; - return str; - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - - -/** An output of a transaction. It contains the public key that the next input - * must be able to sign with to claim it. - */ -class CTxOut -{ -public: - int64 nValue; - CScript scriptPubKey; - - CTxOut() - { - SetNull(); - } - - CTxOut(int64 nValueIn, CScript scriptPubKeyIn) - { - nValue = nValueIn; - scriptPubKey = scriptPubKeyIn; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(nValue); - READWRITE(scriptPubKey); - ) - - void SetNull() - { - nValue = -1; - scriptPubKey.clear(); - } - - bool IsNull() const - { - return (nValue == -1); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - friend bool operator==(const CTxOut& a, const CTxOut& b) - { - return (a.nValue == b.nValue && - a.scriptPubKey == b.scriptPubKey); - } - - friend bool operator!=(const CTxOut& a, const CTxOut& b) - { - return !(a == b); - } - - bool IsDust() const; - - std::string ToString() const - { - if (scriptPubKey.size() < 6) - return "CTxOut(error)"; - return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str()); - } - - void print() const - { - printf("%s\n", ToString().c_str()); - } -}; - - - enum GetMinFee_mode { GMF_BLOCK, @@ -412,215 +263,71 @@ enum GetMinFee_mode int64 GetMinFee(const CTransaction& tx, unsigned int nBlockSize = 1, bool fAllowFree = true, enum GetMinFee_mode mode = GMF_BLOCK); -/** The basic transaction that is broadcasted on the network and contained in - * blocks. A transaction can contain multiple inputs and outputs. - */ -class CTransaction -{ -public: - static int64 nMinTxFee; - static int64 nMinRelayTxFee; - static const int CURRENT_VERSION=1; - int nVersion; - std::vector vin; - std::vector vout; - unsigned int nLockTime; - - CTransaction() - { - SetNull(); - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); - ) - - void SetNull() - { - nVersion = CTransaction::CURRENT_VERSION; - vin.clear(); - vout.clear(); - nLockTime = 0; - } - - bool IsNull() const - { - return (vin.empty() && vout.empty()); - } - - uint256 GetHash() const - { - return SerializeHash(*this); - } - - bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const - { - // Time based nLockTime implemented in 0.1.6 - if (nLockTime == 0) - return true; - if (nBlockHeight == 0) - nBlockHeight = nBestHeight; - if (nBlockTime == 0) - nBlockTime = GetAdjustedTime(); - if ((int64)nLockTime < ((int64)nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime)) - return true; - BOOST_FOREACH(const CTxIn& txin, vin) - if (!txin.IsFinal()) - return false; - return true; - } - - bool IsNewerThan(const CTransaction& old) const - { - if (vin.size() != old.vin.size()) - return false; - for (unsigned int i = 0; i < vin.size(); i++) - if (vin[i].prevout != old.vin[i].prevout) - return false; - - bool fNewer = false; - unsigned int nLowest = std::numeric_limits::max(); - for (unsigned int i = 0; i < vin.size(); i++) - { - if (vin[i].nSequence != old.vin[i].nSequence) - { - if (vin[i].nSequence <= nLowest) - { - fNewer = false; - nLowest = vin[i].nSequence; - } - if (old.vin[i].nSequence < nLowest) - { - fNewer = true; - nLowest = old.vin[i].nSequence; - } - } - } - return fNewer; - } - - bool IsCoinBase() const - { - return (vin.size() == 1 && vin[0].prevout.IsNull()); - } - - /** Check for standard transaction types - @return True if all outputs (scriptPubKeys) use only standard transaction forms - */ - bool IsStandard() const; +// +// Check transaction inputs, and make sure any +// pay-to-script-hash transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// /** Check for standard transaction types - @param[in] mapInputs Map of previous transactions that have outputs we're spending + @param[in] mapInputs Map of previous transactions that have outputs we're spending @return True if all inputs (scriptSigs) use only standard transaction forms */ - bool AreInputsStandard(CCoinsViewCache& mapInputs) const; +bool AreInputsStandard(const CTransaction& tx, CCoinsViewCache& mapInputs); - /** Count ECDSA signature operations the old-fashioned (pre-0.6) way - @return number of sigops this transaction's outputs will produce when spent - */ - unsigned int GetLegacySigOpCount() const; +/** Count ECDSA signature operations the old-fashioned (pre-0.6) way + @return number of sigops this transaction's outputs will produce when spent + @see CTransaction::FetchInputs +*/ +unsigned int GetLegacySigOpCount(const CTransaction& tx); - /** Count ECDSA signature operations in pay-to-script-hash inputs. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return maximum number of sigops required to validate this transaction's inputs - */ - unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const; - - /** Amount of bitcoins spent by this transaction. - @return sum of all outputs (note: does not include fees) - */ - int64 GetValueOut() const - { - int64 nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("CTransaction::GetValueOut() : value out of range"); - } - return nValueOut; - } - - /** Amount of bitcoins coming in to this transaction - Note that lightweight clients may not know anything besides the hash of previous transactions, - so may not be able to calculate this. - - @param[in] mapInputs Map of previous transactions that have outputs we're spending - @return Sum of value of all inputs (scriptSigs) - */ - int64 GetValueIn(CCoinsViewCache& mapInputs) const; - - static bool AllowFree(double dPriority) - { - // Large (in bytes) low-priority (new, small-coin) transactions - // need a fee. - return dPriority > COIN * 144 / 250; - } - - friend bool operator==(const CTransaction& a, const CTransaction& b) - { - return (a.nVersion == b.nVersion && - a.vin == b.vin && - a.vout == b.vout && - a.nLockTime == b.nLockTime); - } - - friend bool operator!=(const CTransaction& a, const CTransaction& b) - { - return !(a == b); - } +/** Count ECDSA signature operations in pay-to-script-hash inputs. + @param[in] mapInputs Map of previous transactions that have outputs we're spending + @return maximum number of sigops required to validate this transaction's inputs + @see CTransaction::FetchInputs + */ +unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& mapInputs); - std::string ToString() const - { - std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n", - GetHash().ToString().c_str(), - nVersion, - vin.size(), - vout.size(), - nLockTime); - for (unsigned int i = 0; i < vin.size(); i++) - str += " " + vin[i].ToString() + "\n"; - for (unsigned int i = 0; i < vout.size(); i++) - str += " " + vout[i].ToString() + "\n"; - return str; - } - void print() const - { - printf("%s", ToString().c_str()); - } +inline bool AllowFree(double dPriority) +{ + // Large (in bytes) low-priority (new, small-coin) transactions + // need a fee. + return dPriority > COIN * 144 / 250; +} +// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) +// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it +// instead of being performed inline. +bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, + std::vector *pvChecks = NULL); - // Check whether all prevouts of this transaction are present in the UTXO set represented by view - bool HaveInputs(CCoinsViewCache &view) const; +// Apply the effects of this transaction on the UTXO set represented by view +bool UpdateCoins(const CTransaction& tx, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash); - // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) - // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it - // instead of being performed inline. - bool CheckInputs(CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true, - unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, - std::vector *pvChecks = NULL) const; +// Context-independent validity checks +bool CheckTransaction(const CTransaction& tx, CValidationState& state); - // Apply the effects of this transaction on the UTXO set represented by view - void UpdateCoins(CValidationState &state, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; +/** Check for standard transaction types + @return True if all outputs (scriptPubKeys) use only standard transaction forms +*/ +bool IsStandardTx(const CTransaction& tx); - // Context-independent validity checks - bool CheckTransaction(CValidationState &state) const; +bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64 nBlockTime = 0); - // Try to accept this transaction into the memory pool - bool AcceptToMemoryPool(CValidationState &state, bool fCheckInputs=true, bool fLimitFree = true, bool* pfMissingInputs=NULL); +/** Amount of bitcoins spent by the transaction. + @return sum of all outputs (note: does not include fees) + */ +int64 GetValueOut(const CTransaction& tx); -protected: - static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs); -}; /** wrapper for CTxOut that provides a more compact serialization */ class CTxOutCompressor @@ -2132,6 +1839,21 @@ public: // Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize(); + /** Amount of bitcoins coming in to a transaction + Note that lightweight clients may not know anything besides the hash of previous transactions, + so may not be able to calculate this. + + @param[in] tx transaction for which we are checking input total + @return Sum of value of all inputs (scriptSigs) + @see CTransaction::FetchInputs + */ + int64 GetValueIn(const CTransaction& tx); + + // Check whether all prevouts of the transaction are present in the UTXO set represented by this view + bool HaveInputs(const CTransaction& tx); + + const CTxOut &GetOutputFor(const CTxIn& input); + private: std::map::iterator FetchCoins(const uint256 &txid); }; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index e4dbf9c0d..aeef721ce 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -12,7 +12,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { - if (!wtx.IsFinal()) + if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1); @@ -186,7 +186,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nValue) + "
"; } - int64 nTxFee = nDebit - wtx.GetValueOut(); + int64 nTxFee = nDebit - GetValueOut(wtx); if (nTxFee > 0) strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nTxFee) + "
"; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 40a5f735c..e95450876 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -89,7 +89,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - int64 nTxFee = nDebit - wtx.GetValueOut(); + int64 nTxFee = nDebit - GetValueOut(wtx); for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++) { @@ -162,7 +162,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = nBestHeight; - if (!wtx.IsFinal()) + if (!IsFinalTx(wtx)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) { diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index c1e05466e..917c2f5de 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -555,7 +555,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) if (!fHave) { // push to local node CValidationState state; - if (!tx.AcceptToMemoryPool(state, true, false)) + if (!mempool.accept(state, tx, true, false, NULL)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state } } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index fbad1944d..585bdb2bf 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -408,7 +408,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -454,7 +454,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -478,7 +478,7 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!wtx.IsFinal()) + if (!IsFinalTx(wtx)) continue; int64 nReceived, nSent, nFee; @@ -839,7 +839,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) continue; int nDepth = wtx.GetDepthInMainChain(); @@ -1220,7 +1220,7 @@ Value gettransaction(const Array& params, bool fHelp) int64 nCredit = wtx.GetCredit(); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; - int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); + int64 nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); if (wtx.IsFromMe()) diff --git a/src/script.h b/src/script.h index 3cbb2cf32..f963467c9 100644 --- a/src/script.h +++ b/src/script.h @@ -533,7 +533,7 @@ public: bool IsPayToScriptHash() const; - // Called by CTransaction::IsStandard + // Called by IsStandardTx bool IsPushOnly() const { const_iterator pc = begin(); diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 81e77b7df..a26f4a87d 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -23,7 +23,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], "010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", true], -["Tests for CTransaction::CheckTransaction()"], +["Tests for CheckTransaction()"], ["No inputs"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], "0100000000010000000000000000015100000000", true], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 8aeb30f35..faf911a97 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -50,7 +50,7 @@ [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", true], -["Tests for CTransaction::CheckTransaction()"], +["Tests for CheckTransaction()"], ["MAX_MONEY output"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", true], diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 65f0ad0cd..23cb3a8e0 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(sign) txFrom.vout[i+4].scriptPubKey = standardScripts[i]; txFrom.vout[i+4].nValue = COIN; } - BOOST_CHECK(txFrom.IsStandard()); + BOOST_CHECK(IsStandardTx(txFrom)); CTransaction txTo[8]; // Spending transactions for (int i = 0; i < 8; i++) @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(set) txFrom.vout[i].scriptPubKey = outer[i]; txFrom.vout[i].nValue = CENT; } - BOOST_CHECK(txFrom.IsStandard()); + BOOST_CHECK(IsStandardTx(txFrom)); CTransaction txTo[4]; // Spending transactions for (int i = 0; i < 4; i++) @@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(set) for (int i = 0; i < 4; i++) { BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); - BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i)); + BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i]), strprintf("txTo[%d].IsStandard", i)); } } @@ -305,15 +305,15 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txTo.vin[2].prevout.hash = txFrom.GetHash(); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); - BOOST_CHECK(txTo.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(coins), 1U); + BOOST_CHECK(::AreInputsStandard(txTo, coins)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txTo, coins), 1U); // Make sure adding crap to the scriptSigs makes them non-standard: for (int i = 0; i < 3; i++) { CScript t = txTo.vin[i].scriptSig; txTo.vin[i].scriptSig = (CScript() << 11) + t; - BOOST_CHECK(!txTo.AreInputsStandard(coins)); + BOOST_CHECK(!::AreInputsStandard(txTo, coins)); txTo.vin[i].scriptSig = t; } @@ -329,11 +329,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd.vin[1].prevout.hash = txFrom.GetHash(); txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); - BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11U); + BOOST_CHECK(!::AreInputsStandard(txToNonStd, coins)); + BOOST_CHECK_EQUAL(GetP2SHSigOpCount(txToNonStd, coins), 11U); txToNonStd.vin[0].scriptSig.clear(); - BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); + BOOST_CHECK(!::AreInputsStandard(txToNonStd, coins)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index ddff2acd4..53d1307b6 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(state), strTest); + BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); BOOST_CHECK(state.IsValid()); for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) stream >> tx; CValidationState state; - fValid = tx.CheckTransaction(state) && state.IsValid(); + fValid = CheckTransaction(tx, state) && state.IsValid(); for (unsigned int i = 0; i < tx.vin.size() && fValid; i++) { @@ -163,11 +163,11 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CTransaction tx; stream >> tx; CValidationState state; - BOOST_CHECK_MESSAGE(tx.CheckTransaction(state) && state.IsValid(), "Simple deserialized transaction should be valid."); + BOOST_CHECK_MESSAGE(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(!tx.CheckTransaction(state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); + BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid."); } // @@ -230,16 +230,16 @@ BOOST_AUTO_TEST_CASE(test_Get) t1.vout[0].nValue = 90*CENT; t1.vout[0].scriptPubKey << OP_1; - BOOST_CHECK(t1.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(t1.GetValueIn(coins), (50+21+22)*CENT); + BOOST_CHECK(AreInputsStandard(t1, coins)); + BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT); // Adding extra junk to the scriptSig should make it non-standard: t1.vin[0].scriptSig << OP_11; - BOOST_CHECK(!t1.AreInputsStandard(coins)); + BOOST_CHECK(!AreInputsStandard(t1, coins)); // ... as should not having enough: t1.vin[0].scriptSig = CScript(); - BOOST_CHECK(!t1.AreInputsStandard(coins)); + BOOST_CHECK(!AreInputsStandard(t1, coins)); } BOOST_AUTO_TEST_CASE(test_IsStandard) @@ -260,16 +260,16 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) key.MakeNewKey(true); t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - BOOST_CHECK(t.IsStandard()); + BOOST_CHECK(IsStandardTx(t)); t.vout[0].nValue = 5011; // dust - BOOST_CHECK(!t.IsStandard()); + BOOST_CHECK(!IsStandardTx(t)); t.vout[0].nValue = 6011; // not dust - BOOST_CHECK(t.IsStandard()); + BOOST_CHECK(IsStandardTx(t)); t.vout[0].scriptPubKey = CScript() << OP_1; - BOOST_CHECK(!t.IsStandard()); + BOOST_CHECK(!IsStandardTx(t)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet.cpp b/src/wallet.cpp index da75752bb..7041d49da 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -643,7 +643,7 @@ void CWalletTx::GetAmounts(list >& listReceived, int64 nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - int64 nValueOut = GetValueOut(); + int64 nValueOut = GetValueOut(*this); nFee = nDebit - nValueOut; } @@ -933,7 +933,7 @@ int64 CWallet::GetUnconfirmedBalance() const for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) nTotal += pcoin->GetAvailableCredit(); } } @@ -965,7 +965,7 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const { const CWalletTx* pcoin = &(*it).second; - if (!pcoin->IsFinal()) + if (!IsFinalTx(*pcoin)) continue; if (fOnlyConfirmed && !pcoin->IsConfirmed()) @@ -1178,7 +1178,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) { CTxOut txout(s.second, s.first); - if (txout.IsDust()) + if (txout.IsDust(CTransaction::nMinRelayTxFee)) { strFailReason = _("Transaction amount too small"); return false; @@ -1237,7 +1237,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust()) + if (newTxOut.IsDust(CTransaction::nMinRelayTxFee)) { nFeeRet += nChange; reservekey.ReturnKey(); @@ -1276,7 +1276,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - bool fAllowFree = CTransaction::AllowFree(dPriority); + bool fAllowFree = AllowFree(dPriority); int64 nMinFee = GetMinFee(wtxNew, 1, fAllowFree, GMF_SEND); if (nFeeRet < max(nPayFee, nMinFee)) { @@ -1657,7 +1657,7 @@ std::map CWallet::GetAddressBalances() { CWalletTx *pcoin = &walletEntry.second; - if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + if (!IsFinalTx(*pcoin) || !pcoin->IsConfirmed()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) diff --git a/src/wallet.h b/src/wallet.h index 22dce78e9..674bae66d 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -641,7 +641,7 @@ public: bool IsConfirmed() const { // Quick answer in most cases - if (!IsFinal()) + if (!IsFinalTx(*this)) return false; if (GetDepthInMainChain() >= 1) return true; @@ -658,7 +658,7 @@ public: { const CMerkleTx* ptx = vWorkQueue[i]; - if (!ptx->IsFinal()) + if (!IsFinalTx(*ptx)) return false; if (ptx->GetDepthInMainChain() >= 1) continue; diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 4a73413d2..8910cac4b 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -204,7 +204,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; CValidationState state; - if (wtx.CheckTransaction(state) && (wtx.GetHash() == hash) && state.IsValid()) + if (CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()) wtx.BindWallet(pwallet); else {