From 82798080269cdd36f1714b1d3da6090c0db774d1 Mon Sep 17 00:00:00 2001 From: Mason Fischer Date: Mon, 20 Aug 2018 11:36:43 -0400 Subject: [PATCH] Fix bug at block #116524 What Changed ============ When `size` was 0 on callcode we were returning 0. Instead we should be returning the machine state unchanged. This didn't cause an error until block #116524 where callcode was called with `size = 0`. Other changes ----------- * Added a script to run raw EVM code. I used this while debugging this issue. * Minor EVM README fix --- apps/blockchain/scripts/evm_runner.ex | 65 +++++++++++++++++++ apps/evm/README.md | 2 +- .../operation/environmental_information.ex | 10 +-- .../environmental_information_test.exs | 15 +++++ 4 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 apps/blockchain/scripts/evm_runner.ex diff --git a/apps/blockchain/scripts/evm_runner.ex b/apps/blockchain/scripts/evm_runner.ex new file mode 100644 index 000000000..e46f262d0 --- /dev/null +++ b/apps/blockchain/scripts/evm_runner.ex @@ -0,0 +1,65 @@ +defmodule EVMRunner do + @moduledoc """ + Allows you to run raw evm code. + + Eg. + + $ mix run apps/blockchain/scripts/evm_runner.ex --code 600360050160005260206000f3 --gas-limit 27 + + 10:11:11.929 [debug] Gas Remaining: 3 + + 10:11:11.936 [debug] Result: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8>> + + """ + require Logger + alias EVM.{VM, ExecEnv} + alias EVM.Interface.Mock.MockAccountInterface + alias EVM.Interface.Mock.MockBlockInterface + + def run() do + { + args, + _ + } = OptionParser.parse!(System.argv(), + switches: [ + code: :string, + address: :string, + originator: :string, + timestamp: :integer, + gas_limit: :integer, + ]) + account_interface = MockAccountInterface.new() + block_interface = MockBlockInterface.new(%{ + timestamp: Keyword.get(args, :timestamp, 0), + }) + + gas_limit = Keyword.get(args, :gas_limit, 2_000_000) + code_hex = Keyword.get(args, :code, "") + machine_code = Base.decode16!(code_hex, case: :mixed) + address = args + |> Keyword.get(:address, "") + |> Base.decode16, + originator = args + |> Keyword.get(:originator, "") + |> Base.decode16, + + exec_env = %ExecEnv{ + machine_code: machine_code, + address: Keyword.get(args, :address, "") |> Base.decode16, + originator: Keyword.get(args, :originator, "") |> Base.decode16, + account_interface: account_interface, + block_interface: block_interface, + } + + {gas_remaining, _sub_state, _exec_env, result} = VM.run(gas_limit, exec_env) + Logger.debug fn -> + "Gas Remaining: #{gas_remaining}" + end + + Logger.debug fn -> + "Result: #{inspect result}" + end + end +end + +EVMRunner.run() diff --git a/apps/evm/README.md b/apps/evm/README.md index 324caee03..e4ccdc1d5 100644 --- a/apps/evm/README.md +++ b/apps/evm/README.md @@ -16,7 +16,7 @@ As discussed in the paper, we define a few data structures. Here is an example of a simple program running on the VM: ```elixir -EVM.VM.run(%{}, 5, %EVM.ExecEnv{machine_code: EVM.MachineCode.compile([:push1, 3, :push1, 5, :add, :push1, 0x00, :mstore, :push1, 0, :push1, 32, :return])}) +EVM.VM.run(%{}, 5, %EVM.ExecEnv{machine_code: EVM.MachineCode.compile([:push1, 3, :push1, 5, :add, :push1, 0x00, :mstore, :push1, 32, :push1, 0, :return])}) {%{}, 5, [], [], 0, <<0x08::256>>} ``` diff --git a/apps/evm/lib/evm/operation/environmental_information.ex b/apps/evm/lib/evm/operation/environmental_information.ex index 864c8c271..d759d5413 100644 --- a/apps/evm/lib/evm/operation/environmental_information.ex +++ b/apps/evm/lib/evm/operation/environmental_information.ex @@ -205,14 +205,10 @@ defmodule EVM.Operation.EnvironmentalInformation do exec_env: exec_env, machine_state: machine_state }) do - if size == 0 do - 0 - else - data = Memory.read_zeroed_memory(exec_env.machine_code, code_offset, size) - machine_state = Memory.write(machine_state, mem_offset, data) + data = Memory.read_zeroed_memory(exec_env.machine_code, code_offset, size) + machine_state = Memory.write(machine_state, mem_offset, data) - %{machine_state: machine_state} - end + %{machine_state: machine_state} end @doc """ diff --git a/apps/evm/test/evm/operation/environmental_information_test.exs b/apps/evm/test/evm/operation/environmental_information_test.exs index 19c808f7d..e95a2926a 100644 --- a/apps/evm/test/evm/operation/environmental_information_test.exs +++ b/apps/evm/test/evm/operation/environmental_information_test.exs @@ -1,4 +1,19 @@ defmodule EVM.Operation.EnvironmentalInformationTest do use ExUnit.Case, async: true doctest EVM.Operation.EnvironmentalInformation + + test "returns the vm_map unchanged if `size` is equal to `0`" do + code = <<54>> + mem_offset = 0 + code_offset = 0 + size = 0 + exec_env = %EVM.ExecEnv{machine_code: code} + machine_state = %EVM.MachineState{} + vm_map = %{exec_env: exec_env, machine_state: machine_state} + + result = + EVM.Operation.EnvironmentalInformation.codecopy([mem_offset, code_offset, size], vm_map) + + assert result == %{machine_state: machine_state} + end end