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

[Optimism][Integrate]RIP-7728 #11

Open
wants to merge 3 commits into
base: optimism
Choose a base branch
from

Conversation

mralj
Copy link
Collaborator

@mralj mralj commented Oct 11, 2024

Integrates RIP-7728 implemented in #2 to op-geth.

RIP-7728

This proposal introduces a new precompiled contract, L1SLOAD, that loads several storage slots from L1, requiring a contract address, storage keys, and RPC connection to L1.

Implementation details

To leverage this precompile node runner on L2; it must have the following:

  1. Some Notion of latest L1 block
  2. RPC connection to L1 node, preferably "fast one" since we are making L1 RPC call during EVM precompile execution.

Our rollup-geth has a flag --rollup.l1.rpc_endpoint from which it reads the URL of the required L1 RPC. This flag should be set when starting execution (op-geth) client.

Overriding the default config

Out of the box, rollup-geth provides the following method for "dealing" with the latest L1 block:

var defaultRollupPrecompilesConfig RollupPrecompileActivationConfig = RollupPrecompileActivationConfig{
	L1SLoad: L1SLoad{
		GetLatestL1BlockNumber: LetRPCDecideLatestL1Number, // CHANGE THIS METHOD
	},
}

The code is located in core/vm/contracts_rollup_overrides. We should pass into the GetLatestL1BlockNumber appropriate method for providing the latest L1 block on the op-stack blockchains.

Example:

var defaultRollupPrecompilesConfig RollupPrecompileActivationConfig = RollupPrecompileActivationConfig{
	L1SLoad: L1SLoad{
		GetLatestL1BlockNumber: GetLatestL1BlockNumber
	},
}

func GetLatestL1BlockNumber(state *state.StateDB) func() *big.Int {
	return func() *big.Int {
		addressOfL1BlockContract := common.Address{}
		slotInContractRepresentingL1BlockNumber := common.Hash{}
		return state.GetState(addressOfL1BlockContract, slotInContractRepresentingL1BlockNumber).Big()
	}
}

so that I can work with this branhc
commit 237b78f
Author: mralj <[email protected]>
Date:   Fri Oct 11 12:46:51 2024 +0200

    removed unnecessary call to  vm.SetVmL1RpcClient

commit d4cd646
Author: mralj <[email protected]>
Date:   Fri Oct 11 12:16:24 2024 +0200

    rollup precompile config is glob. variable

    I decided to implement it this way after trying to integrate code with Arbitrum and having a better understanding of the calls that are made to the NewEvm
    This approach makes it easier to both override the default config, and to have the option to "not to think about it"

commit 42855ae
Author: mralj <[email protected]>
Date:   Wed Oct 9 10:10:44 2024 +0200

    concurrent map r/w bugfix

commit 128b120
Author: mralj <[email protected]>
Date:   Mon Oct 7 14:00:57 2024 +0200

    removed unused import - popped up after rebasing

commit ee58cfe
Author: mralj <[email protected]>
Date:   Mon Oct 7 13:00:42 2024 +0200

    missed cleanup of ActivePrecompiles

commit d409ef8
Author: mralj <[email protected]>
Date:   Mon Oct 7 12:02:42 2024 +0200

    bugfixes - l1rpc activated at proper point and precompile address

commit bdd7b7d
Author: mralj <[email protected]>
Date:   Mon Oct 7 10:57:48 2024 +0200

    ethclient moved to node.Node

commit bd56bdc
Author: mralj <[email protected]>
Date:   Fri Oct 4 17:36:36 2024 +0200

    code cleanup after trying to merge into arb/op-geth

commit 76a2339
Author: mralj <[email protected]>
Date:   Tue Oct 1 10:43:04 2024 +0200

    internal/ethapi and tracers use pre-existing function call

commit b72098e
Author: mralj <[email protected]>
Date:   Mon Sep 30 10:20:37 2024 +0200

    added missing "," - fixed comptime bug

commit 1ccbc95
Author: mralj <[email protected]>
Date:   Mon Sep 30 10:13:45 2024 +0200

    simplified the code

commit 0f74390
Author: mralj <[email protected]>
Date:   Sun Sep 29 13:12:00 2024 +0200

    cleaned up code & created more rollup specific files

commit 2a7b7d7
Author: mralj <[email protected]>
Date:   Sun Sep 29 11:45:34 2024 +0200

    cmd - rollup specific files

