mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 19:06:51 +00:00
This removes the ScriptBip16 flag from the txscript package, changes the default semantics to always enforce its behavior, and updates all callers in the repository accordingly. This change is being made to simplify the script engine code since the flag has always been active and required by consensus in Decred, so there is no need to require a flag to conditionally toggle it. Also, since it is no longer possible to invoke the script engine without the flag with the clean stack flag, it removes the now unused ErrInvalidFlags error and associated tests. It should be noted that the test removed from script_tests.json specifically dealt with ensuring a signature script that contained non-data-pushing opcodes was successful when neither the ScriptBip16 or ScriptVerifySigPushOnly flags were set. Therefore, it is no longer necessary. Finally, the P2SH indicator to enable the flag in the test data has been retained for now in order to keep the logic changes separate.
472 lines
18 KiB
Go
472 lines
18 KiB
Go
// Copyright (c) 2013-2017 The btcsuite developers
|
|
// Copyright (c) 2015-2018 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package txscript
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// ErrorCode identifies a kind of script error.
|
|
type ErrorCode int
|
|
|
|
// These constants are used to identify a specific Error.
|
|
const (
|
|
// ErrInternal is returned if internal consistency checks fail. In
|
|
// practice this error should never be seen as it would mean there is an
|
|
// error in the engine logic.
|
|
ErrInternal ErrorCode = iota
|
|
|
|
// ---------------------------------------
|
|
// Failures related to improper API usage.
|
|
// ---------------------------------------
|
|
|
|
// ErrInvalidIndex is returned when an out-of-bounds index is passed to
|
|
// a function.
|
|
ErrInvalidIndex
|
|
|
|
// ErrInvalidSigHashSingleIndex is returned when an attempt is
|
|
// made to sign an input with the SigHashSingle hash type and an
|
|
// index that is greater than or equal to the number of outputs.
|
|
ErrInvalidSigHashSingleIndex
|
|
|
|
// ErrUnsupportedAddress is returned when a concrete type that
|
|
// implements a dcrutil.Address is not a supported type.
|
|
ErrUnsupportedAddress
|
|
|
|
// ErrNotMultisigScript is returned from CalcMultiSigStats when the
|
|
// provided script is not a multisig script.
|
|
ErrNotMultisigScript
|
|
|
|
// ErrTooManyRequiredSigs is returned from MultiSigScript when the
|
|
// specified number of required signatures is larger than the number of
|
|
// provided public keys.
|
|
ErrTooManyRequiredSigs
|
|
|
|
// ErrMalformedCoinbaseNullData is returned when the nulldata output
|
|
// of a coinbase transaction that is used to ensure the coinbase has a
|
|
// unique hash is not properly formed.
|
|
ErrMalformedCoinbaseNullData
|
|
|
|
// ErrTooMuchNullData is returned from NullDataScript when the length of
|
|
// the provided data exceeds MaxDataCarrierSize.
|
|
ErrTooMuchNullData
|
|
|
|
// ------------------------------------------
|
|
// Failures related to final execution state.
|
|
// ------------------------------------------
|
|
|
|
// ErrEarlyReturn is returned when OP_RETURN is executed in the script.
|
|
ErrEarlyReturn
|
|
|
|
// ErrEmptyStack is returned when the script evaluated without error,
|
|
// but terminated with an empty top stack element.
|
|
ErrEmptyStack
|
|
|
|
// ErrEvalFalse is returned when the script evaluated without error but
|
|
// terminated with a false top stack element.
|
|
ErrEvalFalse
|
|
|
|
// ErrScriptUnfinished is returned when CheckErrorCondition is called on
|
|
// a script that has not finished executing.
|
|
ErrScriptUnfinished
|
|
|
|
// ErrScriptDone is returned when an attempt to execute an opcode is
|
|
// made once all of them have already been executed. This can happen
|
|
// due to things such as a second call to Execute or calling Step after
|
|
// all opcodes have already been executed.
|
|
ErrInvalidProgramCounter
|
|
|
|
// -----------------------------------------------------
|
|
// Failures related to exceeding maximum allowed limits.
|
|
// -----------------------------------------------------
|
|
|
|
// ErrScriptTooBig is returned if a script is larger than MaxScriptSize.
|
|
ErrScriptTooBig
|
|
|
|
// ErrElementTooBig is returned if the size of an element to be pushed
|
|
// to the stack is over MaxScriptElementSize.
|
|
ErrElementTooBig
|
|
|
|
// ErrTooManyOperations is returned if a script has more than
|
|
// MaxOpsPerScript opcodes that do not push data.
|
|
ErrTooManyOperations
|
|
|
|
// ErrStackOverflow is returned when stack and altstack combined depth
|
|
// is over the limit.
|
|
ErrStackOverflow
|
|
|
|
// ErrInvalidPubKeyCount is returned when the number of public keys
|
|
// specified for a multsig is either negative or greater than
|
|
// MaxPubKeysPerMultiSig.
|
|
ErrInvalidPubKeyCount
|
|
|
|
// ErrInvalidSignatureCount is returned when the number of signatures
|
|
// specified for a multisig is either negative or greater than the
|
|
// number of public keys.
|
|
ErrInvalidSignatureCount
|
|
|
|
// ErrNumOutOfRange is returned when the argument for an opcode that
|
|
// expects numeric input is larger than the expected maximum number of
|
|
// bytes. For the most part, opcodes that deal with stack manipulation
|
|
// via offsets, arithmetic, numeric comparison, and boolean logic are
|
|
// those that this applies to. However, any opcode that expects numeric
|
|
// input may fail with this code.
|
|
ErrNumOutOfRange
|
|
|
|
// --------------------------------------------
|
|
// Failures related to verification operations.
|
|
// --------------------------------------------
|
|
|
|
// ErrVerify is returned when OP_VERIFY is encountered in a script and
|
|
// the top item on the data stack does not evaluate to true.
|
|
ErrVerify
|
|
|
|
// ErrEqualVerify is returned when OP_EQUALVERIFY is encountered in a
|
|
// script and the top item on the data stack does not evaluate to true.
|
|
ErrEqualVerify
|
|
|
|
// ErrNumEqualVerify is returned when OP_NUMEQUALVERIFY is encountered
|
|
// in a script and the top item on the data stack does not evaluate to
|
|
// true.
|
|
ErrNumEqualVerify
|
|
|
|
// ErrCheckSigVerify is returned when OP_CHECKSIGVERIFY is encountered
|
|
// in a script and the top item on the data stack does not evaluate to
|
|
// true.
|
|
ErrCheckSigVerify
|
|
|
|
// ErrCheckSigVerify is returned when OP_CHECKMULTISIGVERIFY is
|
|
// encountered in a script and the top item on the data stack does not
|
|
// evaluate to true.
|
|
ErrCheckMultiSigVerify
|
|
|
|
// --------------------------------------------
|
|
// Failures related to improper use of opcodes.
|
|
// --------------------------------------------
|
|
|
|
// ErrP2SHStakeOpCodes is returned when one or more stake opcodes are
|
|
// found in the redeem script of a pay-to-script-hash script.
|
|
ErrP2SHStakeOpCodes
|
|
|
|
// ErrDisabledOpcode is returned when a disabled opcode is encountered
|
|
// in a script.
|
|
ErrDisabledOpcode
|
|
|
|
// ErrReservedOpcode is returned when an opcode marked as reserved
|
|
// is encountered in a script.
|
|
ErrReservedOpcode
|
|
|
|
// ErrMalformedPush is returned when a data push opcode tries to push
|
|
// more bytes than are left in the script.
|
|
ErrMalformedPush
|
|
|
|
// ErrInvalidStackOperation is returned when a stack operation is
|
|
// attempted with a number that is invalid for the current stack size.
|
|
ErrInvalidStackOperation
|
|
|
|
// ErrUnbalancedConditional is returned when an OP_ELSE or OP_ENDIF is
|
|
// encountered in a script without first having an OP_IF or OP_NOTIF or
|
|
// the end of script is reached without encountering an OP_ENDIF when
|
|
// an OP_IF or OP_NOTIF was previously encountered.
|
|
ErrUnbalancedConditional
|
|
|
|
// ErrNegativeSubstrIdx is returned when an OP_SUBSTR, OP_LEFT, or
|
|
// OP_RIGHT opcode encounters a negative index.
|
|
ErrNegativeSubstrIdx
|
|
|
|
// ErrOverflowSubstrIdx is returned when an OP_SUBSTR, OP_LEFT, or
|
|
// OP_RIGHT opcode encounters an index that is larger than the max
|
|
// allowed index that can operate on the string or the start index
|
|
// is greater than the end index for OP_SUBSTR.
|
|
ErrOverflowSubstrIdx
|
|
|
|
// ErrNegativeRotation is returned when an OP_ROTL or OP_ROTR attempts
|
|
// to perform a rotation with a negative rotation count.
|
|
ErrNegativeRotation
|
|
|
|
// ErrOverflowRotation is returned when an OP_ROTL or OP_ROTR opcode
|
|
// encounters a rotation count that is larger than the maximum allowed
|
|
// value for a uint32 bit rotation.
|
|
ErrOverflowRotation
|
|
|
|
// ErrDivideByZero is returned when an OP_DIV of OP_MOD attempts to
|
|
// divide by zero.
|
|
ErrDivideByZero
|
|
|
|
// ErrNegativeRotation is returned when an OP_LSHIFT or OP_RSHIFT opcode
|
|
// attempts to perform a shift with a negative count.
|
|
ErrNegativeShift
|
|
|
|
// ErrOverflowShift is returned when an OP_LSHIFT or OP_RSHIFT opcode
|
|
// encounters a shift count that is larger than the maximum allowed value
|
|
// for a shift.
|
|
ErrOverflowShift
|
|
|
|
// ---------------------------------
|
|
// Failures related to malleability.
|
|
// ---------------------------------
|
|
|
|
// ErrMinimalData is returned when the ScriptVerifyMinimalData flag
|
|
// is set and the script contains push operations that do not use
|
|
// the minimal opcode required.
|
|
ErrMinimalData
|
|
|
|
// ErrInvalidSigHashType is returned when a signature hash type is not
|
|
// one of the supported types.
|
|
ErrInvalidSigHashType
|
|
|
|
// ErrSigTooShort is returned when the ScriptVerifyDERSignatures flag is
|
|
// set and a signature that should be a canonically-encoded DER
|
|
// signature is too short.
|
|
ErrSigTooShort
|
|
|
|
// ErrSigTooLong is returned when the ScriptVerifyDERSignatures flag is
|
|
// set and a signature that should be a canonically-encoded DER
|
|
// signature is too long.
|
|
ErrSigTooLong
|
|
|
|
// ErrSigInvalidSeqID is returned when the ScriptVerifyDERSignatures
|
|
// flag is set and a signature that should be a canonically-encoded DER
|
|
// signature does not have the expected ASN.1 sequence ID.
|
|
ErrSigInvalidSeqID
|
|
|
|
// ErrSigInvalidDataLen is returned when the ScriptVerifyDERSignatures
|
|
// flag is set and a signature that should be a canonically-encoded DER
|
|
// signature does not specify the correct number of remaining bytes for
|
|
// the R and S portions.
|
|
ErrSigInvalidDataLen
|
|
|
|
// ErrSigMissingSTypeID is returned when the ScriptVerifyDERSignatures
|
|
// flag is set and a signature that should be a canonically-encoded DER
|
|
// signature does not provide the ASN.1 type ID for S.
|
|
ErrSigMissingSTypeID
|
|
|
|
// ErrSigMissingSLen is returned when the ScriptVerifyDERSignatures flag
|
|
// is set and a signature that should be a canonically-encoded DER
|
|
// signature does not provide the length of S.
|
|
ErrSigMissingSLen
|
|
|
|
// ErrSigInvalidSLen is returned when the ScriptVerifyDERSignatures flag
|
|
// is set and a signature that should be a canonically-encoded DER
|
|
// signature does not specify the correct number of bytes for the S
|
|
// portion.
|
|
ErrSigInvalidSLen
|
|
|
|
// ErrSigInvalidRIntID is returned when the ScriptVerifyDERSignatures
|
|
// flag is set and a signature that should be a canonically-encoded DER
|
|
// signature does not have the expected ASN.1 integer ID for R.
|
|
ErrSigInvalidRIntID
|
|
|
|
// ErrSigZeroRLen is returned when the ScriptVerifyDERSignatures flag is
|
|
// set and a signature that should be a canonically-encoded DER
|
|
// signature has an R length of zero.
|
|
ErrSigZeroRLen
|
|
|
|
// ErrSigNegativeR is returned when the ScriptVerifyDERSignatures flag
|
|
// is set and a signature that should be a canonically-encoded DER
|
|
// signature has a negative value for R.
|
|
ErrSigNegativeR
|
|
|
|
// ErrSigTooMuchRPadding is returned when the ScriptVerifyDERSignatures flag
|
|
// is set and a signature that should be a canonically-encoded DER
|
|
// signature has too much padding for R.
|
|
ErrSigTooMuchRPadding
|
|
|
|
// ErrSigInvalidSIntID is returned when the ScriptVerifyDERSignatures
|
|
// flag is set and a signature that should be a canonically-encoded DER
|
|
// signature does not have the expected ASN.1 integer ID for S.
|
|
ErrSigInvalidSIntID
|
|
|
|
// ErrSigZeroSLen is returned when the ScriptVerifyDERSignatures flag is
|
|
// set and a signature that should be a canonically-encoded DER
|
|
// signature has an S length of zero.
|
|
ErrSigZeroSLen
|
|
|
|
// ErrSigNegativeS is returned when the ScriptVerifyDERSignatures flag
|
|
// is set and a signature that should be a canonically-encoded DER
|
|
// signature has a negative value for S.
|
|
ErrSigNegativeS
|
|
|
|
// ErrSigTooMuchSPadding is returned when the ScriptVerifyDERSignatures flag
|
|
// is set and a signature that should be a canonically-encoded DER
|
|
// signature has too much padding for S.
|
|
ErrSigTooMuchSPadding
|
|
|
|
// ErrSigHighS is returned when the ScriptVerifyDERSignatures flag is
|
|
// set and a signature that should be a canonically-encoded DER signature
|
|
// has an S value that is higher than the curve half order.
|
|
ErrSigHighS
|
|
|
|
// ErrNotPushOnly is returned when a script that is required to only
|
|
// push data to the stack performs other operations. A couple of cases
|
|
// where this applies is for a pay-to-script-hash signature script when
|
|
// bip16 is active and when the ScriptVerifySigPushOnly flag is set.
|
|
ErrNotPushOnly
|
|
|
|
// ErrPubKeyType is returned when the ScriptVerifyStrictEncoding
|
|
// flag is set and the script contains invalid public keys.
|
|
ErrPubKeyType
|
|
|
|
// ErrCleanStack is returned when the ScriptVerifyCleanStack flag
|
|
// is set, and after evalution, the stack does not contain only a
|
|
// single element.
|
|
ErrCleanStack
|
|
|
|
// -------------------------------
|
|
// Failures related to soft forks.
|
|
// -------------------------------
|
|
|
|
// ErrDiscourageUpgradableNOPs is returned when the
|
|
// ScriptDiscourageUpgradableNops flag is set and a NOP opcode is
|
|
// encountered in a script.
|
|
ErrDiscourageUpgradableNOPs
|
|
|
|
// ErrNegativeLockTime is returned when a script contains an opcode that
|
|
// interprets a negative lock time.
|
|
ErrNegativeLockTime
|
|
|
|
// ErrUnsatisfiedLockTime is returned when a script contains an opcode
|
|
// that involves a lock time and the required lock time has not been
|
|
// reached.
|
|
ErrUnsatisfiedLockTime
|
|
|
|
// numErrorCodes is the maximum error code number used in tests. This
|
|
// entry MUST be the last entry in the enum.
|
|
numErrorCodes
|
|
)
|
|
|
|
// Map of ErrorCode values back to their constant names for pretty printing.
|
|
var errorCodeStrings = map[ErrorCode]string{
|
|
ErrInternal: "ErrInternal",
|
|
ErrInvalidIndex: "ErrInvalidIndex",
|
|
ErrInvalidSigHashSingleIndex: "ErrInvalidSigHashSingleIndex",
|
|
ErrUnsupportedAddress: "ErrUnsupportedAddress",
|
|
ErrNotMultisigScript: "ErrNotMultisigScript",
|
|
ErrTooManyRequiredSigs: "ErrTooManyRequiredSigs",
|
|
ErrMalformedCoinbaseNullData: "ErrMalformedCoinbaseNullData",
|
|
ErrTooMuchNullData: "ErrTooMuchNullData",
|
|
ErrEarlyReturn: "ErrEarlyReturn",
|
|
ErrEmptyStack: "ErrEmptyStack",
|
|
ErrEvalFalse: "ErrEvalFalse",
|
|
ErrScriptUnfinished: "ErrScriptUnfinished",
|
|
ErrInvalidProgramCounter: "ErrInvalidProgramCounter",
|
|
ErrScriptTooBig: "ErrScriptTooBig",
|
|
ErrElementTooBig: "ErrElementTooBig",
|
|
ErrTooManyOperations: "ErrTooManyOperations",
|
|
ErrStackOverflow: "ErrStackOverflow",
|
|
ErrInvalidPubKeyCount: "ErrInvalidPubKeyCount",
|
|
ErrInvalidSignatureCount: "ErrInvalidSignatureCount",
|
|
ErrNumOutOfRange: "ErrNumOutOfRange",
|
|
ErrVerify: "ErrVerify",
|
|
ErrEqualVerify: "ErrEqualVerify",
|
|
ErrNumEqualVerify: "ErrNumEqualVerify",
|
|
ErrCheckSigVerify: "ErrCheckSigVerify",
|
|
ErrCheckMultiSigVerify: "ErrCheckMultiSigVerify",
|
|
ErrP2SHStakeOpCodes: "ErrP2SHStakeOpCodes",
|
|
ErrDisabledOpcode: "ErrDisabledOpcode",
|
|
ErrReservedOpcode: "ErrReservedOpcode",
|
|
ErrMalformedPush: "ErrMalformedPush",
|
|
ErrInvalidStackOperation: "ErrInvalidStackOperation",
|
|
ErrUnbalancedConditional: "ErrUnbalancedConditional",
|
|
ErrNegativeSubstrIdx: "ErrNegativeSubstrIdx",
|
|
ErrSigTooMuchSPadding: "ErrSigTooMuchSPadding",
|
|
ErrOverflowSubstrIdx: "ErrOverflowSubstrIdx",
|
|
ErrNegativeRotation: "ErrNegativeRotation",
|
|
ErrOverflowRotation: "ErrOverflowRotation",
|
|
ErrDivideByZero: "ErrDivideByZero",
|
|
ErrNegativeShift: "ErrNegativeShift",
|
|
ErrOverflowShift: "ErrOverflowShift",
|
|
ErrMinimalData: "ErrMinimalData",
|
|
ErrInvalidSigHashType: "ErrInvalidSigHashType",
|
|
ErrSigTooShort: "ErrSigTooShort",
|
|
ErrSigTooLong: "ErrSigTooLong",
|
|
ErrSigInvalidSeqID: "ErrSigInvalidSeqID",
|
|
ErrSigInvalidDataLen: "ErrSigInvalidDataLen",
|
|
ErrSigMissingSTypeID: "ErrSigMissingSTypeID",
|
|
ErrSigMissingSLen: "ErrSigMissingSLen",
|
|
ErrSigInvalidSLen: "ErrSigInvalidSLen",
|
|
ErrSigInvalidRIntID: "ErrSigInvalidRIntID",
|
|
ErrSigZeroRLen: "ErrSigZeroRLen",
|
|
ErrSigNegativeR: "ErrSigNegativeR",
|
|
ErrSigTooMuchRPadding: "ErrSigTooMuchRPadding",
|
|
ErrSigInvalidSIntID: "ErrSigInvalidSIntID",
|
|
ErrSigZeroSLen: "ErrSigZeroSLen",
|
|
ErrSigNegativeS: "ErrSigNegativeS",
|
|
ErrSigHighS: "ErrSigHighS",
|
|
ErrNotPushOnly: "ErrNotPushOnly",
|
|
ErrPubKeyType: "ErrPubKeyType",
|
|
ErrCleanStack: "ErrCleanStack",
|
|
ErrDiscourageUpgradableNOPs: "ErrDiscourageUpgradableNOPs",
|
|
ErrNegativeLockTime: "ErrNegativeLockTime",
|
|
ErrUnsatisfiedLockTime: "ErrUnsatisfiedLockTime",
|
|
}
|
|
|
|
// String returns the ErrorCode as a human-readable name.
|
|
func (e ErrorCode) String() string {
|
|
if s := errorCodeStrings[e]; s != "" {
|
|
return s
|
|
}
|
|
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e))
|
|
}
|
|
|
|
// Error identifies a script-related error. It is used to indicate three
|
|
// classes of errors:
|
|
// 1) Script execution failures due to violating one of the many requirements
|
|
// imposed by the script engine or evaluating to false
|
|
// 2) Improper API usage by callers
|
|
// 3) Internal consistency check failures
|
|
//
|
|
// The caller can use type assertions on the returned errors to access the
|
|
// ErrorCode field to ascertain the specific reason for the error. As an
|
|
// additional convenience, the caller may make use of the IsErrorCode function
|
|
// to check for a specific error code.
|
|
type Error struct {
|
|
ErrorCode ErrorCode
|
|
Description string
|
|
}
|
|
|
|
// Error satisfies the error interface and prints human-readable errors.
|
|
func (e Error) Error() string {
|
|
return e.Description
|
|
}
|
|
|
|
// scriptError creates an Error given a set of arguments.
|
|
func scriptError(c ErrorCode, desc string) Error {
|
|
return Error{ErrorCode: c, Description: desc}
|
|
}
|
|
|
|
// IsErrorCode returns whether or not the provided error is a script error with
|
|
// the provided error code.
|
|
func IsErrorCode(err error, c ErrorCode) bool {
|
|
serr, ok := err.(Error)
|
|
return ok && serr.ErrorCode == c
|
|
}
|
|
|
|
// IsDERSigError returns whether or not the provided error is a script error
|
|
// with one of the error codes which are caused due to encountering a signature
|
|
// that is not a canonically-encoded DER signature.
|
|
//
|
|
// Note that the strict DER signature checks are only performed if scripts
|
|
// are executed with the ScriptVerifyDERSignatures flag.
|
|
func IsDERSigError(err error) bool {
|
|
serr, ok := err.(Error)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
switch serr.ErrorCode {
|
|
case ErrSigTooShort, ErrSigTooLong, ErrSigInvalidSeqID,
|
|
ErrSigInvalidDataLen, ErrSigMissingSTypeID, ErrSigMissingSLen,
|
|
ErrSigInvalidSLen, ErrSigInvalidRIntID, ErrSigZeroRLen, ErrSigNegativeR,
|
|
ErrSigTooMuchRPadding, ErrSigInvalidSIntID, ErrSigZeroSLen,
|
|
ErrSigNegativeS, ErrSigTooMuchSPadding, ErrSigHighS:
|
|
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|