From 2ef82e7db35dc8c499fa9091d768dc99bbaff893 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 17 Aug 2016 23:16:46 -0500 Subject: [PATCH] mempool: Improve tx input standard checks. This changes the transaction input standardness checks as follows: - Allow any script in a pay-to-script-hash transaction to be relayed and mined so long as it has no more than 15 signature operations - Remove the obsolete checks which naively calculated the number of expected inputs in favor of the more accurate ScriptVerifyCleanStack and ScriptVerifySigPushOnly functionality of the script engine that was added after these checks. --- mempool/policy.go | 57 ++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/mempool/policy.go b/mempool/policy.go index 4f40c301..69fc0ef2 100644 --- a/mempool/policy.go +++ b/mempool/policy.go @@ -14,6 +14,10 @@ import ( ) const ( + // maxStandardP2SHSigOps is the maximum number of signature operations + // that are considered standard in a pay-to-script-hash script. + maxStandardP2SHSigOps = 15 + // maxStandardTxSize is the maximum size allowed for transactions that // are considered standard and will therefore be relayed and considered // for mining. @@ -152,12 +156,15 @@ func calcInputValueAge(tx *wire.MsgTx, utxoView *blockchain.UtxoViewpoint, nextB } // checkInputsStandard performs a series of checks on a transaction's inputs -// to ensure they are "standard". A standard transaction input is one that -// that consumes the expected number of elements from the stack and that number -// is the same as the output script pushes. This help prevent resource -// exhaustion attacks by "creative" use of scripts that are super expensive to -// process like OP_DUP OP_CHECKSIG OP_DROP repeated a large number of times -// followed by a final OP_TRUE. +// to ensure they are "standard". A standard transaction input within the +// context of this function is one whose referenced public key script is of a +// standard form and, for pay-to-script-hash, does not have more than +// maxStandardP2SHSigOps signature operations. However, it should also be noted +// that standard inputs also are those which have a clean stack after execution +// and only contain pushed data in their signature scripts. This function does +// not perform those checks because the script engine already does this more +// accurately and concisely via the txscript.ScriptVerifyCleanStack and +// txscript.ScriptVerifySigPushOnly flags. func checkInputsStandard(tx *btcutil.Tx, utxoView *blockchain.UtxoViewpoint) error { // NOTE: The reference implementation also does a coinbase check here, // but coinbases have already been rejected prior to calling this @@ -170,31 +177,21 @@ func checkInputsStandard(tx *btcutil.Tx, utxoView *blockchain.UtxoViewpoint) err prevOut := txIn.PreviousOutPoint entry := utxoView.LookupEntry(&prevOut.Hash) originPkScript := entry.PkScriptByIndex(prevOut.Index) + switch txscript.GetScriptClass(originPkScript) { + case txscript.ScriptHashTy: + numSigOps := txscript.GetPreciseSigOpCount( + txIn.SignatureScript, originPkScript, true) + if numSigOps > maxStandardP2SHSigOps { + str := fmt.Sprintf("transaction input #%d has "+ + "%d signature operations which is more "+ + "than the allowed max amount of %d", + i, numSigOps, maxStandardP2SHSigOps) + return txRuleError(wire.RejectNonstandard, str) + } - // Calculate stats for the script pair. - scriptInfo, err := txscript.CalcScriptInfo(txIn.SignatureScript, - originPkScript, true) - if err != nil { - str := fmt.Sprintf("transaction input #%d script parse "+ - "failure: %v", i, err) - return txRuleError(wire.RejectNonstandard, str) - } - - // A negative value for expected inputs indicates the script is - // non-standard in some way. - if scriptInfo.ExpectedInputs < 0 { - str := fmt.Sprintf("transaction input #%d expects %d "+ - "inputs", i, scriptInfo.ExpectedInputs) - return txRuleError(wire.RejectNonstandard, str) - } - - // The script pair is non-standard if the number of available - // inputs does not match the number of expected inputs. - if scriptInfo.NumInputs != scriptInfo.ExpectedInputs { - str := fmt.Sprintf("transaction input #%d expects %d "+ - "inputs, but referenced output script provides "+ - "%d", i, scriptInfo.ExpectedInputs, - scriptInfo.NumInputs) + case txscript.NonStandardTy: + str := fmt.Sprintf("transaction input #%d has a "+ + "non-standard script form", i) return txRuleError(wire.RejectNonstandard, str) } }