lbcd/txscript/standard.go
Olaoluwa Osuntokun 9054ef8354 BIP 147: enforce NULLDUMMY w/ segwit verification
This commit implements the flag activation portion of BIP 0147. The
verification behavior triggered by the NULLDUMMY script verification
flag has been present within btcd for some time, however it wasn’t
activated by default.

With this commit, once segwit has activated, the ScriptStrictMultiSig
will also be activated within the Script VM. Additionally, the
ScriptStrictMultiSig is now a standard script verification flag which
is used unconditionally within the mempool.
2017-08-13 23:17:40 -05:00

621 lines
20 KiB
Go

// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txscript
import (
"fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
const (
// MaxDataCarrierSize is the maximum number of bytes allowed in pushed
// data to be considered a nulldata transaction
MaxDataCarrierSize = 80
// StandardVerifyFlags are the script flags which are used when
// executing transaction scripts to enforce additional checks which
// are required for the script to be considered standard. These checks
// help reduce issues related to transaction malleability as well as
// allow pay-to-script hash transactions. Note these flags are
// different than what is required for the consensus rules in that they
// are more strict.
//
// TODO: This definition does not belong here. It belongs in a policy
// package.
StandardVerifyFlags = ScriptBip16 |
ScriptVerifyDERSignatures |
ScriptVerifyStrictEncoding |
ScriptVerifyMinimalData |
ScriptStrictMultiSig |
ScriptDiscourageUpgradableNops |
ScriptVerifyCleanStack |
ScriptVerifyNullFail |
ScriptVerifyCheckLockTimeVerify |
ScriptVerifyCheckSequenceVerify |
ScriptVerifyLowS |
ScriptStrictMultiSig |
ScriptVerifyWitness |
ScriptVerifyDiscourageUpgradeableWitnessProgram
)
// ScriptClass is an enumeration for the list of standard types of script.
type ScriptClass byte
// Classes of script payment known about in the blockchain.
const (
NonStandardTy ScriptClass = iota // None of the recognized forms.
PubKeyTy // Pay pubkey.
PubKeyHashTy // Pay pubkey hash.
WitnessV0PubKeyHashTy // Pay witness pubkey hash.
ScriptHashTy // Pay to script hash.
WitnessV0ScriptHashTy // Pay to witness script hash.
MultiSigTy // Multi signature.
NullDataTy // Empty data-only (provably prunable).
)
// scriptClassToName houses the human-readable strings which describe each
// script class.
var scriptClassToName = []string{
NonStandardTy: "nonstandard",
PubKeyTy: "pubkey",
PubKeyHashTy: "pubkeyhash",
WitnessV0PubKeyHashTy: "witness_v0_keyhash",
ScriptHashTy: "scripthash",
WitnessV0ScriptHashTy: "witness_v0_scripthash",
MultiSigTy: "multisig",
NullDataTy: "nulldata",
}
// String implements the Stringer interface by returning the name of
// the enum script class. If the enum is invalid then "Invalid" will be
// returned.
func (t ScriptClass) String() string {
if int(t) > len(scriptClassToName) || int(t) < 0 {
return "Invalid"
}
return scriptClassToName[t]
}
// isPubkey returns true if the script passed is a pay-to-pubkey transaction,
// false otherwise.
func isPubkey(pops []parsedOpcode) bool {
// Valid pubkeys are either 33 or 65 bytes.
return len(pops) == 2 &&
(len(pops[0].data) == 33 || len(pops[0].data) == 65) &&
pops[1].opcode.value == OP_CHECKSIG
}
// isPubkeyHash returns true if the script passed is a pay-to-pubkey-hash
// transaction, false otherwise.
func isPubkeyHash(pops []parsedOpcode) bool {
return len(pops) == 5 &&
pops[0].opcode.value == OP_DUP &&
pops[1].opcode.value == OP_HASH160 &&
pops[2].opcode.value == OP_DATA_20 &&
pops[3].opcode.value == OP_EQUALVERIFY &&
pops[4].opcode.value == OP_CHECKSIG
}
// isMultiSig returns true if the passed script is a multisig transaction, false
// otherwise.
func isMultiSig(pops []parsedOpcode) bool {
// The absolute minimum is 1 pubkey:
// OP_0/OP_1-16 <pubkey> OP_1 OP_CHECKMULTISIG
l := len(pops)
if l < 4 {
return false
}
if !isSmallInt(pops[0].opcode) {
return false
}
if !isSmallInt(pops[l-2].opcode) {
return false
}
if pops[l-1].opcode.value != OP_CHECKMULTISIG {
return false
}
// Verify the number of pubkeys specified matches the actual number
// of pubkeys provided.
if l-2-1 != asSmallInt(pops[l-2].opcode) {
return false
}
for _, pop := range pops[1 : l-2] {
// Valid pubkeys are either 33 or 65 bytes.
if len(pop.data) != 33 && len(pop.data) != 65 {
return false
}
}
return true
}
// isNullData returns true if the passed script is a null data transaction,
// false otherwise.
func isNullData(pops []parsedOpcode) bool {
// A nulldata transaction is either a single OP_RETURN or an
// OP_RETURN SMALLDATA (where SMALLDATA is a data push up to
// MaxDataCarrierSize bytes).
l := len(pops)
if l == 1 && pops[0].opcode.value == OP_RETURN {
return true
}
return l == 2 &&
pops[0].opcode.value == OP_RETURN &&
(isSmallInt(pops[1].opcode) || pops[1].opcode.value <=
OP_PUSHDATA4) &&
len(pops[1].data) <= MaxDataCarrierSize
}
// scriptType returns the type of the script being inspected from the known
// standard types.
func typeOfScript(pops []parsedOpcode) ScriptClass {
if isPubkey(pops) {
return PubKeyTy
} else if isPubkeyHash(pops) {
return PubKeyHashTy
} else if isWitnessPubKeyHash(pops) {
return WitnessV0PubKeyHashTy
} else if isScriptHash(pops) {
return ScriptHashTy
} else if isWitnessScriptHash(pops) {
return WitnessV0ScriptHashTy
} else if isMultiSig(pops) {
return MultiSigTy
} else if isNullData(pops) {
return NullDataTy
}
return NonStandardTy
}
// GetScriptClass returns the class of the script passed.
//
// NonStandardTy will be returned when the script does not parse.
func GetScriptClass(script []byte) ScriptClass {
pops, err := parseScript(script)
if err != nil {
return NonStandardTy
}
return typeOfScript(pops)
}
// expectedInputs returns the number of arguments required by a script.
// If the script is of unknown type such that the number can not be determined
// then -1 is returned. We are an internal function and thus assume that class
// is the real class of pops (and we can thus assume things that were determined
// while finding out the type).
func expectedInputs(pops []parsedOpcode, class ScriptClass) int {
switch class {
case PubKeyTy:
return 1
case PubKeyHashTy:
return 2
case WitnessV0PubKeyHashTy:
return 2
case ScriptHashTy:
// Not including script. That is handled by the caller.
return 1
case WitnessV0ScriptHashTy:
// Not including script. That is handled by the caller.
return 1
case MultiSigTy:
// Standard multisig has a push a small number for the number
// of sigs and number of keys. Check the first push instruction
// to see how many arguments are expected. typeOfScript already
// checked this so we know it'll be a small int. Also, due to
// the original bitcoind bug where OP_CHECKMULTISIG pops an
// additional item from the stack, add an extra expected input
// for the extra push that is required to compensate.
return asSmallInt(pops[0].opcode) + 1
case NullDataTy:
fallthrough
default:
return -1
}
}
// ScriptInfo houses information about a script pair that is determined by
// CalcScriptInfo.
type ScriptInfo struct {
// PkScriptClass is the class of the public key script and is equivalent
// to calling GetScriptClass on it.
PkScriptClass ScriptClass
// NumInputs is the number of inputs provided by the public key script.
NumInputs int
// ExpectedInputs is the number of outputs required by the signature
// script and any pay-to-script-hash scripts. The number will be -1 if
// unknown.
ExpectedInputs int
// SigOps is the number of signature operations in the script pair.
SigOps int
}
// CalcScriptInfo returns a structure providing data about the provided script
// pair. It will error if the pair is in someway invalid such that they can not
// be analysed, i.e. if they do not parse or the pkScript is not a push-only
// script
func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
bip16, segwit bool) (*ScriptInfo, error) {
sigPops, err := parseScript(sigScript)
if err != nil {
return nil, err
}
pkPops, err := parseScript(pkScript)
if err != nil {
return nil, err
}
// Push only sigScript makes little sense.
si := new(ScriptInfo)
si.PkScriptClass = typeOfScript(pkPops)
// Can't have a signature script that doesn't just push data.
if !isPushOnly(sigPops) {
return nil, scriptError(ErrNotPushOnly,
"signature script is not push only")
}
si.ExpectedInputs = expectedInputs(pkPops, si.PkScriptClass)
switch {
// Count sigops taking into account pay-to-script-hash.
case si.PkScriptClass == ScriptHashTy && bip16 && !segwit:
// The pay-to-hash-script is the final data push of the
// signature script.
script := sigPops[len(sigPops)-1].data
shPops, err := parseScript(script)
if err != nil {
return nil, err
}
shInputs := expectedInputs(shPops, typeOfScript(shPops))
if shInputs == -1 {
si.ExpectedInputs = -1
} else {
si.ExpectedInputs += shInputs
}
si.SigOps = getSigOpCount(shPops, true)
// All entries pushed to stack (or are OP_RESERVED and exec
// will fail).
si.NumInputs = len(sigPops)
// If segwit is active, and this is a regular p2wkh output, then we'll
// treat the script as a p2pkh output in essence.
case si.PkScriptClass == WitnessV0PubKeyHashTy && segwit:
si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness)
si.NumInputs = len(witness)
// We'll attempt to detect the nested p2sh case so we can accurately
// count the signature operations involved.
case si.PkScriptClass == ScriptHashTy &&
IsWitnessProgram(sigScript[1:]) && bip16 && segwit:
// Extract the pushed witness program from the sigScript so we
// can determine the number of expected inputs.
pkPops, _ := parseScript(sigScript[1:])
shInputs := expectedInputs(pkPops, typeOfScript(pkPops))
if shInputs == -1 {
si.ExpectedInputs = -1
} else {
si.ExpectedInputs += shInputs
}
si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness)
si.NumInputs = len(witness)
si.NumInputs += len(sigPops)
// If segwit is active, and this is a p2wsh output, then we'll need to
// examine the witness script to generate accurate script info.
case si.PkScriptClass == WitnessV0ScriptHashTy && segwit:
// The witness script is the final element of the witness
// stack.
witnessScript := witness[len(witness)-1]
pops, _ := parseScript(witnessScript)
shInputs := expectedInputs(pops, typeOfScript(pops))
if shInputs == -1 {
si.ExpectedInputs = -1
} else {
si.ExpectedInputs += shInputs
}
si.SigOps = GetWitnessSigOpCount(sigScript, pkScript, witness)
si.NumInputs = len(witness)
default:
si.SigOps = getSigOpCount(pkPops, true)
// All entries pushed to stack (or are OP_RESERVED and exec
// will fail).
si.NumInputs = len(sigPops)
}
return si, nil
}
// 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.
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 {
str := fmt.Sprintf("script %x is not a multisig script", script)
return 0, 0, scriptError(ErrNotMultisigScript, str)
}
numSigs := asSmallInt(pops[0].opcode)
numPubKeys := asSmallInt(pops[len(pops)-2].opcode)
return numPubKeys, numSigs, nil
}
// payToPubKeyHashScript creates a new script to pay a transaction
// output to a 20-byte pubkey hash. It is expected that the input is a valid
// hash.
func payToPubKeyHashScript(pubKeyHash []byte) ([]byte, error) {
return NewScriptBuilder().AddOp(OP_DUP).AddOp(OP_HASH160).
AddData(pubKeyHash).AddOp(OP_EQUALVERIFY).AddOp(OP_CHECKSIG).
Script()
}
// payToWitnessPubKeyHashScript creates a new script to pay to a version 0
// pubkey hash witness program. The passed hash is expected to be valid.
func payToWitnessPubKeyHashScript(pubKeyHash []byte) ([]byte, error) {
return NewScriptBuilder().AddOp(OP_0).AddData(pubKeyHash).Script()
}
// payToScriptHashScript creates a new script to pay a transaction output to a
// script hash. It is expected that the input is a valid hash.
func payToScriptHashScript(scriptHash []byte) ([]byte, error) {
return NewScriptBuilder().AddOp(OP_HASH160).AddData(scriptHash).
AddOp(OP_EQUAL).Script()
}
// payToWitnessPubKeyHashScript creates a new script to pay to a version 0
// script hash witness program. The passed hash is expected to be valid.
func payToWitnessScriptHashScript(scriptHash []byte) ([]byte, error) {
return NewScriptBuilder().AddOp(OP_0).AddData(scriptHash).Script()
}
// payToPubkeyScript creates a new script to pay a transaction output to a
// public key. It is expected that the input is a valid pubkey.
func payToPubKeyScript(serializedPubKey []byte) ([]byte, error) {
return NewScriptBuilder().AddData(serializedPubKey).
AddOp(OP_CHECKSIG).Script()
}
// PayToAddrScript creates a new script to pay a transaction output to a the
// specified address.
func PayToAddrScript(addr btcutil.Address) ([]byte, error) {
const nilAddrErrStr = "unable to generate payment script for nil address"
switch addr := addr.(type) {
case *btcutil.AddressPubKeyHash:
if addr == nil {
return nil, scriptError(ErrUnsupportedAddress,
nilAddrErrStr)
}
return payToPubKeyHashScript(addr.ScriptAddress())
case *btcutil.AddressScriptHash:
if addr == nil {
return nil, scriptError(ErrUnsupportedAddress,
nilAddrErrStr)
}
return payToScriptHashScript(addr.ScriptAddress())
case *btcutil.AddressPubKey:
if addr == nil {
return nil, scriptError(ErrUnsupportedAddress,
nilAddrErrStr)
}
return payToPubKeyScript(addr.ScriptAddress())
case *btcutil.AddressWitnessPubKeyHash:
if addr == nil {
return nil, ErrUnsupportedAddress
}
return payToWitnessPubKeyHashScript(addr.ScriptAddress())
case *btcutil.AddressWitnessScriptHash:
if addr == nil {
return nil, ErrUnsupportedAddress
}
return payToWitnessScriptHashScript(addr.ScriptAddress())
}
str := fmt.Sprintf("unable to generate payment script for unsupported "+
"address type %T", addr)
return nil, scriptError(ErrUnsupportedAddress, str)
}
// NullDataScript creates a provably-prunable script containing OP_RETURN
// followed by the passed data. An Error with the error code ErrTooMuchNullData
// will be returned if the length of the passed data exceeds MaxDataCarrierSize.
func NullDataScript(data []byte) ([]byte, error) {
if len(data) > MaxDataCarrierSize {
str := fmt.Sprintf("data size %d is larger than max "+
"allowed size %d", len(data), MaxDataCarrierSize)
return nil, scriptError(ErrTooMuchNullData, str)
}
return NewScriptBuilder().AddOp(OP_RETURN).AddData(data).Script()
}
// MultiSigScript returns a valid script for a multisignature redemption where
// nrequired of the keys in pubkeys are required to have signed the transaction
// for success. An Error with the error code ErrTooManyRequiredSigs will be
// returned if nrequired is larger than the number of keys provided.
func MultiSigScript(pubkeys []*btcutil.AddressPubKey, nrequired int) ([]byte, error) {
if len(pubkeys) < nrequired {
str := fmt.Sprintf("unable to generate multisig script with "+
"%d required signatures when there are only %d public "+
"keys available", nrequired, len(pubkeys))
return nil, scriptError(ErrTooManyRequiredSigs, str)
}
builder := NewScriptBuilder().AddInt64(int64(nrequired))
for _, key := range pubkeys {
builder.AddData(key.ScriptAddress())
}
builder.AddInt64(int64(len(pubkeys)))
builder.AddOp(OP_CHECKMULTISIG)
return builder.Script()
}
// PushedData returns an array of byte slices containing any pushed data found
// in the passed script. This includes OP_0, but not OP_1 - OP_16.
func PushedData(script []byte) ([][]byte, error) {
pops, err := parseScript(script)
if err != nil {
return nil, err
}
var data [][]byte
for _, pop := range pops {
if pop.data != nil {
data = append(data, pop.data)
} else if pop.opcode.value == OP_0 {
data = append(data, nil)
}
}
return data, nil
}
// ExtractPkScriptAddrs returns the type of script, addresses and required
// signatures associated with the passed PkScript. Note that it only works for
// 'standard' transaction script types. Any data such as public keys which are
// invalid are omitted from the results.
func ExtractPkScriptAddrs(pkScript []byte, chainParams *chaincfg.Params) (ScriptClass, []btcutil.Address, int, error) {
var addrs []btcutil.Address
var requiredSigs int
// No valid addresses or required signatures if the script doesn't
// parse.
pops, err := parseScript(pkScript)
if err != nil {
return NonStandardTy, nil, 0, err
}
scriptClass := typeOfScript(pops)
switch scriptClass {
case PubKeyHashTy:
// A pay-to-pubkey-hash script is of the form:
// OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG
// Therefore the pubkey hash is the 3rd item on the stack.
// Skip the pubkey hash if it's invalid for some reason.
requiredSigs = 1
addr, err := btcutil.NewAddressPubKeyHash(pops[2].data,
chainParams)
if err == nil {
addrs = append(addrs, addr)
}
case WitnessV0PubKeyHashTy:
// A pay-to-witness-pubkey-hash script is of thw form:
// OP_0 <20-byte hash>
// Therefore, the pubkey hash is the second item on the stack.
// Skip the pubkey hash if it's invalid for some reason.
requiredSigs = 1
addr, err := btcutil.NewAddressWitnessPubKeyHash(pops[1].data,
chainParams)
if err == nil {
addrs = append(addrs, addr)
}
case PubKeyTy:
// A pay-to-pubkey script is of the form:
// <pubkey> OP_CHECKSIG
// Therefore the pubkey is the first item on the stack.
// Skip the pubkey if it's invalid for some reason.
requiredSigs = 1
addr, err := btcutil.NewAddressPubKey(pops[0].data, chainParams)
if err == nil {
addrs = append(addrs, addr)
}
case ScriptHashTy:
// A pay-to-script-hash script is of the form:
// OP_HASH160 <scripthash> OP_EQUAL
// Therefore the script hash is the 2nd item on the stack.
// Skip the script hash if it's invalid for some reason.
requiredSigs = 1
addr, err := btcutil.NewAddressScriptHashFromHash(pops[1].data,
chainParams)
if err == nil {
addrs = append(addrs, addr)
}
case WitnessV0ScriptHashTy:
// A pay-to-witness-script-hash script is of the form:
// OP_0 <32-byte hash>
// Therefore, the script hash is the second item on the stack.
// Skip the script hash if it's invalid for some reason.
requiredSigs = 1
addr, err := btcutil.NewAddressWitnessScriptHash(pops[1].data,
chainParams)
if err == nil {
addrs = append(addrs, addr)
}
case MultiSigTy:
// A multi-signature script is of the form:
// <numsigs> <pubkey> <pubkey> <pubkey>... <numpubkeys> OP_CHECKMULTISIG
// Therefore the number of required signatures is the 1st item
// on the stack and the number of public keys is the 2nd to last
// item on the stack.
requiredSigs = asSmallInt(pops[0].opcode)
numPubKeys := asSmallInt(pops[len(pops)-2].opcode)
// Extract the public keys while skipping any that are invalid.
addrs = make([]btcutil.Address, 0, numPubKeys)
for i := 0; i < numPubKeys; i++ {
addr, err := btcutil.NewAddressPubKey(pops[i+1].data,
chainParams)
if err == nil {
addrs = append(addrs, addr)
}
}
case NullDataTy:
// Null data transactions have no addresses or required
// signatures.
case NonStandardTy:
// Don't attempt to extract addresses or required signatures for
// nonstandard transactions.
}
return scriptClass, addrs, requiredSigs, nil
}