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.
This commit is contained in:
Dave Collins 2016-08-17 23:16:46 -05:00
parent c6d50b7abf
commit 2ef82e7db3
No known key found for this signature in database
GPG key ID: B8904D9D9C93D1F2

View file

@ -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)
}
}