smlxl / evm.codes

Source for evm.codes an Ethereum Virtual Machine Opcodes Interactive Reference
https://evm.codes/
MIT License
718 stars 132 forks source link

feat: adding EOFv1 opcodes #339

Closed hangleang closed 2 weeks ago

hangleang commented 1 month ago

reference to issue https://github.com/smlxl/evm.codes/issues/329#issuecomment-2258003723

vercel[bot] commented 1 month ago

@hangleang is attempting to deploy a commit to the smlXL Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot] commented 1 month ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
evm-codes ✅ Ready (Inspect) Visit Preview Aug 30, 2024 1:00pm
hangleang commented 1 month ago

once merge, I would like to close #329 as well

charisra commented 1 month ago

@hangleang please add a PR description mentioning what this PR addresses and how we can test the functionality added. Also remove the WIP from the title, if you've finished the work.

thevaizman commented 1 month ago

@hangleang - hey there! few things i noticed when looking at the preview build:

hangleang commented 1 month ago

@thevaizman sorry, the preview is not up to date with the commits, which is correct what you mentioned above

hangleang commented 1 month ago

@hangleang - few general things that i see missing:

  • the sizes of inputs/outputs of opcodes.

could you give me an example please?

  • examples are all marked as TBD - is there going to be a follow up PR that adds them?

yeah, these are not in the scope of this PR. if adding those example, would require changing the playground, and I'm not sure how big is it to support EOF opcodes

2xic commented 1 month ago

@hangleang - few general things that i see missing:

  • the sizes of inputs/outputs of opcodes.

could you give me an example please?

I think you have already fixed this with your latest fix, but cc @thevaizman in case there was something specific he had in mind.

  • examples are all marked as TBD - is there going to be a follow up PR that adds them?

yeah, these are not in the scope of this PR. if adding those example, would require changing the playground, and I'm not sure how big is it to support EOF opcodes

Currently the playground crashes if you toggle EOF and go into the playground because they changed some functions names. I think we should fix this (fix below) and ideally also add some playground examples. Is that something you can do ?