commit ef91bcd
Author: mralj <[email protected]>
Date:   Fri Sep 27 13:10:01 2024 +0200

    implements L1SLOAD contract

commit 6a98534
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:22:58 2024 +0200

    added L1SLoad sekelton

commit 5f039c5
Merge: 204ef24 56c1f67
Author: mralj <[email protected]>
Date:   Mon Oct 7 13:09:06 2024 +0200

    Merge pull request #4 from NethermindEth/core/rip/7728-precompile-impl

    [P2] Implements RIP-7728

commit 56c1f67
Author: mralj <[email protected]>
Date:   Sat Sep 28 13:00:06 2024 +0200

    added missing mocks for tests

commit e9a5c28
Author: mralj <[email protected]>
Date:   Fri Sep 27 22:44:44 2024 +0200

    added test for too long input edgecase

commit bea23a3
Author: mralj <[email protected]>
Date:   Fri Sep 27 22:40:37 2024 +0200

    added batch call for StoragesAt as well as tests

commit c4b24af
Author: mralj <[email protected]>
Date:   Fri Sep 27 13:32:52 2024 +0200

    added rpc call timeout strategy

commit f0dd217
Author: mralj <[email protected]>
Date:   Fri Sep 27 13:10:01 2024 +0200

    implements L1SLOAD contract

commit 759dda7
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:22:58 2024 +0200

    added L1SLoad sekelton

commit 204ef24
Author: mralj <[email protected]>
Date:   Thu Sep 26 20:21:47 2024 +0200

    added example how code in overrides would change

commit a24608e
Author: mralj <[email protected]>
Date:   Thu Sep 26 19:48:47 2024 +0200

    added ability to activate rollup precompiles from eth/internal and eth/tracers

commit 99ccaf7
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:26:08 2024 +0200

    bugfix

commit 1974d92
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:22:58 2024 +0200

    added L1SLoad sekelton

commit 0ae7e7b
Author: mralj <[email protected]>
Date:   Wed Sep 25 19:08:40 2024 +0200

    dial L1 RPC client passed via required flag

# Conflicts:
#	core/vm/interpreter.go
#	eth/backend.go
#	params/config.go
@mralj mralj marked this pull request as ready for review October 12, 2024 11:26
@mralj mralj self-assigned this Oct 12, 2024
@mralj mralj changed the title Optimism integrate/rip 7728 [Optimism][Integrate]RIP-7728 Oct 12, 2024
Copy link
Member

@jmederosalvarado jmederosalvarado left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link

@protolambda protolambda left a comment

Choose a reason for hiding this comment

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

This is not an endorsement of this particular RIP, just some early quick feedback for the process of building these type of L2-oriented diffs.

About the specific RIP now that we are here and thinking about it: I am very cautious with it, and not as familiar with it, but from a quick look I have several serious concerns:

  • State-root based L1 state access from a L2 makes L1 archive nodes a requirement to fully sync history, this is very centralizing.
  • Makes RPC calls a synchronous part of the EVM, this is not good for performance
  • Affects proving of the rollup: the withdrawal proof now includes additional L1 inputs.
  • The introduction of Verkle on L1 will completely break the storage proving (even if improving, it's still a breaking change to a proving system).
  • The gas cost seems way too cheap for a blocking RPC call in the EVM. This can grind a L2 sequencer to a halt if the gas limit is high.
  • The RIP makes no comment on speculative execution of a tx: this now involves synchronous RPC calls, and makes it costly to pre-validate a tx behavior. Like with EIP-4337 for txs that pay their own fee, or future op-stack work, this execution cost is of great concern.
  • Has no versioning with the precompile input to do things different in the future.

The diff might be small, but the effect on security and sync-ability of a L2 is quite deep, and thus gives a bad perspective of the change.

Feedback on the general diff aspect:

  • Diffs should include testing. I don't see coverage of the new functionality except a few JSON files. Does the L1 get mocked? It's difficult to review the testing.
  • Documentation of the changes is useful; document what was changed, and why, in a change to the fork.yaml like the op-geth codebase does is helpful.

Additional tips, to make the diff more usable / maintainable:

  • Split new functionality into separate files as much as possible
  • Make it easy to understand the feature-flag aspect; how does this new functionality active? When does it run?
  • More doc-comments would be helpful
  • Avoid spurious formatting changes. If the L1 formatting is awkward we keep it, to avoid unnecessary merge conflicts.
  • You can reduce Go-fmt changes by encapsulating the code better. E.g. separating the new struct fields with a blank line, to avoid it affecting the name/field alignment of other fields. And a comment about the below struct-fields being part of a particular rollup change is helpful too.

@tynes
Copy link

tynes commented Oct 23, 2024

The way to solve the archive node requirements for RIP-7728 is to operate the precompile as a key/value store in 2 modes. Block building mode is backed by an RPC and it is expected that each call to the precompile also appends its returndata and a witness as a EIP-7685 Request to the block. In full node mode, the requests are validated before the state transition function and populated into precompile.

@mralj
Copy link
Collaborator Author

mralj commented Oct 24, 2024

@protolambda thank you very much for taking the time and effort for providing the feedback, it's much appreciated!

This is not an endorsement of this particular RIP, just some early quick feedback for the process of building these type of L2-oriented diffs.

About the specific RIP now that we are here and thinking about it: I am very cautious with it, and not as familiar with it, but from a quick look I have several serious concerns:

  • State-root based L1 state access from a L2 makes L1 archive nodes a requirement to fully sync history, this is very centralizing.
  • Makes RPC calls a synchronous part of the EVM, this is not good for performance
  • Affects proving of the rollup: the withdrawal proof now includes additional L1 inputs.
  • The introduction of Verkle on L1 will completely break the storage proving (even if improving, it's still a breaking change to a proving system).
  • The gas cost seems way too cheap for a blocking RPC call in the EVM. This can grind a L2 sequencer to a halt if the gas limit is high.
  • The RIP makes no comment on speculative execution of a tx: this now involves synchronous RPC calls, and makes it costly to pre-validate a tx behavior. Like with EIP-4337 for txs that pay their own fee, or future op-stack work, this execution cost is of great concern.
  • Has no versioning with the precompile input to do things different in the future.

The diff might be small, but the effect on security and sync-ability of a L2 is quite deep, and thus gives a bad perspective of the change.

Firstly I want to stress: we are not endorsing this RIP either. We decided to implement it just as an experiment, since it:

  1. "Touched" different layers of the geth codebase
  2. It was well defined, not too big, not too small
  3. It was proposed by the Scroll team, but Optimism was in looking into adopting something similar as well.

Thus it looked like a good candidate to experiment with.
Regarding the issues you mentioned, while I agree with most of them, I don't think I'm the best person to address them :)

Feedback on the general diff aspect:

  • Diffs should include testing. I don't see coverage of the new functionality except a few JSON files. Does the L1 get mocked? It's difficult to review the testing.

Testing is not in our focus too much during this "experimentation phase", but if the project is to move forwards, it's going to become extremely important.
I think we will need some help from L2s on this (specifically testing strategies of various L2s to make sure nothing breaks and everything works as expected).

That being said, this PR contains couple of "sanity check" tests, just to confirm implementation is not broken.
The added tests follow blueprint from the geth team for the other precompiles. I am relying on their testing setup that's why only a couple of json files were enough to cover these basic tests (specifically already existing testJson and testJsonFail functions).

Does the L1 get mocked?

Yeah, it does: link

Additionally, while this is by no means any proof that this was properly implemented, I have ran optimism node connected to op-geth with these changes applied locally, and the calling precompile from Solidty worked as expected and nothing was breaking. I understand this is far from proper testing but, at least it worked on my machine™️ 😅

  • Documentation of the changes is useful; document what was changed, and why, in a change to the fork.yaml like the op-geth codebase does is helpful.

Additional tips, to make the diff more usable / maintainable:

  • Split new functionality into separate files as much as possible
  • Make it easy to understand the feature-flag aspect; how does this new functionality active? When does it run?
  • More doc-comments would be helpful
  • Avoid spurious formatting changes. If the L1 formatting is awkward we keep it, to avoid unnecessary merge conflicts.
  • You can reduce Go-fmt changes by encapsulating the code better. E.g. separating the new struct fields with a blank line, to avoid it affecting the name/field alignment of other fields. And a comment about the below struct-fields being part of a particular rollup change is helpful too.

Regarding all above I can only say, again, thank you very much for the feeback!
We are already doing some "stuff" you suggested (e.g. separating to new files as much as possible, and all of these are suffixed with rollup, e.g. precompiles_rollup.go), but certainly, as you noticed there is room for improvement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants