txscript: Optimize CalcMultiSigStats.

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%
This commit is contained in:
Dave Collins 2019-03-13 01:12:13 -05:00 committed by Olaoluwa Osuntokun
parent 8c54905959
commit 7791f92f6f
No known key found for this signature in database
GPG key ID: 3BBD59E99B280306
2 changed files with 12 additions and 22 deletions

View file

@ -656,27 +656,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

View file

@ -832,7 +832,7 @@ func TestCalcMultiSigStats(t *testing.T) {
name: "short script",
script: "0x046708afdb0fe5548271967f1a67130b7105cd6a828" +
"e03909a67962e0ea1f61d",
err: scriptError(ErrMalformedPush, ""),
err: scriptError(ErrNotMultisigScript, ""),
},
{
name: "stack underflow",
@ -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,
},