evm v0.1.14 EVM.Operation
Code to handle encoding and decoding operations from opcodes.
Link to this section Summary
Functions
Returns the given operation for a given opcode
Returns the given opcode for an operation
Returns the current operation at a given program counter address
Returns an operation’s inputs
Merges the state from an opcode with the current environment
Returns metadata about a given operation or opcode, or nil
Returns the next operation position given a current position and the type of operation. This is to bypass push operands
Normalizes op_results. If the result is an integer it encodes it and pushes it onto the stack. If it’s a list pushes each element onto the stack. Otherwise it returns what’s given to it
Executes a single operation. This simply does the effects of the operation itself, ignoring the rest of the actions of an operation cycle. This will effect, for instance, the stack, but will not effect the gas, etc
Link to this section Types
vm_map() :: %{optional(:state) => EVM.world_state, optional(:stack) => EVM.Stack.t, optional(:machine_state) => EVM.MachineState.t, optional(:sub_state) => EVM.SubState.t, optional(:exec_env) => EVM.ExecEnv.t, optional(:block_interface) => EVM.BlockInterface.t, optional(:contract_interface) => EVM.ContractInterface.t, optional(:account_interface) => EVM.AccountInterface.t}
Link to this section Functions
Returns the given operation for a given opcode.
Examples
iex> EVM.Operation.decode(0x00)
:stop
iex> EVM.Operation.decode(0x01)
:add
iex> EVM.Operation.decode(0x02)
:mul
iex> EVM.Operation.decode(0xffff)
nil
Returns the given opcode for an operation.
Examples
iex> EVM.Operation.encode(:stop)
0x00
iex> EVM.Operation.encode(:add)
0x01
iex> EVM.Operation.encode(:mul)
0x02
iex> EVM.Operation.encode(:salmon)
nil
get_operation_at(EVM.MachineCode.t, EVM.MachineState.program_counter) :: opcode
Returns the current operation at a given program counter address.
Examples
iex> EVM.Operation.get_operation_at(<<0x11, 0x01, 0x02>>, 0)
17
iex> EVM.Operation.get_operation_at(<<0x11, 0x01, 0x02>>, 1)
1
iex> EVM.Operation.get_operation_at(<<0x11, 0x01, 0x02>>, 2)
2
iex> EVM.Operation.get_operation_at(<<0x11, 0x01, 0x02>>, 3)
0
inputs(EVM.Stack.t, Operation.t) :: [EVM.val]
Returns an operation’s inputs
Examples
#
iex> EVM.Operation.inputs(EVM.Operation.metadata(:add), %{stack: [1, 2, 3]})
[1, 2]
merge_state(EVM.Operation.Impl.op_result, operation, EVM.MachineState.t, EVM.SubState.t, EVM.ExecEnv.t) :: {EVM.world_state, EVM.MachineState.t, EVM.SubState.t, EVM.ExecEnv.t}
Merges the state from an opcode with the current environment
Examples
iex> EVM.Operation.merge_state(:noop, EVM.Operation.metadata(:add), %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{}}
iex> EVM.Operation.merge_state(:unimplemented, EVM.Operation.metadata(:blarg), %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{}}
iex> EVM.Operation.merge_state(%{stack: [1, 2, 3]}, :add, %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{stack: [1, 2, 3]}, %EVM.SubState{}, %EVM.ExecEnv{}}
iex> EVM.Operation.merge_state(%{machine_state: %EVM.MachineState{stack: [1, 2, 3]}}, :add, %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{stack: [1, 2, 3]}, %EVM.SubState{}, %EVM.ExecEnv{}}
iex> EVM.Operation.merge_state(%{machine_state: %EVM.MachineState{}, sub_state: %EVM.SubState{refund: 5}}, :add, %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{}, %EVM.SubState{refund: 5}, %EVM.ExecEnv{}}
iex> EVM.Operation.merge_state(%{exec_env: %EVM.ExecEnv{stack_depth: 1}}, :add, %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{stack_depth: 1}}
iex> EVM.Operation.merge_state(%{stack: [1, 2, 3], machine_state: %EVM.MachineState{program_counter: 5, stack: [4, 5]}}, :add, %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{program_counter: 5, stack: [1, 2, 3]}, %EVM.SubState{}, %EVM.ExecEnv{}}
iex> EVM.Operation.merge_state(%EVM.MachineState{program_counter: 5, stack: [4, 5]}, :add, %EVM.MachineState{}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{program_counter: 5, stack: [4, 5]}, %EVM.SubState{}, %EVM.ExecEnv{}}
metadata(operation | opcode) :: EVM.Operation.Metadata.t | nil
Returns metadata about a given operation or opcode, or nil.
Examples
iex> EVM.Operation.metadata(:stop)
%EVM.Operation.Metadata{id: 0x00, sym: :stop, input_count: 0, output_count: 0, description: "Halts execution", group: :stop_and_arithmetic}
iex> EVM.Operation.metadata(0x00)
%EVM.Operation.Metadata{id: 0x00, sym: :stop, input_count: 0, output_count: 0, description: "Halts execution", group: :stop_and_arithmetic}
iex> EVM.Operation.metadata(:add)
%EVM.Operation.Metadata{id: 0x01, sym: :add, input_count: 2, output_count: 1, description: "Addition operation", group: :stop_and_arithmetic}
iex> EVM.Operation.metadata(:push1)
%EVM.Operation.Metadata{id: 0x60, sym: :push1, fun: :push_n, args: [1], input_count: 0, output_count: 1, description: "Place 1-byte item on stack", group: :push, machine_code_offset: 1}
iex> EVM.Operation.metadata(0xfe)
nil
iex> EVM.Operation.metadata(nil)
nil
next_instr_pos(EVM.MachineState.program_counter, operation) :: EVM.MachineState.program_counter
Returns the next operation position given a current position and the type of operation. This is to bypass push operands.
Examples
iex> EVM.Operation.next_instr_pos(10, :add)
11
iex> EVM.Operation.next_instr_pos(20, :mul)
21
iex> EVM.Operation.next_instr_pos(10, :push1)
12
iex> EVM.Operation.next_instr_pos(10, :push32)
43
Normalizes op_results. If the result is an integer it encodes it and pushes it onto the stack. If it’s a list pushes each element onto the stack. Otherwise it returns what’s given to it.
Examples
#
iex> EVM.Operation.normalize_op_result(1, [])
%{stack: [1]}
iex> EVM.Operation.normalize_op_result([1,2], [])
%{stack: [1, 2]}
run_operation(EVM.Operation.Metadata.t, EVM.MachineState.t, EVM.SubState.t, EVM.ExecEnv.t) :: {EVM.MachineState.t, EVM.SubState.t, EVM.ExecEnv.t}
Executes a single operation. This simply does the effects of the operation itself, ignoring the rest of the actions of an operation cycle. This will effect, for instance, the stack, but will not effect the gas, etc.
Examples
# TODO: How to handle trie state in tests?
# Add
iex> EVM.Operation.run_operation(EVM.Operation.metadata(:add), %EVM.MachineState{stack: [1, 2]}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{stack: [3]}, %EVM.SubState{}, %EVM.ExecEnv{}}
# Push
iex> EVM.Operation.run_operation(EVM.Operation.metadata(:push1), %EVM.MachineState{stack: [1, 2]}, %EVM.SubState{}, %EVM.ExecEnv{machine_code: <<00, 01>>})
{%EVM.MachineState{stack: [1, 1, 2]}, %EVM.SubState{}, %EVM.ExecEnv{machine_code: <<0, 1>>}}
# nil
iex> EVM.Operation.run_operation(EVM.Operation.metadata(:stop), %EVM.MachineState{stack: [1, 2]}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{stack: [1, 2]}, %EVM.SubState{}, %EVM.ExecEnv{}}
# Unimplemented
iex> EVM.Operation.run_operation(EVM.Operation.metadata(:log0), %EVM.MachineState{stack: [1, 2]}, %EVM.SubState{}, %EVM.ExecEnv{})
{%EVM.MachineState{stack: []}, %EVM.SubState{}, %EVM.ExecEnv{}}