Permalink
Browse files

Updates to isFinal and fix off by one errors

  • Loading branch information...
Tranz5 committed Aug 19, 2014
1 parent da59be1 commit ecc08571f3bc241461f386c5d23553d60b4511ad
Showing with 88 additions and 275 deletions.
  1. +63 −7 src/main.cpp
  2. +7 −24 src/main.h
  3. +1 −1 src/miner.cpp
  4. +2 −2 src/qt/transactiondesc.cpp
  5. +2 −2 src/qt/transactionrecord.cpp
  6. +5 −4 src/rpcwallet.cpp
  7. +2 −2 src/script.h
  8. +0 −227 src/test/miner_tests.cpp
  9. +4 −4 src/wallet.cpp
  10. +2 −2 src/wallet.h
View
@@ -345,12 +345,49 @@ bool CTransaction::ReadFromDisk(COutPoint prevout)
return ReadFromDisk(txdb, prevout, txindex);
}
bool CTransaction::IsStandard() const
bool IsStandardTx(const CTransaction& tx)
{
if (nVersion > CTransaction::CURRENT_VERSION)
if (tx.nVersion > CTransaction::CURRENT_VERSION)
return false;
BOOST_FOREACH(const CTxIn& txin, vin)
// Treat non-final transactions as non-standard to prevent a specific type
// of double-spend attack, as well as DoS attacks. (if the transaction
// can't be mined, the attacker isn't expending resources broadcasting it)
// Basically we don't want to propagate transactions that can't included in
// the next block.
//
// However, IsFinalTx() is confusing... Without arguments, it uses
// chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height()
// is set to the value of nHeight in the block. However, when IsFinalTx()
// is called within CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a transaction can
// be part of the *next* block, we need to call IsFinalTx() with one more
// than chainActive.Height().
//
// Timestamps on the other hand don't get any special treatment, because we
// can't know what timestamp the next block will have, and there aren't
// timestamp applications where it matters.
if (!IsFinalTx(tx, nBestHeight + 1)) {
return false;
}
// nTime has different purpose from nLockTime but can be used in similar attacks
if (tx.nTime > FutureDrift(GetAdjustedTime())) {
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 = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz >= MAX_STANDARD_TX_SIZE)
return false;
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
@@ -365,7 +402,7 @@ bool CTransaction::IsStandard() const
unsigned int nDataOut = 0;
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, vout) {
BOOST_FOREACH(const CTxOut& txout, tx.vout) {
if (!::IsStandard(txout.scriptPubKey, whichType))
return false;
if (whichType == TX_NULL_DATA)
@@ -384,6 +421,25 @@ bool CTransaction::IsStandard() const
return true;
}
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
AssertLockHeld(cs_main);
// 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;
}
//
// Check transaction inputs, and make sure any
// pay-to-script-hash transactions are evaluating IsStandard scripts
@@ -635,7 +691,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
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");
// Do we already have it?
@@ -663,7 +719,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
if (i != 0)
return false;
ptxOld = mapNextTx[outpoint].ptx;
if (ptxOld->IsFinal())
if (IsFinalTx(*ptxOld))
return false;
if (!tx.IsNewerThan(*ptxOld))
return false;
@@ -2260,7 +2316,7 @@ bool CBlock::AcceptBlock()
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, vtx)
if (!tx.IsFinal(nHeight, GetBlockTime()))
if (!IsFinalTx(tx, nHeight, GetBlockTime()))
return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
// Check that the block chain matches the known block chain up to a checkpoint
View
@@ -29,6 +29,8 @@ class CNode;
static const unsigned int MAX_BLOCK_SIZE = 1000000;
/** The maximum size for mined blocks */
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
/** The maximum size for transactions we're willing to relay/mine **/
static const unsigned int MAX_STANDARD_TX_SIZE = MAX_BLOCK_SIZE_GEN/5;
/** The maximum allowed number of signature check operations in a block (network rule) */
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
/** The maximum number of orphan transactions kept in memory */
@@ -499,24 +501,6 @@ class CTransaction
return SerializeHash(*this);
}
bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const
{
AssertLockHeld(cs_main);
// 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())
@@ -557,11 +541,6 @@ class CTransaction
return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty());
}
/** Check for standard transaction types
@return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
bool IsStandard() const;
/** Check for standard transaction types
@param[in] mapInputs Map of previous transactions that have outputs we're spending
@return True if all inputs (scriptSigs) use only standard transaction forms
@@ -730,8 +709,12 @@ class CTransaction
const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
};
/** Check for standard transaction types
@return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
bool IsStandardTx(const CTransaction& tx);
bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0);
/** A transaction with a merkle branch linking it to the block chain. */
View
@@ -184,7 +184,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake)
for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
{
CTransaction& tx = (*mi).second;
if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal())
if (tx.IsCoinBase() || tx.IsCoinStake() || !IsFinalTx(tx, pindexPrev->nHeight + 1))
continue;
COrphan* porphan = NULL;
@@ -12,10 +12,10 @@
QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
{
AssertLockHeld(cs_main);
if (!wtx.IsFinal())
if (!IsFinalTx(wtx, nBestHeight + 1))
{
if (wtx.nLockTime < LOCKTIME_THRESHOLD)
return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1);
return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight);
else
return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime));
}
@@ -175,12 +175,12 @@ 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)
{
status.status = TransactionStatus::OpenUntilBlock;
status.open_for = wtx.nLockTime - nBestHeight + 1;
status.open_for = wtx.nLockTime - nBestHeight;
}
else
{
View
@@ -502,7 +502,7 @@ Value getreceivedbyaddress(CWallet* pWallet, const Array& params, bool fHelp)
for (map<uint256, CWalletTx>::iterator it = pWallet->mapWallet.begin(); it != pWallet->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
@@ -548,7 +548,7 @@ Value getreceivedbyaccount(CWallet* pWallet, const Array& params, bool fHelp)
for (map<uint256, CWalletTx>::iterator it = pWallet->mapWallet.begin(); it != pWallet->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
@@ -572,7 +572,7 @@ int64 GetAccountBalance(CWallet* pWallet, CWalletDB& walletdb, const string& str
for (map<uint256, CWalletTx>::iterator it = pWallet->mapWallet.begin(); it != pWallet->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
if (!wtx.IsFinal() || wtx.GetDepthInMainChain() < 0)
if (!IsFinalTx(wtx) || wtx.GetDepthInMainChain() < 0)
continue;
int64 nReceived, nSent, nFee;
@@ -950,7 +950,7 @@ Value ListReceived(CWallet* pWallet,const Array& params, bool fByAccounts)
{
const CWalletTx& wtx = (*it).second;
if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
if (wtx.IsCoinBase() || wtx.IsCoinStake() || !IsFinalTx(wtx))
continue;
int nDepth = wtx.GetDepthInMainChain();
@@ -1749,6 +1749,7 @@ Value reservebalance(CWallet* pWallet, const Array& params, bool fHelp)
if (params.size() > 0)
{
bool fReserve = params[0].get_bool();
LOCK(pWallet->cs_wallet);
if (fReserve)
{
if (params.size() == 1)
View
@@ -525,7 +525,7 @@ class CScript : public std::vector<unsigned char>
bool IsPayToScriptHash() const;
// Called by CTransaction::IsStandard and P2SH VerifyScript (which makes it consensus-critical).
// Called by IsStandardTx and P2SH VerifyScript (which makes it consensus-critical).
bool IsPushOnly() const
{
const_iterator pc = begin();
@@ -540,7 +540,7 @@ class CScript : public std::vector<unsigned char>
return true;
}
// Called by CTransaction::IsStandard.
// Called by IsStandardTx.
bool HasCanonicalPushes() const;
Oops, something went wrong.

0 comments on commit ecc0857

Please sign in to comment.