Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Smaller Interpreter Suggestion Footprint #51

Merged
merged 2 commits into from
Sep 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 1 addition & 99 deletions bscript/interpreter/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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{},
}

Expand Down
72 changes: 37 additions & 35 deletions bscript/interpreter/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -108,7 +110,7 @@ func TestCheckErrorCondition(t *testing.T) {
}

vm := &thread{
scriptParser: &parser{},
scriptParser: &DefaultOpcodeParser{},
cfg: &beforeGenesisConfig{},
}

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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{
Expand All @@ -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)
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
17 changes: 3 additions & 14 deletions bscript/interpreter/error.go → bscript/interpreter/errs/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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...)}
}

Expand All @@ -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()))
}
Loading