Skip to content

Commit

Permalink
txscript: Optimize CalcSignatureHash.
Browse files Browse the repository at this point in the history
This modifies the CalcSignatureHash function to make use of the new
signature hash calculation function that accepts raw scripts without
needing to first parse them.  Consequently, it also doubles as a slight
optimization to the execution time and a significant reduction in the
number of allocations.

In order to convert the CalcScriptHash function and keep the same
semantics, a new function named checkScriptParses is introduced which
will quickly determine if a script can be fully parsed without failure
and return the parse failure in the case it can't.

The following is a before and after comparison of analyzing a large
multiple input transaction:

benchmark                  old ns/op     new ns/op     delta
BenchmarkCalcSigHash-8     3627895       3619477       -0.23%

benchmark                  old allocs     new allocs     delta
BenchmarkCalcSigHash-8     1335           801            -40.00%

benchmark                  old bytes     new bytes     delta
BenchmarkCalcSigHash-8     1373812       1293354       -5.86%
  • Loading branch information
davecgh authored and cfromknecht committed Feb 5, 2021
1 parent 04ed404 commit bcbf950
Showing 1 changed file with 18 additions and 4 deletions.
22 changes: 18 additions & 4 deletions txscript/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,12 +567,17 @@ func shallowCopyTx(tx *wire.MsgTx) wire.MsgTx {
// CalcSignatureHash will, given a script and hash type for the current script
// engine instance, calculate the signature hash to be used for signing and
// verification.
//
// 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 CalcSignatureHash(script []byte, hashType SigHashType, tx *wire.MsgTx, idx int) ([]byte, error) {
parsedScript, err := parseScript(script)
if err != nil {
return nil, fmt.Errorf("cannot parse output script: %v", err)
const scriptVersion = 0
if err := checkScriptParses(scriptVersion, script); err != nil {
return nil, err
}
return calcSignatureHash(parsedScript, hashType, tx, idx)

return calcSignatureHashRaw(script, hashType, tx, idx), nil
}

// calcSignatureHashRaw computes the signature hash for the specified input of
Expand Down Expand Up @@ -850,6 +855,15 @@ func getWitnessSigOps(pkScript []byte, witness wire.TxWitness) int {
return 0
}

// checkScriptParses returns an error if the provided script fails to parse.
func checkScriptParses(scriptVersion uint16, script []byte) error {
tokenizer := MakeScriptTokenizer(scriptVersion, script)
for tokenizer.Next() {
// Nothing to do.
}
return tokenizer.Err()
}

// IsUnspendable returns whether the passed public key script is unspendable, or
// guaranteed to fail at execution. This allows inputs to be pruned instantly
// when entering the UTXO set.
Expand Down

0 comments on commit bcbf950

Please sign in to comment.