txscript: Optimize IsPayToPubKey
This converts the IsPayToScriptHash function to analyze the raw script instead of using the far less efficient parseScript, thereby significantly optimizing the function. In order to accomplish this, it introduces four new functions: extractCompressedPubKey, extractUncompressedPubKey, extractPubKey, and isPubKeyScript. The extractPubKey function makes use of extractCompressedPubKey and extractUncompressedPubKey to combine their functionality as a convenience and isPubKeyScript is defined in terms of extractPubKey. The extractCompressedPubKey works with the raw script bytes to simultaneously determine if the script is a pay-to-compressed-pubkey script, and in the case it is, extract and return the raw compressed pubkey bytes. Similarly, the extractUncompressedPubKey works in the same way except it determines if the script is a pay-to-uncompressed-pubkey script and returns the raw uncompressed pubkey bytes in the case it is. The extract function approach was chosen because it is common for callers to want to only extract relevant details from a script if the script is of the specific type. Extracting those details requires performing the exact same checks to ensure the script is of the correct type, so it is more efficient to combine the two into one and define the type determination in terms of the result so long as the extraction does not require allocations. The following is a before and after comparison of analyzing a large script: benchmark old ns/op new ns/op delta BenchmarkIsPubKeyScript-8 62323 2.97 -100.00% benchmark old allocs new allocs delta BenchmarkIsPubKeyScript-8 1 0 -100.00% benchmark old bytes new bytes delta BenchmarkIsPubKeyScript-8 311299 0 -100.00%
This commit is contained in:
parent
28eaf3492d
commit
5d18558bc6
2 changed files with 59 additions and 5 deletions
|
@ -66,11 +66,7 @@ func isScriptHash(pops []parsedOpcode) bool {
|
||||||
// IsPayToPubKey returns true if the script is in the standard pay-to-pubkey
|
// IsPayToPubKey returns true if the script is in the standard pay-to-pubkey
|
||||||
// (P2PK) format, false otherwise.
|
// (P2PK) format, false otherwise.
|
||||||
func IsPayToPubKey(script []byte) bool {
|
func IsPayToPubKey(script []byte) bool {
|
||||||
pops, err := parseScript(script)
|
return isPubKeyScript(script)
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return isPubkey(pops)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPayToScriptHash returns true if the script is in the standard
|
// IsPayToScriptHash returns true if the script is in the standard
|
||||||
|
|
|
@ -85,6 +85,64 @@ func (t ScriptClass) String() string {
|
||||||
return scriptClassToName[t]
|
return scriptClassToName[t]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractCompressedPubKey extracts a compressed public key from the passed
|
||||||
|
// script if it is a standard pay-to-compressed-secp256k1-pubkey script. It
|
||||||
|
// will return nil otherwise.
|
||||||
|
func extractCompressedPubKey(script []byte) []byte {
|
||||||
|
// A pay-to-compressed-pubkey script is of the form:
|
||||||
|
// OP_DATA_33 <33-byte compressed pubkey> OP_CHECKSIG
|
||||||
|
|
||||||
|
// All compressed secp256k1 public keys must start with 0x02 or 0x03.
|
||||||
|
if len(script) == 35 &&
|
||||||
|
script[34] == OP_CHECKSIG &&
|
||||||
|
script[0] == OP_DATA_33 &&
|
||||||
|
(script[1] == 0x02 || script[1] == 0x03) {
|
||||||
|
|
||||||
|
return script[1:34]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractUncompressedPubKey extracts an uncompressed public key from the
|
||||||
|
// passed script if it is a standard pay-to-uncompressed-secp256k1-pubkey
|
||||||
|
// script. It will return nil otherwise.
|
||||||
|
func extractUncompressedPubKey(script []byte) []byte {
|
||||||
|
// A pay-to-uncompressed-pubkey script is of the form:
|
||||||
|
// OP_DATA_65 <65-byte uncompressed pubkey> OP_CHECKSIG
|
||||||
|
//
|
||||||
|
// All non-hybrid uncompressed secp256k1 public keys must start with 0x04.
|
||||||
|
// Hybrid uncompressed secp256k1 public keys start with 0x06 or 0x07:
|
||||||
|
// - 0x06 => hybrid format for even Y coords
|
||||||
|
// - 0x07 => hybrid format for odd Y coords
|
||||||
|
if len(script) == 67 &&
|
||||||
|
script[66] == OP_CHECKSIG &&
|
||||||
|
script[0] == OP_DATA_65 &&
|
||||||
|
(script[1] == 0x04 || script[1] == 0x06 || script[1] == 0x07) {
|
||||||
|
|
||||||
|
return script[1:66]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractPubKey extracts either compressed or uncompressed public key from the
|
||||||
|
// passed script if it is a either a standard pay-to-compressed-secp256k1-pubkey
|
||||||
|
// or pay-to-uncompressed-secp256k1-pubkey script, respectively. It will return
|
||||||
|
// nil otherwise.
|
||||||
|
func extractPubKey(script []byte) []byte {
|
||||||
|
if pubKey := extractCompressedPubKey(script); pubKey != nil {
|
||||||
|
return pubKey
|
||||||
|
}
|
||||||
|
return extractUncompressedPubKey(script)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isPubKeyScript returns whether or not the passed script is either a standard
|
||||||
|
// pay-to-compressed-secp256k1-pubkey or pay-to-uncompressed-secp256k1-pubkey
|
||||||
|
// script.
|
||||||
|
func isPubKeyScript(script []byte) bool {
|
||||||
|
return extractPubKey(script) != nil
|
||||||
|
}
|
||||||
|
|
||||||
// isPubkey returns true if the script passed is a pay-to-pubkey transaction,
|
// isPubkey returns true if the script passed is a pay-to-pubkey transaction,
|
||||||
// false otherwise.
|
// false otherwise.
|
||||||
func isPubkey(pops []parsedOpcode) bool {
|
func isPubkey(pops []parsedOpcode) bool {
|
||||||
|
|
Loading…
Reference in a new issue