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

feat(host): Implement missing hint types in prefetch() #249

Closed
4 of 6 tasks
zobront opened this issue Jun 14, 2024 · 3 comments
Closed
4 of 6 tasks

feat(host): Implement missing hint types in prefetch() #249

zobront opened this issue Jun 14, 2024 · 3 comments

Comments

@zobront
Copy link
Contributor

zobront commented Jun 14, 2024

Is your feature request related to a problem? Please describe.

The prefetch() method takes in a hint, parses the hint type, and uses the hint to generate the required preimages in the oracle. Currently, there are a few hint types that are unimplemented:

  • L1Blob
  • L2BlockHeader
  • L2Transactions
  • L2Code
  • L2Output
  • L2StateNode

As additional client code is added to run the derivation pipeline, these will be necessary.

Describe the solution you'd like

Here are proposed solutions for each type:

  • L1Blob => TBD
  • L2BlockHeader: take in block hash as a hint, call debug_getRawHeader on the l2provider and save hash => raw header.
  • L2Transactions: take in block hash as a hint, call get_block_by_hash() and pull out the transactions, use the existing store_transactions() machinery but adapt it for OP by using the op-alloy repo and OpTxEnvelope (which should work for L1 transactions too, need to confirm), this stores preimage => hash all the way up the transaction trie.
  • L2Code => take in block number and address, convert block number to BlockId, call get_code_at(), hash the code, store codehash => code.
  • L2Output => take in block hash, get block header to pull state root, set the L2 message passer contract as a constant, call provider.get_proof() for L2 message passer to get storage root, use similar code to compute_output_root() to hash version | state root | storage root | input hash, see questions on what to store.
  • L2StateNode => take in block hash as hint, get the block header and pull the state root, see questions on what to store.

Questions:

  • Any reason you can think of why we'd want to pull the whole account for L2Code to get the codehash from there rather than just calculating it ourselves?
  • To confirm, L2StateNode is intended to return the state root of the block, right?
  • For L2Output and L2StateNode, the output we are wanting does not directly hash into the input. This means the proof requires proving the location of the state root in a block header and proving the block header hashes into block hash. I see you have many PreimageKeyTypes — have you put any thought into the best abstraction to handle this situation?

Describe alternatives you've considered

N/A

Additional context

N/A

@clabby
Copy link
Collaborator

clabby commented Jun 14, 2024

Thanks for getting this together! Looks great so far.

L2Code => take in block number and address

Needs to take in just the code hash.

L2Output => take in block hash, get block header to pull state root, set the L2 message passer contract as a constant, [...]

I believe this route is already implemented, but it should take in no data (just hint ID). It's only responsible for returning the preimage of the starting L2 output passed in through the boot info. #232

Any reason you can think of why we'd want to pull the whole account for L2Code to get the codehash from there rather than just calculating it ourselves?

Specifically, we do not have access to the account information when the code is fetched. We need to use an endpoint that returns the preimage of the codehash, such as geth's debug_dbGet. See https://github.com/ethereum/go-ethereum/blob/fd5078c779f2aab50ea759bd3dcd3c5d4f2556f8/core/rawdb/schema.go#L112

To confirm, L2StateNode is intended to return the state root of the block, right?

Nope, this is a trie-node-by-hash function. It should reveal the preimage of a state node hash. Also can use debug_dbGet here for now. We should improve this in the future, see #235.

For L2Output and L2StateNode, the output we are wanting does not directly hash into the input. This means the proof requires proving the location of the state root in a block header and proving the block header hashes into block hash. I see you have many PreimageKeyTypes — have you put any thought into the best abstraction to handle this situation?

For both of these routes, the output we are wanting does directly hash to the input. L2Output should return the preimage of the output root passed through the boot info, and L2StateNode should return the preimage of the state node hash passed.

@zobront
Copy link
Contributor Author

zobront commented Jun 14, 2024

Thanks @clabby — this all seems good, just three questions:

  1. Can you explain the situations when we are needing the code for an account but wouldn't be able to pass the address as a hint? Similarly, any info on when L2StateNode will be used would be helpful in making sure I'm understanding appropriately.

  2. I'm also thinking about the L2Transactions proof. This won't be proving against the block, right? Because we don't have the final block, and proving it against a block that we create seems circular. I believe it should be proving against the derivation pipeline — do you have a plan for what that hint would look like?

  3. I'm going to merge feat(host): More hint routes #232 into my branch and open a PR against that to keep it clean. If you have a different preference, just let me know.

@clabby
Copy link
Collaborator

clabby commented Jun 14, 2024

Can you explain the situations when we are needing the code for an account but wouldn't be able to pass the address as a hint? Similarly, any info on when L2StateNode will be used would be helpful in making sure I'm understanding appropriately.

revm's Database trait defines code_by_hash(B256), which does not contain a ref to the account to which the code hash belongs to. We can also implement a secondary caching layer in TrieDB, that inserts the bytecode into a map within basic, though it would effectively double-store the bytecode of each contract, increasing memory usage (sensitive resource inside of the verifiable environment.)

Glad to explore this later, but for now, sticking w/ the single-layer cache.

I'm also thinking about the L2Transactions proof. This won't be proving against the block, right? Because we don't have the final block, and proving it against a block that we create seems circular. I believe it should be proving against the derivation pipeline — do you have a plan for what that hint would look like?

The L2Transactions route is meant to store the preimages of all transactions within a block as well as the intermediate nodes within the transactionsRoot. An impl can be found here: https://github.com/ethereum-optimism/optimism/blob/develop/op-program/client/l2/oracle.go#L73-L83

I'm going to merge #232 into my branch and open a PR against that to keep it clean

sg. We just merged into main :)

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 a pull request may close this issue.

2 participants