fire event on failed funds transfer in vm

This commit is contained in:
Ethan Buchman 2015-05-15 15:30:24 -04:00
parent bda9a38544
commit 0dbf17653a
2 changed files with 96 additions and 26 deletions

View File

@ -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
}
/*

View File

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