(You can use something like EOFTests if you don't want to reinvent the wheel for the examples).

Click to see the diff ```diff diff --git a/context/ethereumContext.tsx b/context/ethereumContext.tsx index 76860ff..5ac90af 100644 --- a/context/ethereumContext.tsx +++ b/context/ethereumContext.tsx @@ -14,11 +14,14 @@ import { RunState } from '@ethereumjs/evm/dist/cjs/interpreter' import { Opcode, OpcodeList } from '@ethereumjs/evm/src/opcodes' import { TypedTransaction, TxData, TransactionFactory } from '@ethereumjs/tx' import { Address, Account, bytesToHex } from '@ethereumjs/util' -import { VM } from '@ethereumjs/vm' +import { RunTxOpts, VM } from '@ethereumjs/vm' import { Common as EOFCommon } from '@ethjs-eof/common' // @ts-ignore it confused with pre-EOF version import { createEVM, EVM as EOFEVM } from '@ethjs-eof/evm' -import { VM as EOFVM } from '@ethjs-eof/vm' +// @ts-ignore it confused with pre-EOF version +import { createTxFromTxData as createTxFromTxDataEOF } from '@ethjs-eof/tx' +// @ts-ignore it confused with pre-EOF version +import { VM as EOFVM, runTx as RunTxEOF } from '@ethjs-eof/vm' import OpcodesMeta from 'opcodes.json' import PrecompiledMeta from 'precompiled.json' import { @@ -59,7 +62,7 @@ const gasLimit = 0xffffffffffffn const postMergeHardforkNames: Array = ['merge', 'shanghai', 'cancun'] export const prevrandaoDocName = '44_merge' const EOF_EIPS = [ - 663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7698, + 663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698, ] type ContextProps = { @@ -306,8 +309,11 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => { data: '0x' + data, nonce: account?.nonce, } - - return TransactionFactory.fromTxData(txData).sign(privateKey) + if (vm.evm instanceof EOFEVM) { + return createTxFromTxDataEOF(txData).sign(privateKey) + } else { + return TransactionFactory.fromTxData(txData).sign(privateKey) + } } /** @@ -616,10 +622,10 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => { const origMethod = target[propKey] return (...args: any[]) => { const result = origMethod.apply(target, args) - if (propKey == 'clearContractStorage') { + if (propKey == 'clearContractStorage' || propKey == 'clearStorage') { _clearContractStorage(args[0]) } - if (propKey == 'putContractStorage') { + if (propKey == 'putContractStorage' || propKey == 'putStorage') { _putContractStorage(args[0], args[1], args[2]) } return result @@ -681,17 +687,21 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => { ) evm.transientStorage.put = transientStorageMethodProxy.put } else if (evm instanceof EOFEVM) { + // NOTE: they renamed a few functions with the EOF changes // Storage handler const proxyStateManager = traceStorageMethodCalls(evm.stateManager) // @ts-ignore confused package - evm.stateManager.putStorage = proxyStateManager.putContractStorage + evm.stateManager.putStorage = proxyStateManager.putStorage // @ts-ignore confused package - evm.stateManager.clearStorage = proxyStateManager.clearContractStorage + evm.stateManager.clearStorage = proxyStateManager.clearStorage // Transient storage handler const transientStorageMethodProxy = traceTransientStorageMethodCalls( evm.transientStorage, ) evm.transientStorage.put = transientStorageMethodProxy.put + // @ts-ignore it's confused because of the pre eof version + evm.stateManager.putContractCode = evm.stateManager.putCode + vm.runTx = (opts: RunTxOpts) => RunTxEOF(vm, opts) } storageMemory.clear() ```
hangleang commented 1 month ago

@2xic I reverted your change on the setup_monorepo.sh script, since it will introduced internal conflicts in the monorepo itself

2xic commented 4 weeks ago

@2xic I reverted your change on the setup_monorepo.sh script, since it will introduced internal conflicts in the monorepo itself

oh, yeah, good point. No worries about that then.

hangleang commented 4 weeks ago

Thank you, I believe it would be more sense if your team could add those examples, maybe update the doc reference as well. You understand the context and how those opcodes are stack together better than I do.

2xic commented 4 weeks ago

Thank you, I believe it would be more sense if your team could add those examples, maybe update the doc reference as well. You understand the context and how those opcodes are stack together better than I do.

Alright, I'll check that up 🫡

hangleang commented 3 weeks ago

hey @2xic, as your suggestion, I removed the EOF toggle, and add prague fork with EOF opcodes

2xic commented 3 weeks ago

hey @2xic, as your suggestion, I removed the EOF toggle, and add prague fork with EOF opcodes

Awesome @hangleang , two minor things (I hope)

  1. can we call it just EOF instead of Prague ?

We had some discussion internally and since things are still a bit in flux about whether it will be in Prague or not, we feel it better to just call it EOF)

  1. Can we still have the default fork be Cancun as that is the current active fork ?

Let me know if anything is unclear / it's something we need to do ourselves.

hangleang commented 3 weeks ago
  1. I override the prague hardfork with those EOF opcodes, I was trying to use customHardfork prop, but didn't work. could you point me to that custom configs? custom_hardfork
2xic commented 3 weeks ago
  1. I override the prague hardfork with those EOF opcodes, I was trying to use customHardfork prop, but didn't work. could you point me to that custom configs? custom_hardfork

True, so I think we can just add some fallback (show a different fork in the UI, but just still have EOFCommon use the Prague fork as that is what EthereumJS uses)

Here is some example diff that seems to work (we probably need to update all the docs that references prague and just change it to EOF which should be simple, I updated just two of them to test )

diff --git a/components/ChainSelector.tsx b/components/ChainSelector.tsx
index f7d967f..f2c76f8 100644
--- a/components/ChainSelector.tsx
+++ b/components/ChainSelector.tsx
@@ -6,21 +6,19 @@ import Select, { OnChangeValue, components } from 'react-select'

 import { EthereumContext } from 'context/ethereumContext'

