mirror of
https://github.com/btcsuite/btcd.git
synced 2025-02-24 14:51:01 +01:00
Merge pull request #1431 from wpaulino/var-length-p2pkh
txscript: handle variable length P2PKH signatures in ComputePkScript
This commit is contained in:
commit
a0d1e3e36d
3 changed files with 66 additions and 46 deletions
|
@ -85,10 +85,10 @@ func (sig *Signature) IsEqual(otherSig *Signature) bool {
|
||||||
sig.S.Cmp(otherSig.S) == 0
|
sig.S.Cmp(otherSig.S) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// minSigLen is the minimum length of a DER encoded signature and is
|
// MinSigLen is the minimum length of a DER encoded signature and is when both R
|
||||||
// when both R and S are 1 byte each.
|
// and S are 1 byte each.
|
||||||
// 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
|
// 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
|
||||||
const minSigLen = 8
|
const MinSigLen = 8
|
||||||
|
|
||||||
func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error) {
|
func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error) {
|
||||||
// Originally this code used encoding/asn1 in order to parse the
|
// Originally this code used encoding/asn1 in order to parse the
|
||||||
|
@ -103,7 +103,7 @@ func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error)
|
||||||
|
|
||||||
signature := &Signature{}
|
signature := &Signature{}
|
||||||
|
|
||||||
if len(sigStr) < minSigLen {
|
if len(sigStr) < MinSigLen {
|
||||||
return nil, errors.New("malformed signature: too short")
|
return nil, errors.New("malformed signature: too short")
|
||||||
}
|
}
|
||||||
// 0x30
|
// 0x30
|
||||||
|
@ -118,7 +118,7 @@ func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error)
|
||||||
|
|
||||||
// siglen should be less than the entire message and greater than
|
// siglen should be less than the entire message and greater than
|
||||||
// the minimal message size.
|
// the minimal message size.
|
||||||
if int(siglen+2) > len(sigStr) || int(siglen+2) < minSigLen {
|
if int(siglen+2) > len(sigStr) || int(siglen+2) < MinSigLen {
|
||||||
return nil, errors.New("malformed signature: bad length")
|
return nil, errors.New("malformed signature: bad length")
|
||||||
}
|
}
|
||||||
// trim the slice we're working on so we only look at what matters.
|
// trim the slice we're working on so we only look at what matters.
|
||||||
|
|
|
@ -13,13 +13,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// pubKeyHashSigScriptLen is the length of a signature script attempting
|
// minPubKeyHashSigScriptLen is the minimum length of a signature script
|
||||||
// to spend a P2PKH script. The only other possible length value is 107
|
// that spends a P2PKH output. The length is composed of the following:
|
||||||
// bytes, due to the signature within it. This length is determined by
|
// Signature length (1 byte)
|
||||||
// the following:
|
// Signature (min 8 bytes)
|
||||||
// 0x47 or 0x48 (71 or 72 byte data push) | <71 or 72 byte sig> |
|
// Signature hash type (1 byte)
|
||||||
// 0x21 (33 byte data push) | <33 byte compressed pubkey>
|
// Public key length (1 byte)
|
||||||
pubKeyHashSigScriptLen = 106
|
// Public key (33 byte)
|
||||||
|
minPubKeyHashSigScriptLen = 1 + btcec.MinSigLen + 1 + 1 + 33
|
||||||
|
|
||||||
|
// maxPubKeyHashSigScriptLen is the maximum length of a signature script
|
||||||
|
// that spends a P2PKH output. The length is composed of the following:
|
||||||
|
// Signature length (1 byte)
|
||||||
|
// Signature (max 72 bytes)
|
||||||
|
// Signature hash type (1 byte)
|
||||||
|
// Public key length (1 byte)
|
||||||
|
// Public key (33 byte)
|
||||||
|
maxPubKeyHashSigScriptLen = 1 + 72 + 1 + 1 + 33
|
||||||
|
|
||||||
// compressedPubKeyLen is the length in bytes of a compressed public
|
// compressedPubKeyLen is the length in bytes of a compressed public
|
||||||
// key.
|
// key.
|
||||||
|
@ -146,26 +156,35 @@ func (s PkScript) String() string {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComputePkScript computes the pkScript of an transaction output by looking at
|
// ComputePkScript computes the script of an output by looking at the spending
|
||||||
// the transaction input's signature script or witness.
|
// input's signature script or witness.
|
||||||
//
|
//
|
||||||
// NOTE: Only P2PKH, P2SH, P2WSH, and P2WPKH redeem scripts are supported.
|
// NOTE: Only P2PKH, P2SH, P2WSH, and P2WPKH redeem scripts are supported.
|
||||||
func ComputePkScript(sigScript []byte, witness wire.TxWitness) (PkScript, error) {
|
func ComputePkScript(sigScript []byte, witness wire.TxWitness) (PkScript, error) {
|
||||||
var pkScript PkScript
|
|
||||||
|
|
||||||
// Ensure that either an input's signature script or a witness was
|
|
||||||
// provided.
|
|
||||||
if len(sigScript) == 0 && len(witness) == 0 {
|
|
||||||
return pkScript, ErrUnsupportedScriptType
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll start by checking the input's signature script, if provided.
|
|
||||||
switch {
|
switch {
|
||||||
|
case len(sigScript) > 0:
|
||||||
|
return computeNonWitnessPkScript(sigScript)
|
||||||
|
case len(witness) > 0:
|
||||||
|
return computeWitnessPkScript(witness)
|
||||||
|
default:
|
||||||
|
return PkScript{}, ErrUnsupportedScriptType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeNonWitnessPkScript computes the script of an output by looking at the
|
||||||
|
// spending input's signature script.
|
||||||
|
func computeNonWitnessPkScript(sigScript []byte) (PkScript, error) {
|
||||||
|
switch {
|
||||||
|
// Since we only support P2PKH and P2SH scripts as the only non-witness
|
||||||
|
// script types, we should expect to see a push only script.
|
||||||
|
case !IsPushOnlyScript(sigScript):
|
||||||
|
return PkScript{}, ErrUnsupportedScriptType
|
||||||
|
|
||||||
// If a signature script is provided with a length long enough to
|
// If a signature script is provided with a length long enough to
|
||||||
// represent a P2PKH script, then we'll attempt to parse the compressed
|
// represent a P2PKH script, then we'll attempt to parse the compressed
|
||||||
// public key from it.
|
// public key from it.
|
||||||
case len(sigScript) == pubKeyHashSigScriptLen ||
|
case len(sigScript) >= minPubKeyHashSigScriptLen &&
|
||||||
len(sigScript) == pubKeyHashSigScriptLen+1:
|
len(sigScript) <= maxPubKeyHashSigScriptLen:
|
||||||
|
|
||||||
// The public key should be found as the last part of the
|
// The public key should be found as the last part of the
|
||||||
// signature script. We'll attempt to parse it to ensure this is
|
// signature script. We'll attempt to parse it to ensure this is
|
||||||
|
@ -175,49 +194,49 @@ func ComputePkScript(sigScript []byte, witness wire.TxWitness) (PkScript, error)
|
||||||
pubKeyHash := hash160(pubKey)
|
pubKeyHash := hash160(pubKey)
|
||||||
script, err := payToPubKeyHashScript(pubKeyHash)
|
script, err := payToPubKeyHashScript(pubKeyHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pkScript, err
|
return PkScript{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pkScript.class = PubKeyHashTy
|
pkScript := PkScript{class: PubKeyHashTy}
|
||||||
copy(pkScript.script[:], script)
|
copy(pkScript.script[:], script)
|
||||||
return pkScript, nil
|
return pkScript, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it isn't, we'll assume it is a P2SH signature script.
|
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
||||||
// If we failed to parse a compressed public key from the script in the
|
// If we failed to parse a compressed public key from the script in the
|
||||||
// case above, or if the script length is not that of a P2PKH one, and
|
// case above, or if the script length is not that of a P2PKH one, we
|
||||||
// our redeem script is only composed of data pushed, we can assume it's
|
// can assume it's a P2SH signature script.
|
||||||
// a P2SH signature script.
|
default:
|
||||||
case len(sigScript) > 0 && IsPushOnlyScript(sigScript):
|
|
||||||
// The redeem script will always be the last data push of the
|
// The redeem script will always be the last data push of the
|
||||||
// signature script, so we'll parse the script into opcodes to
|
// signature script, so we'll parse the script into opcodes to
|
||||||
// obtain it.
|
// obtain it.
|
||||||
parsedOpcodes, err := parseScript(sigScript)
|
parsedOpcodes, err := parseScript(sigScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pkScript, err
|
return PkScript{}, err
|
||||||
}
|
}
|
||||||
redeemScript := parsedOpcodes[len(parsedOpcodes)-1].data
|
redeemScript := parsedOpcodes[len(parsedOpcodes)-1].data
|
||||||
|
|
||||||
scriptHash := hash160(redeemScript)
|
scriptHash := hash160(redeemScript)
|
||||||
script, err := payToScriptHashScript(scriptHash)
|
script, err := payToScriptHashScript(scriptHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pkScript, err
|
return PkScript{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pkScript.class = ScriptHashTy
|
pkScript := PkScript{class: ScriptHashTy}
|
||||||
copy(pkScript.script[:], script)
|
copy(pkScript.script[:], script)
|
||||||
return pkScript, nil
|
return pkScript, nil
|
||||||
|
|
||||||
case len(sigScript) > 0:
|
|
||||||
return pkScript, ErrUnsupportedScriptType
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If a witness was provided instead, we'll use the last item of the
|
// computeWitnessPkScript computes the script of an output by looking at the
|
||||||
// witness stack to determine the proper witness type.
|
// spending input's witness.
|
||||||
|
func computeWitnessPkScript(witness wire.TxWitness) (PkScript, error) {
|
||||||
|
// We'll use the last item of the witness stack to determine the proper
|
||||||
|
// witness type.
|
||||||
lastWitnessItem := witness[len(witness)-1]
|
lastWitnessItem := witness[len(witness)-1]
|
||||||
|
|
||||||
|
var pkScript PkScript
|
||||||
switch {
|
switch {
|
||||||
// If the witness stack has a size of 2 and its last item is a
|
// If the witness stack has a size of 2 and its last item is a
|
||||||
// compressed public key, then this is a P2WPKH witness.
|
// compressed public key, then this is a P2WPKH witness.
|
||||||
|
@ -230,7 +249,6 @@ func ComputePkScript(sigScript []byte, witness wire.TxWitness) (PkScript, error)
|
||||||
|
|
||||||
pkScript.class = WitnessV0PubKeyHashTy
|
pkScript.class = WitnessV0PubKeyHashTy
|
||||||
copy(pkScript.script[:], script)
|
copy(pkScript.script[:], script)
|
||||||
return pkScript, nil
|
|
||||||
|
|
||||||
// For any other witnesses, we'll assume it's a P2WSH witness.
|
// For any other witnesses, we'll assume it's a P2WSH witness.
|
||||||
default:
|
default:
|
||||||
|
@ -242,8 +260,9 @@ func ComputePkScript(sigScript []byte, witness wire.TxWitness) (PkScript, error)
|
||||||
|
|
||||||
pkScript.class = WitnessV0ScriptHashTy
|
pkScript.class = WitnessV0ScriptHashTy
|
||||||
copy(pkScript.script[:], script)
|
copy(pkScript.script[:], script)
|
||||||
return pkScript, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return pkScript, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash160 returns the RIPEMD160 hash of the SHA-256 HASH of the given data.
|
// hash160 returns the RIPEMD160 hash of the SHA-256 HASH of the given data.
|
||||||
|
|
|
@ -208,9 +208,9 @@ func TestComputePkScript(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "P2PKH sigScript",
|
name: "P2PKH sigScript",
|
||||||
sigScript: []byte{
|
sigScript: []byte{
|
||||||
// OP_DATA_71,
|
// OP_DATA_73,
|
||||||
0x47,
|
0x49,
|
||||||
// <71-byte sig>
|
// <73-byte sig>
|
||||||
0x30, 0x44, 0x02, 0x20, 0x65, 0x92, 0xd8, 0x8e,
|
0x30, 0x44, 0x02, 0x20, 0x65, 0x92, 0xd8, 0x8e,
|
||||||
0x1d, 0x0a, 0x4a, 0x3c, 0xc5, 0x9f, 0x92, 0xae,
|
0x1d, 0x0a, 0x4a, 0x3c, 0xc5, 0x9f, 0x92, 0xae,
|
||||||
0xfe, 0x62, 0x54, 0x74, 0xa9, 0x4d, 0x13, 0xa5,
|
0xfe, 0x62, 0x54, 0x74, 0xa9, 0x4d, 0x13, 0xa5,
|
||||||
|
@ -219,7 +219,8 @@ func TestComputePkScript(t *testing.T) {
|
||||||
0x36, 0x96, 0x19, 0x1f, 0xb7, 0x00, 0xc5, 0xa7,
|
0x36, 0x96, 0x19, 0x1f, 0xb7, 0x00, 0xc5, 0xa7,
|
||||||
0x7e, 0x22, 0xd9, 0xfb, 0x6b, 0x42, 0x67, 0x42,
|
0x7e, 0x22, 0xd9, 0xfb, 0x6b, 0x42, 0x67, 0x42,
|
||||||
0xa4, 0x2c, 0xac, 0xdb, 0x74, 0xa2, 0x7c, 0x43,
|
0xa4, 0x2c, 0xac, 0xdb, 0x74, 0xa2, 0x7c, 0x43,
|
||||||
0xcd, 0x89, 0xa0, 0xf9, 0x44, 0x54, 0x01,
|
0xcd, 0x89, 0xa0, 0xf9, 0x44, 0x54, 0x12, 0x74,
|
||||||
|
0x01,
|
||||||
// OP_DATA_33
|
// OP_DATA_33
|
||||||
0x21,
|
0x21,
|
||||||
// <33-byte compressed pubkey>
|
// <33-byte compressed pubkey>
|
||||||
|
|
Loading…
Add table
Reference in a new issue