Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor interpreter dispatch #65

Merged
merged 42 commits into from
Jul 6, 2018
Merged

Conversation

mratsim
Copy link
Contributor

@mratsim mratsim commented Jul 3, 2018

This completely removes the need of an OO approach in the EVM as in Py-EVM to use a more classical "switch" as in Geth, Parity and Cpp-Ethereum (and NimVM).

It is a follow-up to #49 and #52.

Summary:

  • 1300 lines added, 2200 deleted
  • 29 files deleted
  • Arguably much easier to maintain
  • Very probably much better performance
  • Needed step if we want a Nimbus EVM stand-alone (like EVMJIT, Hera)

Fixes:

Expected benefits:

Note on regressions

There are regressions on arith, boolean and mktx tests that test that the CALL opcode which is stubbed.

Similarly the expXY_success test passes even though it uses SLOAD. It passes because SLOAD is stubbed with a non-zero value.

Note on new jump tests passed specific jump tests

I'm not too sure why I pass new jump/control flow tests, the new tests were enabled by this commit https://github.com/status-im/nimbus/pull/65/commits/63b6fc251f77146b60b85bf032429cd5489f9e33, where I changed log proc back to templates as in master (I had some import/symbol visibility issue with templates during branch implementation).

The following tests are passing with -d:release and failing in debug mode:

  • BlockNumberDynamicJumpiOutsideBoundary
  • DynamicJumpiOutsideBoundary
  • jumpiOutsideBoundary
  • kv1

CI failure:

Pending - status-im/nim-rocksdb#3 Solved

@mratsim mratsim force-pushed the refactor-interpreter-dispatch branch from 81a9297 to 63b6fc2 Compare July 4, 2018 11:01
@mratsim mratsim requested review from coffeepots, yglukhov and zah July 4, 2018 11:22
@mratsim mratsim changed the title [WIP] Refactor interpreter dispatch Refactor interpreter dispatch Jul 4, 2018

# proc _executeFrontierTransaction*(vmState: FrontierVMState;
# transaction: FrontierTransaction): FrontierComputation =
# transaction.validate()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deleted code here seems quite complicated. Was it translated in any way in the new implementation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I feel that the code is much too complex and it has been refactored/deleted in Py-EVM since our port from January so it's better if we rewrite it from scratch.

Notice how there is no execute_frontier_transaction in the following screenshot and how all methods are about 10 lines long:

2018-07-05_11-47-27


op add, FkFrontier, inline = true, lhs, rhs:
## 0x01, Addition
push: lhs + rhs
Copy link
Contributor

@zah zah Jul 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The push construct seems a bit unnatural, because it suggests that an arbitrary block may be given, but is this actually true? You could have defined it as a simple dirty template accepting a single expression and making use of the computation variable.

No need for the whole replacePush business.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I did too much macros recently and completely forgot about dirty templates

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,759 @@
# Nimbus
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that all opcode implementations were moved will make merging this with the other state-ops branch very hard. Any suggestions about how should we solve this problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like most of the changes in https://github.com/status-im/nimbus/tree/state-ops-implementation are pretty self-contained.

State-ops can be merged first, and I rebase and port changes over.
I also have to carry over the changes from https://github.com/status-im/nimbus/tree/blocknumber-size-refactoring

