You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
462 lines
14 KiB
462 lines
14 KiB
// Copyright (c) 2013-2015 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 ( |
|
"github.com/btcsuite/btcd/chaincfg" |
|
"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 |
|
) |
|
|
|
// 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. |
|
ScriptHashTy // Pay to 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", |
|
ScriptHashTy: "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 |
|
} |
|
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 && |
|
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 isScriptHash(pops) { |
|
return ScriptHashTy |
|
} 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 ScriptHashTy: |
|
// 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, bip16 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 pkScript that doesn't just push data. |
|
if !isPushOnly(sigPops) { |
|
return nil, ErrStackNonPushOnly |
|
} |
|
|
|
si.ExpectedInputs = expectedInputs(pkPops, si.PkScriptClass) |
|
|
|
// All entries pushed to stack (or are OP_RESERVED and exec will fail). |
|
si.NumInputs = len(sigPops) |
|
|
|
// Count sigops taking into account pay-to-script-hash. |
|
if si.PkScriptClass == ScriptHashTy && bip16 { |
|
// 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) |
|
} else { |
|
si.SigOps = getSigOpCount(pkPops, true) |
|
} |
|
|
|
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 { |
|
return 0, 0, ErrStackUnderflow |
|
} |
|
|
|
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() |
|
} |
|
|
|
// 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() |
|
} |
|
|
|
// 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) { |
|
switch addr := addr.(type) { |
|
case *btcutil.AddressPubKeyHash: |
|
if addr == nil { |
|
return nil, ErrUnsupportedAddress |
|
} |
|
return payToPubKeyHashScript(addr.ScriptAddress()) |
|
|
|
case *btcutil.AddressScriptHash: |
|
if addr == nil { |
|
return nil, ErrUnsupportedAddress |
|
} |
|
return payToScriptHashScript(addr.ScriptAddress()) |
|
|
|
case *btcutil.AddressPubKey: |
|
if addr == nil { |
|
return nil, ErrUnsupportedAddress |
|
} |
|
return payToPubKeyScript(addr.ScriptAddress()) |
|
} |
|
|
|
return nil, ErrUnsupportedAddress |
|
} |
|
|
|
// 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 ErrBadNumRequired 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 { |
|
return nil, ErrBadNumRequired |
|
} |
|
|
|
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 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 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 |
|
}
|
|
|