-
Notifications
You must be signed in to change notification settings - Fork 129
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
Conversation
81a9297
to
63b6fc2
Compare
|
||
# proc _executeFrontierTransaction*(vmState: FrontierVMState; | ||
# transaction: FrontierTransaction): FrontierComputation = | ||
# transaction.validate() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
op add, FkFrontier, inline = true, lhs, rhs: | ||
## 0x01, Addition | ||
push: lhs + rhs |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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
…Copy and gas price opcode
…an macro is broken by latest devel
00baaf3
to
6d53c46
Compare
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?) |
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. |
There was a problem hiding this 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.
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:
Fixes:
Expected benefits:
Maintenance is significantly improved for complex opcodes:
CALL needed to be special-cased depending on forks https://github.com/status-im/nimbus/blob/6f28d1186675f648e0587f44e730b9dc23291bab/nimbus/vm/interpreter/opcodes_impl/call.nim#L19-L41
CREATE and SELFDESTRUCT and more generally all procs with a different logic depending on hardforks beyond "simple" gas changes introduced dispatch issues https://github.com/status-im/nimbus/blob/6f28d1186675f648e0587f44e730b9dc23291bab/nimbus/vm/interpreter/opcodes_impl/system_ops.nim#L19-L24
Previous dispatching logic used exceptions for control flow (EVM is using exceptions for non-exceptional situations #62) in Return, Revert and SelfDestruct: https://github.com/status-im/nimbus/blob/6f28d1186675f648e0587f44e730b9dc23291bab/nimbus/vm/computation.nim#L247-L291
Reimplementation of opcodes in single file of 750 lines instead of 18 files totaling 1220 lines
No inheritance instances to track.
Performance
Nimbus EVM as standalone
Note on regressions
There are regressions on
arith
,boolean
andmktx
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 passedspecific jump testsI'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:
CI failure:
Pending - status-im/nim-rocksdb#3Solved