txscript: Convert CalcScriptInfo.

This converts CalcScriptInfo and dependent expectedInputs to make use of
the new script tokenizer as well as several of the other recently added
raw script analysis functions in order to remove the reliance on parsed
opcodes as a step towards utlimately removing them altogether.

It is worth noting that this has the side effect of significantly
optimizing the function as well, however, since it is deprecated, no
benchmarks are provided.
This commit is contained in:
Dave Collins 2019-03-13 01:12:09 -05:00 committed by Roy Lee
parent 0edfee87d6
commit ded9b8c506

View file

@ -481,7 +481,11 @@ func NewScriptClass(name string) (*ScriptClass, error) {
// then -1 is returned. We are an internal function and thus assume that class // then -1 is returned. We are an internal function and thus assume that class
// is the real class of pops (and we can thus assume things that were determined // is the real class of pops (and we can thus assume things that were determined
// while finding out the type). // while finding out the type).
func expectedInputs(pops []parsedOpcode, class ScriptClass) int { //
// NOTE: This function is only valid for version 0 scripts. Since the function
// does not accept a script version, the results are undefined for other script
// versions.
func expectedInputs(script []byte, class ScriptClass) int {
switch class { switch class {
case PubKeyTy: case PubKeyTy:
return 1 return 1
@ -508,7 +512,7 @@ func expectedInputs(pops []parsedOpcode, class ScriptClass) int {
// the original bitcoind bug where OP_CHECKMULTISIG pops an // the original bitcoind bug where OP_CHECKMULTISIG pops an
// additional item from the stack, add an extra expected input // additional item from the stack, add an extra expected input
// for the extra push that is required to compensate. // for the extra push that is required to compensate.
return asSmallInt(pops[0].opcode.value) + 1 return asSmallInt(script[0]) + 1
case NullDataTy: case NullDataTy:
fallthrough fallthrough
@ -549,52 +553,52 @@ type ScriptInfo struct {
func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness, func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
bip16, segwit bool) (*ScriptInfo, error) { bip16, segwit bool) (*ScriptInfo, error) {
// Count the number of opcodes in the signature script while also ensuring
// that successfully parses. Since there is a check below to ensure the
// script is push only, this equates to the number of inputs to the public
// key script.
const scriptVersion = 0 const scriptVersion = 0
var numInputs int
sigPops, err := parseScript(sigScript) tokenizer := MakeScriptTokenizer(scriptVersion, sigScript)
if err != nil { for tokenizer.Next() {
numInputs++
}
if err := tokenizer.Err(); err != nil {
return nil, err return nil, err
} }
pkPops, err := parseScript(pkScript) if err := checkScriptParses(scriptVersion, pkScript); err != nil {
if err != nil {
return nil, err return nil, err
} }
// Can't have a signature script that doesn't just push data.
if !IsPushOnlyScript(sigScript) {
return nil, scriptError(ErrNotPushOnly,
"signature script is not push only")
}
si := new(ScriptInfo) si := new(ScriptInfo)
si.PkScriptClass = typeOfScript(scriptVersion, pkScript) si.PkScriptClass = typeOfScript(scriptVersion, pkScript)
// Can't have a signature script that doesn't just push data. si.ExpectedInputs = expectedInputs(pkScript, si.PkScriptClass)
if !isPushOnly(sigPops) {
return nil, scriptError(ErrNotPushOnly,
"signature script is not push only")
}
si.ExpectedInputs = expectedInputs(pkPops, si.PkScriptClass)
switch { switch {
// Count sigops taking into account pay-to-script-hash. // Count sigops taking into account pay-to-script-hash.
case si.PkScriptClass == ScriptHashTy && bip16 && !segwit: case si.PkScriptClass == ScriptHashTy && bip16 && !segwit:
// The pay-to-hash-script is the final data push of the // The redeem script is the final data push of the signature script.
// signature script. redeemScript := finalOpcodeData(scriptVersion, sigScript)
script := sigPops[len(sigPops)-1].data reedeemClass := typeOfScript(scriptVersion, redeemScript)
shPops, err := parseScript(script) rsInputs := expectedInputs(redeemScript, reedeemClass)
if err != nil { if rsInputs == -1 {
return nil, err
}
redeemClass := typeOfScript(scriptVersion, script)
shInputs := expectedInputs(shPops, redeemClass)
if shInputs == -1 {
si.ExpectedInputs = -1 si.ExpectedInputs = -1
} else { } else {
si.ExpectedInputs += shInputs si.ExpectedInputs += rsInputs
} }
si.SigOps = getSigOpCount(shPops, true) si.SigOps = countSigOpsV0(redeemScript, true)
// All entries pushed to stack (or are OP_RESERVED and exec // All entries pushed to stack (or are OP_RESERVED and exec
// will fail). // will fail).
si.NumInputs = len(sigPops) si.NumInputs = numInputs
// If segwit is active, and this is a regular p2wkh output, then we'll // If segwit is active, and this is a regular p2wkh output, then we'll
// treat the script as a p2pkh output in essence. // treat the script as a p2pkh output in essence.
@ -610,10 +614,8 @@ func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
// Extract the pushed witness program from the sigScript so we // Extract the pushed witness program from the sigScript so we
// can determine the number of expected inputs. // can determine the number of expected inputs.
pkPops, _ := parseScript(sigScript[1:])
redeemClass := typeOfScript(scriptVersion, sigScript[1:]) redeemClass := typeOfScript(scriptVersion, sigScript[1:])
shInputs := expectedInputs(pkPops, redeemClass) shInputs := expectedInputs(sigScript[1:], redeemClass)
if shInputs == -1 { if shInputs == -1 {
si.ExpectedInputs = -1 si.ExpectedInputs = -1
} else { } else {
@ -623,18 +625,14 @@ func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness) si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness)
si.NumInputs = len(witness) si.NumInputs = len(witness)
si.NumInputs += len(sigPops) si.NumInputs += numInputs
// If segwit is active, and this is a p2wsh output, then we'll need to // If segwit is active, and this is a p2wsh output, then we'll need to
// examine the witness script to generate accurate script info. // examine the witness script to generate accurate script info.
case si.PkScriptClass == WitnessV0ScriptHashTy && segwit: case si.PkScriptClass == WitnessV0ScriptHashTy && segwit:
// The witness script is the final element of the witness
// stack.
witnessScript := witness[len(witness)-1] witnessScript := witness[len(witness)-1]
pops, _ := parseScript(witnessScript)
redeemClass := typeOfScript(scriptVersion, witnessScript) redeemClass := typeOfScript(scriptVersion, witnessScript)
shInputs := expectedInputs(pops, redeemClass) shInputs := expectedInputs(witnessScript, redeemClass)
if shInputs == -1 { if shInputs == -1 {
si.ExpectedInputs = -1 si.ExpectedInputs = -1
} else { } else {
@ -645,11 +643,11 @@ func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
si.NumInputs = len(witness) si.NumInputs = len(witness)
default: default:
si.SigOps = getSigOpCount(pkPops, true) si.SigOps = countSigOpsV0(pkScript, true)
// All entries pushed to stack (or are OP_RESERVED and exec // All entries pushed to stack (or are OP_RESERVED and exec
// will fail). // will fail).
si.NumInputs = len(sigPops) si.NumInputs = numInputs
} }
return si, nil return si, nil