diff --git a/src/core.cpp b/src/core.cpp index cb1053b6..b12c90ef 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 bd1ddbf5..21ca6618 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 1eee4d24..a1dbfd2a 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 5a1345d4..28b32101 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 c3c7ee39..7069d4ba 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 e4dbf9c0..aeef721c 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 40a5f735..e9545087 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 c1e05466..917c2f5d 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 fbad1944..585bdb2b 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 3cbb2cf3..f963467c 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 81e77b7d..a26f4a87 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 8aeb30f3..faf911a9 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 65f0ad0c..23cb3a8e 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 ddff2acd..53d1307b 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 da75752b..7041d49d 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 22dce78e..674bae66 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 4a73413d..8910cac4 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 {