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.
This commit is contained in:
Roman Khimov 2019-10-03 16:26:32 +03:00
parent 8441b31b4b
commit 1bf232ad50
2 changed files with 29 additions and 16 deletions

View file

@ -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")
}
}

View file

@ -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 {