@ -275,26 +275,22 @@ bool CTransaction::IsStandard() const
// expensive-to-check-upon-redemption script like:
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
//
bool CTransaction : : AreInputsStandard ( const std : : map < uint256 , std : : pair < CTxIndex , CTransaction > > & mapInputs ) const
bool CTransaction : : AreInputsStandard ( const MapPrevTx & mapInputs ) const
{
{
if ( fTestNet )
if ( fTestNet )
return true ; // Allow non-standard on testnet
return true ; // Allow non-standard on testnet
if ( IsCoinBase ( ) )
return true ; // Coinbases are allowed to have any input
for ( int i = 0 ; i < vin . size ( ) ; i + + )
for ( int i = 0 ; i < vin . size ( ) ; i + + )
{
{
COutPoint prevout = vin [ i ] . prevout ;
const CTxOut & prev = GetOutputFor ( vin [ i ] , mapInputs ) ;
std : : map < uint256 , std : : pair < CTxIndex , CTransaction > > : : const_iterator mi = mapInputs . find ( prevout . hash ) ;
if ( mi = = mapInputs . end ( ) )
return false ;
const CTransaction & txPrev = ( mi - > second ) . second ;
assert ( prevout . n < txPrev . vout . size ( ) ) ;
vector < vector < unsigned char > > vSolutions ;
vector < vector < unsigned char > > vSolutions ;
txnouttype whichType ;
txnouttype whichType ;
// get the scriptPubKey corresponding to this input:
// get the scriptPubKey corresponding to this input:
const CScript & prevScript = txPrev . vout [ prevout . n ] . scriptPubKey ;
const CScript & prevScript = prev . scriptPubKey ;
if ( ! Solver ( prevScript , whichType , vSolutions ) )
if ( ! Solver ( prevScript , whichType , vSolutions ) )
return false ;
return false ;
if ( whichType = = TX_SCRIPTHASH )
if ( whichType = = TX_SCRIPTHASH )
@ -494,7 +490,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if ( fCheckInputs )
if ( fCheckInputs )
{
{
map < uint256 , pair < CTxIndex , CTransaction > > mapInputs ;
MapPrevTx mapInputs ;
map < uint256 , CTxIndex > mapUnused ;
map < uint256 , CTxIndex > mapUnused ;
if ( ! FetchInputs ( txdb , mapUnused , false , false , mapInputs ) )
if ( ! FetchInputs ( txdb , mapUnused , false , false , mapInputs ) )
{
{
@ -507,27 +503,20 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if ( ! AreInputsStandard ( mapInputs ) )
if ( ! AreInputsStandard ( mapInputs ) )
return error ( " AcceptToMemoryPool() : nonstandard transaction input " ) ;
return error ( " AcceptToMemoryPool() : nonstandard transaction input " ) ;
// Check against previous transactions
int64 nFees = GetValueIn ( mapInputs ) - GetValueOut ( ) ;
int64 nFees = 0 ;
int nSigOps = GetSigOpCount ( mapInputs ) ;
int nSigOps = 0 ;
unsigned int nSize = : : GetSerializeSize ( * this , SER_NETWORK ) ;
if ( ! ConnectInputs ( mapInputs , mapUnused , CDiskTxPos ( 1 , 1 , 1 ) , pindexBest , nFees , false , false , nSigOps ) )
{
// Don't accept it if it can't get into a block
if ( pfMissingInputs )
if ( nFees < GetMinFee ( 1000 , true , GMF_RELAY ) )
* pfMissingInputs = true ;
return error ( " AcceptToMemoryPool() : not enough fees " ) ;
return error ( " AcceptToMemoryPool() : ConnectInputs failed % s " , hash.ToString().substr(0,10).c_str()) ;
}
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// attacks disallow transactions with more than one SigOp per 65 bytes.
// attacks disallow transactions with more than one SigOp per 65 bytes.
// 65 bytes because that is the minimum size of an ECDSA signature
// 65 bytes because that is the minimum size of an ECDSA signature
unsigned int nSize = : : GetSerializeSize ( * this , SER_NETWORK ) ;
if ( nSigOps > nSize / 65 | | nSize < 100 )
if ( nSigOps > nSize / 65 | | nSize < 100 )
return error ( " AcceptToMemoryPool() : transaction with out - of - bounds SigOpCount " ) ;
return error ( " AcceptToMemoryPool() : transaction with out - of - bounds SigOpCount " ) ;
// Don't accept it if it can't get into a block
if ( nFees < GetMinFee ( 1000 , true , GMF_RELAY ) )
return error ( " AcceptToMemoryPool() : not enough fees " ) ;
// Continuously rate-limit free transactions
// Continuously rate-limit free transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make other's transactions take longer to confirm.
// be annoying or make other's transactions take longer to confirm.
@ -552,6 +541,15 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
dFreeCount + = nSize ;
dFreeCount + = nSize ;
}
}
}
}
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if ( ! ConnectInputs ( mapInputs , mapUnused , CDiskTxPos ( 1 , 1 , 1 ) , pindexBest , false , false ) )
{
if ( pfMissingInputs )
* pfMissingInputs = true ;
return error ( " AcceptToMemoryPool() : ConnectInputs failed % s " , hash.ToString().substr(0,10).c_str()) ;
}
}
}
// Store transaction in memory
// Store transaction in memory
@ -925,7 +923,7 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
bool CTransaction : : FetchInputs ( CTxDB & txdb , const map < uint256 , CTxIndex > & mapTestPool ,
bool CTransaction : : FetchInputs ( CTxDB & txdb , const map < uint256 , CTxIndex > & mapTestPool ,
bool fBlock , bool fMiner , map < uint256 , pair < CTxIndex , CTransaction > > & inputsRet )
bool fBlock , bool fMiner , MapPrevTx & inputsRet )
{
{
if ( IsCoinBase ( ) )
if ( IsCoinBase ( ) )
return true ; // Coinbase transactions have no inputs to fetch.
return true ; // Coinbase transactions have no inputs to fetch.
@ -978,6 +976,7 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTes
for ( int i = 0 ; i < vin . size ( ) ; i + + )
for ( int i = 0 ; i < vin . size ( ) ; i + + )
{
{
const COutPoint prevout = vin [ i ] . prevout ;
const COutPoint prevout = vin [ i ] . prevout ;
assert ( inputsRet . count ( prevout . hash ) ! = 0 ) ;
const CTxIndex & txindex = inputsRet [ prevout . hash ] . first ;
const CTxIndex & txindex = inputsRet [ prevout . hash ] . first ;
const CTransaction & txPrev = inputsRet [ prevout . hash ] . second ;
const CTransaction & txPrev = inputsRet [ prevout . hash ] . second ;
if ( prevout . n > = txPrev . vout . size ( ) | | prevout . n > = txindex . vSpent . size ( ) )
if ( prevout . n > = txPrev . vout . size ( ) | | prevout . n > = txindex . vSpent . size ( ) )
@ -987,9 +986,49 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTes
return true ;
return true ;
}
}
bool CTransaction : : ConnectInputs ( map < uint256 , pair < CTxIndex , CTransaction > > inputs ,
const CTxOut & CTransaction : : GetOutputFor ( const CTxIn & input , const MapPrevTx & inputs ) const
{
MapPrevTx : : const_iterator mi = inputs . find ( input . prevout . hash ) ;
if ( mi = = inputs . end ( ) )
throw std : : runtime_error ( " CTransaction::GetOutputFor() : prevout.hash not found " ) ;
const CTransaction & txPrev = ( mi - > second ) . second ;
if ( input . prevout . n > = txPrev . vout . size ( ) )
throw std : : runtime_error ( " CTransaction::GetOutputFor() : prevout.n out of range " ) ;
return txPrev . vout [ input . prevout . n ] ;
}
int64 CTransaction : : GetValueIn ( const MapPrevTx & inputs ) const
{
if ( IsCoinBase ( ) )
return 0 ;
int64 nResult = 0 ;
for ( int i = 0 ; i < vin . size ( ) ; i + + )
{
nResult + = GetOutputFor ( vin [ i ] , inputs ) . nValue ;
}
return nResult ;
}
int CTransaction : : GetSigOpCount ( const MapPrevTx & inputs ) const
{
if ( IsCoinBase ( ) )
return 0 ;
int nSigOps = 0 ;
for ( int i = 0 ; i < vin . size ( ) ; i + + )
{
nSigOps + = GetOutputFor ( vin [ i ] , inputs ) . scriptPubKey . GetSigOpCount ( vin [ i ] . scriptSig ) ;
}
return nSigOps ;
}
bool CTransaction : : ConnectInputs ( MapPrevTx inputs ,
map < uint256 , CTxIndex > & mapTestPool , const CDiskTxPos & posThisTx ,
map < uint256 , CTxIndex > & mapTestPool , const CDiskTxPos & posThisTx ,
const CBlockIndex * pindexBlock , int64 & nFees , bool fBlock , bool fMiner , int & nSigOpsRet , int64 nMinFee )
const CBlockIndex * pindexBlock , bool fBlock , bool fMiner )
{
{
// Take over previous transactions' spent pointers
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
@ -998,6 +1037,7 @@ bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inp
if ( ! IsCoinBase ( ) )
if ( ! IsCoinBase ( ) )
{
{
int64 nValueIn = 0 ;
int64 nValueIn = 0 ;
int64 nFees = 0 ;
for ( int i = 0 ; i < vin . size ( ) ; i + + )
for ( int i = 0 ; i < vin . size ( ) ; i + + )
{
{
COutPoint prevout = vin [ i ] . prevout ;
COutPoint prevout = vin [ i ] . prevout ;
@ -1014,6 +1054,17 @@ bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inp
if ( pindex - > nBlockPos = = txindex . pos . nBlockPos & & pindex - > nFile = = txindex . pos . nFile )
if ( pindex - > nBlockPos = = txindex . pos . nBlockPos & & pindex - > nFile = = txindex . pos . nFile )
return error ( " ConnectInputs() : tried to spend coinbase at depth % d " , pindexBlock->nHeight - pindex->nHeight) ;
return error ( " ConnectInputs() : tried to spend coinbase at depth % d " , pindexBlock->nHeight - pindex->nHeight) ;
// Check for conflicts (double-spend)
// 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 ( ! txindex . vSpent [ prevout . n ] . IsNull ( ) )
return fMiner ? false : error ( " ConnectInputs() : %s prev tx already used at %s " , GetHash ( ) . ToString ( ) . substr ( 0 , 10 ) . c_str ( ) , txindex . vSpent [ prevout . n ] . ToString ( ) . c_str ( ) ) ;
// Check for negative or overflow input values
nValueIn + = txPrev . vout [ prevout . n ] . nValue ;
if ( ! MoneyRange ( txPrev . vout [ prevout . n ] . nValue ) | | ! MoneyRange ( nValueIn ) )
return DoS ( 100 , error ( " ConnectInputs() : txin values out of range " )) ;
bool fStrictPayToScriptHash = true ;
bool fStrictPayToScriptHash = true ;
if ( fBlock )
if ( fBlock )
{
{
@ -1038,20 +1089,6 @@ bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inp
return DoS ( 100 , error ( " ConnectInputs() : % s VerifySignature failed " , GetHash().ToString().substr(0,10).c_str())) ;
return DoS ( 100 , error ( " ConnectInputs() : % s VerifySignature failed " , GetHash().ToString().substr(0,10).c_str())) ;
}
}
// Check for conflicts (double-spend)
// 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 ( ! txindex . vSpent [ prevout . n ] . IsNull ( ) )
return fMiner ? false : error ( " ConnectInputs() : %s prev tx already used at %s " , GetHash ( ) . ToString ( ) . substr ( 0 , 10 ) . c_str ( ) , txindex . vSpent [ prevout . n ] . ToString ( ) . c_str ( ) ) ;
// Check for negative or overflow input values
nValueIn + = txPrev . vout [ prevout . n ] . nValue ;
if ( ! MoneyRange ( txPrev . vout [ prevout . n ] . nValue ) | | ! MoneyRange ( nValueIn ) )
return DoS ( 100 , error ( " ConnectInputs() : txin values out of range " )) ;
// Calculate sigOps accurately:
nSigOpsRet + = txPrev . vout [ prevout . n ] . scriptPubKey . GetSigOpCount ( vin [ i ] . scriptSig ) ;
// Mark outpoints as spent
// Mark outpoints as spent
txindex . vSpent [ prevout . n ] = posThisTx ;
txindex . vSpent [ prevout . n ] = posThisTx ;
@ -1069,8 +1106,6 @@ bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inp
int64 nTxFee = nValueIn - GetValueOut ( ) ;
int64 nTxFee = nValueIn - GetValueOut ( ) ;
if ( nTxFee < 0 )
if ( nTxFee < 0 )
return DoS ( 100 , error ( " ConnectInputs() : % s nTxFee < 0 " , GetHash().ToString().substr(0,10).c_str())) ;
return DoS ( 100 , error ( " ConnectInputs() : % s nTxFee < 0 " , GetHash().ToString().substr(0,10).c_str())) ;
if ( nTxFee < nMinFee )
return false ;
nFees + = nTxFee ;
nFees + = nTxFee ;
if ( ! MoneyRange ( nFees ) )
if ( ! MoneyRange ( nFees ) )
return DoS ( 100 , error ( " ConnectInputs() : nFees out of range " )) ;
return DoS ( 100 , error ( " ConnectInputs() : nFees out of range " )) ;
@ -1176,20 +1211,27 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
CDiskTxPos posThisTx ( pindex - > nFile , pindex - > nBlockPos , nTxPos ) ;
CDiskTxPos posThisTx ( pindex - > nFile , pindex - > nBlockPos , nTxPos ) ;
nTxPos + = : : GetSerializeSize ( tx , SER_DISK ) ;
nTxPos + = : : GetSerializeSize ( tx , SER_DISK ) ;
map < uint256 , pair < CTxIndex , CTransaction > > mapInputs ;
MapPrevTx mapInputs ;
if ( ! tx . IsCoinBase ( ) )
{
if ( ! tx . FetchInputs ( txdb , mapQueuedChanges , true , false , mapInputs ) )
if ( ! tx . FetchInputs ( txdb , mapQueuedChanges , true , false , mapInputs ) )
return false ;
return false ;
int nTxOps = 0 ;
int nTxOps = tx . GetSigOpCount ( mapInputs ) ;
if ( ! tx . ConnectInputs ( mapInputs , mapQueuedChanges , posThisTx , pindex , nFees , true , false , nTxOps ) )
return false ;
nSigOps + = nTxOps ;
nSigOps + = nTxOps ;
if ( nSigOps > MAX_BLOCK_SIGOPS )
if ( nSigOps > MAX_BLOCK_SIGOPS )
return DoS ( 100 , error ( " ConnectBlock() : too many sigops " )) ;
return DoS ( 100 , error ( " ConnectBlock() : too many sigops " )) ;
// There is a different MAX_BLOCK_SIGOPS check in AcceptBlock();
// There is a different MAX_BLOCK_SIGOPS check in AcceptBlock();
// a block must satisfy both to make it into the best-chain
// a block must satisfy both to make it into the best-chain
// (AcceptBlock() is always called before ConnectBlock())
// (AcceptBlock() is always called before ConnectBlock())
nFees + = tx . GetValueIn ( mapInputs ) - tx . GetValueOut ( ) ;
}
// It seems wrong that ConnectInputs must be called on the coinbase transaction
// (which has no inputs) : TODO: refactor the code at the end of ConnectInputs out...
if ( ! tx . ConnectInputs ( mapInputs , mapQueuedChanges , posThisTx , pindex , true , false ) )
return false ;
}
}
// Write queued txindex changes
// Write queued txindex changes
@ -3031,15 +3073,20 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
// Connecting shouldn't fail due to dependency on other memory pool transactions
// Connecting shouldn't fail due to dependency on other memory pool transactions
// because we're already processing them in order of dependency
// because we're already processing them in order of dependency
map < uint256 , CTxIndex > mapTestPoolTmp ( mapTestPool ) ;
map < uint256 , CTxIndex > mapTestPoolTmp ( mapTestPool ) ;
map < uint256 , pair < CTxIndex , CTransaction > > mapInputs ;
MapPrevTx mapInputs ;
if ( ! tx . FetchInputs ( txdb , mapTestPoolTmp , false , true , mapInputs ) )
if ( ! tx . FetchInputs ( txdb , mapTestPoolTmp , false , true , mapInputs ) )
continue ;
continue ;
int nTxSigOps2 = 0 ;
int64 nFees = tx . GetValueIn ( mapInputs ) - tx . GetValueOut ( ) ;
if ( ! tx . ConnectInputs ( mapInputs , mapTestPoolTmp , CDiskTxPos ( 1 , 1 , 1 ) , pindexPrev , nFees , false , true , nTxSigOps2 , nMinFee ) )
if ( nFees < nMinFee )
continue ;
continue ;
int nTxSigOps2 = tx . GetSigOpCount ( mapInputs ) ;
if ( nBlockSigOps2 + nTxSigOps2 > = MAX_BLOCK_SIGOPS )
if ( nBlockSigOps2 + nTxSigOps2 > = MAX_BLOCK_SIGOPS )
continue ;
continue ;
if ( ! tx . ConnectInputs ( mapInputs , mapTestPoolTmp , CDiskTxPos ( 1 , 1 , 1 ) , pindexPrev , false , true ) )
continue ;
swap ( mapTestPool , mapTestPoolTmp ) ;
swap ( mapTestPool , mapTestPoolTmp ) ;
// Added
// Added