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

Link to this type noop()
noop() :: :noop
Link to this type opcode()
opcode() :: byte
Link to this type operation()
operation() :: atom
Link to this type stack_args()
stack_args() :: [EVM.val]
Link to this type vm_map()
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

Link to this function decode(opcode)
decode(opcode) :: operation | nil

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
Link to this function encode(operation)
encode(operation) :: opcode | 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
Link to this function get_operation_at(machine_code, program_counter)

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
Link to this function inputs(operation, machine_state)
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]
Link to this function jump_operations()
Link to this function merge_state(updated_machine_state, operation, machine_state, sub_state, exec_env)

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{}}
Link to this function metadata(operation)

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

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
Link to this function normalize_op_result(op_result, updated_stack)
normalize_op_result(EVM.val | [EVM.val] | Operation.op_result, EVM.stack) :: Operation.op_result

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]}
Link to this function run_operation(operation, machine_state, sub_state, exec_env)

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{}}