-import { CURRENT_FORK, EOF_ENABLED_FORK } from 'util/constants'
+import { CURRENT_FORK } from 'util/constants'
 import { toKeyIndex } from 'util/string'

 import { Icon, Label } from 'components/ui'

 const ChainOption = (props: any) => {
-  const { data, children } = props
+  const { data, label } = props
   const isCurrent = data.value === CURRENT_FORK
-  const isEOFEnabled = data.value === EOF_ENABLED_FORK

   return (
     <components.Option {...props}>
-      {children}
+      {label}
       {isCurrent && <Label>Live</Label>}
-      {isEOFEnabled && <Label>EOF</Label>}
     </components.Option>
   )
 }
@@ -60,10 +58,15 @@ const ChainSelector = () => {
     }

     if (!router.query.fork) {
-      const latestFork = forks.at(-1)
-      const fork = forkOptions.find((fork) => fork.value === latestFork?.name)
+      const fork = forkOptions.find((fork) => fork.value === CURRENT_FORK)
       setForkValue(fork as any)
       onForkChange(fork?.value as string)
+    } else {
+      const fork = forkOptions.find((fork) => fork.value === router.query.fork)
+      if (fork) {
+        setForkValue(fork as any)
+        onForkChange(fork?.value as string)
+      }
     }

     // eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/context/ethereumContext.tsx b/context/ethereumContext.tsx
index 572401a..e726ae0 100644
--- a/context/ethereumContext.tsx
+++ b/context/ethereumContext.tsx
@@ -37,6 +37,7 @@ import {
 import {
   CURRENT_FORK,
   EOF_ENABLED_FORK,
+  EOF_FORK_NAME,
   FORKS_WITH_TIMESTAMPS,
 } from 'util/constants'
 import {
@@ -182,10 +183,11 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => {
     chainId?: Chain,
     fork?: string,
   ) => {
+    const forName = fork == EOF_FORK_NAME ? EOF_ENABLED_FORK : fork
     common = new EOFCommon({
       chain: Chain.Mainnet,
-      hardfork: fork || CURRENT_FORK,
-      eips: fork == EOF_ENABLED_FORK ? EOF_EIPS : [],
+      hardfork: forName || CURRENT_FORK,
+      eips: forName == EOF_ENABLED_FORK ? EOF_EIPS : [],
     })

     vm = await EOFVM.create({ common })
@@ -488,6 +490,11 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => {
       }
     })

+    forks.push({
+      name: EOF_FORK_NAME,
+      block: null,
+    })
+
     setForks(forks)
   }

diff --git a/docs/opcodes/E0.mdx b/docs/opcodes/E0.mdx
index 8834dcc..71cc7cd 100644
--- a/docs/opcodes/E0.mdx
+++ b/docs/opcodes/E0.mdx
@@ -1,5 +1,5 @@
 ---
-fork: Prague
+fork: EOF
 group: Stack Memory Storage and Flow Operations
 ---

diff --git a/docs/opcodes/EE.mdx b/docs/opcodes/EE.mdx
index ad89755..a7651c7 100644
--- a/docs/opcodes/EE.mdx
+++ b/docs/opcodes/EE.mdx
@@ -1,5 +1,5 @@
 ---
-fork: Prague
+fork: EOF
 group: System operations
 ---

diff --git a/util/constants.ts b/util/constants.ts
index c75762e..4810963 100644
--- a/util/constants.ts
+++ b/util/constants.ts
@@ -4,9 +4,9 @@ export const GITHUB_REPO_URL = 'https://github.com/smlxl/evm.codes'
 // See: https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/common/src/hardforks
 export const CURRENT_FORK = 'cancun'
 export const EOF_ENABLED_FORK = 'prague'
+export const EOF_FORK_NAME = 'EOF'

 export const FORKS_WITH_TIMESTAMPS: { [name: string]: number } = {
   shanghai: 1681338455,
   cancun: 1710338135,
-  prague: 1710338135,
 }
2xic commented 2 weeks ago

@hangleang Can you apply the diff below diff also ? I noticed dynamic fees got hidden because we changed the name (and also that sometimes the editor would act weird when loading a fork, so updated the loading init code)

