diff --git a/.dialyzer.ignore-warnings b/.dialyzer.ignore-warnings index 2f8f5d0fd..3f347b468 100644 --- a/.dialyzer.ignore-warnings +++ b/.dialyzer.ignore-warnings @@ -39,21 +39,21 @@ # evm warnings -apps/evm/lib/evm/functions.ex:172: Function 'not_enough_gas?'/2 has no local return -apps/evm/lib/evm/functions.ex:184: Function 'is_invalid_jump_destination?'/3 will never be called -apps/evm/lib/evm/functions.ex:194: Function 'static_state_modification?'/2 will never be called -apps/evm/lib/evm/gas.ex:134: Function cost/2 has no local return -apps/evm/lib/evm/gas.ex:134: Function cost/3 has no local return -apps/evm/lib/evm/gas.ex:139: The call 'Elixir.EVM.Gas':operation_cost(atom(),_inputs@1::[integer()],_machine_state@1::#{'__struct__':='Elixir.EVM.MachineState', 'active_words':=integer(), 'gas':=integer(), 'last_return_data':=binary(), 'memory':=binary(), 'previously_active_words':=integer(), 'program_counter':=integer(), 'stack':=[integer()]},_exec_env@1::#{'__struct__':='Elixir.EVM.ExecEnv', 'account_interface':=atom(), 'address':=<<_:160>>, 'block_interface':=atom(), 'config':=atom(), 'data':=binary(), 'gas_price':=integer(), 'machine_code':=binary(), 'originator':=<<_:160>>, 'sender':=<<_:160>>, 'stack_depth':=integer(), 'static':=boolean(), 'value_in_wei':=integer()}) breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' -apps/evm/lib/evm/gas.ex:300: Function operation_cost/0 has no local return -apps/evm/lib/evm/gas.ex:300: The call 'Elixir.EVM.Gas':operation_cost('nil','nil','nil','nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' -apps/evm/lib/evm/gas.ex:300: Function operation_cost/1 has no local return -apps/evm/lib/evm/gas.ex:300: The call 'Elixir.EVM.Gas':operation_cost(__@1::any(),'nil','nil','nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' -apps/evm/lib/evm/gas.ex:300: Function operation_cost/2 has no local return -apps/evm/lib/evm/gas.ex:300: The call 'Elixir.EVM.Gas':operation_cost(__@1::any(),__@2::any(),'nil','nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' -apps/evm/lib/evm/gas.ex:300: Function operation_cost/3 has no local return -apps/evm/lib/evm/gas.ex:300: The call 'Elixir.EVM.Gas':operation_cost(__@1::any(),__@2::any(),__@3::any(),'nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' -apps/evm/lib/evm/gas.ex:559: Function gas_cost_for_nested_operation/2 will never be called +apps/evm/lib/evm/functions.ex:181: Function 'not_enough_gas?'/2 has no local return +apps/evm/lib/evm/functions.ex:193: Function 'is_invalid_jump_destination?'/3 will never be called +apps/evm/lib/evm/functions.ex:203: Function 'static_state_modification?'/2 will never be called +apps/evm/lib/evm/gas.ex:137: Function cost/2 has no local return +apps/evm/lib/evm/gas.ex:137: Function cost/3 has no local return +apps/evm/lib/evm/gas.ex:142: The call 'Elixir.EVM.Gas':operation_cost(atom(),_inputs@1::[integer()],_machine_state@1::#{'__struct__':='Elixir.EVM.MachineState', 'active_words':=integer(), 'gas':=integer(), 'last_return_data':=binary(), 'memory':=binary(), 'previously_active_words':=integer(), 'program_counter':=integer(), 'stack':=[integer()]},_exec_env@1::#{'__struct__':='Elixir.EVM.ExecEnv', 'account_interface':=atom(), 'address':=<<_:160>>, 'block_interface':=atom(), 'config':=atom(), 'data':=binary(), 'gas_price':=integer(), 'machine_code':=binary(), 'originator':=<<_:160>>, 'sender':=<<_:160>>, 'stack_depth':=integer(), 'static':=boolean(), 'value_in_wei':=integer()}) breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' +apps/evm/lib/evm/gas.ex:303: Function operation_cost/0 has no local return +apps/evm/lib/evm/gas.ex:303: The call 'Elixir.EVM.Gas':operation_cost('nil','nil','nil','nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' +apps/evm/lib/evm/gas.ex:303: Function operation_cost/1 has no local return +apps/evm/lib/evm/gas.ex:303: The call 'Elixir.EVM.Gas':operation_cost(__@1::any(),'nil','nil','nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' +apps/evm/lib/evm/gas.ex:303: Function operation_cost/2 has no local return +apps/evm/lib/evm/gas.ex:303: The call 'Elixir.EVM.Gas':operation_cost(__@1::any(),__@2::any(),'nil','nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' +apps/evm/lib/evm/gas.ex:303: Function operation_cost/3 has no local return +apps/evm/lib/evm/gas.ex:303: The call 'Elixir.EVM.Gas':operation_cost(__@1::any(),__@2::any(),__@3::any(),'nil') breaks the contract (atom(),['Elixir.EVM':val()],['Elixir.EVM':val()],'Elixir.EVM.MachineState':t()) -> t() | 'nil' +apps/evm/lib/evm/gas.ex:562: Function gas_cost_for_nested_operation/2 will never be called apps/evm/lib/evm/machine_state.ex:53: Function subtract_gas/2 has no local return apps/evm/lib/evm/operation/environmental_information.ex:114: Function calldataload/2 has no local return apps/evm/lib/evm/operation/environmental_information.ex:117: The call 'Elixir.EVM.Helpers':decode_signed(binary()) breaks the contract (integer() | 'nil') -> 'Elixir.EVM':val() | 'nil' diff --git a/apps/blockchain/config/test.exs b/apps/blockchain/config/test.exs index d2d855e6d..d91d771b9 100644 --- a/apps/blockchain/config/test.exs +++ b/apps/blockchain/config/test.exs @@ -1 +1,3 @@ use Mix.Config + +config :ex_unit, case_load_timeout: 120_000 diff --git a/apps/blockchain/scripts/generate_blockchain_tests.ex b/apps/blockchain/scripts/generate_blockchain_tests.ex index 21448a8ea..6c89e5c17 100644 --- a/apps/blockchain/scripts/generate_blockchain_tests.ex +++ b/apps/blockchain/scripts/generate_blockchain_tests.ex @@ -7,7 +7,7 @@ defmodule GenerateBlockchainTests do alias Block.Header @base_path System.cwd() <> "/../../ethereum_common_tests/BlockchainTests/" - @allowed_forks ["Byzantium", "Frontier", "Homestead", "EIP150", "EIP158"] + @allowed_forks ["Constantinople", "Byzantium", "Frontier", "Homestead", "EIP150", "EIP158"] @byzantium_failing_tests_path System.cwd() <> "/test/support/byzantium_failing_tests.txt" @initial_pass_fail {[], []} @number_of_test_groups 20 @@ -164,6 +164,9 @@ defmodule GenerateBlockchainTests do "Byzantium" -> Chain.load_chain(:byzantium_test, config) + "Constantinople" -> + Chain.load_chain(:constantinople_test, config) + _ -> nil end @@ -186,6 +189,9 @@ defmodule GenerateBlockchainTests do "Byzantium" -> EVM.Configuration.Byzantium.new() + "Constantinople" -> + EVM.Configuration.Constantinople.new() + _ -> nil end diff --git a/apps/blockchain/scripts/generate_state_tests.ex b/apps/blockchain/scripts/generate_state_tests.ex index e1bd5bde3..8bf3aa635 100644 --- a/apps/blockchain/scripts/generate_state_tests.ex +++ b/apps/blockchain/scripts/generate_state_tests.ex @@ -7,7 +7,7 @@ defmodule GenerateStateTests do use EthCommonTest.Harness - @hardforks ["EIP158", "EIP150", "Homestead", "Frontier", "Byzantium"] + @hardforks ["EIP158", "EIP150", "Homestead", "Frontier", "Byzantium", "Constantinople"] @twenty_minutes 60 * 20 * 1000 @initial_state %{ passing: %{ @@ -15,14 +15,16 @@ defmodule GenerateStateTests do "Frontier" => [], "EIP150" => [], "EIP158" => [], - "Byzantium" => [] + "Byzantium" => [], + "Constantinople" => [] }, failing: %{ "Homestead" => [], "Frontier" => [], "EIP150" => [], "EIP158" => [], - "Byzantium" => [] + "Byzantium" => [], + "Constantinople" => [] } } @@ -204,6 +206,9 @@ defmodule GenerateStateTests do "Byzantium" -> EVM.Configuration.Byzantium.new() + "Constantinople" -> + EVM.Configuration.Constantinople.new() + _ -> nil end diff --git a/apps/blockchain/test/blockchain/state_test.exs b/apps/blockchain/test/blockchain/state_test.exs index bad106170..51247837c 100644 --- a/apps/blockchain/test/blockchain/state_test.exs +++ b/apps/blockchain/test/blockchain/state_test.exs @@ -46,6 +46,13 @@ defmodule Blockchain.StateTest do "stTransactionTest/EmptyTransaction2", "stZeroKnowledge2/ecmul_0-3_5616_28000_96" ], + "Constantinople" => [ + "stCreateTest/CreateOOGafterInitCodeReturndata", + "stReturnDataTest/returndatacopy_following_failing_call", + "stReturnDataTest/returndatacopy_initial_256", + "stRevertTest/RevertOpcodeMultipleSubCalls", + "stZeroKnowledge2/ecmul_0-3_5616_28000_96" + ], "EIP150" => [ "stInitCodeTest/NotEnoughCashContractCreation", "stInitCodeTest/OutOfGasContractCreation", @@ -359,6 +366,9 @@ defmodule Blockchain.StateTest do "Byzantium" -> EVM.Configuration.Byzantium.new() + "Constantinople" -> + EVM.Configuration.Constantinople.new() + _ -> nil end diff --git a/apps/blockchain/test/blockchain_test.exs b/apps/blockchain/test/blockchain_test.exs index 09641a051..6002c71bf 100644 --- a/apps/blockchain/test/blockchain_test.exs +++ b/apps/blockchain/test/blockchain_test.exs @@ -20,8 +20,14 @@ defmodule BlockchainTest do "GeneralStateTests/stSpecialTest/failed_tx_xcf416c53_d0g0v0.json" ], "Byzantium" => String.split(@failing_byzantium_tests, "\n"), + "Constantinople" => [ + "GeneralStateTests/stCreateTest/CreateOOGafterInitCodeReturndata_d0g1v0.json", + "GeneralStateTests/stReturnDataTest/modexp_modsize0_returndatasize_d0g1v0.json", + "GeneralStateTests/stReturnDataTest/modexp_modsize0_returndatasize_d0g2v0.json", + "GeneralStateTests/stReturnDataTest/modexp_modsize0_returndatasize_d0g3v0.json", + "bcStateTests/blockhashNonConstArg.json" + ], # the rest are not implemented yet - "Constantinople" => [], "EIP158ToByzantiumAt5" => [], "FrontierToHomesteadAt5" => [], "HomesteadToDaoAt5" => [], @@ -158,6 +164,9 @@ defmodule BlockchainTest do "Byzantium" -> Chain.load_chain(:byzantium_test, config) + "Constantinople" -> + Chain.load_chain(:constantinople_test, config) + _ -> nil end @@ -180,6 +189,9 @@ defmodule BlockchainTest do "Byzantium" -> EVM.Configuration.Byzantium.new() + "Constantinople" -> + EVM.Configuration.Constantinople.new() + _ -> nil end diff --git a/apps/evm/lib/evm/configuration.ex b/apps/evm/lib/evm/configuration.ex index d4bafe429..7369aab89 100644 --- a/apps/evm/lib/evm/configuration.ex +++ b/apps/evm/lib/evm/configuration.ex @@ -96,4 +96,8 @@ defprotocol EVM.Configuration do # EIP198 @spec has_mod_exp_builtin?(t) :: boolean() def has_mod_exp_builtin?(t) + + # EIP145 + @spec has_shift_operations?(t) :: boolean() + def has_shift_operations?(t) end diff --git a/apps/evm/lib/evm/configuration/byzantium.ex b/apps/evm/lib/evm/configuration/byzantium.ex index c637fa4a4..17871168b 100644 --- a/apps/evm/lib/evm/configuration/byzantium.ex +++ b/apps/evm/lib/evm/configuration/byzantium.ex @@ -93,4 +93,8 @@ defimpl EVM.Configuration, for: EVM.Configuration.Byzantium do @spec has_ec_pairing_builtin?(Configuration.t()) :: boolean() def has_ec_pairing_builtin?(config), do: config.has_ec_pairing_builtin + + @spec has_shift_operations?(Configuration.t()) :: boolean() + def has_shift_operations?(config), + do: Configuration.has_shift_operations?(config.fallback_config) end diff --git a/apps/evm/lib/evm/configuration/constantinople.ex b/apps/evm/lib/evm/configuration/constantinople.ex new file mode 100644 index 000000000..c4410a142 --- /dev/null +++ b/apps/evm/lib/evm/configuration/constantinople.ex @@ -0,0 +1,95 @@ +defmodule EVM.Configuration.Constantinople do + defstruct fallback_config: EVM.Configuration.Byzantium.new(), + has_shift_operations: true + + def new do + %__MODULE__{} + end +end + +defimpl EVM.Configuration, for: EVM.Configuration.Constantinople do + alias EVM.Configuration + + @spec contract_creation_cost(Configuration.t()) :: integer() + def contract_creation_cost(config), + do: Configuration.contract_creation_cost(config.fallback_config) + + @spec has_delegate_call?(Configuration.t()) :: boolean() + def has_delegate_call?(config), do: Configuration.has_delegate_call?(config.fallback_config) + + @spec max_signature_s(Configuration.t()) :: atom() + def max_signature_s(config), do: Configuration.max_signature_s(config.fallback_config) + + @spec fail_contract_creation_lack_of_gas?(Configuration.t()) :: boolean() + def fail_contract_creation_lack_of_gas?(config), + do: Configuration.fail_contract_creation_lack_of_gas?(config.fallback_config) + + @spec extcodesize_cost(Configuration.t()) :: integer() + def extcodesize_cost(config), do: Configuration.extcodesize_cost(config.fallback_config) + + @spec extcodecopy_cost(Configuration.t()) :: integer() + def extcodecopy_cost(config), do: Configuration.extcodecopy_cost(config.fallback_config) + + @spec balance_cost(Configuration.t()) :: integer() + def balance_cost(config), do: Configuration.balance_cost(config.fallback_config) + + @spec sload_cost(Configuration.t()) :: integer() + def sload_cost(config), do: Configuration.sload_cost(config.fallback_config) + + @spec call_cost(Configuration.t()) :: integer() + def call_cost(config), do: Configuration.call_cost(config.fallback_config) + + @spec selfdestruct_cost(Configuration.t(), keyword()) :: integer() + def selfdestruct_cost(config, params), + do: Configuration.selfdestruct_cost(config.fallback_config, params) + + @spec fail_nested_operation_lack_of_gas?(Configuration.t()) :: boolean() + def fail_nested_operation_lack_of_gas?(config), + do: Configuration.fail_nested_operation_lack_of_gas?(config.fallback_config) + + @spec exp_byte_cost(Configuration.t()) :: integer() + def exp_byte_cost(config), do: Configuration.exp_byte_cost(config.fallback_config) + + @spec limit_contract_code_size?(Configuration.t(), integer()) :: boolean() + def limit_contract_code_size?(config, size), + do: Configuration.limit_contract_code_size?(config.fallback_config, size) + + @spec increment_nonce_on_create?(Configuration.t()) :: boolean() + def increment_nonce_on_create?(config), + do: Configuration.increment_nonce_on_create?(config.fallback_config) + + @spec empty_account_value_transfer?(Configuration.t()) :: boolean() + def empty_account_value_transfer?(config), + do: Configuration.empty_account_value_transfer?(config.fallback_config) + + @spec clean_touched_accounts?(Configuration.t()) :: boolean() + def clean_touched_accounts?(config), + do: Configuration.clean_touched_accounts?(config.fallback_config) + + @spec has_revert?(Configuration.t()) :: boolean() + def has_revert?(config), do: Configuration.has_revert?(config.fallback_config) + + @spec has_static_call?(Configuration.t()) :: boolean() + def has_static_call?(config), do: Configuration.has_static_call?(config.fallback_config) + + @spec support_variable_length_return_value?(Configuration.t()) :: boolean() + def support_variable_length_return_value?(config), + do: Configuration.support_variable_length_return_value?(config.fallback_config) + + @spec has_mod_exp_builtin?(Configuration.t()) :: boolean() + def has_mod_exp_builtin?(config), do: Configuration.has_mod_exp_builtin?(config.fallback_config) + + @spec has_ec_add_builtin?(Configuration.t()) :: boolean() + def has_ec_add_builtin?(config), do: Configuration.has_ec_add_builtin?(config.fallback_config) + + @spec has_ec_mult_builtin?(Configuration.t()) :: boolean() + def has_ec_mult_builtin?(config), do: Configuration.has_ec_mult_builtin?(config.fallback_config) + + @spec has_ec_pairing_builtin?(Configuration.t()) :: boolean() + def has_ec_pairing_builtin?(config), + do: Configuration.has_ec_pairing_builtin?(config.fallback_config) + + @spec has_shift_operations?(Configuration.t()) :: boolean() + def has_shift_operations?(config), + do: config.has_shift_operations +end diff --git a/apps/evm/lib/evm/configuration/eip150.ex b/apps/evm/lib/evm/configuration/eip150.ex index f241d8b3a..352daf534 100644 --- a/apps/evm/lib/evm/configuration/eip150.ex +++ b/apps/evm/lib/evm/configuration/eip150.ex @@ -96,4 +96,8 @@ defimpl EVM.Configuration, for: EVM.Configuration.EIP150 do @spec has_ec_pairing_builtin?(Configuration.t()) :: boolean() def has_ec_pairing_builtin?(config), do: Configuration.has_ec_pairing_builtin?(config.fallback_config) + + @spec has_shift_operations?(Configuration.t()) :: boolean() + def has_shift_operations?(config), + do: Configuration.has_shift_operations?(config.fallback_config) end diff --git a/apps/evm/lib/evm/configuration/eip158.ex b/apps/evm/lib/evm/configuration/eip158.ex index 621858960..d312cc345 100644 --- a/apps/evm/lib/evm/configuration/eip158.ex +++ b/apps/evm/lib/evm/configuration/eip158.ex @@ -88,4 +88,8 @@ defimpl EVM.Configuration, for: EVM.Configuration.EIP158 do @spec has_ec_pairing_builtin?(Configuration.t()) :: boolean() def has_ec_pairing_builtin?(config), do: Configuration.has_ec_pairing_builtin?(config.fallback_config) + + @spec has_shift_operations?(Configuration.t()) :: boolean() + def has_shift_operations?(config), + do: Configuration.has_shift_operations?(config.fallback_config) end diff --git a/apps/evm/lib/evm/configuration/frontier.ex b/apps/evm/lib/evm/configuration/frontier.ex index 9f4f8521d..008bd940d 100644 --- a/apps/evm/lib/evm/configuration/frontier.ex +++ b/apps/evm/lib/evm/configuration/frontier.ex @@ -21,7 +21,8 @@ defmodule EVM.Configuration.Frontier do has_mod_exp_builtin: false, has_ec_add_builtin: false, has_ec_mult_builtin: false, - has_ec_pairing_builtin: false + has_ec_pairing_builtin: false, + has_shift_operations: false def new do %__MODULE__{} @@ -100,4 +101,7 @@ defimpl EVM.Configuration, for: EVM.Configuration.Frontier do @spec has_ec_pairing_builtin?(Configuration.t()) :: boolean() def has_ec_pairing_builtin?(config), do: config.has_ec_pairing_builtin + + @spec has_shift_operations?(Configuration.t()) :: boolean() + def has_shift_operations?(config), do: config.has_shift_operations end diff --git a/apps/evm/lib/evm/configuration/homestead.ex b/apps/evm/lib/evm/configuration/homestead.ex index f7963312a..763330cc9 100644 --- a/apps/evm/lib/evm/configuration/homestead.ex +++ b/apps/evm/lib/evm/configuration/homestead.ex @@ -89,4 +89,8 @@ defimpl EVM.Configuration, for: EVM.Configuration.Homestead do @spec has_ec_pairing_builtin?(Configuration.t()) :: boolean() def has_ec_pairing_builtin?(config), do: Configuration.has_ec_pairing_builtin?(config.fallback_config) + + @spec has_shift_operations?(Configuration.t()) :: boolean() + def has_shift_operations?(config), + do: Configuration.has_shift_operations?(config.fallback_config) end diff --git a/apps/evm/lib/evm/functions.ex b/apps/evm/lib/evm/functions.ex index fa5e43db4..04d9c2968 100644 --- a/apps/evm/lib/evm/functions.ex +++ b/apps/evm/lib/evm/functions.ex @@ -4,7 +4,7 @@ defmodule EVM.Functions do fit in other modules. """ - alias EVM.{ExecEnv, MachineCode, MachineState, Operation, Stack, Gas} + alias EVM.{ExecEnv, MachineCode, MachineState, Operation, Stack, Gas, Configuration} alias EVM.Operation.Metadata @max_stack 1024 @@ -146,22 +146,31 @@ defmodule EVM.Functions do case operation_metadata.sym do :delegatecall -> - if EVM.Configuration.has_delegate_call?(config), do: operation_metadata + if Configuration.has_delegate_call?(config), do: operation_metadata :revert -> - if EVM.Configuration.has_revert?(config), do: operation_metadata + if Configuration.has_revert?(config), do: operation_metadata :staticcall -> - if EVM.Configuration.has_static_call?(config), do: operation_metadata + if Configuration.has_static_call?(config), do: operation_metadata :returndatasize -> - if EVM.Configuration.support_variable_length_return_value?(config), + if Configuration.support_variable_length_return_value?(config), do: operation_metadata :returndatacopy -> - if EVM.Configuration.support_variable_length_return_value?(config), + if Configuration.support_variable_length_return_value?(config), do: operation_metadata + :shl -> + if Configuration.has_shift_operations?(config), do: operation_metadata + + :shr -> + if Configuration.has_shift_operations?(config), do: operation_metadata + + :sar -> + if Configuration.has_shift_operations?(config), do: operation_metadata + _ -> operation_metadata end diff --git a/apps/evm/lib/evm/gas.ex b/apps/evm/lib/evm/gas.ex index 3479e910f..208d7f9a2 100644 --- a/apps/evm/lib/evm/gas.ex +++ b/apps/evm/lib/evm/gas.ex @@ -113,7 +113,10 @@ defmodule EVM.Gas do :calldataload, :mload, :mstore, - :mstore8 + :mstore8, + :shl, + :shr, + :sar ] ++ @push_instrs ++ @dup_instrs ++ @swap_instrs @w_low_instr [:mul, :div, :sdiv, :mod, :smod, :signextend] @w_mid_instr [:addmod, :mulmod, :jump] diff --git a/apps/evm/lib/evm/operation/metadata/stop_and_arithmetic.ex b/apps/evm/lib/evm/operation/metadata/stop_and_arithmetic.ex index 13bf9ea3d..19f1889ca 100644 --- a/apps/evm/lib/evm/operation/metadata/stop_and_arithmetic.ex +++ b/apps/evm/lib/evm/operation/metadata/stop_and_arithmetic.ex @@ -95,6 +95,30 @@ defmodule EVM.Operation.Metadata.StopAndArithmetic do group: :stop_and_arithmetic, input_count: 2, output_count: 1 + }, + %{ + id: 0x1B, + description: "Logical shift left", + sym: :shl, + group: :stop_and_arithmetic, + input_count: 2, + output_count: 1 + }, + %{ + id: 0x1C, + description: "Logical shift right", + sym: :shr, + group: :stop_and_arithmetic, + input_count: 2, + output_count: 1 + }, + %{ + id: 0x1D, + description: "Arithmetic shift right", + sym: :sar, + group: :stop_and_arithmetic, + input_count: 2, + output_count: 1 } ], do: struct(EVM.Operation.Metadata, operation) diff --git a/apps/evm/lib/evm/operation/stop_and_arithmetic.ex b/apps/evm/lib/evm/operation/stop_and_arithmetic.ex index bd9f2d8be..29ccb95d7 100644 --- a/apps/evm/lib/evm/operation/stop_and_arithmetic.ex +++ b/apps/evm/lib/evm/operation/stop_and_arithmetic.ex @@ -164,4 +164,34 @@ defmodule EVM.Operation.StopAndArithmetic do band(s1, (1 <<< Helpers.bit_position(s0)) - 1) end end + + @doc """ + Logical shift left. + """ + @spec shl(Operation.stack_args(), Operation.vm_map()) :: EVM.val() + def shl([s0, _s1], _) when s0 >= 256, do: 0 + + def shl([s0, s1], _), do: s1 <<< s0 + + @doc """ + Logical shift right. + """ + @spec shr(Operation.stack_args(), Operation.vm_map()) :: EVM.val() + def shr([s0, _s1], _) when s0 >= 256, do: 0 + + def shr([s0, s1], _), do: s1 >>> s0 + + @doc """ + Arithmetic shift right. + """ + @spec sar(Operation.stack_args(), Operation.vm_map()) :: EVM.val() + def sar([s0, s1], _) do + s1_signed = Helpers.decode_signed(s1) + + if s0 >= 256 do + if s1_signed >= 0, do: 0, else: -1 + else + s1_signed >>> s0 + end + end end diff --git a/apps/evm/mix.exs b/apps/evm/mix.exs index cfd299b2e..13198beba 100644 --- a/apps/evm/mix.exs +++ b/apps/evm/mix.exs @@ -51,7 +51,7 @@ defmodule EVM.Mixfile do {:merkle_patricia_tree, in_umbrella: true}, {:exth_crypto, in_umbrella: true}, {:ex_rlp, "~> 0.3.0"}, - {:bn, "~> 0.2.0"} + {:bn, "~> 0.2.1"} ] end diff --git a/mix.lock b/mix.lock index 2f9eae751..a0833a367 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "binary": {:hex, :binary, "0.0.5", "20d816f7274ea34f1b673b4cff2fdb9ebec9391a7a68c349070d515c66b1b2cf", [:mix], [], "hexpm"}, - "bn": {:hex, :bn, "0.2.0", "d9125710fb3cfe7e37c0f5ef0ee35d92575f1e28680ce0fc575f12dc04f79b2f", [:mix], [], "hexpm"}, + "bn": {:hex, :bn, "0.2.1", "06bf8731b05fc96031de7ad5cba40cebf910e97a338798ce67bef8307c45cff3", [:mix], [], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, "certifi": {:hex, :certifi, "2.3.1", "d0f424232390bf47d82da8478022301c561cf6445b5b5fb6a84d49a9e76d2639", [:rebar3], [{:parse_trans, "3.2.0", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, "credo": {:hex, :credo, "0.10.0", "66234a95effaf9067edb19fc5d0cd5c6b461ad841baac42467afed96c78e5e9e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},