4

It seems when a single function is called, the Ethereum Virtual Machine:

  1. Does 'pre-call checks' for exceptions/errors.

  2. Computes the amount of gas needed, checks that the accounts involved exist.

  3. Gets the function parameters.

  4. Creates a calldata buffer & returndata buffer in memory, as subtly depicted in py-evm code:

source https://github.com/ethereum/py-evm/blob/0ff7bc0f820250d7f15e804bc09870776ea34eb5/eth/vm/logic/call.py

  1. Consumes Gas and performs the compute via stack operations & opcodes. (SideQuestion: Are both stack operations & opcodes stored in the Virtual ROM EVM code?)

  2. It seems the evm does a '1st pass' where the values & pointers are added to the stack [and to the stack's memory scratchpad as needed]. (Uncertain as to this ^ process?)

  3. Then once the function completes/returns, the EVM discards memory (including the the 2 buffers) & stack frame data.

Main source: Ethereum's py-evm i.e. https://github.com/ethereum/py-evm/blob/0ff7bc0f820250d7f15e804bc09870776ea34eb5/eth/vm/logic/call.py

What am I missing here?

When I step through on Remix to view the stack, it seems the EVM does a '1st pass' where it loads what the function needs into the function's stack + memory, is this correct? Is this referred to as the function's call stack/call frame?

It seems many devs including myself are unclear on this 'under the hood' sequence. Thank you.

  • You can refer to source code under this folder /go-ethereum/core/vm – Ha ĐANG Sep 8 '18 at 2:25
2
+50

There will be differences between EVM implementations as to how they get the job done, but they all should produce the same results if they conform to the yellowpaper. I took a look at the VM code for EthereumJS - runCode.js which I have outlined below. You can also use these resources to understand how a VM might be implemented:

Here is my understanding of the core VM code flow of EthereumJS which is one way you could code an Ethereum VM. The python code you are looking at may have similarities.

  1. Execute the VM given the following input:

    • the code to be run
    • the address of the account that is executing this code
    • the address where the call originated from
    • the address that ran this code
    • the account that the executing code belongs to
    • the block that the transaction is part of
    • the gasLimit
    • the input data
    • the value - the amount the being transfered
  2. For current run state, preprocess valid jumps and put them in an array (this may be an optimization).

  3. Iterate through the op codes until we encounter an error or we hit STOP.

  4. For each iteration maintain state of:

    • programCounter,
    • gas remaining,
    • current opcode,
    • stack,
    • depth,
    • address,
    • account,
    • stateManager.cache,
    • memory
  5. Lookup current opcode and get opcode info:

    • name
    • opcode
    • fee (gas cost)
    • in
    • out
    • dynamic
    • async
  6. Validate the following and return if invalid:

    • is valid opcode
    • in and out cause stack overflow or underflow
    • enough gas left for the opcode fee
  7. Advance programCounter

  8. Push run state on stack

  9. Run the opcode function (found in opFns.js )

  10. If no error: save results to stack

  11. If error or revert: consume gas

  12. Parse the VM results and return:

    • logs (if no error)
    • gas left and gas refund
    • any error
    • any return values

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.