let FrontierOpDispatch {.compileTime.}: array[Op, NimNode] = block:
fill_enum_table_holes(Op, newIdentNode"invalidInstruction"):
[
Stop: newIdentNode "toBeReplacedByBreak",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should try to avoid programming constructs that break grepping, "go to definition" and ntags. The previous opcode table was nicer in this regard, because it used the actual proc names and I believe we can keep this property.

Can you explain in more details how the different forks will feature different opcode implementations? My previous understanding was that this is a rare occurrence and we'll handle it by defining separate op code implementation only where differences exist (and these separate implementations will have different proc names). Any shared code may be defined as helper templates.

EDIT: I noticed how genCall is defined, but I still think the whole suffix-appending approach is probably unnecessary (the op macro doesn't need the ForkName parameter).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I will suffix the changed ops with their EIP (like Py-EVM) or fork (might be more dev friendly) but the default one will have an easy name.

I also agree with the grepping issue, I was often wondering where ceil32 was defined and it was defined by ceilXX(32)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proc selfdestructEIP150(computation) =
let beneficiary = stack.popAddress()
# TODO: with
# with computation.vm_state.state_db(read_only=True) as state_db:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find these TODO items and commented code quite useful. We should try to keep them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I find them useful as well, I try to keep them:

I preferred to delete it to reduce cognitive load given that:

  • the comment didn't match with the current Nim syntax,
  • the op is relatively straightforward,
  • I can always use git history to get it back.

and I intend to finish this as soon as this PR and state ops are merged.

mratsim referenced this pull request Jul 5, 2018
Merge note: currently cannot compile due to `quasiBoolean` (#63). This will be solved by https://github.com/status-im/nimbus/pull/65
----

* Implemented most of the stubbed out state handling instructions

The code compiles, but still fails at the moment due to incorrect

initialization of the VM. Don't merge yet. More commits will be
pushed in the coming days.

* Fixed crash

* trie put and del are void now

* getBlockTransactionData and getReceipts

* Working code for extcodesize0.json

* fix origin.json

* fix calldatasize1

* fix calldataloadSizeTooHighPartial

* fix calldataloadSizeTooHigh

* more efficient PushX implementation

* fix and, or, xor
@mratsim mratsim force-pushed the refactor-interpreter-dispatch branch from 00baaf3 to 6d53c46 Compare July 5, 2018 13:43
@mratsim
Copy link
Contributor Author

mratsim commented Jul 5, 2018

I've rebased on the new master. I still have to port to opcode_impl.nim some of the new opcode implementations that include the #59 additions.

@zah I'm getting Illegal storage accesses with the new hexaryTree:

[Suite] parse bytecode
  [OK] accepts bytes
  [OK] next returns the correct opcode
  [OK] peek returns next opcode without changing location
  [OK] stop opcode is returned when end reached
  [OK] [] returns opcode
  [OK] isValidOpcode invalidates after PUSHXX
  [OK] isValidOpcode 0
  [OK] isValidOpcode 1
  [OK] right number of bytes invalidates

[Suite] gasMeter
  [OK] consume spends
  [OK] consume errors
  [OK] return refund works correctly

[Suite] memory
  [OK] write
  [OK] write rejects valyes beyond memory size
  [OK] extends appropriately extends memory
  [OK] read returns correct bytes

[Suite] stack
    test_stack.nim(30, 16): Expect Failed, unexpected exception was thrown.
  [FAILED] push only valid
  [OK] push does not allow stack to exceed 1024
  [OK] dup does not allow stack to exceed 1024
  [OK] pop returns latest stack item
  [OK] swap correct
  [OK] dup correct
  [OK] pop raises InsufficientStack appropriately
  [OK] swap raises InsufficientStack appropriately
  [OK] dup raises InsufficientStack appropriately
  [OK] binary operations raises InsufficientStack appropriately

[Suite] opcodes
  [OK] add
Traceback (most recent call last)
test_opcode.nim(79)      test_opcode
test_opcode.nim(45)      testCode
interpreter_dispatch.nim(227) executeOpcodes
interpreter_dispatch.nim(194) frontierVM
opcodes_impl.nim(195)    balance
vm_state.nim(122)        readOnlyStateDB
db_chain.nim(231)        getStateDb
state_db.nim(26)         newAccountStateDB
hexary.nim(55)           initHexaryTrie
assign.nim(124)          genericAssign
assign.nim(100)          genericAssignAux
assign.nim(24)           genericAssignAux
assign.nim(21)           genericAssignAux
assign.nim(106)          genericAssignAux
SIGSEGV: Illegal storage access. (Attempt to read from nil?)

@zah
Copy link
Contributor

zah commented Jul 5, 2018

I'm a bit worried about the separation of VM/VmState that is no longer present, but we can always restore it in the future if necessary. I'm still looking forward to see how the fork-specific opcode implementations will work out (we've discussed on Slack that these may be mapped to an additional static parameter for the forked instructions).

All in all, let's merge this, so the rest of the team doesn't accumulate too many conflicts.

Copy link
Contributor

@coffeepots coffeepots left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Originally wasn't sure about combining all the ops into one module but after looking at it, I think it will be easier to work with.

@mratsim mratsim merged commit 4b5eada into master Jul 6, 2018
@zah zah deleted the refactor-interpreter-dispatch branch July 12, 2018 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants