@ -696,30 +696,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
@@ -696,30 +696,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
{
// Basic checks that don't depend on any context
if ( tx . vin . empty ( ) )
return state . DoS ( 10 , error ( " CheckTransaction(): vin empty " ) ,
REJECT_INVALID , " bad-txns-vin-empty " ) ;
return state . DoS ( 10 , false , REJECT_INVALID , " bad-txns-vin-empty " ) ;
if ( tx . vout . empty ( ) )
return state . DoS ( 10 , error ( " CheckTransaction(): vout empty " ) ,
REJECT_INVALID , " bad-txns-vout-empty " ) ;
return state . DoS ( 10 , false , REJECT_INVALID , " bad-txns-vout-empty " ) ;
// Size limits
if ( : : GetSerializeSize ( tx , SER_NETWORK , PROTOCOL_VERSION ) > MAX_BLOCK_SIZE )
return state . DoS ( 100 , error ( " CheckTransaction(): size limits failed " ) ,
REJECT_INVALID , " bad-txns-oversize " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-oversize " ) ;
// Check for negative or overflow output values
CAmount nValueOut = 0 ;
BOOST_FOREACH ( const CTxOut & txout , tx . vout )
{
if ( txout . nValue < 0 )
return state . DoS ( 100 , error ( " CheckTransaction(): txout.nValue negative " ) ,
REJECT_INVALID , " bad-txns-vout-negative " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-vout-negative " ) ;
if ( txout . nValue > MAX_MONEY )
return state . DoS ( 100 , error ( " CheckTransaction(): txout.nValue too high " ) ,
REJECT_INVALID , " bad-txns-vout-toolarge " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-vout-toolarge " ) ;
nValueOut + = txout . nValue ;
if ( ! MoneyRange ( nValueOut ) )
return state . DoS ( 100 , error ( " CheckTransaction(): txout total out of range " ) ,
REJECT_INVALID , " bad-txns-txouttotal-toolarge " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-txouttotal-toolarge " ) ;
}
// Check for duplicate inputs
@ -727,23 +721,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
@@ -727,23 +721,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
{
if ( vInOutPoints . count ( txin . prevout ) )
return state . DoS ( 100 , error ( " CheckTransaction(): duplicate inputs " ) ,
REJECT_INVALID , " bad-txns-inputs-duplicate " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-inputs-duplicate " ) ;
vInOutPoints . insert ( txin . prevout ) ;
}
if ( tx . IsCoinBase ( ) )
{
if ( tx . vin [ 0 ] . scriptSig . size ( ) < 2 | | tx . vin [ 0 ] . scriptSig . size ( ) > 100 )
return state . DoS ( 100 , error ( " CheckTransaction(): coinbase script size " ) ,
REJECT_INVALID , " bad-cb-length " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-cb-length " ) ;
}
else
{
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
if ( txin . prevout . IsNull ( ) )
return state . DoS ( 10 , error ( " CheckTransaction(): prevout is null " ) ,
REJECT_INVALID , " bad-txns-prevout-null " ) ;
return state . DoS ( 10 , false , REJECT_INVALID , " bad-txns-prevout-null " ) ;
}
return true ;
@ -778,6 +769,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF
@@ -778,6 +769,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF
return nMinFee ;
}
/** Convert CValidationState to a human-readable message for logging */
static std : : string FormatStateMessage ( const CValidationState & state )
{
return strprintf ( " %s%s (code %i) " ,
state . GetRejectReason ( ) ,
state . GetDebugMessage ( ) . empty ( ) ? " " : " , " + state . GetDebugMessage ( ) ,
state . GetRejectCode ( ) ) ;
}
bool AcceptToMemoryPool ( CTxMemPool & pool , CValidationState & state , const CTransaction & tx , bool fLimitFree ,
bool * pfMissingInputs , bool fRejectAbsurdFee )
@ -787,31 +786,27 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -787,31 +786,27 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
* pfMissingInputs = false ;
if ( ! CheckTransaction ( tx , state ) )
return error ( " AcceptToMemoryPool: CheckTransaction failed " ) ;
return false ;
// Coinbase is only valid in a block, not as a loose transaction
if ( tx . IsCoinBase ( ) )
return state . DoS ( 100 , error ( " AcceptToMemoryPool: coinbase as individual tx " ) ,
REJECT_INVALID , " coinbase " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " coinbase " ) ;
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason ;
if ( fRequireStandard & & ! IsStandardTx ( tx , reason ) )
return state . DoS ( 0 ,
error ( " AcceptToMemoryPool: nonstandard transaction: %s " , reason ) ,
REJECT_NONSTANDARD , reason ) ;
return state . DoS ( 0 , false , REJECT_NONSTANDARD , reason ) ;
// 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
// be mined yet.
if ( ! CheckFinalTx ( tx ) )
return state . DoS ( 0 , error ( " AcceptToMemoryPool: non-final " ) ,
REJECT_NONSTANDARD , " non-final " ) ;
return state . DoS ( 0 , false , REJECT_NONSTANDARD , " non-final " ) ;
// is it already in the memory pool?
uint256 hash = tx . GetHash ( ) ;
if ( pool . exists ( hash ) )
return false ;
return state . Invalid ( false , REJECT_ALREADY_KNOWN , " txn-already-in-mempool " ) ;
// Check for conflicts with in-memory transactions
{
@ -822,7 +817,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -822,7 +817,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
if ( pool . mapNextTx . count ( outpoint ) )
{
// Disable replacement feature for now
return false ;
return state . Invalid ( false , REJECT_CONFLICT , " txn-mempool-conflict " ) ;
}
}
}
@ -839,7 +834,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -839,7 +834,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// do we already have it?
if ( view . HaveCoins ( hash ) )
return false ;
return state . Invalid ( false , REJECT_ALREADY_KNOWN , " txn-already-known " ) ;
// do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that),
@ -848,14 +843,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -848,14 +843,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
if ( ! view . HaveCoins ( txin . prevout . hash ) ) {
if ( pfMissingInputs )
* pfMissingInputs = true ;
return false ;
return false ; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
}
}
// are the actual inputs available?
if ( ! view . HaveInputs ( tx ) )
return state . Invalid ( error ( " AcceptToMemoryPool: inputs already spent " ) ,
REJECT_DUPLICATE , " bad-txns-inputs-spent " ) ;
return state . Invalid ( false , REJECT_DUPLICATE , " bad-txns-inputs-spent " ) ;
// Bring the best block into scope
view . GetBestBlock ( ) ;
@ -868,7 +862,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -868,7 +862,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Check for non-standard pay-to-script-hash in inputs
if ( fRequireStandard & & ! AreInputsStandard ( tx , view ) )
return error ( " AcceptToMemoryPool: nonstandard transaction input " ) ;
return state . Invalid ( false , REJECT_NONSTANDARD , " bad-txns-nonstandard-inputs " ) ;
// Check that the transaction doesn't have an excessive number of
// sigops, making it impossible to mine. Since the coinbase transaction
@ -878,10 +872,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -878,10 +872,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
unsigned int nSigOps = GetLegacySigOpCount ( tx ) ;
nSigOps + = GetP2SHSigOpCount ( tx , view ) ;
if ( nSigOps > MAX_STANDARD_TX_SIGOPS )
return state . DoS ( 0 ,
error ( " AcceptToMemoryPool: too many sigops %s, %d > %d " ,
hash . ToString ( ) , nSigOps , MAX_STANDARD_TX_SIGOPS ) ,
REJECT_NONSTANDARD , " bad-txns-too-many-sigops " ) ;
return state . DoS ( 0 , false , REJECT_NONSTANDARD , " bad-txns-too-many-sigops " , false ,
strprintf ( " %d > %d " , nSigOps , MAX_STANDARD_TX_SIGOPS ) ) ;
CAmount nValueOut = tx . GetValueOut ( ) ;
CAmount nFees = nValueIn - nValueOut ;
@ -893,9 +885,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -893,9 +885,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Don't accept it if it can't get into a block
CAmount txMinFee = GetMinRelayFee ( tx , nSize , true ) ;
if ( fLimitFree & & nFees < txMinFee )
return state . DoS ( 0 , error ( " AcceptToMemoryPool: not enough fees %s, %d < %d " ,
hash . ToString ( ) , nFees , txMinFee ) ,
REJECT_INSUFFICIENTFEE , " insufficient fee " ) ;
return state . DoS ( 0 , false , REJECT_INSUFFICIENTFEE , " insufficient fee " , false ,
strprintf ( " %d < %d " , nFees , txMinFee ) ) ;
// 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 ) ) ) {
@ -920,24 +911,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -920,24 +911,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
if ( dFreeCount > = GetArg ( " -limitfreerelay " , 15 ) * 10 * 1000 )
return state . DoS ( 0 , error ( " AcceptToMemoryPool: free transaction rejected by rate limiter " ) ,
REJECT_INSUFFICIENTFEE , " rate limited free transaction " ) ;
return state . DoS ( 0 , false , REJECT_INSUFFICIENTFEE , " rate limited free transaction " ) ;
LogPrint ( " mempool " , " Rate limit dFreeCount: %g => %g \n " , dFreeCount , dFreeCount + nSize ) ;
dFreeCount + = nSize ;
}
if ( fRejectAbsurdFee & & nFees > : : minRelayTxFee . GetFee ( nSize ) * 10000 )
return state . Invalid ( error ( " AcceptToMemoryPool: absurdly high fees %s, %d > %d " ,
hash . ToString ( ) ,
nFees , : : minRelayTxFee . GetFee ( nSize ) * 10000 ) ,
REJECT_HIGHFEE , " absurdly-high-fee " ) ;
return state . Invalid ( false ,
REJECT_HIGHFEE , " absurdly-high-fee " ,
strprintf ( " %d > %d " , nFees , : : minRelayTxFee . GetFee ( nSize ) * 10000 ) ) ;
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if ( ! CheckInputs ( tx , state , view , true , STANDARD_SCRIPT_VERIFY_FLAGS , true ) )
{
return error ( " AcceptToMemoryPool: ConnectInputs failed %s " , hash . ToString ( ) ) ;
}
return false ;
// Check again against just the consensus-critical mandatory script
// verification flags, in case of bugs in the standard flags that cause
@ -950,7 +937,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
@@ -950,7 +937,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// can be exploited as a DoS attack.
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
@ -1241,7 +1229,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
@@ -1241,7 +1229,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
if ( state . IsInvalid ( nDoS ) ) {
std : : map < uint256 , NodeId > : : iterator it = mapBlockSource . find ( pindex - > GetBlockHash ( ) ) ;
if ( it ! = mapBlockSource . end ( ) & & State ( it - > second ) ) {
assert ( state . GetRejectCode ( ) < 0x100 ) ;
assert ( state . GetRejectCode ( ) < REJECT_INTERNAL ) ; // Blocks are never rejected with internal reject codes
CBlockReject reject = { ( unsigned char ) state . GetRejectCode ( ) , state . GetRejectReason ( ) . substr ( 0 , MAX_REJECT_MESSAGE_LENGTH ) , pindex - > GetBlockHash ( ) } ;
State ( it - > second ) - > rejects . push_back ( reject ) ;
if ( nDoS > 0 )
@ -1292,7 +1280,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
@@ -1292,7 +1280,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
bool CScriptCheck : : operator ( ) ( ) {
const CScript & scriptSig = ptxTo - > vin [ nIn ] . scriptSig ;
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 ;
}
@ -1310,7 +1298,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
@@ -1310,7 +1298,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
// for an attacker to attempt to split the network.
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 nFees = 0 ;
@ -1323,33 +1311,29 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
@@ -1323,33 +1311,29 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
// If prev is coinbase, check that it's matured
if ( coins - > IsCoinBase ( ) ) {
if ( nSpendHeight - coins - > nHeight < COINBASE_MATURITY )
return state . Invalid (
error ( " CheckInputs(): tried to spend coinbase at depth %d " , nSpendHeight - coins - > nHeight ) ,
REJECT_INVALID , " bad-txns-premature-spend-of-coinbase " ) ;
return state . Invalid ( false ,
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
nValueIn + = coins - > vout [ prevout . n ] . nValue ;
if ( ! MoneyRange ( coins - > vout [ prevout . n ] . nValue ) | | ! MoneyRange ( nValueIn ) )
return state . DoS ( 100 , error ( " CheckInputs(): txin values out of range " ) ,
REJECT_INVALID , " bad-txns-inputvalues-outofrange " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-inputvalues-outofrange " ) ;
}
if ( nValueIn < tx . GetValueOut ( ) )
return state . DoS ( 100 , error ( " CheckInputs(): %s value in (%s) < value out (%s) " ,
tx . GetHash ( ) . ToString ( ) , FormatMoney ( nValueIn ) , FormatMoney ( tx . GetValueOut ( ) ) ) ,
REJECT_INVALID , " bad-txns-in-belowout " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-in-belowout " , false ,
strprintf ( " value in (%s) < value out (%s) " , FormatMoney ( nValueIn ) , FormatMoney ( tx . GetValueOut ( ) ) ) ) ;
// Tally transaction fees
CAmount nTxFee = nValueIn - tx . GetValueOut ( ) ;
if ( nTxFee < 0 )
return state . DoS ( 100 , error ( " CheckInputs(): %s nTxFee < 0 " , tx . GetHash ( ) . ToString ( ) ) ,
REJECT_INVALID , " bad-txns-fee-negative " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-fee-negative " ) ;
nFees + = nTxFee ;
if ( ! MoneyRange ( nFees ) )
return state . DoS ( 100 , error ( " CheckInputs(): nFees out of range " ) ,
REJECT_INVALID , " bad-txns-fee-outofrange " ) ;
return state . DoS ( 100 , false , REJECT_INVALID , " bad-txns-fee-outofrange " ) ;
return true ;
}
} // namespace Consensus
@ -1795,7 +1779,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
@@ -1795,7 +1779,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
std : : vector < CScriptCheck > vChecks ;
if ( ! CheckInputs ( tx , state , view , fScriptChecks , flags , false , nScriptCheckThreads ? & vChecks : NULL ) )
return false ;
return error ( " ConnectBlock(): CheckInputs on %s failed with %s " ,
tx . GetHash ( ) . ToString ( ) , FormatStateMessage ( state ) ) ;
control . Add ( vChecks ) ;
}
@ -2622,7 +2607,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
@@ -2622,7 +2607,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
// Check transactions
BOOST_FOREACH ( const CTransaction & tx , block . vtx )
if ( ! CheckTransaction ( tx , state ) )
return error ( " CheckBlock() : CheckTransaction failed " ) ;
return error ( " CheckBlock(): CheckTransaction of %s failed with %s " ,
tx . GetHash ( ) . ToString ( ) ,
FormatStateMessage ( state ) ) ;
unsigned int nSigOps = 0 ;
BOOST_FOREACH ( const CTransaction & tx , block . vtx )
@ -4357,9 +4344,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
@@ -4357,9 +4344,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
int nDoS = 0 ;
if ( state . IsInvalid ( nDoS ) )
{
LogPrint ( " mempool " , " %s from peer=%d %s was not accepted into the memory pool: %s \n " , tx . GetHash ( ) . ToString ( ) ,
LogPrint ( " mempoolrej " , " %s from peer=%d %s was not accepted into the memory pool: %s \n " , tx . GetHash ( ) . ToString ( ) ,
pfrom - > id , pfrom - > cleanSubVer ,
state . GetRejectReason ( ) ) ;
FormatStateMessage ( state ) ) ;
if ( state . GetRejectCode ( ) < REJECT_INTERNAL ) // Never send AcceptToMemoryPool's internal codes over P2P
pfrom - > PushMessage ( " reject " , strCommand , state . GetRejectCode ( ) ,
state . GetRejectReason ( ) . substr ( 0 , MAX_REJECT_MESSAGE_LENGTH ) , inv . hash ) ;
if ( nDoS > 0 )
@ -4441,6 +4429,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
@@ -4441,6 +4429,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
ProcessNewBlock ( state , pfrom , & block , forceProcessing , NULL ) ;
int nDoS ;
if ( state . IsInvalid ( nDoS ) ) {
assert ( state . GetRejectCode ( ) < REJECT_INTERNAL ) ; // Blocks are never rejected with internal reject codes
pfrom - > PushMessage ( " reject " , strCommand , state . GetRejectCode ( ) ,
state . GetRejectReason ( ) . substr ( 0 , MAX_REJECT_MESSAGE_LENGTH ) , inv . hash ) ;
if ( nDoS > 0 ) {