diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 3de7cdc14..6ed1417b4 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1514,7 +1514,11 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa } resEl := vm.Estack().Pop() if resEl != nil { - if !resEl.Bool() { + res, err := resEl.Item().TryBool() + if err != nil { + return fmt.Errorf("%w: invalid return value", ErrVerificationFailed) + } + if !res { return fmt.Errorf("%w: invalid signature", ErrVerificationFailed) } if vm.Estack().Len() != 0 { diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 0138d6b76..1eb347744 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -401,6 +401,17 @@ func TestVerifyHashAgainstScript(t *testing.T) { err := bc.verifyHashAgainstScript(hash.Hash160(verif), w, ic, false, gas) require.True(t, errors.Is(err, ErrVerificationFailed)) }) + t.Run("BadResult", func(t *testing.T) { + verif := make([]byte, 66) + verif[0] = byte(opcode.PUSHDATA1) + verif[1] = 64 + w := &transaction.Witness{ + InvocationScript: []byte{byte(opcode.NOP)}, + VerificationScript: verif, + } + err := bc.verifyHashAgainstScript(hash.Hash160(verif), w, ic, false, gas) + require.True(t, errors.Is(err, ErrVerificationFailed)) + }) t.Run("TooManyResults", func(t *testing.T) { verif := []byte{byte(opcode.NOP)} w := &transaction.Witness{ diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index cf869efa4..a7187cbb0 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -577,8 +577,12 @@ func compareContractStates(t *testing.T, expected *state.Contract, actual stacki require.Equal(t, 4, len(act)) require.Equal(t, expected.Script, act[0].Value().([]byte)) require.Equal(t, expectedManifest, act[1].Value().([]byte)) - require.Equal(t, expected.HasStorage(), act[2].Bool()) - require.Equal(t, expected.IsPayable(), act[3].Bool()) + hasstorage, err := act[2].TryBool() + require.NoError(t, err) + ispayable, err := act[3].TryBool() + require.NoError(t, err) + require.Equal(t, expected.HasStorage(), hasstorage) + require.Equal(t, expected.IsPayable(), ispayable) } func TestContractUpdate(t *testing.T) { diff --git a/pkg/core/native/native_neo_candidate.go b/pkg/core/native/native_neo_candidate.go index 2865852ca..0fa014eb6 100644 --- a/pkg/core/native/native_neo_candidate.go +++ b/pkg/core/native/native_neo_candidate.go @@ -42,7 +42,10 @@ func (c *candidate) fromStackItem(item stackitem.Item) *candidate { if err != nil { panic(err) } - c.Registered = arr[0].Bool() + c.Registered, err = arr[0].TryBool() + if err != nil { + panic(err) + } c.Votes = *vs return c } diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 79893d299..ce36ff783 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -191,8 +191,8 @@ func (c *Context) Dup() stackitem.Item { return c } -// Bool implements stackitem.Item interface. -func (c *Context) Bool() bool { panic("can't convert Context to Bool") } +// TryBool implements stackitem.Item interface. +func (c *Context) TryBool() (bool, error) { panic("can't convert Context to Bool") } // TryBytes implements stackitem.Item interface. func (c *Context) TryBytes() ([]byte, error) { diff --git a/pkg/vm/exception.go b/pkg/vm/exception.go index 73b8e23cc..9d80ef933 100644 --- a/pkg/vm/exception.go +++ b/pkg/vm/exception.go @@ -54,8 +54,8 @@ func (c *exceptionHandlingContext) Dup() stackitem.Item { return c } -// Bool implements stackitem.Item interface. -func (c *exceptionHandlingContext) Bool() bool { +// TryBool implements stackitem.Item interface. +func (c *exceptionHandlingContext) TryBool() (bool, error) { panic("can't convert exceptionHandlingContext to Bool") } diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index 969a65a1b..dd2a9656a 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -80,9 +80,14 @@ func (e *Element) BigInt() *big.Int { return val } -// Bool converts an underlying value of the element to a boolean. +// Bool converts an underlying value of the element to a boolean if it's +// possible to do so, it will panic otherwise. func (e *Element) Bool() bool { - return e.value.Bool() + b, err := e.value.TryBool() + if err != nil { + panic(err) + } + return b } // Bytes attempts to get the underlying value of the element as a byte array. diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 324086dfa..c20701b50 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -31,8 +31,8 @@ type Item interface { Value() interface{} // Dup duplicates current Item. Dup() Item - // Bool converts Item to a boolean value. - Bool() bool + // TryBool converts Item to a boolean value. + TryBool() (bool, error) // TryBytes converts Item to a byte slice. TryBytes() ([]byte, error) // TryInteger converts Item to an integer. @@ -155,7 +155,11 @@ func convertPrimitive(item Item, typ Type) (Item, error) { } return NewByteArray(b), nil case BooleanT: - return NewBool(item.Bool()), nil + b, err := item.TryBool() + if err != nil { + return nil, err + } + return NewBool(b), nil default: return nil, errInvalidConversion } @@ -210,8 +214,8 @@ func (i *Struct) Dup() Item { return i } -// Bool implements Item interface. -func (i *Struct) Bool() bool { return true } +// TryBool implements Item interface. +func (i *Struct) TryBool() (bool, error) { return true, nil } // TryBytes implements Item interface. func (i *Struct) TryBytes() ([]byte, error) { @@ -255,7 +259,7 @@ func (i *Struct) Convert(typ Type) (Item, error) { copy(arr, i.value) return NewArray(arr), nil case BooleanT: - return NewBool(i.Bool()), nil + return NewBool(true), nil default: return nil, errInvalidConversion } @@ -296,8 +300,8 @@ func (i Null) Dup() Item { return i } -// Bool implements Item interface. -func (i Null) Bool() bool { return false } +// TryBool implements Item interface. +func (i Null) TryBool() (bool, error) { return false, nil } // TryBytes implements Item interface. func (i Null) TryBytes() ([]byte, error) { @@ -346,9 +350,9 @@ func (i *BigInteger) Bytes() []byte { return bigint.ToBytes(i.value) } -// Bool implements Item interface. -func (i *BigInteger) Bool() bool { - return i.value.Sign() != 0 +// TryBool implements Item interface. +func (i *BigInteger) TryBool() (bool, error) { + return i.value.Sign() != 0, nil } // TryBytes implements Item interface. @@ -431,8 +435,8 @@ func (i *Bool) Dup() Item { return &Bool{i.value} } -// Bool implements Item interface. -func (i *Bool) Bool() bool { return i.value } +// TryBool implements Item interface. +func (i *Bool) TryBool() (bool, error) { return i.value, nil } // Bytes converts Bool to bytes. func (i *Bool) Bytes() []byte { @@ -500,17 +504,17 @@ func (i *ByteArray) String() string { return "ByteString" } -// Bool implements Item interface. -func (i *ByteArray) Bool() bool { +// TryBool implements Item interface. +func (i *ByteArray) TryBool() (bool, error) { if len(i.value) > MaxBigIntegerSizeBits/8 { - return true + return false, errors.New("too big byte string") } for _, b := range i.value { if b != 0 { - return true + return true, nil } } - return false + return false, nil } // TryBytes implements Item interface. @@ -601,8 +605,8 @@ func (i *Array) String() string { return "Array" } -// Bool implements Item interface. -func (i *Array) Bool() bool { return true } +// TryBool implements Item interface. +func (i *Array) TryBool() (bool, error) { return true, nil } // TryBytes implements Item interface. func (i *Array) TryBytes() ([]byte, error) { @@ -638,7 +642,7 @@ func (i *Array) Convert(typ Type) (Item, error) { copy(arr, i.value) return NewStruct(arr), nil case BooleanT: - return NewBool(i.Bool()), nil + return NewBool(true), nil default: return nil, errInvalidConversion } @@ -691,8 +695,8 @@ func (i *Map) Len() int { return len(i.value) } -// Bool implements Item interface. -func (i *Map) Bool() bool { return true } +// TryBool implements Item interface. +func (i *Map) TryBool() (bool, error) { return true, nil } // TryBytes implements Item interface. func (i *Map) TryBytes() ([]byte, error) { @@ -743,7 +747,7 @@ func (i *Map) Convert(typ Type) (Item, error) { case MapT: return i, nil case BooleanT: - return NewBool(i.Bool()), nil + return NewBool(true), nil default: return nil, errInvalidConversion } @@ -807,8 +811,8 @@ func (i *Interop) Dup() Item { return i } -// Bool implements Item interface. -func (i *Interop) Bool() bool { return true } +// TryBool implements Item interface. +func (i *Interop) TryBool() (bool, error) { return true, nil } // TryBytes implements Item interface. func (i *Interop) TryBytes() ([]byte, error) { @@ -840,7 +844,7 @@ func (i *Interop) Convert(typ Type) (Item, error) { case InteropT: return i, nil case BooleanT: - return NewBool(i.Bool()), nil + return NewBool(true), nil default: return nil, errInvalidConversion } @@ -886,9 +890,9 @@ func (p *Pointer) Dup() Item { } } -// Bool implements Item interface. -func (p *Pointer) Bool() bool { - return true +// TryBool implements Item interface. +func (p *Pointer) TryBool() (bool, error) { + return true, nil } // TryBytes implements Item interface. @@ -921,7 +925,7 @@ func (p *Pointer) Convert(typ Type) (Item, error) { case PointerT: return p, nil case BooleanT: - return NewBool(p.Bool()), nil + return NewBool(true), nil default: return nil, errInvalidConversion } @@ -959,9 +963,9 @@ func (i *Buffer) String() string { return "Buffer" } -// Bool implements Item interface. -func (i *Buffer) Bool() bool { - return true +// TryBool implements Item interface. +func (i *Buffer) TryBool() (bool, error) { + return true, nil } // TryBytes implements Item interface. @@ -998,7 +1002,7 @@ func (i *Buffer) Type() Type { return BufferT } func (i *Buffer) Convert(typ Type) (Item, error) { switch typ { case BooleanT: - return NewBool(i.Bool()), nil + return NewBool(true), nil case BufferT: return i, nil case ByteArrayT: diff --git a/pkg/vm/testdata/neo-vm b/pkg/vm/testdata/neo-vm index 359e8631e..377464ed4 160000 --- a/pkg/vm/testdata/neo-vm +++ b/pkg/vm/testdata/neo-vm @@ -1 +1 @@ -Subproject commit 359e8631ee2ddfefe8261afcec1a5bab9d9bddf9 +Subproject commit 377464ed475a3de108e1bf9c834bd2279b72624e