fire event on failed funds transfer in vm
This commit is contained in:
parent
bda9a38544
commit
0dbf17653a
|
@ -9,6 +9,8 @@ import (
|
|||
"time"
|
||||
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
"github.com/tendermint/tendermint/events"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
. "github.com/tendermint/tendermint/vm"
|
||||
)
|
||||
|
||||
|
@ -47,8 +49,8 @@ func TestVM(t *testing.T) {
|
|||
Address: Uint64ToWord256(101),
|
||||
}
|
||||
|
||||
var gas uint64 = 1000
|
||||
N := []byte{0xff, 0xff}
|
||||
var gas uint64 = 100000
|
||||
N := []byte{0x0f, 0x0f}
|
||||
// Loop N times
|
||||
code := []byte{0x60, 0x00, 0x60, 0x20, 0x52, 0x5B, byte(0x60 + len(N) - 1)}
|
||||
for i := 0; i < len(N); i++ {
|
||||
|
@ -59,6 +61,9 @@ func TestVM(t *testing.T) {
|
|||
output, err := ourVm.Call(account1, account2, code, []byte{}, 0, &gas)
|
||||
fmt.Printf("Output: %v Error: %v\n", output, err)
|
||||
fmt.Println("Call took:", time.Since(start))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the code for a subcurrency contract compiled by serpent
|
||||
|
@ -88,7 +93,9 @@ func TestSubcurrency(t *testing.T) {
|
|||
data, _ := hex.DecodeString("693200CE0000000000000000000000004B4363CDE27C2EB05E66357DB05BC5C88F850C1A0000000000000000000000000000000000000000000000000000000000000005")
|
||||
output, err := ourVm.Call(account1, account2, code, data, 0, &gas)
|
||||
fmt.Printf("Output: %v Error: %v\n", output, err)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test sending tokens from a contract to another account
|
||||
|
@ -108,8 +115,72 @@ func TestSendCall(t *testing.T) {
|
|||
}
|
||||
|
||||
// account1 will call account2 which will trigger CALL opcode to account3
|
||||
|
||||
addr := account3.Address.Postfix(20)
|
||||
contractCode := callContractCode(addr)
|
||||
|
||||
//----------------------------------------------
|
||||
// account2 has insufficient balance, should fail
|
||||
|
||||
exception := runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
|
||||
if exception == "" {
|
||||
t.Fatal("Expected exception")
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// give account2 sufficient balance, should pass
|
||||
|
||||
account2.Balance = 100000
|
||||
exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 1000)
|
||||
if exception != "" {
|
||||
t.Fatal("Unexpected exception", exception)
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// insufficient gas, should fail
|
||||
|
||||
account2.Balance = 100000
|
||||
exception = runVMWaitEvents(t, ourVm, account1, account2, addr, contractCode, 100)
|
||||
if exception == "" {
|
||||
t.Fatal("Expected exception")
|
||||
}
|
||||
}
|
||||
|
||||
// subscribes to an AccReceive, runs the vm, returns the exception
|
||||
func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas uint64) string {
|
||||
// we need to catch the event from the CALL to check for exceptions
|
||||
evsw := new(events.EventSwitch)
|
||||
evsw.Start()
|
||||
ch := make(chan interface{})
|
||||
fmt.Printf("subscribe to %x\n", subscribeAddr)
|
||||
evsw.AddListenerForEvent("test", types.EventStringAccReceive(subscribeAddr), func(msg interface{}) {
|
||||
ch <- msg
|
||||
})
|
||||
evc := events.NewEventCache(evsw)
|
||||
ourVm.SetFireable(evc)
|
||||
go func() {
|
||||
start := time.Now()
|
||||
output, err := ourVm.Call(caller, callee, contractCode, []byte{}, 0, &gas)
|
||||
fmt.Printf("Output: %v Error: %v\n", output, err)
|
||||
fmt.Println("Call took:", time.Since(start))
|
||||
if err != nil {
|
||||
ch <- err.Error()
|
||||
}
|
||||
evc.Flush()
|
||||
}()
|
||||
msg := <-ch
|
||||
switch ev := msg.(type) {
|
||||
case types.EventMsgCallTx:
|
||||
return ev.Exception
|
||||
case types.EventMsgCall:
|
||||
return ev.Exception
|
||||
case string:
|
||||
return ev
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// this is code to call another contract (hardcoded as addr)
|
||||
func callContractCode(addr []byte) []byte {
|
||||
gas1, gas2 := byte(0x1), byte(0x1)
|
||||
value := byte(0x69)
|
||||
inOff, inSize := byte(0x0), byte(0x0) // no call data
|
||||
|
@ -118,15 +189,7 @@ func TestSendCall(t *testing.T) {
|
|||
contractCode := []byte{0x60, retSize, 0x60, retOff, 0x60, inSize, 0x60, inOff, 0x60, value, 0x73}
|
||||
contractCode = append(contractCode, addr...)
|
||||
contractCode = append(contractCode, []byte{0x61, gas1, gas2, 0xf1, 0x60, 0x20, 0x60, 0x0, 0xf3}...)
|
||||
|
||||
var gas uint64 = 1000
|
||||
start := time.Now()
|
||||
output, err := ourVm.Call(account1, account2, contractCode, []byte{}, 0, &gas)
|
||||
fmt.Printf("Output: %v Error: %v\n", output, err)
|
||||
fmt.Println("Call took:", time.Since(start))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return contractCode
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
33
vm/vm.go
33
vm/vm.go
|
@ -74,17 +74,33 @@ func (vm *VM) SetFireable(evc events.Fireable) {
|
|||
// code: May be nil, since the CALL opcode may be used to send value from contracts to accounts
|
||||
func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, gas *uint64) (output []byte, err error) {
|
||||
|
||||
exception := new(string)
|
||||
defer func() {
|
||||
// if callDepth is 0 the event is fired from ExecTx (along with the Input event)
|
||||
// otherwise, we fire from here.
|
||||
if vm.callDepth != 0 && vm.evc != nil {
|
||||
fmt.Println("FIRE AWAY!", types.EventStringAccReceive(callee.Address.Postfix(20)))
|
||||
vm.evc.FireEvent(types.EventStringAccReceive(callee.Address.Postfix(20)), types.EventMsgCall{
|
||||
&types.CallData{caller.Address.Postfix(20), callee.Address.Postfix(20), input, value, *gas},
|
||||
vm.origin.Postfix(20),
|
||||
vm.txid,
|
||||
output,
|
||||
*exception,
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
if err = transfer(caller, callee, value); err != nil {
|
||||
*exception = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
exception := ""
|
||||
if len(code) > 0 {
|
||||
vm.callDepth += 1
|
||||
output, err = vm.call(caller, callee, code, input, value, gas)
|
||||
vm.callDepth -= 1
|
||||
if err != nil {
|
||||
exception = err.Error()
|
||||
*exception = err.Error()
|
||||
err := transfer(callee, caller, value)
|
||||
if err != nil {
|
||||
panic("Could not return value to caller")
|
||||
|
@ -92,17 +108,6 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga
|
|||
}
|
||||
}
|
||||
|
||||
// if callDepth is 0 the event is fired from ExecTx (along with the Input event)
|
||||
// otherwise, we fire from here.
|
||||
if vm.callDepth != 0 && vm.evc != nil {
|
||||
vm.evc.FireEvent(types.EventStringAccReceive(callee.Address.Postfix(20)), types.EventMsgCall{
|
||||
&types.CallData{caller.Address.Postfix(20), callee.Address.Postfix(20), input, value, *gas},
|
||||
vm.origin.Postfix(20),
|
||||
vm.txid,
|
||||
output,
|
||||
exception,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -737,6 +742,8 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga
|
|||
|
||||
// Push result
|
||||
if err != nil {
|
||||
dbg.Printf("error on call: %s", err.Error())
|
||||
// TODO: fire event
|
||||
stack.Push(Zero256)
|
||||
} else {
|
||||
stack.Push(One256)
|
||||
|
|
Loading…
Reference in New Issue