mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* [NOD-532] Change chain to DAG in the root package. * [NOD-532] Change chain to DAG in checkpoints.go. * [NOD-532] Change chain to DAG in blockdag. * [NOD-532] Change chain to DAG in cmd. * [NOD-532] Change chain to DAG in dagconfig. * [NOD-532] Change chain to DAG in database. * [NOD-532] Change chain to DAG in mempool. * [NOD-532] Change chain to DAG in mempool. * [NOD-532] Change chain to DAG in netsync. * [NOD-532] Change chain to DAG in rpcclient. * [NOD-532] Change chain to DAG in server. * [NOD-532] Change chain to DAG in txscript. * [NOD-532] Change chain to DAG in util. * [NOD-532] Change chain to DAG in wire. * [NOD-532] Remove block heights in dagio.go examples. * [NOD-532] Rename fakeChain to fakeDAG. * [NOD-532] Fix comments, remove unused EnableBCInfoHacks flag. * [NOD-532] Fix comments and variable names. * [NOD-532] Fix comments. * [NOD-532] Fix merge errors. * [NOD-532] Formatted project.
401 lines
12 KiB
Go
401 lines
12 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/pkg/errors"
|
|
|
|
"github.com/kaspanet/kaspad/dagconfig"
|
|
"github.com/kaspanet/kaspad/util"
|
|
)
|
|
|
|
const (
|
|
// 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 = ScriptDiscourageUpgradableNops
|
|
)
|
|
|
|
// ScriptClass is an enumeration for the list of standard types of script.
|
|
type ScriptClass byte
|
|
|
|
// Classes of script payment known about in the blockDAG.
|
|
const (
|
|
NonStandardTy ScriptClass = iota // None of the recognized forms.
|
|
PubKeyHashTy // Pay pubkey hash.
|
|
ScriptHashTy // Pay to script hash.
|
|
)
|
|
|
|
// scriptClassToName houses the human-readable strings which describe each
|
|
// script class.
|
|
var scriptClassToName = []string{
|
|
NonStandardTy: "nonstandard",
|
|
PubKeyHashTy: "pubkeyhash",
|
|
ScriptHashTy: "scripthash",
|
|
}
|
|
|
|
// 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]
|
|
}
|
|
|
|
// 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 == OpDup &&
|
|
pops[1].opcode.value == OpHash160 &&
|
|
pops[2].opcode.value == OpData20 &&
|
|
pops[3].opcode.value == OpEqualVerify &&
|
|
pops[4].opcode.value == OpCheckSig
|
|
|
|
}
|
|
|
|
// scriptType returns the type of the script being inspected from the known
|
|
// standard types.
|
|
func typeOfScript(pops []parsedOpcode) ScriptClass {
|
|
if isPubkeyHash(pops) {
|
|
return PubKeyHashTy
|
|
} else if isScriptHash(pops) {
|
|
return ScriptHashTy
|
|
}
|
|
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 PubKeyHashTy:
|
|
return 2
|
|
|
|
case ScriptHashTy:
|
|
// Not including script. That is handled by the caller.
|
|
return 1
|
|
|
|
default:
|
|
return -1
|
|
}
|
|
}
|
|
|
|
// ScriptInfo houses information about a script pair that is determined by
|
|
// CalcScriptInfo.
|
|
type ScriptInfo struct {
|
|
// ScriptPubKeyClass is the class of the public key script and is equivalent
|
|
// to calling GetScriptClass on it.
|
|
ScriptPubKeyClass 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 scriptPubKey is not a push-only
|
|
// script
|
|
func CalcScriptInfo(sigScript, scriptPubKey []byte, isP2SH bool) (*ScriptInfo, error) {
|
|
sigPops, err := parseScript(sigScript)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
scriptPubKeyPops, err := parseScript(scriptPubKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Push only sigScript makes little sense.
|
|
si := new(ScriptInfo)
|
|
si.ScriptPubKeyClass = typeOfScript(scriptPubKeyPops)
|
|
|
|
// 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(scriptPubKeyPops, si.ScriptPubKeyClass)
|
|
|
|
// All entries pushed to stack (or are OP_RESERVED and exec will fail).
|
|
si.NumInputs = len(sigPops)
|
|
|
|
if si.ScriptPubKeyClass == ScriptHashTy && isP2SH {
|
|
// 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(scriptPubKeyPops, true)
|
|
}
|
|
|
|
return si, 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(OpDup).AddOp(OpHash160).
|
|
AddData(pubKeyHash).AddOp(OpEqualVerify).AddOp(OpCheckSig).
|
|
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(OpHash160).AddData(scriptHash).
|
|
AddOp(OpEqual).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(OpCheckSig).Script()
|
|
}
|
|
|
|
// PayToAddrScript creates a new script to pay a transaction output to a the
|
|
// specified address.
|
|
func PayToAddrScript(addr util.Address) ([]byte, error) {
|
|
const nilAddrErrStr = "unable to generate payment script for nil address"
|
|
|
|
switch addr := addr.(type) {
|
|
case *util.AddressPubKeyHash:
|
|
if addr == nil {
|
|
return nil, scriptError(ErrUnsupportedAddress,
|
|
nilAddrErrStr)
|
|
}
|
|
return payToPubKeyHashScript(addr.ScriptAddress())
|
|
|
|
case *util.AddressScriptHash:
|
|
if addr == nil {
|
|
return nil, scriptError(ErrUnsupportedAddress,
|
|
nilAddrErrStr)
|
|
}
|
|
return payToScriptHashScript(addr.ScriptAddress())
|
|
}
|
|
|
|
str := fmt.Sprintf("unable to generate payment script for unsupported "+
|
|
"address type %T", addr)
|
|
return nil, scriptError(ErrUnsupportedAddress, str)
|
|
}
|
|
|
|
// PayToScriptHashScript takes a script and returns an equivalent pay-to-script-hash script
|
|
func PayToScriptHashScript(redeemScript []byte) ([]byte, error) {
|
|
redeemScriptHash := util.Hash160(redeemScript)
|
|
script, err := NewScriptBuilder().
|
|
AddOp(OpHash160).AddData(redeemScriptHash).
|
|
AddOp(OpEqual).Script()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return script, nil
|
|
}
|
|
|
|
// PayToScriptHashSignatureScript generates a signature script that fits a pay-to-script-hash script
|
|
func PayToScriptHashSignatureScript(redeemScript []byte, signature []byte) ([]byte, error) {
|
|
redeemScriptAsData, err := NewScriptBuilder().AddData(redeemScript).Script()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
signatureScript := make([]byte, len(signature)+len(redeemScriptAsData))
|
|
copy(signatureScript, signature)
|
|
copy(signatureScript[len(signature):], redeemScriptAsData)
|
|
return signatureScript, nil
|
|
}
|
|
|
|
// 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 == Op0 {
|
|
data = append(data, nil)
|
|
}
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// ExtractScriptPubKeyAddress returns the type of script and its addresses.
|
|
// Note that it only works for 'standard' transaction script types. Any data such
|
|
// as public keys which are invalid will return a nil address.
|
|
func ExtractScriptPubKeyAddress(scriptPubKey []byte, dagParams *dagconfig.Params) (ScriptClass, util.Address, error) {
|
|
// No valid address if the script doesn't parse.
|
|
pops, err := parseScript(scriptPubKey)
|
|
if err != nil {
|
|
return NonStandardTy, nil, 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.
|
|
// If the pubkey hash is invalid for some reason, return a nil address.
|
|
addr, err := util.NewAddressPubKeyHash(pops[2].data,
|
|
dagParams.Prefix)
|
|
if err != nil {
|
|
return scriptClass, nil, nil
|
|
}
|
|
return scriptClass, addr, nil
|
|
|
|
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.
|
|
// If the script hash ss invalid for some reason, return a nil address.
|
|
addr, err := util.NewAddressScriptHashFromHash(pops[1].data,
|
|
dagParams.Prefix)
|
|
if err != nil {
|
|
return scriptClass, nil, nil
|
|
}
|
|
return scriptClass, addr, nil
|
|
|
|
case NonStandardTy:
|
|
// Don't attempt to extract addresses or required signatures for
|
|
// nonstandard transactions.
|
|
return NonStandardTy, nil, nil
|
|
}
|
|
|
|
return NonStandardTy, nil, errors.Errorf("Cannot handle script class %s", scriptClass)
|
|
}
|
|
|
|
// AtomicSwapDataPushes houses the data pushes found in atomic swap contracts.
|
|
type AtomicSwapDataPushes struct {
|
|
RecipientHash160 [20]byte
|
|
RefundHash160 [20]byte
|
|
SecretHash [32]byte
|
|
SecretSize int64
|
|
LockTime uint64
|
|
}
|
|
|
|
// ExtractAtomicSwapDataPushes returns the data pushes from an atomic swap
|
|
// contract. If the script is not an atomic swap contract,
|
|
// ExtractAtomicSwapDataPushes returns (nil, nil). Non-nil errors are returned
|
|
// for unparsable scripts.
|
|
//
|
|
// NOTE: Atomic swaps are not considered standard script types by the dcrd
|
|
// mempool policy and should be used with P2SH. The atomic swap format is also
|
|
// expected to change to use a more secure hash function in the future.
|
|
//
|
|
// This function is only defined in the txscript package due to API limitations
|
|
// which prevent callers using txscript to parse nonstandard scripts.
|
|
func ExtractAtomicSwapDataPushes(version uint16, scriptPubKey []byte) (*AtomicSwapDataPushes, error) {
|
|
pops, err := parseScript(scriptPubKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(pops) != 20 {
|
|
return nil, nil
|
|
}
|
|
isAtomicSwap := pops[0].opcode.value == OpIf &&
|
|
pops[1].opcode.value == OpSize &&
|
|
canonicalPush(pops[2]) &&
|
|
pops[3].opcode.value == OpEqualVerify &&
|
|
pops[4].opcode.value == OpSHA256 &&
|
|
pops[5].opcode.value == OpData32 &&
|
|
pops[6].opcode.value == OpEqualVerify &&
|
|
pops[7].opcode.value == OpDup &&
|
|
pops[8].opcode.value == OpHash160 &&
|
|
pops[9].opcode.value == OpData20 &&
|
|
pops[10].opcode.value == OpElse &&
|
|
canonicalPush(pops[11]) &&
|
|
pops[12].opcode.value == OpCheckLockTimeVerify &&
|
|
pops[13].opcode.value == OpDrop &&
|
|
pops[14].opcode.value == OpDup &&
|
|
pops[15].opcode.value == OpHash160 &&
|
|
pops[16].opcode.value == OpData20 &&
|
|
pops[17].opcode.value == OpEndIf &&
|
|
pops[18].opcode.value == OpEqualVerify &&
|
|
pops[19].opcode.value == OpCheckSig
|
|
if !isAtomicSwap {
|
|
return nil, nil
|
|
}
|
|
|
|
pushes := new(AtomicSwapDataPushes)
|
|
copy(pushes.SecretHash[:], pops[5].data)
|
|
copy(pushes.RecipientHash160[:], pops[9].data)
|
|
copy(pushes.RefundHash160[:], pops[16].data)
|
|
if pops[2].data != nil {
|
|
locktime, err := makeScriptNum(pops[2].data, 5)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
pushes.SecretSize = int64(locktime)
|
|
} else if op := pops[2].opcode; isSmallInt(op) {
|
|
pushes.SecretSize = int64(asSmallInt(op))
|
|
} else {
|
|
return nil, nil
|
|
}
|
|
if pops[11].data != nil {
|
|
locktime, err := makeScriptNum(pops[11].data, 5)
|
|
if err != nil {
|
|
return nil, nil
|
|
}
|
|
pushes.LockTime = uint64(locktime)
|
|
} else if op := pops[11].opcode; isSmallInt(op) {
|
|
pushes.LockTime = uint64(asSmallInt(op))
|
|
} else {
|
|
return nil, nil
|
|
}
|
|
return pushes, nil
|
|
}
|