From 83fb255e471e0bf738ed9053e547d65321947e18 Mon Sep 17 00:00:00 2001 From: smblucker <smblucker@outlook.com> Date: Mon, 5 Mar 2018 16:32:22 -0500 Subject: [PATCH] WIP implementation of REVERT opcode Signed-off-by: smblucker <smblucker@outlook.com> Signed-off-by: Silas Davis <silas@monax.io> --- execution/evm/asm/opcodes.go | 2 ++ execution/evm/vm.go | 20 ++++++++++++++++++++ execution/evm/vm_test.go | 22 ++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/execution/evm/asm/opcodes.go b/execution/evm/asm/opcodes.go index 0a4e3531..f85f3b4d 100644 --- a/execution/evm/asm/opcodes.go +++ b/execution/evm/asm/opcodes.go @@ -182,6 +182,7 @@ const ( DELEGATECALL // 0x70 range - other + REVERT = 0xfd SELFDESTRUCT = 0xff ) @@ -334,6 +335,7 @@ var opCodeNames = map[OpCode]string{ DELEGATECALL: "DELEGATECALL", // 0x70 range - other + REVERT: "REVERT", SELFDESTRUCT: "SELFDESTRUCT", } diff --git a/execution/evm/vm.go b/execution/evm/vm.go index ed760c17..03a7249e 100644 --- a/execution/evm/vm.go +++ b/execution/evm/vm.go @@ -48,6 +48,7 @@ var ( ErrDataStackUnderflow = errors.New("Data stack underflow") ErrInvalidContract = errors.New("Invalid contract") ErrNativeContractCodeCopy = errors.New("Tried to copy native contract code") + ErrExecutionReverted = errors.New("Execution reverted") ) type ErrPermission struct { @@ -773,6 +774,10 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value stack.Push(newAccount.Address().Word256()) } + if err_ == ErrExecutionReverted { + return ret, nil + } + case CALL, CALLCODE, DELEGATECALL: // 0xF1, 0xF2, 0xF4 if !HasPermission(vm.state, callee, permission.Call) { return nil, ErrPermission{"call"} @@ -863,6 +868,10 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value // TODO: we probably don't want to return the error - decide //err = firstErr(err, callErr) stack.Push(Zero256) + + if callErr == ErrExecutionReverted { + memory.Write(retOffset, RightPadBytes(ret, int(retSize))) + } } else { stack.Push(One256) @@ -891,6 +900,17 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(output), output) return output, nil + case REVERT: // 0xFD + offset, size := stack.PopBigInt(), stack.PopBigInt() + output, memErr := memory.Read(offset, size) + if memErr != nil { + vm.Debugf(" => Memory err: %s", memErr) + return nil, firstErr(err, ErrMemoryOutOfBounds) + } + + vm.Debugf(" => [%v, %v] (%d) 0x%X\n", offset, size, len(output), output) + return output, ErrExecutionReverted + case SELFDESTRUCT: // 0xFF addr := stack.Pop() if useGasNegative(gas, GasGetAccount, &err) { diff --git a/execution/evm/vm_test.go b/execution/evm/vm_test.go index a6335dee..0d8ee817 100644 --- a/execution/evm/vm_test.go +++ b/execution/evm/vm_test.go @@ -158,6 +158,28 @@ func TestSubcurrency(t *testing.T) { } } +//This test case is taken from EIP-140 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-140.md); +//it is meant to test the implementation of the REVERT opcode +func TestRevert(t *testing.T) { + ourVm := NewVM(newAppState(), DefaultDynamicMemoryProvider, newParams(), acm.ZeroAddress, nil, logger) + + // Create accounts + account1 := newAccount(1) + account2 := newAccount(1, 0, 1) + + var gas uint64 = 100000 + + bytecode := MustSplice(PUSH32, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, PUSH1, 0x00, MSTORE, PUSH1, 0x0E, PUSH1, 0x00, REVERT) + + start := time.Now() + output, err := ourVm.Call(account1, account2, bytecode, []byte{}, 0, &gas) + assert.Error(t, err, "Expected execution reverted error") + fmt.Printf("Output: %v Error: %v\n", output, err) + fmt.Println("Call took:", time.Since(start)) +} + // Test sending tokens from a contract to another account func TestSendCall(t *testing.T) { fakeAppState := newAppState() -- GitLab