@ -696,30 +696,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
{
{
// Basic checks that don't depend on any context
// Basic checks that don't depend on any context
if ( tx . vin . empty ( ) )
if ( tx . vin . empty ( ) )
return state . DoS ( 10 , error ( " CheckTransaction(): vin empty " ) ,
return state . DoS ( 10 , false , REJECT_INVALID , " bad-txns-vin-empty " ) ;
REJECT_INVALID , " bad-txns-vin-empty " ) ;
if ( tx . vout . empty ( ) )
if ( tx . vout . empty ( ) )
return state . DoS ( 10 , error ( " CheckTransaction(): vout empty " ) ,
return state . DoS ( 10 , false , REJECT_INVALID , " bad-txns-vout-empty " ) ;
REJECT_INVALID , " bad-txns-vout-empty " ) ;
// Size limits
// Size limits
if ( : : GetSerializeSize ( tx , SER_NETWORK , PROTOCOL_VERSION ) > MAX_BLOCK_SIZE )
if ( : : GetSerializeSize ( tx , SER_NETWORK , PROTOCOL_VERSION ) > MAX_BLOCK_SIZE )
return state . DoS ( 100 , error ( " CheckTransaction(): size limits failed " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-oversize " ) ;
REJECT_INVALID , " bad-txns-oversize " ) ;
// Check for negative or overflow output values
// Check for negative or overflow output values
CAmount nValueOut = 0 ;
CAmount nValueOut = 0 ;
BOOST_FOREACH ( const CTxOut & txout , tx . vout )
BOOST_FOREACH ( const CTxOut & txout , tx . vout )
{
{
if ( txout . nValue < 0 )
if ( txout . nValue < 0 )
return state . DoS ( 100 , error ( " CheckTransaction(): txout.nValue negative " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-vout-negative " ) ;
REJECT_INVALID , " bad-txns-vout-negative " ) ;
if ( txout . nValue > MAX_MONEY )
if ( txout . nValue > MAX_MONEY )
return state . DoS ( 100 , error ( " CheckTransaction(): txout.nValue too high " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-vout-toolarge " ) ;
REJECT_INVALID , " bad-txns-vout-toolarge " ) ;
nValueOut + = txout . nValue ;
nValueOut + = txout . nValue ;
if ( ! MoneyRange ( nValueOut ) )
if ( ! MoneyRange ( nValueOut ) )
return state . DoS ( 100 , error ( " CheckTransaction(): txout total out of range " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-txouttotal-toolarge " ) ;
REJECT_INVALID , " bad-txns-txouttotal-toolarge " ) ;
}
}
// Check for duplicate inputs
// Check for duplicate inputs
@ -727,23 +721,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
{
{
if ( vInOutPoints . count ( txin . prevout ) )
if ( vInOutPoints . count ( txin . prevout ) )
return state . DoS ( 100 , error ( " CheckTransaction(): duplicate inputs " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-inputs-duplicate " ) ;
REJECT_INVALID , " bad-txns-inputs-duplicate " ) ;
vInOutPoints . insert ( txin . prevout ) ;
vInOutPoints . insert ( txin . prevout ) ;
}
}
if ( tx . IsCoinBase ( ) )
if ( tx . IsCoinBase ( ) )
{
{
if ( tx . vin [ 0 ] . scriptSig . size ( ) < 2 | | tx . vin [ 0 ] . scriptSig . size ( ) > 100 )
if ( tx . vin [ 0 ] . scriptSig . size ( ) < 2 | | tx . vin [ 0 ] . scriptSig . size ( ) > 100 )
return state . DoS ( 100 , error ( " CheckTransaction(): coinbase script size " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-cb-length " ) ;
REJECT_INVALID , " bad-cb-length " ) ;
}
}
else
else
{
{
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
if ( txin . prevout . IsNull ( ) )
if ( txin . prevout . IsNull ( ) )
return state . DoS ( 10 , error ( " CheckTransaction(): prevout is null " ) ,
return state . DoS ( 10 , false , REJECT_INVALID , " bad-txns-prevout-null " ) ;
REJECT_INVALID , " bad-txns-prevout-null " ) ;
}
}
return true ;
return true ;
@ -795,26 +786,22 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
* pfMissingInputs = false ;
* pfMissingInputs = false ;
if ( ! CheckTransaction ( tx , state ) )
if ( ! CheckTransaction ( tx , state ) )
return error ( " AcceptToMemoryPool: CheckTransaction failed " ) ;
return false ;
// Coinbase is only valid in a block, not as a loose transaction
// Coinbase is only valid in a block, not as a loose transaction
if ( tx . IsCoinBase ( ) )
if ( tx . IsCoinBase ( ) )
return state . DoS ( 100 , error ( " AcceptToMemoryPool: coinbase as individual tx " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " coinbase " ) ;
REJECT_INVALID , " coinbase " ) ;
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason ;
string reason ;
if ( fRequireStandard & & ! IsStandardTx ( tx , reason ) )
if ( fRequireStandard & & ! IsStandardTx ( tx , reason ) )
return state . DoS ( 0 ,
return state . DoS ( 0 , false , REJECT_NONSTANDARD , reason ) ;
error ( " AcceptToMemoryPool: nonstandard transaction: %s " , reason ) ,
REJECT_NONSTANDARD , reason ) ;
// Only accept nLockTime-using transactions that can be mined in the next
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
// be mined yet.
if ( ! CheckFinalTx ( tx ) )
if ( ! CheckFinalTx ( tx ) )
return state . DoS ( 0 , error ( " AcceptToMemoryPool: non-final " ) ,
return state . DoS ( 0 , false , REJECT_NONSTANDARD , " non-final " ) ;
REJECT_NONSTANDARD , " non-final " ) ;
// is it already in the memory pool?
// is it already in the memory pool?
uint256 hash = tx . GetHash ( ) ;
uint256 hash = tx . GetHash ( ) ;
@ -862,8 +849,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// are the actual inputs available?
// are the actual inputs available?
if ( ! view . HaveInputs ( tx ) )
if ( ! view . HaveInputs ( tx ) )
return state . Invalid ( error ( " AcceptToMemoryPool: inputs already spent " ) ,
return state . Invalid ( false , REJECT_DUPLICATE , " bad-txns-inputs-spent " ) ;
REJECT_DUPLICATE , " bad-txns-inputs-spent " ) ;
// Bring the best block into scope
// Bring the best block into scope
view . GetBestBlock ( ) ;
view . GetBestBlock ( ) ;
@ -886,10 +872,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
unsigned int nSigOps = GetLegacySigOpCount ( tx ) ;
unsigned int nSigOps = GetLegacySigOpCount ( tx ) ;
nSigOps + = GetP2SHSigOpCount ( tx , view ) ;
nSigOps + = GetP2SHSigOpCount ( tx , view ) ;
if ( nSigOps > MAX_STANDARD_TX_SIGOPS )
if ( nSigOps > MAX_STANDARD_TX_SIGOPS )
return state . DoS ( 0 ,
return state . DoS ( 0 , false , REJECT_NONSTANDARD , " bad-txns-too-many-sigops " , false ,
error ( " AcceptToMemoryPool: too many sigops %s, %d > %d " ,
strprintf ( " %d > %d " , nSigOps , MAX_STANDARD_TX_SIGOPS ) ) ;
hash . ToString ( ) , nSigOps , MAX_STANDARD_TX_SIGOPS ) ,
REJECT_NONSTANDARD , " bad-txns-too-many-sigops " ) ;
CAmount nValueOut = tx . GetValueOut ( ) ;
CAmount nValueOut = tx . GetValueOut ( ) ;
CAmount nFees = nValueIn - nValueOut ;
CAmount nFees = nValueIn - nValueOut ;
@ -901,9 +885,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Don't accept it if it can't get into a block
// Don't accept it if it can't get into a block
CAmount txMinFee = GetMinRelayFee ( tx , nSize , true ) ;
CAmount txMinFee = GetMinRelayFee ( tx , nSize , true ) ;
if ( fLimitFree & & nFees < txMinFee )
if ( fLimitFree & & nFees < txMinFee )
return state . DoS ( 0 , error ( " AcceptToMemoryPool: not enough fees %s, %d < %d " ,
return state . DoS ( 0 , false , REJECT_INSUFFICIENTFEE , " insufficient fee " , false ,
hash . ToString ( ) , nFees , txMinFee ) ,
strprintf ( " %d < %d " , nFees , txMinFee ) ) ;
REJECT_INSUFFICIENTFEE , " insufficient fee " ) ;
// Require that free transactions have sufficient priority to be mined in the next block.
// Require that free transactions have sufficient priority to be mined in the next block.
if ( GetBoolArg ( " -relaypriority " , true ) & & nFees < : : minRelayTxFee . GetFee ( nSize ) & & ! AllowFree ( view . GetPriority ( tx , chainActive . Height ( ) + 1 ) ) ) {
if ( GetBoolArg ( " -relaypriority " , true ) & & nFees < : : minRelayTxFee . GetFee ( nSize ) & & ! AllowFree ( view . GetPriority ( tx , chainActive . Height ( ) + 1 ) ) ) {
@ -928,24 +911,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// -limitfreerelay unit is thousand-bytes-per-minute
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
// At default rate it would take over a month to fill 1GB
if ( dFreeCount > = GetArg ( " -limitfreerelay " , 15 ) * 10 * 1000 )
if ( dFreeCount > = GetArg ( " -limitfreerelay " , 15 ) * 10 * 1000 )
return state . DoS ( 0 , error ( " AcceptToMemoryPool: free transaction rejected by rate limiter " ) ,
return state . DoS ( 0 , false , REJECT_INSUFFICIENTFEE , " rate limited free transaction " ) ;
REJECT_INSUFFICIENTFEE , " rate limited free transaction " ) ;
LogPrint ( " mempool " , " Rate limit dFreeCount: %g => %g \n " , dFreeCount , dFreeCount + nSize ) ;
LogPrint ( " mempool " , " Rate limit dFreeCount: %g => %g \n " , dFreeCount , dFreeCount + nSize ) ;
dFreeCount + = nSize ;
dFreeCount + = nSize ;
}
}
if ( fRejectAbsurdFee & & nFees > : : minRelayTxFee . GetFee ( nSize ) * 10000 )
if ( fRejectAbsurdFee & & nFees > : : minRelayTxFee . GetFee ( nSize ) * 10000 )
return state . Invalid ( error ( " AcceptToMemoryPool: absurdly high fees %s, %d > %d " ,
return state . Invalid ( false ,
hash . ToString ( ) ,
REJECT_HIGHFEE , " absurdly-high-fee " ,
nFees , : : minRelayTxFee . GetFee ( nSize ) * 10000 ) ,
strprintf ( " %d > %d " , nFees , : : minRelayTxFee . GetFee ( nSize ) * 10000 ) ) ;
REJECT_HIGHFEE , " absurdly-high-fee " ) ;
// Check against previous transactions
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if ( ! CheckInputs ( tx , state , view , true , STANDARD_SCRIPT_VERIFY_FLAGS , true ) )
if ( ! CheckInputs ( tx , state , view , true , STANDARD_SCRIPT_VERIFY_FLAGS , true ) )
{
return false ;
return error ( " AcceptToMemoryPool: ConnectInputs failed %s " , hash . ToString ( ) ) ;
}
// Check again against just the consensus-critical mandatory script
// Check again against just the consensus-critical mandatory script
// verification flags, in case of bugs in the standard flags that cause
// verification flags, in case of bugs in the standard flags that cause
@ -958,7 +937,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// can be exploited as a DoS attack.
// can be exploited as a DoS attack.
if ( ! CheckInputs ( tx , state , view , true , MANDATORY_SCRIPT_VERIFY_FLAGS , true ) )
if ( ! CheckInputs ( tx , state , view , true , MANDATORY_SCRIPT_VERIFY_FLAGS , true ) )
{
{
return error ( " AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s " , hash . ToString ( ) ) ;
return error ( " %s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s " ,
__func__ , hash . ToString ( ) , FormatStateMessage ( state ) ) ;
}
}
// Store transaction in memory
// Store transaction in memory
@ -1298,7 +1278,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
bool CScriptCheck : : operator ( ) ( ) {
bool CScriptCheck : : operator ( ) ( ) {
const CScript & scriptSig = ptxTo - > vin [ nIn ] . scriptSig ;
const CScript & scriptSig = ptxTo - > vin [ nIn ] . scriptSig ;
if ( ! VerifyScript ( scriptSig , scriptPubKey , nFlags , CachingTransactionSignatureChecker ( ptxTo , nIn , cacheStore ) , & error ) ) {
if ( ! VerifyScript ( scriptSig , scriptPubKey , nFlags , CachingTransactionSignatureChecker ( ptxTo , nIn , cacheStore ) , & error ) ) {
return : : error ( " CScriptCheck(): %s:%d VerifySignature failed: %s " , ptxTo - > GetHash ( ) . ToString ( ) , nIn , ScriptErrorString ( error ) ) ;
return false ;
}
}
return true ;
return true ;
}
}
@ -1316,7 +1296,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// 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.
// for an attacker to attempt to split the network.
if ( ! inputs . HaveInputs ( tx ) )
if ( ! inputs . HaveInputs ( tx ) )
return state . Invalid ( error ( " CheckInputs(): %s inputs unavailable " , tx . GetHash ( ) . ToString ( ) ) ) ;
return state . Invalid ( false , 0 , " " , " Inputs unavailable " ) ;
CAmount nValueIn = 0 ;
CAmount nValueIn = 0 ;
CAmount nFees = 0 ;
CAmount nFees = 0 ;
@ -1329,33 +1309,29 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
// If prev is coinbase, check that it's matured
// If prev is coinbase, check that it's matured
if ( coins - > IsCoinBase ( ) ) {
if ( coins - > IsCoinBase ( ) ) {
if ( nSpendHeight - coins - > nHeight < COINBASE_MATURITY )
if ( nSpendHeight - coins - > nHeight < COINBASE_MATURITY )
return state . Invalid (
return state . Invalid ( false ,
error ( " CheckInputs(): tried to spend coinbase at depth %d " , nSpendHeight - coins - > nHeight ) ,
REJECT_INVALID , " bad-txns-premature-spend-of-coinbase " ,
REJECT_INVALID , " bad-txns-premature-spend-of-coinbase " ) ;
strprintf ( " tried to spend coinbase at depth %d " , nSpendHeight - coins - > nHeight ) ) ;
}
}
// Check for negative or overflow input values
// Check for negative or overflow input values
nValueIn + = coins - > vout [ prevout . n ] . nValue ;
nValueIn + = coins - > vout [ prevout . n ] . nValue ;
if ( ! MoneyRange ( coins - > vout [ prevout . n ] . nValue ) | | ! MoneyRange ( nValueIn ) )
if ( ! MoneyRange ( coins - > vout [ prevout . n ] . nValue ) | | ! MoneyRange ( nValueIn ) )
return state . DoS ( 100 , error ( " CheckInputs(): txin values out of range " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-inputvalues-outofrange " ) ;
REJECT_INVALID , " bad-txns-inputvalues-outofrange " ) ;
}
}
if ( nValueIn < tx . GetValueOut ( ) )
if ( nValueIn < tx . GetValueOut ( ) )
return state . DoS ( 100 , error ( " CheckInputs(): %s value in (%s) < value out (%s) " ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-in-belowout " , false ,
tx . GetHash ( ) . ToString ( ) , FormatMoney ( nValueIn ) , FormatMoney ( tx . GetValueOut ( ) ) ) ,
strprintf ( " value in (%s) < value out (%s) " , FormatMoney ( nValueIn ) , FormatMoney ( tx . GetValueOut ( ) ) ) ) ;
REJECT_INVALID , " bad-txns-in-belowout " ) ;
// Tally transaction fees
// Tally transaction fees
CAmount nTxFee = nValueIn - tx . GetValueOut ( ) ;
CAmount nTxFee = nValueIn - tx . GetValueOut ( ) ;
if ( nTxFee < 0 )
if ( nTxFee < 0 )
return state . DoS ( 100 , error ( " CheckInputs(): %s nTxFee < 0 " , tx . GetHash ( ) . ToString ( ) ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-fee-negative " ) ;
REJECT_INVALID , " bad-txns-fee-negative " ) ;
nFees + = nTxFee ;
nFees + = nTxFee ;
if ( ! MoneyRange ( nFees ) )
if ( ! MoneyRange ( nFees ) )
return state . DoS ( 100 , error ( " CheckInputs(): nFees out of range " ) ,
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-fee-outofrange " ) ;
REJECT_INVALID , " bad-txns-fee-outofrange " ) ;
return true ;
return true ;
}
}
} // namespace Consensus
} // namespace Consensus