diff --git a/bscript/interpreter/engine.go b/bscript/interpreter/engine.go index bc04038b..25bc7947 100644 --- a/bscript/interpreter/engine.go +++ b/bscript/interpreter/engine.go @@ -5,104 +5,6 @@ package interpreter -import ( - "math/big" - - "github.com/libsv/go-bk/bec" -) - -// ScriptFlags is a bitmask defining additional operations or tests that will be -// done when executing a script pair. -type ScriptFlags uint32 - -const ( - // ScriptBip16 defines whether the bip16 threshold has passed and thus - // pay-to-script hash transactions will be fully validated. - ScriptBip16 ScriptFlags = 1 << iota - - // ScriptStrictMultiSig defines whether to verify the stack item - // used by CHECKMULTISIG is zero length. - ScriptStrictMultiSig - - // ScriptDiscourageUpgradableNops defines whether to verify that - // NOP1 through NOP10 are reserved for future soft-fork upgrades. This - // flag must not be used for consensus critical code nor applied to - // blocks as this flag is only for stricter standard transaction - // checks. This flag is only applied when the above opcodes are - // executed. - ScriptDiscourageUpgradableNops - - // ScriptVerifyCheckLockTimeVerify defines whether to verify that - // a transaction output is spendable based on the locktime. - // This is BIP0065. - ScriptVerifyCheckLockTimeVerify - - // ScriptVerifyCheckSequenceVerify defines whether to allow execution - // pathways of a script to be restricted based on the age of the output - // being spent. This is BIP0112. - ScriptVerifyCheckSequenceVerify - - // ScriptVerifyCleanStack defines that the stack must contain only - // one stack element after evaluation and that the element must be - // true if interpreted as a boolean. This is rule 6 of BIP0062. - // This flag should never be used without the ScriptBip16 flag. - ScriptVerifyCleanStack - - // ScriptVerifyDERSignatures defines that signatures are required - // to comply with the DER format. - ScriptVerifyDERSignatures - - // ScriptVerifyLowS defines that signatures are required to comply with - // the DER format and whose S value is <= order / 2. This is rule 5 - // of BIP0062. - ScriptVerifyLowS - - // ScriptVerifyMinimalData defines that signatures must use the smallest - // push operator. This is both rules 3 and 4 of BIP0062. - ScriptVerifyMinimalData - - // ScriptVerifyNullFail defines that signatures must be empty if - // a CHECKSIG or CHECKMULTISIG operation fails. - ScriptVerifyNullFail - - // ScriptVerifySigPushOnly defines that signature scripts must contain - // only pushed data. This is rule 2 of BIP0062. - ScriptVerifySigPushOnly - - // ScriptEnableSighashForkID defined that signature scripts have forkid - // enabled. - ScriptEnableSighashForkID - - // ScriptVerifyStrictEncoding defines that signature scripts and - // public keys must follow the strict encoding requirements. - ScriptVerifyStrictEncoding - - // ScriptVerifyBip143SigHash defines that signature hashes should - // be calculated using the bip0143 signature hashing algorithm. - ScriptVerifyBip143SigHash - - // ScriptUTXOAfterGenesis defines that the utxo was created after - // genesis. - ScriptUTXOAfterGenesis - - // ScriptVerifyMinimalIf defines the enforcement of any conditional statement using the - // minimum required data. - ScriptVerifyMinimalIf -) - -// HasFlag returns whether the ScriptFlags has the passed flag set. -func (s ScriptFlags) HasFlag(flag ScriptFlags) bool { - return s&flag == flag -} - -// AddFlag adds the passed flag to ScriptFlags -func (s *ScriptFlags) AddFlag(flag ScriptFlags) { - *s |= flag -} - -// halfOrder is used to tame ECDSA malleability (see BIP0062). -var halfOrder = new(big.Int).Rsh(bec.S256().N, 1) - // Engine is the virtual machine that executes scripts. type Engine interface { Execute(ExecutionParams) error @@ -122,7 +24,7 @@ func NewEngine() Engine { // for successful validation or an error if one occurred. func (e *engine) Execute(params ExecutionParams) error { th := &thread{ - scriptParser: &parser{}, + scriptParser: &DefaultOpcodeParser{}, cfg: &beforeGenesisConfig{}, } diff --git a/bscript/interpreter/engine_test.go b/bscript/interpreter/engine_test.go index d3384a93..1cc90be5 100644 --- a/bscript/interpreter/engine_test.go +++ b/bscript/interpreter/engine_test.go @@ -9,6 +9,8 @@ import ( "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" + "github.com/libsv/go-bt/v2/bscript/interpreter/scriptflag" "github.com/libsv/go-bt/v2/sighash" ) @@ -52,7 +54,7 @@ func TestBadPC(t *testing.T) { for _, test := range tests { vm := &thread{ - scriptParser: &parser{}, + scriptParser: &DefaultOpcodeParser{}, cfg: &beforeGenesisConfig{}, } err := vm.apply(ExecutionParams{ @@ -108,7 +110,7 @@ func TestCheckErrorCondition(t *testing.T) { } vm := &thread{ - scriptParser: &parser{}, + scriptParser: &DefaultOpcodeParser{}, cfg: &beforeGenesisConfig{}, } @@ -142,8 +144,8 @@ func TestCheckErrorCondition(t *testing.T) { func TestInvalidFlagCombinations(t *testing.T) { t.Parallel() - tests := []ScriptFlags{ - ScriptVerifyCleanStack, + tests := []scriptflag.Flag{ + scriptflag.VerifyCleanStack, } uls, err := bscript.NewFromASM("OP_NOP") @@ -174,7 +176,7 @@ func TestInvalidFlagCombinations(t *testing.T) { for i, test := range tests { vm := &thread{ - scriptParser: &parser{}, + scriptParser: &DefaultOpcodeParser{}, cfg: &beforeGenesisConfig{}, } err := vm.apply(ExecutionParams{ @@ -183,7 +185,7 @@ func TestInvalidFlagCombinations(t *testing.T) { PreviousTxOut: txOut, Flags: test, }) - if !IsErrorCode(err, ErrInvalidFlags) { + if !errs.IsErrorCode(err, errs.ErrInvalidFlags) { t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+ "error: %v", i, err) } @@ -235,7 +237,7 @@ func TestCheckPubKeyEncoding(t *testing.T) { }, } - vm := thread{flags: ScriptVerifyStrictEncoding} + vm := thread{scriptflag: scriptflag.VerifyStrictEncoding} for _, test := range tests { err := vm.checkPubKeyEncoding(test.key) if err != nil && test.isValid { @@ -407,7 +409,7 @@ func TestCheckSignatureEncoding(t *testing.T) { }, } - vm := thread{flags: ScriptVerifyStrictEncoding} + vm := thread{scriptflag: scriptflag.VerifyStrictEncoding} for _, test := range tests { err := vm.checkSignatureEncoding(test.sig) if err != nil && test.isValid { @@ -425,140 +427,140 @@ func TestCheckHashTypeEncoding(t *testing.T) { var SigHashBug sighash.Flag = 0x20 encodingTests := []struct { SigHash sighash.Flag - EngineFlags ScriptFlags + EngineFlags scriptflag.Flag ShouldFail bool }{ { sighash.All, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, false, }, { sighash.None, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, false, }, { sighash.Single, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, false, }, { sighash.All | sighash.AnyOneCanPay, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, false, }, { sighash.None | sighash.AnyOneCanPay, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, false, }, { sighash.Single | sighash.AnyOneCanPay, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, false, }, { sighash.All | sighash.ForkID, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, true, }, { sighash.None | sighash.ForkID, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, true, }, { sighash.Single | sighash.ForkID, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, true, }, { sighash.All | sighash.AnyOneCanPay | sighash.ForkID, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, true, }, { sighash.None | sighash.AnyOneCanPay | sighash.ForkID, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, true, }, { sighash.Single | sighash.AnyOneCanPay | sighash.ForkID, - ScriptVerifyStrictEncoding, + scriptflag.VerifyStrictEncoding, true, }, { sighash.All | sighash.ForkID, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, false, }, { sighash.None | sighash.ForkID, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, false, }, { sighash.Single | sighash.ForkID, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, false, }, { sighash.All | sighash.AnyOneCanPay | sighash.ForkID, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, false, }, { sighash.None | sighash.AnyOneCanPay | sighash.ForkID, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, false, }, { sighash.Single | sighash.AnyOneCanPay | sighash.ForkID, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, false, }, { sighash.All, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, true, }, { sighash.None, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, true, }, { sighash.Single, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, true, }, { sighash.All | sighash.AnyOneCanPay, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, true, }, { sighash.None | sighash.AnyOneCanPay, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, true, }, { sighash.Single | sighash.AnyOneCanPay, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, true, }, { sighash.Single | sighash.AnyOneCanPay | sighash.ForkID | SigHashBug, - ScriptVerifyStrictEncoding | ScriptVerifyBip143SigHash, + scriptflag.VerifyStrictEncoding | scriptflag.VerifyBip143SigHash, true, }, } for i, test := range encodingTests { - e := thread{flags: test.EngineFlags} + e := thread{scriptflag: test.EngineFlags} err := e.checkHashTypeEncoding(test.SigHash) if test.ShouldFail && err == nil { t.Errorf("Expected test %d to fail", i) diff --git a/bscript/interpreter/error.go b/bscript/interpreter/errs/error.go similarity index 96% rename from bscript/interpreter/error.go rename to bscript/interpreter/errs/error.go index f9fc71b2..d0136bf9 100644 --- a/bscript/interpreter/error.go +++ b/bscript/interpreter/errs/error.go @@ -2,13 +2,11 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package interpreter +package errs import ( "errors" "fmt" - - "github.com/libsv/go-bt/v2/bscript" ) // ErrorCode identifies a kind of script error. @@ -421,8 +419,8 @@ func (e Error) Error() string { return e.Description } -// scriptError creates an Error given a set of arguments. -func scriptError(c ErrorCode, desc string, fmtArgs ...interface{}) Error { +// NewError creates an Error given a set of arguments. +func NewError(c ErrorCode, desc string, fmtArgs ...interface{}) Error { return Error{ErrorCode: c, Description: fmt.Sprintf(desc, fmtArgs...)} } @@ -433,12 +431,3 @@ func IsErrorCode(err error, c ErrorCode) bool { ok := errors.As(err, e) return ok && e.ErrorCode == c } - -// NewErrMinimalDataPush returns an Error with code ErrMinimalData -func NewErrMinimalDataPush(op, expected opcode, length int) Error { - fmtStr := "data push of %d bytes encoded with opcode %s, use %s instead" - if (bscript.Op1 <= op.val && op.val <= bscript.Op16) || op.val == bscript.Op1NEGATE { - fmtStr = "data push of value %d encoded with opcode %s, use %s instead" - } - return scriptError(ErrMinimalData, fmt.Sprintf(fmtStr, length, op.Name(), expected.Name())) -} diff --git a/bscript/interpreter/error_test.go b/bscript/interpreter/errs/error_test.go similarity index 99% rename from bscript/interpreter/error_test.go rename to bscript/interpreter/errs/error_test.go index 354a0235..d09461cf 100644 --- a/bscript/interpreter/error_test.go +++ b/bscript/interpreter/errs/error_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package interpreter +package errs import ( "testing" diff --git a/bscript/interpreter/example_test.go b/bscript/interpreter/example_test.go index 0e955238..ab4b9298 100644 --- a/bscript/interpreter/example_test.go +++ b/bscript/interpreter/example_test.go @@ -7,6 +7,7 @@ import ( "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript/interpreter" + "github.com/libsv/go-bt/v2/bscript/interpreter/scriptflag" "golang.org/x/sync/errgroup" ) @@ -46,7 +47,7 @@ func ExampleEngine_Execute() { Tx: tx, InputIdx: inputIdx, PreviousTxOut: prevOutput, - Flags: interpreter.ScriptEnableSighashForkID | interpreter.ScriptUTXOAfterGenesis, + Flags: scriptflag.EnableSighashForkID | scriptflag.UTXOAfterGenesis, }); err != nil { fmt.Println(err) return @@ -77,7 +78,7 @@ func ExampleEngine_Execute_error() { Tx: tx, InputIdx: inputIdx, PreviousTxOut: prevOutput, - Flags: interpreter.ScriptEnableSighashForkID | interpreter.ScriptUTXOAfterGenesis, + Flags: scriptflag.EnableSighashForkID | scriptflag.UTXOAfterGenesis, }); err != nil { fmt.Println(err) return diff --git a/bscript/interpreter/opcodeparser.go b/bscript/interpreter/opcodeparser.go index 3cad2983..dcbe767b 100644 --- a/bscript/interpreter/opcodeparser.go +++ b/bscript/interpreter/opcodeparser.go @@ -3,9 +3,9 @@ package interpreter import ( "bytes" "encoding/binary" - "fmt" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" ) // OpcodeParser parses *bscript.Script into a ParsedScript, and unparsing back @@ -17,12 +17,8 @@ type OpcodeParser interface { // ParsedScript is a slice of ParsedOp type ParsedScript []ParsedOp -type parser struct{} - -// NewOpcodeParser returns an OpcodeParser -func NewOpcodeParser() OpcodeParser { - return &parser{} -} +// DefaultOpcodeParser is a standard parser which can be used from zero value. +type DefaultOpcodeParser struct{} // ParsedOp is a parsed opcode type ParsedOp struct { @@ -70,31 +66,51 @@ func (o *ParsedOp) IsConditional() bool { func (o *ParsedOp) EnforceMinimumDataPush() error { dataLen := len(o.Data) if dataLen == 0 && o.Op.val != bscript.Op0 { - return NewErrMinimalDataPush(o.Op, opcodeArray[bscript.Op0], 0) + return errs.NewError( + errs.ErrMinimalData, + "zero length data push is encoded with opcode %s instead of OP_0", + o.Op.name, + ) } if dataLen == 1 && (1 <= o.Data[0] && o.Data[0] <= 16) && o.Op.val != bscript.Op1+o.Data[0]-1 { - return NewErrMinimalDataPush(o.Op, opcodeArray[bscript.Op1+o.Data[0]-1], int(o.Data[0])) + return errs.NewError( + errs.ErrMinimalData, + "data push of the value %d encoded with opcode %s instead of OP_%d", o.Data[0], o.Op.name, o.Data[0], + ) } if dataLen == 1 && o.Data[0] == 0x81 && o.Op.val != bscript.Op1NEGATE { - return NewErrMinimalDataPush(o.Op, opcodeArray[bscript.Op1NEGATE], -1) + return errs.NewError( + errs.ErrMinimalData, + "data push of the value -1 encoded with opcode %s instead of OP_1NEGATE", o.Op.name, + ) } if dataLen <= 75 { if int(o.Op.val) != dataLen { - return NewErrMinimalDataPush(o.Op, opcodeArray[byte(dataLen)], dataLen) + return errs.NewError( + errs.ErrMinimalData, + "data push of %d bytes encoded with opcode %s instead of OP_DATA_%d", dataLen, o.Op.name, dataLen, + ) } } else if dataLen <= 255 { if o.Op.val != bscript.OpPUSHDATA1 { - return NewErrMinimalDataPush(o.Op, opcodeArray[bscript.OpPUSHDATA1], dataLen) + return errs.NewError( + errs.ErrMinimalData, + "data push of %d bytes encoded with opcode %s instead of OP_PUSHDATA1", dataLen, o.Op.name, + ) } } else if dataLen <= 65535 { if o.Op.val != bscript.OpPUSHDATA2 { - return NewErrMinimalDataPush(o.Op, opcodeArray[bscript.OpPUSHDATA2], dataLen) + return errs.NewError( + errs.ErrMinimalData, + "data push of %d bytes encoded with opcode %s instead of OP_PUSHDATA2", dataLen, o.Op.name, + ) } } return nil } -func (p *parser) Parse(s *bscript.Script) (ParsedScript, error) { +// Parse takes a *bscript.Script and returns a []interpreter.ParsedOp +func (p *DefaultOpcodeParser) Parse(s *bscript.Script) (ParsedScript, error) { script := *s parsedOps := make([]ParsedOp, 0, len(script)) @@ -108,8 +124,8 @@ func (p *parser) Parse(s *bscript.Script) (ParsedScript, error) { i++ case parsedOp.Op.length > 1: if len(script[i:]) < parsedOp.Op.length { - return nil, scriptError(ErrMalformedPush, fmt.Sprintf("opcode %s required %d bytes, script has %d remaining", - parsedOp.Name(), parsedOp.Op.length, len(script[i:]))) + return nil, errs.NewError(errs.ErrMalformedPush, "opcode %s required %d bytes, script has %d remaining", + parsedOp.Name(), parsedOp.Op.length, len(script[i:])) } parsedOp.Data = script[i+1 : i+parsedOp.Op.length] i += parsedOp.Op.length @@ -117,8 +133,8 @@ func (p *parser) Parse(s *bscript.Script) (ParsedScript, error) { var l uint offset := i + 1 if len(script[offset:]) < -parsedOp.Op.length { - return nil, scriptError(ErrMalformedPush, fmt.Sprintf("opcode %s required %d bytes, script has %d remaining", - parsedOp.Name(), parsedOp.Op.length, len(script[offset:]))) + return nil, errs.NewError(errs.ErrMalformedPush, "opcode %s required %d bytes, script has %d remaining", + parsedOp.Name(), parsedOp.Op.length, len(script[offset:])) } // Next -length bytes are little endian length of data. switch parsedOp.Op.length { @@ -133,13 +149,13 @@ func (p *parser) Parse(s *bscript.Script) (ParsedScript, error) { (uint(script[offset+1]) << 8) | uint(script[offset])) default: - return nil, scriptError(ErrMalformedPush, fmt.Sprintf("invalid opcode length %d", parsedOp.Op.length)) + return nil, errs.NewError(errs.ErrMalformedPush, "invalid opcode length %d", parsedOp.Op.length) } offset += -parsedOp.Op.length if int(l) > len(script[offset:]) || int(l) < 0 { - return nil, scriptError(ErrMalformedPush, fmt.Sprintf("opcode %s pushes %d bytes, script has %d remaining", - parsedOp.Name(), l, len(script[offset:]))) + return nil, errs.NewError(errs.ErrMalformedPush, "opcode %s pushes %d bytes, script has %d remaining", + parsedOp.Name(), l, len(script[offset:])) } parsedOp.Data = script[offset : offset+int(l)] @@ -151,9 +167,9 @@ func (p *parser) Parse(s *bscript.Script) (ParsedScript, error) { return parsedOps, nil } -// Unparse reversed the action of parseScript and returns the -// parsedOpcodes as a list of bytes -func (p *parser) Unparse(pscr ParsedScript) (*bscript.Script, error) { +// Unparse reverses the action of Parse and returns the +// ParsedScript as a *bscript.Script +func (p *DefaultOpcodeParser) Unparse(pscr ParsedScript) (*bscript.Script, error) { script := make(bscript.Script, 0, len(pscr)) for _, pop := range pscr { b, err := pop.bytes() @@ -240,11 +256,11 @@ func (o *ParsedOp) bytes() ([]byte, error) { retbytes[0] = o.Op.val if o.Op.length == 1 { if len(o.Data) != 0 { - str := fmt.Sprintf("internal consistency error - "+ - "parsed opcode %s has data length %d when %d "+ - "was expected", o.Name(), len(o.Data), - 0) - return nil, scriptError(ErrInternal, str) + return nil, errs.NewError( + errs.ErrInternal, + "internal consistency error - parsed opcode %s has data length %d when %d was expected", + o.Name(), len(o.Data), 0, + ) } return retbytes, nil } @@ -273,10 +289,10 @@ func (o *ParsedOp) bytes() ([]byte, error) { retbytes = append(retbytes, o.Data...) if len(retbytes) != nbytes { - str := fmt.Sprintf("internal consistency error - "+ - "parsed opcode %s has data length %d when %d was "+ - "expected", o.Name(), len(retbytes), nbytes) - return nil, scriptError(ErrInternal, str) + return nil, errs.NewError(errs.ErrInternal, + "internal consistency error - parsed opcode %s has data length %d when %d was expected", + o.Name(), len(retbytes), nbytes, + ) } return retbytes, nil diff --git a/bscript/interpreter/opcodeparser_test.go b/bscript/interpreter/opcodeparser_test.go index 700133a3..78d5bcf6 100644 --- a/bscript/interpreter/opcodeparser_test.go +++ b/bscript/interpreter/opcodeparser_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" ) // TestOpcodeDisabled tests the opcodeDisabled function manually because all @@ -20,9 +21,9 @@ func TestOpcodeDisabled(t *testing.T) { for _, opcodeVal := range tests { pop := ParsedOp{Op: opcodeArray[opcodeVal], Data: nil} err := opcodeDisabled(&pop, nil) - if !IsErrorCode(err, ErrDisabledOpcode) { + if !errs.IsErrorCode(err, errs.ErrDisabledOpcode) { t.Errorf("opcodeDisabled: unexpected error - got %v, "+ - "want %v", err, ErrDisabledOpcode) + "want %v", err, errs.ErrDisabledOpcode) continue } } diff --git a/bscript/interpreter/operations.go b/bscript/interpreter/operations.go index c56a8b7c..c1731465 100644 --- a/bscript/interpreter/operations.go +++ b/bscript/interpreter/operations.go @@ -4,22 +4,23 @@ import ( "bytes" "crypto/sha1" //nolint:gosec // OP_SHA1 support requires this "crypto/sha256" - "fmt" "hash" "github.com/libsv/go-bk/bec" "github.com/libsv/go-bk/crypto" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" + "github.com/libsv/go-bt/v2/bscript/interpreter/scriptflag" "github.com/libsv/go-bt/v2/sighash" "golang.org/x/crypto/ripemd160" ) // Conditional execution constants. const ( - OpCondFalse = 0 - OpCondTrue = 1 - OpCondSkip = 2 + opCondFalse = 0 + opCondTrue = 1 + opCondSkip = 2 ) type opcode struct { @@ -325,7 +326,7 @@ var opcodeArray = [256]opcode{ // dictate the script doesn't fail until the program counter passes over a // disabled opcode (even when they appear in a branch that is not executed). func opcodeDisabled(op *ParsedOp, t *thread) error { - return scriptError(ErrDisabledOpcode, "attempt to execute disabled opcode %s", op.Name()) + return errs.NewError(errs.ErrDisabledOpcode, "attempt to execute disabled opcode %s", op.Name()) } func opcodeVerConditional(op *ParsedOp, t *thread) error { @@ -338,13 +339,13 @@ func opcodeVerConditional(op *ParsedOp, t *thread) error { // opcodeReserved is a common handler for all reserved opcodes. It returns an // appropriate error indicating the opcode is reserved. func opcodeReserved(op *ParsedOp, t *thread) error { - return scriptError(ErrReservedOpcode, "attempt to execute reserved opcode %s", op.Name()) + return errs.NewError(errs.ErrReservedOpcode, "attempt to execute reserved opcode %s", op.Name()) } // opcodeInvalid is a common handler for all invalid opcodes. It returns an // appropriate error indicating the opcode is invalid. func opcodeInvalid(op *ParsedOp, t *thread) error { - return scriptError(ErrReservedOpcode, "attempt to execute invalid opcode %s", op.Name()) + return errs.NewError(errs.ErrReservedOpcode, "attempt to execute invalid opcode %s", op.Name()) } // opcodeFalse pushes an empty array to the data stack to represent false. Note @@ -385,9 +386,9 @@ func opcodeNop(op *ParsedOp, t *thread) error { switch op.Op.val { case bscript.OpNOP1, bscript.OpNOP4, bscript.OpNOP5, bscript.OpNOP6, bscript.OpNOP7, bscript.OpNOP8, bscript.OpNOP9, bscript.OpNOP10: - if t.hasFlag(ScriptDiscourageUpgradableNops) { - return scriptError( - ErrDiscourageUpgradableNOPs, + if t.hasFlag(scriptflag.DiscourageUpgradableNops) { + return errs.NewError( + errs.ErrDiscourageUpgradableNOPs, "bscript.OpNOP%d reserved for soft-fork upgrades", op.Op.val-(bscript.OpNOP1-1), ) @@ -399,17 +400,17 @@ func opcodeNop(op *ParsedOp, t *thread) error { // popIfBool pops the top item off the stack and returns a bool func popIfBool(t *thread) (bool, error) { - if t.hasFlag(ScriptVerifyMinimalIf) { + if t.hasFlag(scriptflag.VerifyMinimalIf) { b, err := t.dstack.PopByteArray() if err != nil { return false, err } if len(b) > 1 { - return false, scriptError(ErrMinimalIf, "conditionl has data of length %d", len(b)) + return false, errs.NewError(errs.ErrMinimalIf, "conditionl has data of length %d", len(b)) } if len(b) == 1 && b[0] != 1 { - return false, scriptError(ErrMinimalIf, "conditional failed") + return false, errs.NewError(errs.ErrMinimalIf, "conditional failed") } return asBool(b), nil @@ -434,7 +435,7 @@ func popIfBool(t *thread) (bool, error) { // Data stack transformation: [... bool] -> [...] // Conditional stack transformation: [...] -> [... OpCondValue] func opcodeIf(op *ParsedOp, t *thread) error { - condVal := OpCondFalse + condVal := opCondFalse if t.shouldExec(*op) { if t.isBranchExecuting() { ok, err := popIfBool(t) @@ -443,10 +444,10 @@ func opcodeIf(op *ParsedOp, t *thread) error { } if ok { - condVal = OpCondTrue + condVal = opCondTrue } } else { - condVal = OpCondSkip + condVal = opCondSkip } } @@ -472,7 +473,7 @@ func opcodeIf(op *ParsedOp, t *thread) error { // Data stack transformation: [... bool] -> [...] // Conditional stack transformation: [...] -> [... OpCondValue] func opcodeNotIf(op *ParsedOp, t *thread) error { - condVal := OpCondFalse + condVal := opCondFalse if t.shouldExec(*op) { if t.isBranchExecuting() { ok, err := popIfBool(t) @@ -481,10 +482,10 @@ func opcodeNotIf(op *ParsedOp, t *thread) error { } if !ok { - condVal = OpCondTrue + condVal = opCondTrue } } else { - condVal = OpCondSkip + condVal = opCondSkip } } @@ -500,7 +501,7 @@ func opcodeNotIf(op *ParsedOp, t *thread) error { // Conditional stack transformation: [... OpCondValue] -> [... !OpCondValue] func opcodeElse(op *ParsedOp, t *thread) error { if len(t.condStack) == 0 { - return scriptError(ErrUnbalancedConditional, + return errs.NewError(errs.ErrUnbalancedConditional, "encountered opcode %s with no matching opcode to begin conditional execution", op.Name()) } @@ -510,17 +511,17 @@ func opcodeElse(op *ParsedOp, t *thread) error { return err } if ok { - return scriptError(ErrUnbalancedConditional, + return errs.NewError(errs.ErrUnbalancedConditional, "encountered opcode %s with no matching opcode to begin conditional execution", op.Name()) } conditionalIdx := len(t.condStack) - 1 switch t.condStack[conditionalIdx] { - case OpCondTrue: - t.condStack[conditionalIdx] = OpCondFalse - case OpCondFalse: - t.condStack[conditionalIdx] = OpCondTrue - case OpCondSkip: + case opCondTrue: + t.condStack[conditionalIdx] = opCondFalse + case opCondFalse: + t.condStack[conditionalIdx] = opCondTrue + case opCondSkip: // Value doesn't change in skip since it indicates this opcode // is nested in a non-executed branch. } @@ -537,7 +538,7 @@ func opcodeElse(op *ParsedOp, t *thread) error { // Conditional stack transformation: [... OpCondValue] -> [...] func opcodeEndif(op *ParsedOp, t *thread) error { if len(t.condStack) == 0 { - return scriptError(ErrUnbalancedConditional, + return errs.NewError(errs.ErrUnbalancedConditional, "encountered opcode %s with no matching opcode to begin conditional execution", op.Name()) } @@ -554,13 +555,13 @@ func opcodeEndif(op *ParsedOp, t *thread) error { // item on the stack or when that item evaluates to false. In the latter case // where the verification fails specifically due to the top item evaluating // to false, the returned error will use the passed error code. -func abstractVerify(op *ParsedOp, t *thread, c ErrorCode) error { +func abstractVerify(op *ParsedOp, t *thread, c errs.ErrorCode) error { verified, err := t.dstack.PopBool() if err != nil { return err } if !verified { - return scriptError(c, "%s failed", op.Name()) + return errs.NewError(c, "%s failed", op.Name()) } return nil @@ -569,14 +570,14 @@ func abstractVerify(op *ParsedOp, t *thread, c ErrorCode) error { // opcodeVerify examines the top item on the data stack as a boolean value and // verifies it evaluates to true. An error is returned if it does not. func opcodeVerify(op *ParsedOp, t *thread) error { - return abstractVerify(op, t, ErrVerify) + return abstractVerify(op, t, errs.ErrVerify) } // opcodeReturn returns an appropriate error since it is always an error to // return early from a script. func opcodeReturn(op *ParsedOp, t *thread) error { if !t.afterGenesis { - return scriptError(ErrEarlyReturn, "script returned early") + return errs.NewError(errs.ErrEarlyReturn, "script returned early") } t.earlyReturnAfterGenesis = true @@ -595,12 +596,12 @@ func verifyLockTime(txLockTime, threshold, lockTime int64) error { // type. if !((txLockTime < threshold && lockTime < threshold) || (txLockTime >= threshold && lockTime >= threshold)) { - return scriptError(ErrUnsatisfiedLockTime, + return errs.NewError(errs.ErrUnsatisfiedLockTime, "mismatched locktime types -- tx locktime %d, stack locktime %d", txLockTime, lockTime) } if lockTime > txLockTime { - return scriptError(ErrUnsatisfiedLockTime, + return errs.NewError(errs.ErrUnsatisfiedLockTime, "locktime requirement not satisfied -- locktime is greater than the transaction locktime: %d > %d", lockTime, txLockTime) } @@ -616,9 +617,9 @@ func verifyLockTime(txLockTime, threshold, lockTime int64) error { func opcodeCheckLockTimeVerify(op *ParsedOp, t *thread) error { // If the ScriptVerifyCheckLockTimeVerify script flag is not set, treat // opcode as bscript.OpNOP2 instead. - if !t.hasFlag(ScriptVerifyCheckLockTimeVerify) || t.afterGenesis { - if t.hasFlag(ScriptDiscourageUpgradableNops) { - return scriptError(ErrDiscourageUpgradableNOPs, "bscript.OpNOP2 reserved for soft-fork upgrades") + if !t.hasFlag(scriptflag.VerifyCheckLockTimeVerify) || t.afterGenesis { + if t.hasFlag(scriptflag.DiscourageUpgradableNops) { + return errs.NewError(errs.ErrDiscourageUpgradableNOPs, "bscript.OpNOP2 reserved for soft-fork upgrades") } return nil @@ -646,7 +647,7 @@ func opcodeCheckLockTimeVerify(op *ParsedOp, t *thread) error { // arithmetic being done first, you can always use // 0 bscript.OpMAX bscript.OpCHECKLOCKTIMEVERIFY. if lockTime < 0 { - return scriptError(ErrNegativeLockTime, "negative lock time: %d", lockTime) + return errs.NewError(errs.ErrNegativeLockTime, "negative lock time: %d", lockTime) } // The lock time field of a transaction is either a block height at @@ -672,7 +673,7 @@ func opcodeCheckLockTimeVerify(op *ParsedOp, t *thread) error { // another input being unlocked, the opcode execution will still fail when the // input being used by the opcode is locked. if t.tx.Inputs[t.inputIdx].SequenceNumber == bt.MaxTxInSequenceNum { - return scriptError(ErrUnsatisfiedLockTime, "transaction input is finalised") + return errs.NewError(errs.ErrUnsatisfiedLockTime, "transaction input is finalised") } return nil @@ -686,9 +687,9 @@ func opcodeCheckLockTimeVerify(op *ParsedOp, t *thread) error { func opcodeCheckSequenceVerify(op *ParsedOp, t *thread) error { // If the ScriptVerifyCheckSequenceVerify script flag is not set, treat // opcode as bscript.OpNOP3 instead. - if !t.hasFlag(ScriptVerifyCheckSequenceVerify) || t.afterGenesis { - if t.hasFlag(ScriptDiscourageUpgradableNops) { - return scriptError(ErrDiscourageUpgradableNOPs, "bscript.OpNOP3 reserved for soft-fork upgrades") + if !t.hasFlag(scriptflag.VerifyCheckSequenceVerify) || t.afterGenesis { + if t.hasFlag(scriptflag.DiscourageUpgradableNops) { + return errs.NewError(errs.ErrDiscourageUpgradableNOPs, "bscript.OpNOP3 reserved for soft-fork upgrades") } return nil @@ -716,7 +717,7 @@ func opcodeCheckSequenceVerify(op *ParsedOp, t *thread) error { // arithmetic being done first, you can always use // 0 bscript.OpMAX bscript.OpCHECKSEQUENCEVERIFY. if stackSequence < 0 { - return scriptError(ErrNegativeLockTime, "negative sequence: %d", stackSequence) + return errs.NewError(errs.ErrNegativeLockTime, "negative sequence: %d", stackSequence) } sequence := int64(stackSequence) @@ -731,7 +732,7 @@ func opcodeCheckSequenceVerify(op *ParsedOp, t *thread) error { // Transaction version numbers not high enough to trigger CSV rules must // fail. if t.tx.Version < 2 { - return scriptError(ErrUnsatisfiedLockTime, "invalid transaction version: %d", t.tx.Version) + return errs.NewError(errs.ErrUnsatisfiedLockTime, "invalid transaction version: %d", t.tx.Version) } // Sequence numbers with their most significant bit set are not @@ -740,7 +741,7 @@ func opcodeCheckSequenceVerify(op *ParsedOp, t *thread) error { // to get around a CHECKSEQUENCEVERIFY check. txSequence := int64(t.tx.Inputs[t.inputIdx].SequenceNumber) if txSequence&int64(bt.SequenceLockTimeDisabled) != 0 { - return scriptError(ErrUnsatisfiedLockTime, + return errs.NewError(errs.ErrUnsatisfiedLockTime, "transaction sequence has sequence locktime disabled bit set: 0x%x", txSequence) } @@ -951,7 +952,7 @@ func opcodeCat(op *ParsedOp, t *thread) error { c := append(a, b...) if len(c) > t.cfg.MaxScriptElementSize() { - return scriptError(ErrElementTooBig, + return errs.NewError(errs.ErrElementTooBig, "concatenated size %d exceeds max allowed size %d", len(c), t.cfg.MaxScriptElementSize()) } @@ -975,10 +976,10 @@ func opcodeSplit(op *ParsedOp, t *thread) error { } if n.Int32() > int32(len(c)) { - return scriptError(ErrNumberTooBig, "n is larger than length of array") + return errs.NewError(errs.ErrNumberTooBig, "n is larger than length of array") } if n < 0 { - return scriptError(ErrNumberTooSmall, "n is negative") + return errs.NewError(errs.ErrNumberTooSmall, "n is negative") } a := c[:n] @@ -1007,7 +1008,7 @@ func opcodeNum2bin(op *ParsedOp, t *thread) error { size := int(n.Int32()) if size > t.cfg.MaxScriptElementSize() { - return scriptError(ErrNumberTooBig, "n is larger than the max of %d", defaultScriptNumLen) + return errs.NewError(errs.ErrNumberTooBig, "n is larger than the max of %d", defaultScriptNumLen) } // encode a as a script num so that we we take the bytes it @@ -1019,7 +1020,7 @@ func opcodeNum2bin(op *ParsedOp, t *thread) error { b := sn.Bytes() if len(b) > size { - return scriptError(ErrNumberTooSmall, "cannot fit it into n sized array") + return errs.NewError(errs.ErrNumberTooSmall, "cannot fit it into n sized array") } if len(b) == size { t.dstack.PushByteArray(b) @@ -1058,8 +1059,7 @@ func opcodeBin2num(op *ParsedOp, t *thread) error { return err } if len(n.Bytes()) > defaultScriptNumLen { - return scriptError(ErrNumberTooBig, - fmt.Sprintf("script numbers are limited to %d bytes", defaultScriptNumLen)) + return errs.NewError(errs.ErrNumberTooBig, "script numbers are limited to %d bytes", defaultScriptNumLen) } t.dstack.PushInt(n) @@ -1099,7 +1099,7 @@ func opcodeInvert(op *ParsedOp, t *thread) error { // opcodeAnd executes a boolean and between each bit in the operands // // Stack transformation: x1 x2 bscript.OpAND -> out -func opcodeAnd(op *ParsedOp, t *thread) error { +func opcodeAnd(op *ParsedOp, t *thread) error { //nolint:dupl // to keep functionality with function signature a, err := t.dstack.PopByteArray() if err != nil { return err @@ -1111,7 +1111,7 @@ func opcodeAnd(op *ParsedOp, t *thread) error { } if len(a) != len(b) { - return scriptError(ErrInvalidInputLength, "byte arrays are not the same length") + return errs.NewError(errs.ErrInvalidInputLength, "byte arrays are not the same length") } c := make([]byte, len(a)) @@ -1126,7 +1126,7 @@ func opcodeAnd(op *ParsedOp, t *thread) error { // opcodeOr executes a boolean or between each bit in the operands // // Stack transformation: x1 x2 bscript.OpOR -> out -func opcodeOr(op *ParsedOp, t *thread) error { +func opcodeOr(op *ParsedOp, t *thread) error { //nolint:dupl // to keep functionality with function signature a, err := t.dstack.PopByteArray() if err != nil { return err @@ -1138,7 +1138,7 @@ func opcodeOr(op *ParsedOp, t *thread) error { } if len(a) != len(b) { - return scriptError(ErrInvalidInputLength, "byte arrays are not the same length") + return errs.NewError(errs.ErrInvalidInputLength, "byte arrays are not the same length") } c := make([]byte, len(a)) @@ -1153,7 +1153,7 @@ func opcodeOr(op *ParsedOp, t *thread) error { // opcodeXor executes a boolean xor between each bit in the operands // // Stack transformation: x1 x2 bscript.OpXOR -> out -func opcodeXor(op *ParsedOp, t *thread) error { +func opcodeXor(op *ParsedOp, t *thread) error { //nolint:dupl // to keep functionality with function signature a, err := t.dstack.PopByteArray() if err != nil { return err @@ -1165,7 +1165,7 @@ func opcodeXor(op *ParsedOp, t *thread) error { } if len(a) != len(b) { - return scriptError(ErrInvalidInputLength, "byte arrays are not the same length") + return errs.NewError(errs.ErrInvalidInputLength, "byte arrays are not the same length") } c := make([]byte, len(a)) @@ -1208,7 +1208,7 @@ func opcodeEqualVerify(op *ParsedOp, t *thread) error { return err } - return abstractVerify(op, t, ErrEqualVerify) + return abstractVerify(op, t, errs.ErrEqualVerify) } // opcode1Add treats the top item on the data stack as an integer and replaces @@ -1393,7 +1393,7 @@ func opcodeDiv(op *ParsedOp, t *thread) error { } if b == 0 { - return scriptError(ErrDivideByZero, "divide by zero") + return errs.NewError(errs.ErrDivideByZero, "divide by zero") } t.dstack.PushInt(a / b) @@ -1416,7 +1416,7 @@ func opcodeMod(op *ParsedOp, t *thread) error { } if b == 0 { - return scriptError(ErrDivideByZero, "mod by zero") + return errs.NewError(errs.ErrDivideByZero, "mod by zero") } t.dstack.PushInt(a % b) @@ -1430,7 +1430,7 @@ func opcodeLShift(op *ParsedOp, t *thread) error { } if n.Int32() < 0 { - return scriptError(ErrNumberTooSmall, "n less than 0") + return errs.NewError(errs.ErrNumberTooSmall, "n less than 0") } x, err := t.dstack.PopByteArray() @@ -1455,7 +1455,7 @@ func opcodeRShift(op *ParsedOp, t *thread) error { } if n.Int32() < 0 { - return scriptError(ErrNumberTooSmall, "n less than 0") + return errs.NewError(errs.ErrNumberTooSmall, "n less than 0") } x, err := t.dstack.PopByteArray() @@ -1565,7 +1565,7 @@ func opcodeNumEqualVerify(op *ParsedOp, t *thread) error { return err } - return abstractVerify(op, t, ErrNumEqualVerify) + return abstractVerify(op, t, errs.ErrNumEqualVerify) } // opcodeNumNotEqual treats the top two items on the data stack as integers. @@ -1927,7 +1927,7 @@ func opcodeCheckSig(op *ParsedOp, t *thread) error { // Remove the signature since there is no way for a signature // to sign itself. - if !t.hasFlag(ScriptEnableSighashForkID) || !shf.Has(sighash.ForkID) { + if !t.hasFlag(scriptflag.EnableSighashForkID) || !shf.Has(sighash.ForkID) { subScript = subScript.removeOpcodeByData(fullSigBytes) subScript = subScript.removeOpcode(bscript.OpCODESEPARATOR) } @@ -1953,7 +1953,7 @@ func opcodeCheckSig(op *ParsedOp, t *thread) error { } var signature *bec.Signature - if t.hasFlag(ScriptVerifyStrictEncoding) || t.hasFlag(ScriptVerifyDERSignatures) { + if t.hasFlag(scriptflag.VerifyStrictEncoding) || t.hasFlag(scriptflag.VerifyDERSignatures) { signature, err = bec.ParseDERSignature(sigBytes, bec.S256()) } else { signature, err = bec.ParseSignature(sigBytes, bec.S256()) @@ -1964,8 +1964,8 @@ func opcodeCheckSig(op *ParsedOp, t *thread) error { } ok := signature.Verify(hash, pubKey) - if !ok && t.hasFlag(ScriptVerifyNullFail) && len(sigBytes) > 0 { - return scriptError(ErrNullFail, "signature not empty on failed checksig") + if !ok && t.hasFlag(scriptflag.VerifyNullFail) && len(sigBytes) > 0 { + return errs.NewError(errs.ErrNullFail, "signature not empty on failed checksig") } t.dstack.PushBool(ok) @@ -1982,7 +1982,7 @@ func opcodeCheckSigVerify(op *ParsedOp, t *thread) error { return err } - return abstractVerify(op, t, ErrCheckSigVerify) + return abstractVerify(op, t, errs.ErrCheckSigVerify) } // parsedSigInfo houses a raw signature along with its parsed form and a flag @@ -2021,14 +2021,18 @@ func opcodeCheckMultiSig(op *ParsedOp, t *thread) error { numPubKeys := int(numKeys.Int32()) if numPubKeys < 0 { - return scriptError(ErrInvalidPubKeyCount, "number of pubkeys %d is negative", numPubKeys) + return errs.NewError(errs.ErrInvalidPubKeyCount, "number of pubkeys %d is negative", numPubKeys) } if numPubKeys > t.cfg.MaxPubKeysPerMultiSig() { - return scriptError(ErrInvalidPubKeyCount, "too many pubkeys: %d > %d", numPubKeys, t.cfg.MaxPubKeysPerMultiSig()) + return errs.NewError( + errs.ErrInvalidPubKeyCount, + "too many pubkeys: %d > %d", + numPubKeys, t.cfg.MaxPubKeysPerMultiSig(), + ) } t.numOps += numPubKeys if t.numOps > t.cfg.MaxOps() { - return scriptError(ErrTooManyOperations, "exceeded max operation limit of %d", t.cfg.MaxOps()) + return errs.NewError(errs.ErrTooManyOperations, "exceeded max operation limit of %d", t.cfg.MaxOps()) } pubKeys := make([][]byte, 0, numPubKeys) @@ -2047,10 +2051,14 @@ func opcodeCheckMultiSig(op *ParsedOp, t *thread) error { numSignatures := int(numSigs.Int32()) if numSignatures < 0 { - return scriptError(ErrInvalidSignatureCount, "number of signatures %d is negative", numSignatures) + return errs.NewError(errs.ErrInvalidSignatureCount, "number of signatures %d is negative", numSignatures) } if numSignatures > numPubKeys { - return scriptError(ErrInvalidSignatureCount, "more signatures than pubkeys: %d > %d", numSignatures, numPubKeys) + return errs.NewError( + errs.ErrInvalidSignatureCount, + "more signatures than pubkeys: %d > %d", + numSignatures, numPubKeys, + ) } signatures := make([]*parsedSigInfo, 0, numSignatures) @@ -2075,8 +2083,8 @@ func opcodeCheckMultiSig(op *ParsedOp, t *thread) error { // Since the dummy argument is otherwise not checked, it could be any // value which unfortunately provides a source of malleability. Thus, // there is a script flag to force an error when the value is NOT 0. - if t.hasFlag(ScriptStrictMultiSig) && len(dummy) != 0 { - return scriptError(ErrSigNullDummy, "multisig dummy argument has length %d instead of 0", len(dummy)) + if t.hasFlag(scriptflag.StrictMultiSig) && len(dummy) != 0 { + return errs.NewError(errs.ErrSigNullDummy, "multisig dummy argument has length %d instead of 0", len(dummy)) } // Get script starting from the most recent bscript.OpCODESEPARATOR. @@ -2131,8 +2139,8 @@ func opcodeCheckMultiSig(op *ParsedOp, t *thread) error { // Parse the signature. var err error - if t.hasFlag(ScriptVerifyStrictEncoding) || - t.hasFlag(ScriptVerifyDERSignatures) { + if t.hasFlag(scriptflag.VerifyStrictEncoding) || + t.hasFlag(scriptflag.VerifyDERSignatures) { parsedSig, err = bec.ParseDERSignature(signature, bec.S256()) @@ -2188,10 +2196,10 @@ func opcodeCheckMultiSig(op *ParsedOp, t *thread) error { } } - if !success && t.hasFlag(ScriptVerifyNullFail) { + if !success && t.hasFlag(scriptflag.VerifyNullFail) { for _, sig := range signatures { if len(sig.signature) > 0 { - return scriptError(ErrNullFail, "not all signatures empty on failed checkmultisig") + return errs.NewError(errs.ErrNullFail, "not all signatures empty on failed checkmultisig") } } } @@ -2211,9 +2219,9 @@ func opcodeCheckMultiSigVerify(op *ParsedOp, t *thread) error { return err } - return abstractVerify(op, t, ErrCheckMultiSigVerify) + return abstractVerify(op, t, errs.ErrCheckMultiSigVerify) } -func success() Error { - return scriptError(ErrOK, "success") +func success() errs.Error { + return errs.NewError(errs.ErrOK, "success") } diff --git a/bscript/interpreter/reference_test.go b/bscript/interpreter/reference_test.go index ac37a7cc..59bad044 100644 --- a/bscript/interpreter/reference_test.go +++ b/bscript/interpreter/reference_test.go @@ -16,6 +16,8 @@ import ( "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" + "github.com/libsv/go-bt/v2/bscript/interpreter/scriptflag" ) var opcodeByName = make(map[string]byte) @@ -150,8 +152,8 @@ var shortFormOps map[string]byte // parseScriptFlags parses the provided flags string from the format used in the // reference tests into ScriptFlags suitable for use in the script engine. -func parseScriptFlags(flagStr string) (ScriptFlags, error) { - var flags ScriptFlags +func parseScriptFlags(flagStr string) (scriptflag.Flag, error) { + var flags scriptflag.Flag sFlags := strings.Split(flagStr, ",") for _, flag := range sFlags { @@ -159,37 +161,37 @@ func parseScriptFlags(flagStr string) (ScriptFlags, error) { case "": // Nothing. case "CHECKLOCKTIMEVERIFY": - flags |= ScriptVerifyCheckLockTimeVerify + flags |= scriptflag.VerifyCheckLockTimeVerify case "CHECKSEQUENCEVERIFY": - flags |= ScriptVerifyCheckSequenceVerify + flags |= scriptflag.VerifyCheckSequenceVerify case "CLEANSTACK": - flags |= ScriptVerifyCleanStack + flags |= scriptflag.VerifyCleanStack case "DERSIG": - flags |= ScriptVerifyDERSignatures + flags |= scriptflag.VerifyDERSignatures case "DISCOURAGE_UPGRADABLE_NOPS": - flags |= ScriptDiscourageUpgradableNops + flags |= scriptflag.DiscourageUpgradableNops case "LOW_S": - flags |= ScriptVerifyLowS + flags |= scriptflag.VerifyLowS case "MINIMALDATA": - flags |= ScriptVerifyMinimalData + flags |= scriptflag.VerifyMinimalData case "NONE": // Nothing. case "NULLDUMMY": - flags |= ScriptStrictMultiSig + flags |= scriptflag.StrictMultiSig case "NULLFAIL": - flags |= ScriptVerifyNullFail + flags |= scriptflag.VerifyNullFail case "P2SH": - flags |= ScriptBip16 + flags |= scriptflag.Bip16 case "SIGPUSHONLY": - flags |= ScriptVerifySigPushOnly + flags |= scriptflag.VerifySigPushOnly case "STRICTENC": - flags |= ScriptVerifyStrictEncoding + flags |= scriptflag.VerifyStrictEncoding case "UTXO_AFTER_GENESIS": - flags |= ScriptUTXOAfterGenesis + flags |= scriptflag.UTXOAfterGenesis case "MINIMALIF": - flags |= ScriptVerifyMinimalIf + flags |= scriptflag.VerifyMinimalIf case "SIGHASH_FORKID": - flags |= ScriptEnableSighashForkID + flags |= scriptflag.EnableSighashForkID default: return flags, fmt.Errorf("invalid flag: %s", flag) } @@ -200,89 +202,89 @@ func parseScriptFlags(flagStr string) (ScriptFlags, error) { // parseExpectedResult parses the provided expected result string into allowed // script error codes. An error is returned if the expected result string is // not supported. -func parseExpectedResult(expected string) ([]ErrorCode, error) { +func parseExpectedResult(expected string) ([]errs.ErrorCode, error) { switch expected { case "OK": return nil, nil case "INVALID_NUMBER_RANGE", "SPLIT_RANGE": - return []ErrorCode{ErrNumberTooBig, ErrNumberTooSmall}, nil + return []errs.ErrorCode{errs.ErrNumberTooBig, errs.ErrNumberTooSmall}, nil case "OPERAND_SIZE": - return []ErrorCode{ErrInvalidInputLength}, nil + return []errs.ErrorCode{errs.ErrInvalidInputLength}, nil case "PUBKEYTYPE": - return []ErrorCode{ErrPubKeyType}, nil + return []errs.ErrorCode{errs.ErrPubKeyType}, nil case "SIG_DER": - return []ErrorCode{ErrSigTooShort, ErrSigTooLong, - ErrSigInvalidSeqID, ErrSigInvalidDataLen, ErrSigMissingSTypeID, - ErrSigMissingSLen, ErrSigInvalidSLen, - ErrSigInvalidRIntID, ErrSigZeroRLen, ErrSigNegativeR, - ErrSigTooMuchRPadding, ErrSigInvalidSIntID, - ErrSigZeroSLen, ErrSigNegativeS, ErrSigTooMuchSPadding, - ErrInvalidSigHashType}, nil + return []errs.ErrorCode{errs.ErrSigTooShort, errs.ErrSigTooLong, + errs.ErrSigInvalidSeqID, errs.ErrSigInvalidDataLen, errs.ErrSigMissingSTypeID, + errs.ErrSigMissingSLen, errs.ErrSigInvalidSLen, + errs.ErrSigInvalidRIntID, errs.ErrSigZeroRLen, errs.ErrSigNegativeR, + errs.ErrSigTooMuchRPadding, errs.ErrSigInvalidSIntID, + errs.ErrSigZeroSLen, errs.ErrSigNegativeS, errs.ErrSigTooMuchSPadding, + errs.ErrInvalidSigHashType}, nil case "EVAL_FALSE": - return []ErrorCode{ErrEvalFalse, ErrEmptyStack}, nil + return []errs.ErrorCode{errs.ErrEvalFalse, errs.ErrEmptyStack}, nil case "EQUALVERIFY": - return []ErrorCode{ErrEqualVerify}, nil + return []errs.ErrorCode{errs.ErrEqualVerify}, nil case "NULLFAIL": - return []ErrorCode{ErrNullFail}, nil + return []errs.ErrorCode{errs.ErrNullFail}, nil case "SIG_HIGH_S": - return []ErrorCode{ErrSigHighS}, nil + return []errs.ErrorCode{errs.ErrSigHighS}, nil case "SIG_HASHTYPE": - return []ErrorCode{ErrInvalidSigHashType}, nil + return []errs.ErrorCode{errs.ErrInvalidSigHashType}, nil case "SIG_NULLDUMMY": - return []ErrorCode{ErrSigNullDummy}, nil + return []errs.ErrorCode{errs.ErrSigNullDummy}, nil case "SIG_PUSHONLY": - return []ErrorCode{ErrNotPushOnly}, nil + return []errs.ErrorCode{errs.ErrNotPushOnly}, nil case "CLEANSTACK": - return []ErrorCode{ErrCleanStack}, nil + return []errs.ErrorCode{errs.ErrCleanStack}, nil case "BAD_OPCODE": - return []ErrorCode{ErrReservedOpcode, ErrMalformedPush}, nil + return []errs.ErrorCode{errs.ErrReservedOpcode, errs.ErrMalformedPush}, nil case "UNBALANCED_CONDITIONAL": - return []ErrorCode{ErrUnbalancedConditional, - ErrInvalidStackOperation}, nil + return []errs.ErrorCode{errs.ErrUnbalancedConditional, + errs.ErrInvalidStackOperation}, nil case "OP_RETURN": - return []ErrorCode{ErrEarlyReturn}, nil + return []errs.ErrorCode{errs.ErrEarlyReturn}, nil case "VERIFY": - return []ErrorCode{ErrVerify}, nil + return []errs.ErrorCode{errs.ErrVerify}, nil case "INVALID_STACK_OPERATION", "INVALID_ALTSTACK_OPERATION": - return []ErrorCode{ErrInvalidStackOperation}, nil + return []errs.ErrorCode{errs.ErrInvalidStackOperation}, nil case "DISABLED_OPCODE": - return []ErrorCode{ErrDisabledOpcode}, nil + return []errs.ErrorCode{errs.ErrDisabledOpcode}, nil case "DISCOURAGE_UPGRADABLE_NOPS": - return []ErrorCode{ErrDiscourageUpgradableNOPs}, nil + return []errs.ErrorCode{errs.ErrDiscourageUpgradableNOPs}, nil case "SCRIPTNUM_OVERFLOW": - return []ErrorCode{ErrNumberTooBig}, nil + return []errs.ErrorCode{errs.ErrNumberTooBig}, nil case "NUMBER_SIZE": - return []ErrorCode{ErrNumberTooBig, ErrNumberTooSmall}, nil + return []errs.ErrorCode{errs.ErrNumberTooBig, errs.ErrNumberTooSmall}, nil case "PUSH_SIZE": - return []ErrorCode{ErrElementTooBig}, nil + return []errs.ErrorCode{errs.ErrElementTooBig}, nil case "OP_COUNT": - return []ErrorCode{ErrTooManyOperations}, nil + return []errs.ErrorCode{errs.ErrTooManyOperations}, nil case "STACK_SIZE": - return []ErrorCode{ErrStackOverflow}, nil + return []errs.ErrorCode{errs.ErrStackOverflow}, nil case "SCRIPT_SIZE": - return []ErrorCode{ErrScriptTooBig}, nil + return []errs.ErrorCode{errs.ErrScriptTooBig}, nil case "ELEMENT_SIZE": - return []ErrorCode{ErrElementTooBig}, nil + return []errs.ErrorCode{errs.ErrElementTooBig}, nil case "PUBKEY_COUNT": - return []ErrorCode{ErrInvalidPubKeyCount}, nil + return []errs.ErrorCode{errs.ErrInvalidPubKeyCount}, nil case "SIG_COUNT": - return []ErrorCode{ErrInvalidSignatureCount}, nil + return []errs.ErrorCode{errs.ErrInvalidSignatureCount}, nil case "MINIMALDATA": - return []ErrorCode{ErrMinimalData}, nil + return []errs.ErrorCode{errs.ErrMinimalData}, nil case "MINIMALIF": - return []ErrorCode{ErrMinimalIf}, nil + return []errs.ErrorCode{errs.ErrMinimalIf}, nil case "NEGATIVE_LOCKTIME": - return []ErrorCode{ErrNegativeLockTime}, nil + return []errs.ErrorCode{errs.ErrNegativeLockTime}, nil case "UNSATISFIED_LOCKTIME": - return []ErrorCode{ErrUnsatisfiedLockTime}, nil + return []errs.ErrorCode{errs.ErrUnsatisfiedLockTime}, nil case "SCRIPTNUM_MINENCODE": - return []ErrorCode{ErrMinimalData}, nil + return []errs.ErrorCode{errs.ErrMinimalData}, nil case "DIV_BY_ZERO", "MOD_BY_ZERO": - return []ErrorCode{ErrDivideByZero}, nil + return []errs.ErrorCode{errs.ErrDivideByZero}, nil case "CHECKSIGVERIFY": - return []ErrorCode{ErrCheckSigVerify}, nil + return []errs.ErrorCode{errs.ErrCheckSigVerify}, nil case "ILLEGAL_FORKID": - return []ErrorCode{ErrIllegalForkID}, nil + return []errs.ErrorCode{errs.ErrIllegalForkID}, nil } return nil, fmt.Errorf("unrecognised expected result in test data: %v", @@ -449,13 +451,13 @@ func TestScripts(t *testing.T) { // the execution matches it. success := false for _, code := range allowedErrorCodes { - if IsErrorCode(err, code) { + if errs.IsErrorCode(err, code) { success = true break } } if !success { - serr := &Error{} + serr := &errs.Error{} if ok := errors.As(err, serr); ok { t.Errorf("%s: want error codes %v, got %v", name, allowedErrorCodes, serr.ErrorCode) continue diff --git a/bscript/interpreter/scriptflag/scriptflag.go b/bscript/interpreter/scriptflag/scriptflag.go new file mode 100644 index 00000000..3f1e08cb --- /dev/null +++ b/bscript/interpreter/scriptflag/scriptflag.go @@ -0,0 +1,90 @@ +package scriptflag + +// Flag is a bitmask defining additional operations or tests that will be +// done when executing a script pair. +type Flag uint32 + +const ( + // Bip16 defines whether the bip16 threshold has passed and thus + // pay-to-script hash transactions will be fully validated. + Bip16 Flag = 1 << iota + + // StrictMultiSig defines whether to verify the stack item + // used by CHECKMULTISIG is zero length. + StrictMultiSig + + // DiscourageUpgradableNops defines whether to verify that + // NOP1 through NOP10 are reserved for future soft-fork upgrades. This + // flag must not be used for consensus critical code nor applied to + // blocks as this flag is only for stricter standard transaction + // checks. This flag is only applied when the above opcodes are + // executed. + DiscourageUpgradableNops + + // VerifyCheckLockTimeVerify defines whether to verify that + // a transaction output is spendable based on the locktime. + // This is BIP0065. + VerifyCheckLockTimeVerify + + // VerifyCheckSequenceVerify defines whether to allow execution + // pathways of a script to be restricted based on the age of the output + // being spent. This is BIP0112. + VerifyCheckSequenceVerify + + // VerifyCleanStack defines that the stack must contain only + // one stack element after evaluation and that the element must be + // true if interpreted as a boolean. This is rule 6 of BIP0062. + // This flag should never be used without the Bip16 flag. + VerifyCleanStack + + // VerifyDERSignatures defines that signatures are required + // to comply with the DER format. + VerifyDERSignatures + + // VerifyLowS defines that signatures are required to comply with + // the DER format and whose S value is <= order / 2. This is rule 5 + // of BIP0062. + VerifyLowS + + // VerifyMinimalData defines that signatures must use the smallest + // push operator. This is both rules 3 and 4 of BIP0062. + VerifyMinimalData + + // VerifyNullFail defines that signatures must be empty if + // a CHECKSIG or CHECKMULTISIG operation fails. + VerifyNullFail + + // VerifySigPushOnly defines that signature scripts must contain + // only pushed data. This is rule 2 of BIP0062. + VerifySigPushOnly + + // EnableSighashForkID defined that signature scripts have forkid + // enabled. + EnableSighashForkID + + // VerifyStrictEncoding defines that signature scripts and + // public keys must follow the strict encoding requirements. + VerifyStrictEncoding + + // VerifyBip143SigHash defines that signature hashes should + // be calculated using the bip0143 signature hashing algorithm. + VerifyBip143SigHash + + // UTXOAfterGenesis defines that the utxo was created after + // genesis. + UTXOAfterGenesis + + // VerifyMinimalIf defines the enforcement of any conditional statement using the + // minimum required data. + VerifyMinimalIf +) + +// HasFlag returns whether the Flags has the passed flag set. +func (s Flag) HasFlag(flag Flag) bool { + return s&flag == flag +} + +// AddFlag adds the passed flag to Flags +func (s *Flag) AddFlag(flag Flag) { + *s |= flag +} diff --git a/bscript/interpreter/scriptnum.go b/bscript/interpreter/scriptnum.go index 988e3a62..f9c19b25 100644 --- a/bscript/interpreter/scriptnum.go +++ b/bscript/interpreter/scriptnum.go @@ -5,7 +5,7 @@ package interpreter import ( - "fmt" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" ) const ( @@ -65,9 +65,7 @@ func checkMinimalDataEncoding(v []byte) error { // is +-255, which encode to 0xff00 and 0xff80 respectively. // (big-endian). if len(v) == 1 || v[len(v)-2]&0x80 == 0 { - str := fmt.Sprintf("numeric value encoded as %x is "+ - "not minimally encoded", v) - return scriptError(ErrMinimalData, str) + return errs.NewError(errs.ErrMinimalData, "numeric value encoded as %x is not minimally encoded", v) } } @@ -179,7 +177,7 @@ func (n scriptNum) Int64() int64 { // requireMinimal enabled. // // The scriptNumLen is the maximum number of bytes the encoded value can be -// before an ErrStackNumberTooBig is returned. This effectively limits the +// before an errs.ErrStackNumberTooBig is returned. This effectively limits the // range of allowed values. // WARNING: Great care should be taken if passing a value larger than // defaultScriptNumLen, which could lead to addition and multiplication @@ -190,10 +188,11 @@ func makeScriptNum(v []byte, requireMinimal bool, scriptNumLen int) (scriptNum, // Interpreting data requires that it is not larger than // the passed scriptNumLen value. if len(v) > scriptNumLen { - str := fmt.Sprintf("numeric value encoded as %x is %d bytes "+ - "which exceeds the max allowed of %d", v, len(v), - scriptNumLen) - return 0, scriptError(ErrNumberTooBig, str) + return 0, errs.NewError( + errs.ErrNumberTooBig, + "numeric value encoded as %x is %d bytes which exceeds the max allowed of %d", + v, len(v), scriptNumLen, + ) } // Enforce minimal encoded if requested. diff --git a/bscript/interpreter/scriptnum_test.go b/bscript/interpreter/scriptnum_test.go index 433fdc1d..10234bcf 100644 --- a/bscript/interpreter/scriptnum_test.go +++ b/bscript/interpreter/scriptnum_test.go @@ -11,6 +11,8 @@ import ( "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" + "github.com/libsv/go-bt/v2/bscript/interpreter/scriptflag" ) // hexToBytes converts the passed hex string into bytes and will panic if there @@ -96,8 +98,8 @@ func TestMakeScriptNum(t *testing.T) { // Errors used in the tests below defined here for convenience and to // keep the horizontal test size shorter. - errNumTooBig := scriptError(ErrNumberTooBig, "") - errMinimalData := scriptError(ErrMinimalData, "") + errNumTooBig := errs.NewError(errs.ErrNumberTooBig, "") + errMinimalData := errs.NewError(errs.ErrMinimalData, "") tests := []struct { serialised []byte @@ -287,7 +289,7 @@ func TestDisasmString(t *testing.T) { PreviousTxOut: &bt.Output{LockingScript: prev}, Tx: tx, InputIdx: 0, - Flags: ScriptBip16 | ScriptVerifyCleanStack, + Flags: scriptflag.Bip16 | scriptflag.VerifyCleanStack, }) if err != nil { t.Error(err) diff --git a/bscript/interpreter/stack.go b/bscript/interpreter/stack.go index 590ce757..3586a43e 100644 --- a/bscript/interpreter/stack.go +++ b/bscript/interpreter/stack.go @@ -6,7 +6,8 @@ package interpreter import ( "encoding/hex" - "fmt" + + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" ) // asBool gets the boolean value of the byte array. @@ -106,9 +107,7 @@ func (s *stack) PopBool() (bool, error) { func (s *stack) PeekByteArray(idx int32) ([]byte, error) { sz := int32(len(s.stk)) if idx < 0 || idx >= sz { - str := fmt.Sprintf("index %d is invalid for stack size %d", idx, - sz) - return nil, scriptError(ErrInvalidStackOperation, str) + return nil, errs.NewError(errs.ErrInvalidStackOperation, "index %d is invalid for stack size %d", idx, sz) } return s.stk[sz-idx-1], nil @@ -146,9 +145,7 @@ func (s *stack) PeekBool(idx int32) (bool, error) { func (s *stack) nipN(idx int32) ([]byte, error) { sz := int32(len(s.stk)) if idx < 0 || idx > sz-1 { - str := fmt.Sprintf("index %d is invalid for stack size %d", idx, - sz) - return nil, scriptError(ErrInvalidStackOperation, str) + return nil, errs.NewError(errs.ErrInvalidStackOperation, "index %d is invalid for stack size %d", idx, sz) } so := s.stk[sz-idx-1] @@ -204,8 +201,7 @@ func (s *stack) Tuck() error { // DropN(2): [... x1 x2] -> [...] func (s *stack) DropN(n int32) error { if n < 1 { - str := fmt.Sprintf("attempt to drop %d items from stack", n) - return scriptError(ErrInvalidStackOperation, str) + return errs.NewError(errs.ErrInvalidStackOperation, "attempt to drop %d items from stack", n) } for ; n > 0; n-- { @@ -224,8 +220,7 @@ func (s *stack) DropN(n int32) error { // DupN(2): [... x1 x2] -> [... x1 x2 x1 x2] func (s *stack) DupN(n int32) error { if n < 1 { - str := fmt.Sprintf("attempt to dup %d stack items", n) - return scriptError(ErrInvalidStackOperation, str) + return errs.NewError(errs.ErrInvalidStackOperation, "attempt to dup %d stack items", n) } // Iteratively duplicate the value n-1 down the stack n times. @@ -247,8 +242,7 @@ func (s *stack) DupN(n int32) error { // RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2] func (s *stack) RotN(n int32) error { if n < 1 { - str := fmt.Sprintf("attempt to rotate %d stack items", n) - return scriptError(ErrInvalidStackOperation, str) + return errs.NewError(errs.ErrInvalidStackOperation, "attempt to rotate %d stack items", n) } // Nip the 3n-1th item from the stack to the top n times to rotate @@ -272,8 +266,7 @@ func (s *stack) RotN(n int32) error { // SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2] func (s *stack) SwapN(n int32) error { if n < 1 { - str := fmt.Sprintf("attempt to swap %d stack items", n) - return scriptError(ErrInvalidStackOperation, str) + return errs.NewError(errs.ErrInvalidStackOperation, "attempt to swap %d stack items", n) } entry := 2*n - 1 @@ -296,9 +289,7 @@ func (s *stack) SwapN(n int32) error { // OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2] func (s *stack) OverN(n int32) error { if n < 1 { - str := fmt.Sprintf("attempt to perform over on %d stack items", - n) - return scriptError(ErrInvalidStackOperation, str) + return errs.NewError(errs.ErrInvalidStackOperation, "attempt to perform over on %d stack items", n) } // Copy 2n-1th entry to top of the stack. diff --git a/bscript/interpreter/stack_test.go b/bscript/interpreter/stack_test.go index df7b6c0d..c1e7ba59 100644 --- a/bscript/interpreter/stack_test.go +++ b/bscript/interpreter/stack_test.go @@ -10,6 +10,8 @@ import ( "fmt" "reflect" "testing" + + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" ) // tstCheckScriptError ensures the type of the two passed errors are of the @@ -26,7 +28,7 @@ func tstCheckScriptError(gotErr, wantErr error) error { } // Ensure the want error type is a script error. - werr := &Error{} + werr := &errs.Error{} if ok := errors.As(wantErr, werr); !ok { return fmt.Errorf("unexpected test error type %T", wantErr) //nolint:errorlint // test code } @@ -34,7 +36,7 @@ func tstCheckScriptError(gotErr, wantErr error) error { // Ensure the error codes match. It's safe to use a raw type assert // here since the code above already proved they are the same type and // the want error is a script error. - gotErrorCode := gotErr.(Error).ErrorCode //nolint:errorlint // test code + gotErrorCode := gotErr.(errs.Error).ErrorCode //nolint:errorlint // test code if gotErrorCode != werr.ErrorCode { return fmt.Errorf("mismatched error code - got %v (%w), want %v", gotErrorCode, gotErr, werr.ErrorCode) } @@ -69,7 +71,7 @@ func TestStack(t *testing.T) { _, err := s.PeekByteArray(5) return err }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -79,7 +81,7 @@ func TestStack(t *testing.T) { _, err := s.PeekInt(5) return err }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -89,7 +91,7 @@ func TestStack(t *testing.T) { _, err := s.PeekBool(5) return err }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -135,7 +137,7 @@ func TestStack(t *testing.T) { } return nil }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -179,7 +181,7 @@ func TestStack(t *testing.T) { _, err := s.PopBool() return err }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -401,7 +403,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.DupN(0) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -410,7 +412,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.DupN(-1) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -419,7 +421,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.DupN(2) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -550,7 +552,7 @@ func TestStack(t *testing.T) { // bite off more than we can chew return s.NipN(3) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), [][]byte{{2}, {3}}, }, { @@ -568,7 +570,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.Tuck() }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -577,7 +579,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.Tuck() }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -622,7 +624,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.DropN(5) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -631,7 +633,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.DropN(0) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -658,7 +660,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.RotN(1) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -667,7 +669,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.RotN(0) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -694,7 +696,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.SwapN(1) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -703,7 +705,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.SwapN(0) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -730,7 +732,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.OverN(1) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -739,7 +741,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.OverN(0) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -766,7 +768,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.PickN(1) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -793,7 +795,7 @@ func TestStack(t *testing.T) { func(s *stack) error { return s.RollN(1) }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, { @@ -896,7 +898,7 @@ func TestStack(t *testing.T) { _, err := s.PopInt() return err }, - scriptError(ErrInvalidStackOperation, ""), + errs.NewError(errs.ErrInvalidStackOperation, ""), nil, }, } diff --git a/bscript/interpreter/thread.go b/bscript/interpreter/thread.go index 78323bc7..57ff045b 100644 --- a/bscript/interpreter/thread.go +++ b/bscript/interpreter/thread.go @@ -3,11 +3,17 @@ package interpreter import ( "math/big" + "github.com/libsv/go-bk/bec" "github.com/libsv/go-bt/v2" "github.com/libsv/go-bt/v2/bscript" + "github.com/libsv/go-bt/v2/bscript/interpreter/errs" + "github.com/libsv/go-bt/v2/bscript/interpreter/scriptflag" "github.com/libsv/go-bt/v2/sighash" ) +// halfOrder is used to tame ECDSA malleability (see BIP0062). +var halfOrder = new(big.Int).Rsh(bec.S256().N, 1) + type thread struct { dstack stack // data stack astack stack // alt stack @@ -31,8 +37,8 @@ type thread struct { numOps int - flags ScriptFlags - bip16 bool // treat execution as pay-to-script-hash + scriptflag scriptflag.Flag + bip16 bool // treat execution as pay-to-script-hash afterGenesis bool earlyReturnAfterGenesis bool @@ -43,16 +49,16 @@ type ExecutionParams struct { PreviousTxOut *bt.Output Tx *bt.Tx InputIdx int - Flags ScriptFlags + Flags scriptflag.Flag } // hasFlag returns whether the script engine instance has the passed flag set. -func (t *thread) hasFlag(flag ScriptFlags) bool { - return t.flags.HasFlag(flag) +func (t *thread) hasFlag(flag scriptflag.Flag) bool { + return t.scriptflag.HasFlag(flag) } -func (t *thread) addFlag(flag ScriptFlags) { - t.flags.AddFlag(flag) +func (t *thread) addFlag(flag scriptflag.Flag) { + t.scriptflag.AddFlag(flag) } // isBranchExecuting returns whether the current conditional branch is @@ -60,7 +66,7 @@ func (t *thread) addFlag(flag ScriptFlags) { // and an OP_IF is encountered, the branch is inactive until an OP_ELSE or // OP_ENDIF is encountered. It properly handles nested conditionals. func (t *thread) isBranchExecuting() bool { - return len(t.condStack) == 0 || t.condStack[len(t.condStack)-1] == OpCondTrue + return len(t.condStack) == 0 || t.condStack[len(t.condStack)-1] == opCondTrue } // executeOpcode performs execution on the passed opcode. It takes into account @@ -68,7 +74,7 @@ func (t *thread) isBranchExecuting() bool { // tested in this case. func (t *thread) executeOpcode(pop ParsedOp) error { if len(pop.Data) > t.cfg.MaxScriptElementSize() { - return scriptError(ErrElementTooBig, + return errs.NewError(errs.ErrElementTooBig, "element size %d exceeds max allowed size %d", len(pop.Data), t.cfg.MaxScriptElementSize()) } @@ -76,25 +82,25 @@ func (t *thread) executeOpcode(pop ParsedOp) error { // Disabled opcodes are fail on program counter. if pop.IsDisabled() && (!t.afterGenesis || exec) { - return scriptError(ErrDisabledOpcode, "attempt to execute disabled opcode %s", pop.Name()) + return errs.NewError(errs.ErrDisabledOpcode, "attempt to execute disabled opcode %s", pop.Name()) } // Always-illegal opcodes are fail on program counter. if pop.AlwaysIllegal() && !t.afterGenesis { - return scriptError(ErrReservedOpcode, "attempt to execute reserved opcode %s", pop.Name()) + return errs.NewError(errs.ErrReservedOpcode, "attempt to execute reserved opcode %s", pop.Name()) } // Note that this includes OP_RESERVED which counts as a push operation. if pop.Op.val > bscript.Op16 { t.numOps++ if t.numOps > t.cfg.MaxOps() { - return scriptError(ErrTooManyOperations, "exceeded max operation limit of %d", t.cfg.MaxOps()) + return errs.NewError(errs.ErrTooManyOperations, "exceeded max operation limit of %d", t.cfg.MaxOps()) } } if len(pop.Data) > t.cfg.MaxScriptElementSize() { - return scriptError(ErrElementTooBig, + return errs.NewError(errs.ErrElementTooBig, "element size %d exceeds max allowed size %d", len(pop.Data), t.cfg.MaxScriptElementSize()) } @@ -125,11 +131,11 @@ func (t *thread) executeOpcode(pop ParsedOp) error { // execution, nil otherwise. func (t *thread) validPC() error { if t.scriptIdx >= len(t.scripts) { - return scriptError(ErrInvalidProgramCounter, + return errs.NewError(errs.ErrInvalidProgramCounter, "past input scripts %v:%v %v:xxxx", t.scriptIdx, t.scriptOff, len(t.scripts)) } if t.scriptOff >= len(t.scripts[t.scriptIdx]) { - return scriptError(ErrInvalidProgramCounter, "past input scripts %v:%v %v:%04d", t.scriptIdx, t.scriptOff, + return errs.NewError(errs.ErrInvalidProgramCounter, "past input scripts %v:%v %v:%04d", t.scriptIdx, t.scriptOff, t.scriptIdx, len(t.scripts[t.scriptIdx])) } return nil @@ -140,11 +146,11 @@ func (t *thread) validPC() error { // including if the script has not finished. func (t *thread) CheckErrorCondition(finalScript bool) error { if t.dstack.Depth() < 1 { - return scriptError(ErrEmptyStack, "stack empty at end of script execution") + return errs.NewError(errs.ErrEmptyStack, "stack empty at end of script execution") } - if finalScript && t.hasFlag(ScriptVerifyCleanStack) && t.dstack.Depth() != 1 { - return scriptError(ErrCleanStack, "stack contains %d unexpected items", t.dstack.Depth()-1) + if finalScript && t.hasFlag(scriptflag.VerifyCleanStack) && t.dstack.Depth() != 1 { + return errs.NewError(errs.ErrCleanStack, "stack contains %d unexpected items", t.dstack.Depth()-1) } v, err := t.dstack.PopBool() @@ -152,7 +158,7 @@ func (t *thread) CheckErrorCondition(finalScript bool) error { return err } if !v { - return scriptError(ErrEvalFalse, "false stack entry at end of script execution") + return errs.NewError(errs.ErrEvalFalse, "false stack entry at end of script execution") } return nil @@ -177,7 +183,7 @@ func (t *thread) Step() (bool, error) { // disabled opcodes, illegal opcodes, maximum allowed operations per // script, maximum script element sizes, and conditionals. if err := t.executeOpcode(opcode); err != nil { - if ok := IsErrorCode(err, ErrOK); ok { + if ok := errs.IsErrorCode(err, errs.ErrOK); ok { // If returned early, move onto the next script t.shiftScript() return t.scriptIdx >= len(t.scripts), nil @@ -189,7 +195,7 @@ func (t *thread) Step() (bool, error) { // must not exceed the maximum number of stack elements allowed. combinedStackSize := t.dstack.Depth() + t.astack.Depth() if combinedStackSize > int32(t.cfg.MaxStackSize()) { - return false, scriptError(ErrStackOverflow, + return false, errs.NewError(errs.ErrStackOverflow, "combined stack size %d > max allowed %d", combinedStackSize, t.cfg.MaxStackSize()) } @@ -200,7 +206,7 @@ func (t *thread) Step() (bool, error) { // Prepare for next instruction. // Illegal to have an `if' that straddles two scripts. if len(t.condStack) != 0 { - return false, scriptError(ErrUnbalancedConditional, "end of script reached in conditional execution") + return false, errs.NewError(errs.ErrUnbalancedConditional, "end of script reached in conditional execution") } // Alt stack doesn't persist. @@ -249,7 +255,7 @@ func (t *thread) Step() (bool, error) { func (t *thread) apply(params ExecutionParams) error { t.tx = params.Tx - t.flags = params.Flags + t.scriptflag = params.Flags t.inputIdx = params.InputIdx t.prevOutput = params.PreviousTxOut @@ -261,12 +267,12 @@ func (t *thread) apply(params ExecutionParams) error { // Thus, allowing the clean stack flag without the P2SH flag would make // it possible to have a situation where P2SH would not be a soft fork // when it should be. - if t.hasFlag(ScriptEnableSighashForkID) { - t.addFlag(ScriptVerifyStrictEncoding) + if t.hasFlag(scriptflag.EnableSighashForkID) { + t.addFlag(scriptflag.VerifyStrictEncoding) } t.elseStack = &nopBoolStack{} - if t.hasFlag(ScriptUTXOAfterGenesis) { + if t.hasFlag(scriptflag.UTXOAfterGenesis) { t.elseStack = &stack{} t.afterGenesis = true t.cfg = &afterGenesisConfig{} @@ -274,8 +280,8 @@ func (t *thread) apply(params ExecutionParams) error { // The provided transaction input index must refer to a valid input. if t.inputIdx < 0 || t.inputIdx > t.tx.InputCount()-1 { - return scriptError( - ErrInvalidIndex, + return errs.NewError( + errs.ErrInvalidIndex, "transaction input index %d is negative or >= %d", params.InputIdx, len(params.Tx.Inputs), ) } @@ -288,24 +294,24 @@ func (t *thread) apply(params ExecutionParams) error { // empty which is equivalent to a false top element. Thus, just return // the relevant error now as an optimization. if (uls == nil || len(*uls) == 0) && (ls == nil || len(*ls) == 0) { - return scriptError(ErrEvalFalse, "false stack entry at end of script execution") + return errs.NewError(errs.ErrEvalFalse, "false stack entry at end of script execution") } - if t.hasFlag(ScriptVerifyCleanStack) && (!t.hasFlag(ScriptBip16)) { - return scriptError(ErrInvalidFlags, "invalid flags combination") + if t.hasFlag(scriptflag.VerifyCleanStack) && (!t.hasFlag(scriptflag.Bip16)) { + return errs.NewError(errs.ErrInvalidFlags, "invalid scriptflag combination") } if len(*uls) > t.cfg.MaxScriptSize() { - return scriptError( - ErrScriptTooBig, + return errs.NewError( + errs.ErrScriptTooBig, "unlocking script size %d is larger than the max allowed size %d", len(*uls), t.cfg.MaxScriptSize(), ) } if len(*ls) > t.cfg.MaxScriptSize() { - return scriptError( - ErrScriptTooBig, + return errs.NewError( + errs.ErrScriptTooBig, "locking script size %d is larger than the max allowed size %d", len(*uls), t.cfg.MaxScriptSize(), @@ -328,8 +334,8 @@ func (t *thread) apply(params ExecutionParams) error { // The signature script must only contain data pushes when the // associated flag is set. - if t.hasFlag(ScriptVerifySigPushOnly) && !t.scripts[0].IsPushOnly() { - return scriptError(ErrNotPushOnly, "signature script is not push only") + if t.hasFlag(scriptflag.VerifySigPushOnly) && !t.scripts[0].IsPushOnly() { + return errs.NewError(errs.ErrNotPushOnly, "signature script is not push only") } // Advance the program counter to the public key script if the signature @@ -339,15 +345,15 @@ func (t *thread) apply(params ExecutionParams) error { t.scriptIdx++ } - if t.hasFlag(ScriptBip16) && ls.IsP2SH() { + if t.hasFlag(scriptflag.Bip16) && ls.IsP2SH() { // Only accept input scripts that push data for P2SH. if !t.scripts[0].IsPushOnly() { - return scriptError(ErrNotPushOnly, "pay to script hash is not push only") + return errs.NewError(errs.ErrNotPushOnly, "pay to script hash is not push only") } t.bip16 = true } - if t.hasFlag(ScriptVerifyMinimalData) { + if t.hasFlag(scriptflag.VerifyMinimalData) { t.dstack.verifyMinimalData = true t.astack.verifyMinimalData = true } @@ -392,34 +398,34 @@ func (t *thread) subScript() ParsedScript { // checkHashTypeEncoding returns whether the passed hashtype adheres to // the strict encoding requirements if enabled. func (t *thread) checkHashTypeEncoding(shf sighash.Flag) error { - if !t.hasFlag(ScriptVerifyStrictEncoding) { + if !t.hasFlag(scriptflag.VerifyStrictEncoding) { return nil } sigHashType := shf & ^sighash.AnyOneCanPay - if t.hasFlag(ScriptVerifyBip143SigHash) { + if t.hasFlag(scriptflag.VerifyBip143SigHash) { sigHashType ^= sighash.ForkID if shf&sighash.ForkID == 0 { - return scriptError(ErrInvalidSigHashType, "hash type does not contain uahf forkID 0x%x", shf) + return errs.NewError(errs.ErrInvalidSigHashType, "hash type does not contain uahf forkID 0x%x", shf) } } if !sigHashType.Has(sighash.ForkID) { if sigHashType < sighash.All || sigHashType > sighash.Single { - return scriptError(ErrInvalidSigHashType, "invalid hash type 0x%x", shf) + return errs.NewError(errs.ErrInvalidSigHashType, "invalid hash type 0x%x", shf) } return nil } if sigHashType < sighash.AllForkID || sigHashType > sighash.SingleForkID { - return scriptError(ErrInvalidSigHashType, "invalid hash type 0x%x", shf) + return errs.NewError(errs.ErrInvalidSigHashType, "invalid hash type 0x%x", shf) } - if !t.hasFlag(ScriptEnableSighashForkID) && shf.Has(sighash.ForkID) { - return scriptError(ErrIllegalForkID, "fork id sighash set without flag") + if !t.hasFlag(scriptflag.EnableSighashForkID) && shf.Has(sighash.ForkID) { + return errs.NewError(errs.ErrIllegalForkID, "fork id sighash set without flag") } - if t.hasFlag(ScriptEnableSighashForkID) && !shf.Has(sighash.ForkID) { - return scriptError(ErrIllegalForkID, "fork id sighash not set with flag") + if t.hasFlag(scriptflag.EnableSighashForkID) && !shf.Has(sighash.ForkID) { + return errs.NewError(errs.ErrIllegalForkID, "fork id sighash not set with flag") } return nil @@ -428,7 +434,7 @@ func (t *thread) checkHashTypeEncoding(shf sighash.Flag) error { // checkPubKeyEncoding returns whether the passed public key adheres to // the strict encoding requirements if enabled. func (t *thread) checkPubKeyEncoding(pubKey []byte) error { - if !t.hasFlag(ScriptVerifyStrictEncoding) { + if !t.hasFlag(scriptflag.VerifyStrictEncoding) { return nil } @@ -441,13 +447,15 @@ func (t *thread) checkPubKeyEncoding(pubKey []byte) error { return nil } - return scriptError(ErrPubKeyType, "unsupported public key type") + return errs.NewError(errs.ErrPubKeyType, "unsupported public key type") } // checkSignatureEncoding returns whether the passed signature adheres to // the strict encoding requirements if enabled. func (t *thread) checkSignatureEncoding(sig []byte) error { - if !t.hasFlag(ScriptVerifyDERSignatures) && !t.hasFlag(ScriptVerifyLowS) && !t.hasFlag(ScriptVerifyStrictEncoding) { + if !t.hasFlag(scriptflag.VerifyDERSignatures) && + !t.hasFlag(scriptflag.VerifyLowS) && + !t.hasFlag(scriptflag.VerifyStrictEncoding) { return nil } @@ -510,21 +518,24 @@ func (t *thread) checkSignatureEncoding(sig []byte) error { // The signature must adhere to the minimum and maximum allowed length. sigLen := len(sig) if sigLen < minSigLen { - return scriptError(ErrSigTooShort, "malformed signature: too short: %d < %d", sigLen, minSigLen) + return errs.NewError(errs.ErrSigTooShort, "malformed signature: too short: %d < %d", sigLen, minSigLen) } if sigLen > maxSigLen { - return scriptError(ErrSigTooLong, "malformed signature: too long: %d > %d", sigLen, maxSigLen) + return errs.NewError(errs.ErrSigTooLong, "malformed signature: too long: %d > %d", sigLen, maxSigLen) } // The signature must start with the ASN.1 sequence identifier. if sig[sequenceOffset] != asn1SequenceID { - return scriptError(ErrSigInvalidSeqID, "malformed signature: format has wrong type: %#x", sig[sequenceOffset]) + return errs.NewError(errs.ErrSigInvalidSeqID, "malformed signature: format has wrong type: %#x", sig[sequenceOffset]) } // The signature must indicate the correct amount of data for all elements // related to R and S. if int(sig[dataLenOffset]) != sigLen-2 { - return scriptError(ErrSigInvalidDataLen, "malformed signature: bad length: %d != %d", sig[dataLenOffset], sigLen-2) + return errs.NewError(errs.ErrSigInvalidDataLen, + "malformed signature: bad length: %d != %d", + sig[dataLenOffset], sigLen-2, + ) } // Calculate the offsets of the elements related to S and ensure S is inside @@ -542,10 +553,10 @@ func (t *thread) checkSignatureEncoding(sig []byte) error { sTypeOffset := rOffset + rLen sLenOffset := sTypeOffset + 1 if sTypeOffset >= sigLen { - return scriptError(ErrSigMissingSTypeID, "malformed signature: S type indicator missing") + return errs.NewError(errs.ErrSigMissingSTypeID, "malformed signature: S type indicator missing") } if sLenOffset >= sigLen { - return scriptError(ErrSigMissingSLen, "malformed signature: S length missing") + return errs.NewError(errs.ErrSigMissingSLen, "malformed signature: S length missing") } // The lengths of R and S must match the overall length of the signature. @@ -555,51 +566,51 @@ func (t *thread) checkSignatureEncoding(sig []byte) error { sOffset := sLenOffset + 1 sLen := int(sig[sLenOffset]) if sOffset+sLen != sigLen { - return scriptError(ErrSigInvalidSLen, "malformed signature: invalid S length") + return errs.NewError(errs.ErrSigInvalidSLen, "malformed signature: invalid S length") } // R elements must be ASN.1 integers. if sig[rTypeOffset] != asn1IntegerID { - return scriptError(ErrSigInvalidRIntID, + return errs.NewError(errs.ErrSigInvalidRIntID, "malformed signature: R integer marker: %#x != %#x", sig[rTypeOffset], asn1IntegerID) } // Zero-length integers are not allowed for R. if rLen == 0 { - return scriptError(ErrSigZeroRLen, "malformed signature: R length is zero") + return errs.NewError(errs.ErrSigZeroRLen, "malformed signature: R length is zero") } // R must not be negative. if sig[rOffset]&0x80 != 0 { - return scriptError(ErrSigNegativeR, "malformed signature: R is negative") + return errs.NewError(errs.ErrSigNegativeR, "malformed signature: R is negative") } // Null bytes at the start of R are not allowed, unless R would otherwise be // interpreted as a negative number. if rLen > 1 && sig[rOffset] == 0x00 && sig[rOffset+1]&0x80 == 0 { - return scriptError(ErrSigTooMuchRPadding, "malformed signature: R value has too much padding") + return errs.NewError(errs.ErrSigTooMuchRPadding, "malformed signature: R value has too much padding") } // S elements must be ASN.1 integers. if sig[sTypeOffset] != asn1IntegerID { - return scriptError(ErrSigInvalidSIntID, + return errs.NewError(errs.ErrSigInvalidSIntID, "malformed signature: S integer marker: %#x != %#x", sig[sTypeOffset], asn1IntegerID) } // Zero-length integers are not allowed for S. if sLen == 0 { - return scriptError(ErrSigZeroSLen, "malformed signature: S length is zero") + return errs.NewError(errs.ErrSigZeroSLen, "malformed signature: S length is zero") } // S must not be negative. if sig[sOffset]&0x80 != 0 { - return scriptError(ErrSigNegativeS, "malformed signature: S is negative") + return errs.NewError(errs.ErrSigNegativeS, "malformed signature: S is negative") } // Null bytes at the start of S are not allowed, unless S would otherwise be // interpreted as a negative number. if sLen > 1 && sig[sOffset] == 0x00 && sig[sOffset+1]&0x80 == 0 { - return scriptError(ErrSigTooMuchSPadding, "malformed signature: S value has too much padding") + return errs.NewError(errs.ErrSigTooMuchSPadding, "malformed signature: S value has too much padding") } // Verify the S value is <= half the order of the curve. This check is done @@ -609,10 +620,10 @@ func (t *thread) checkSignatureEncoding(sig []byte) error { // transaction with the complement while still being a valid signature that // verifies. This would result in changing the transaction hash and thus is // a source of malleability. - if t.hasFlag(ScriptVerifyLowS) { + if t.hasFlag(scriptflag.VerifyLowS) { sValue := new(big.Int).SetBytes(sig[sOffset : sOffset+sLen]) if sValue.Cmp(halfOrder) > 0 { - return scriptError(ErrSigHighS, "signature is not canonical due to unnecessarily high S value") + return errs.NewError(errs.ErrSigHighS, "signature is not canonical due to unnecessarily high S value") } } return nil @@ -647,7 +658,7 @@ func (t *thread) shouldExec(pop ParsedOp) bool { } var count int for _, v := range t.condStack { - if v == OpCondFalse { + if v == opCondFalse { count++ } }