diff --git a/context/ethereumContext.tsx b/context/ethereumContext.tsx
index 9596285..4c9dc62 100644
--- a/context/ethereumContext.tsx
+++ b/context/ethereumContext.tsx
@@ -171,18 +171,19 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => {
   const breakpointIds = useRef<number[]>([])

   useEffect(() => {
-    initVmInstance()
     // eslint-disable-next-line react-hooks/exhaustive-deps
+    _loadChainAndForks(
+      new EOFCommon({
+        chain: Chain.Mainnet,
+        hardfork: EOF_ENABLED_FORK,
+      }),
+    )
   }, [])

   /**
    * Initializes the EVM instance.
    */
-  const initVmInstance = async (
-    skipChainsLoading?: boolean,
-    chainId?: Chain,
-    fork?: string,
-  ) => {
+  const initVmInstance = async (fork?: string) => {
     const forkName = fork == EOF_FORK_NAME ? EOF_ENABLED_FORK : fork
     common = new EOFCommon({
       chain: Chain.Mainnet,
@@ -198,10 +199,6 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => {

     currentOpcodes = evm.getActiveOpcodes()

-    if (!skipChainsLoading) {
-      _loadChainAndForks(common)
-    }
-
     _loadOpcodes()
     _loadPrecompiled()
     _setupStateManager()
@@ -224,7 +221,7 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => {
     if (chain) {
       setSelectedChain(chain)
       resetExecution()
-      initVmInstance(true, chainId, selectedFork?.name)
+      initVmInstance(selectedFork?.name)
     }
   }

@@ -237,7 +234,7 @@ export const EthereumProvider: React.FC<{}> = ({ children }) => {
     if (fork) {
       setSelectedFork(fork)
       resetExecution()
-      initVmInstance(true, selectedChain?.id, forkName)
+      initVmInstance(forkName)
     }
   }

diff --git a/opcodes.json b/opcodes.json
index 338bba8..bac0333 100644
--- a/opcodes.json
+++ b/opcodes.json
@@ -1456,7 +1456,7 @@
     "output": "",
     "description": "Copy a segment of data section to the stack",
     "dynamicFee": {
-      "prague": {
+      "EOF": {
         "inputs": {
           "offset": {
             "type": "number",
@@ -1599,7 +1599,7 @@
     "output": "address",
     "description": "Create a contract using EOF container at given index",
     "dynamicFee": {
-      "prague": {
+      "EOF": {
         "inputs": {
           "offset": {
             "type": "number",
@@ -1639,7 +1639,7 @@
     "output": "",
     "description": "Deploy container to an address",
     "dynamicFee": {
-      "prague": {
+      "EOF": {
         "inputs": {
           "auxDataOffset": {
             "type": "number",
@@ -2023,7 +2023,7 @@
     "output": "status",
     "description": "Drop-in replacement for CALL instruction",
     "dynamicFee": {
-      "prague": {
+      "EOF": {
         "inputs": {
           "value": {
             "type": "number",
@@ -2062,7 +2062,7 @@
     "output": "status",
     "description": "Drop-in replacement for DELEGATECALL instruction",
     "dynamicFee": {
-      "prague": {
+      "EOF": {
         "inputs": {
           "offset": {
             "type": "number",
@@ -2160,7 +2160,7 @@
     "output": "status",
     "description": "Drop-in replacement for STATICCALL instruction",
     "dynamicFee": {
-      "prague": {
+      "EOF": {
         "inputs": {
           "offset": {
             "type": "number",
hangleang commented 2 weeks ago

@2xic thank for correct me, it seems work as expected, but don't forget to double check since I'm not following the whole diff.

I still keep initVmInstance when page loaded, just apply block number for the EOF fork. also I renamed all prague.mdx files to EOF.mdx to show gas formula on dynamic fee

2xic commented 2 weeks ago

@2xic thank for correct me, it seems work as expected, but don't forget to double check since I'm not following the whole diff.

I still keep initVmInstance when page loaded, just apply block number for the EOF fork. also I renamed all prague.mdx files to EOF.mdx to show gas formula on dynamic fee

Awesome - that also seemed to have done the trick. Thank you!

2xic commented 2 weeks ago

Thanks @hangleang . There was some changes we added in https://github.com/smlxl/evm.codes/pull/342, but built it all on top of your commits. Thanks so much for your contribution!