Skip to content

Commit

Permalink
txscript: Optimize CalcMultiSigStats.
Browse files Browse the repository at this point in the history
This converts the CalcMultiSigStats function to make use of the new
extractMultisigScriptDetails function instead of the far less efficient
parseScript thereby significantly optimizing the function.

The tests are also updated accordingly.

The following is a before and after comparison of analyzing a standard
multisig script:

benchmark                    old ns/op    new ns/op    delta
---------------------------------------------------------------
BenchmarkCalcMultiSigStats   972          79.5         -91.82%

benchmark                    old allocs   new allocs   delta
---------------------------------------------------------------
BenchmarkCalcMultiSigStats   1            0            -100.00%

benchmark                    old bytes    new bytes    delta
---------------------------------------------------------------
BenchmarkCalcMultiSigStats   2304         0            -100.00%
  • Loading branch information
davecgh authored and cfromknecht committed Feb 5, 2021
1 parent fae615d commit 1df85f4
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 22 deletions.
26 changes: 10 additions & 16 deletions txscript/standard.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,27 +648,21 @@ func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
// CalcMultiSigStats returns the number of public keys and signatures from
// a multi-signature transaction script. The passed script MUST already be
// known to be a multi-signature script.
//
// 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 CalcMultiSigStats(script []byte) (int, int, error) {
pops, err := parseScript(script)
if err != nil {
return 0, 0, err
}

// A multi-signature script is of the pattern:
// NUM_SIGS PUBKEY PUBKEY PUBKEY... NUM_PUBKEYS OP_CHECKMULTISIG
// Therefore the number of signatures is the oldest item on the stack
// and the number of pubkeys is the 2nd to last. Also, the absolute
// minimum for a multi-signature script is 1 pubkey, so at least 4
// items must be on the stack per:
// OP_1 PUBKEY OP_1 OP_CHECKMULTISIG
if len(pops) < 4 {
// The public keys are not needed here, so pass false to avoid the extra
// allocation.
const scriptVersion = 0
details := extractMultisigScriptDetails(scriptVersion, script, false)
if !details.valid {
str := fmt.Sprintf("script %x is not a multisig script", script)
return 0, 0, scriptError(ErrNotMultisigScript, str)
}

numSigs := asSmallInt(pops[0].opcode.value)
numPubKeys := asSmallInt(pops[len(pops)-2].opcode.value)
return numPubKeys, numSigs, nil
return details.numPubKeys, details.requiredSigs, nil
}

// payToPubKeyHashScript creates a new script to pay a transaction
Expand Down
8 changes: 2 additions & 6 deletions txscript/standard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ func TestCalcMultiSigStats(t *testing.T) {
name: "short script",
script: "0x046708afdb0fe5548271967f1a67130b7105cd6a828" +
"e03909a67962e0ea1f61d",
err: scriptError(ErrMalformedPush, ""),
err: scriptError(ErrNotMultisigScript, ""),
},
{
name: "stack underflow",
Expand All @@ -843,11 +843,7 @@ func TestCalcMultiSigStats(t *testing.T) {
},
{
name: "multisig script",
script: "0 DATA_72 0x30450220106a3e4ef0b51b764a2887226" +
"2ffef55846514dacbdcbbdd652c849d395b4384022100" +
"e03ae554c3cbb40600d31dd46fc33f25e47bf8525b1fe" +
"07282e3b6ecb5f3bb2801 CODESEPARATOR 1 DATA_33 " +
"0x0232abdc893e7f0631364d7fd01cb33d24da45329a0" +
script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da45329a0" +
"0357b3a7886211ab414d55a 1 CHECKMULTISIG",
err: nil,
},
Expand Down

0 comments on commit 1df85f4

Please sign in to comment.