From 1bf232ad50612464cc5cb1065459c124ae3572ae Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 3 Oct 2019 16:26:32 +0300 Subject: [PATCH] vm: introduce TryBool() for Element and use it in VerifyWitnesses Script can return non-bool results that can still be converted to bool according to the usual VM rules. Unfortunately Bool() panics if this conversion fails which is OK for things done in vm.execute(), but certainly not for VerifyWitnesses(), thus there is a need for TryBool() that will just return an error in this case. --- pkg/core/blockchain.go | 15 +++++++++------ pkg/vm/stack.go | 30 ++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 59e4bc5b5..0365c2517 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1045,14 +1045,17 @@ func (bc *Blockchain) VerifyWitnesses(t *transaction.Transaction) error { if vm.HasFailed() { return errors.Errorf("vm failed to execute the script") } - res := vm.PopResult() - switch res.(type) { - case bool: - if !(res.(bool)) { + resEl := vm.Estack().Pop() + if resEl != nil { + res, err := resEl.TryBool() + if err != nil { + return err + } + if !res { return errors.Errorf("signature check failed") } - default: - return errors.Errorf("vm returned non-boolean result") + } else { + return errors.Errorf("no result returned from the script") } } diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index c9ce195be..5dd5b2dd1 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -80,30 +80,40 @@ func (e *Element) BigInt() *big.Int { } } -// Bool attempts to get the underlying value of the element as a boolean. -// Will panic if the assertion failed which will be caught by the VM. -func (e *Element) Bool() bool { +// TryBool attempts to get the underlying value of the element as a boolean. +// Returns error if can't convert value to boolean type. +func (e *Element) TryBool() (bool, error) { switch t := e.value.(type) { case *BigIntegerItem: - return t.value.Int64() != 0 + return t.value.Int64() != 0, nil case *BoolItem: - return t.value + return t.value, nil case *ArrayItem, *StructItem: - return true + return true, nil case *ByteArrayItem: for _, b := range t.value { if b != 0 { - return true + return true, nil } } - return false + return false, nil case *InteropItem: - return t.value != nil + return t.value != nil, nil default: - panic("can't convert to bool: " + t.String()) + return false, fmt.Errorf("can't convert to bool: " + t.String()) } } +// Bool attempts to get the underlying value of the element as a boolean. +// Will panic if the assertion failed which will be caught by the VM. +func (e *Element) Bool() bool { + val, err := e.TryBool() + if err != nil { + panic(err) + } + return val +} + // Bytes attempts to get the underlying value of the element as a byte array. // Will panic if the assertion failed which will be caught by the VM. func (e *Element) Bytes() []byte {