Browse Source

fix send funds from contract

pull/66/head
Ethan Buchman 10 years ago
parent
commit
7b1fc780aa
3 changed files with 36 additions and 22 deletions
  1. +1
    -5
      vm/test/fake_app_state.go
  2. +9
    -3
      vm/test/vm_test.go
  3. +26
    -14
      vm/vm.go

+ 1
- 5
vm/test/fake_app_state.go View File

@ -14,11 +14,7 @@ type FakeAppState struct {
func (fas *FakeAppState) GetAccount(addr Word256) *Account {
account := fas.accounts[addr.String()]
if account != nil {
return account
} else {
panic(Fmt("Invalid account addr: %X", addr))
}
return account
}
func (fas *FakeAppState) UpdateAccount(account *Account) {


+ 9
- 3
vm/test/vm_test.go View File

@ -103,10 +103,13 @@ func TestSendCall(t *testing.T) {
account2 := &Account{
Address: Uint64ToWord256(101),
}
fakeAppState.UpdateAccount(account1)
fakeAppState.UpdateAccount(account2)
account3 := &Account{
Address: Uint64ToWord256(102),
}
// account1 will call account2 which will trigger CALL opcode to account3
addr := account1.Address.Postfix(20)
addr := account3.Address.Postfix(20)
gas1, gas2 := byte(0x1), byte(0x1)
value := byte(0x69)
inOff, inSize := byte(0x0), byte(0x0) // no call data
@ -121,6 +124,9 @@ func TestSendCall(t *testing.T) {
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)
}
}
/*


+ 26
- 14
vm/vm.go View File

@ -71,27 +71,27 @@ func (vm *VM) SetFireable(evc events.Fireable) {
// CONTRACT appState is aware of caller and callee, so we can just mutate them.
// value: To be transferred from caller to callee. Refunded upon error.
// gas: Available gas. No refunds for gas.
// 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) {
if len(code) == 0 {
panic("Call() requires code")
}
if err = transfer(caller, callee, value); err != nil {
return
}
vm.callDepth += 1
output, err = vm.call(caller, callee, code, input, value, gas)
vm.callDepth -= 1
exception := ""
if err != nil {
exception = err.Error()
err := transfer(callee, caller, value)
if len(code) > 0 {
vm.callDepth += 1
output, err = vm.call(caller, callee, code, input, value, gas)
vm.callDepth -= 1
if err != nil {
panic("Could not return value to caller")
exception = err.Error()
err := transfer(callee, caller, value)
if err != nil {
panic("Could not return value to caller")
}
}
}
// 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 {
@ -713,12 +713,24 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga
return nil, firstErr(err, ErrInsufficientGas)
}
acc := vm.appState.GetAccount(addr)
if acc == nil {
return nil, firstErr(err, ErrUnknownAddress)
}
// since CALL is used also for sending funds,
// acc may not exist yet. This is an error for
// CALLCODE, but not for CALL, though I don't think
// ethereum actually cares
if op == CALLCODE {
if acc == nil {
return nil, firstErr(err, ErrUnknownAddress)
}
ret, err = vm.Call(callee, callee, acc.Code, args, value, gas)
} else {
if acc == nil {
// if we have not seen the account before, create it
// so we can send funds
acc = &Account{
Address: addr,
}
vm.appState.UpdateAccount(acc)
}
ret, err = vm.Call(callee, acc, acc.Code, args, value, gas)
}
}


Loading…
Cancel
Save