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

Can we split block_hash into a separate Blockchain trait? #298

Closed
Wodann opened this issue Dec 16, 2022 · 13 comments
Closed

Can we split block_hash into a separate Blockchain trait? #298

Wodann opened this issue Dec 16, 2022 · 13 comments

Comments

@Wodann
Copy link
Contributor

Wodann commented Dec 16, 2022

In our implementation of Project Rethnet for Hardhat, we're running into an issue because we have separate data models for the blockchain (blocks) and state management (accounts, account storage, code) when implementing the Database[Ref] traits.

A possible solution would be to separate that trait into two traits: Database[Ref] for state queries and a Blockchain trait that directly deals with the blockchain for retrieving block hashes by their block number`.

Is this something that you think would make sense for revm's internals?

If so, would you want to have two separate containers for caching internally?

@Wodann
Copy link
Contributor Author

Wodann commented Dec 17, 2022

I created a reference implementation: #299

@gakonst
Copy link
Collaborator

gakonst commented Dec 18, 2022

Seems fine as a solution, but just to ask the question: Why not write a struct Wrapper which is impl Database that abstracts over your 2 databases? Instead of adding a new generic like #299?

@Wodann
Copy link
Contributor Author

Wodann commented Dec 18, 2022

The necessity for a wrapper type makes it such that you need to introduce thread-safe synchronisation patterns for the wrapped objects, thus limiting the individual access of two otherwise completely disparate data models

@rakita
Copy link
Member

rakita commented Dec 19, 2022

For history, copying my response from telegram:

Yeah, they are saparate things, it didnt felt like it is a big of a deal as you can always have a wrapper around Database that takes two traits. Idea for database is to have only one trait that you need to implement to bind the outside of revm, spliting it in two is maybe the way

So lets do this. We can have those two traits BlockHash and State for example, that is requirements that you have/need and it does make sense.

And have DatabaseComponents<BH,S> that would take those two and implement Database trait, revm would take only Database and you would have separate components. How does this sound to you?

@Wodann
Copy link
Contributor Author

Wodann commented Dec 20, 2022

The only potential downside of combining the two traits into a wrapper trait Database is that end-users won't be able to have separate error types for BlockHash vs State. I guess one might work around that by adding a:

pub enum DatabaseError<BH, S> {
  BlockHashError(A),
  StateError(S),
}

but then we're back to the situation where both the BH & S generics need to be passed to all functions & structs, which I assume you're trying to avoid?

My reference implementation still uses the same error type between the two, but that's only because we internally store the error (and I didn't want to do the extra work of changing that before we agreed).

@rakita
Copy link
Member

rakita commented Dec 23, 2022

Database has its own Error type

pub trait Database {
type Error;

Could you have that type DatabaseError without propagating BH and S?

@Wodann
Copy link
Contributor Author

Wodann commented Dec 28, 2022

If you want the two traits to allow for separate error types but only pass one trait to the EVM, you'd need two types on the Database trait:


trait Database {
  type StateError;
  type BlockchainError;

  ...
}

At that point it's starting to feel like code duplication to me, though.

@rakita
Copy link
Member

rakita commented Dec 28, 2022

pub struct DatabaseComponents<BH:BlockHash,S:State> {
   block_hash: BH,
   state: S,
}

pub enum ComponentError<BHE, SE> {
  BlockHashError(BHE),
  StateError(SE),
}

impl<S:State, BH: BlockHash> Database for DatabaseComponents<S,BH> {
    type Error = ComponentError<S::Error, BH::Error>
    
    ...
    // wrap component error to `ComponentError` type when is triggered inside fn
}

ComponentError aggregates it and when that error is returned you can extract it back if needed.

Note: I have just noticed that db error is not even returned, it is set when something happens inside EvmData but it is not returned back. Added comment here: #309 (comment)

@Wodann
Copy link
Contributor Author

Wodann commented Dec 28, 2022

Note: I have just noticed that db error is not even returned, it is set when something happens inside EvmData but it is not returned back. Added comment here

Good catch!

ComponentError aggregates it and when that error is returned you can extract it back if needed.

Yeah, implementing it like that would indeed avoid having to have two generics on the EVM, so if you prefer adding the wrapper instead, feel free to. In the end the decision is yours 🙂

@Wodann
Copy link
Contributor Author

Wodann commented Jan 9, 2023

I finally got around to implementing the design you proposed, @rakita: #324

It had some difficulties that I needed to work around to avoid duplication of error types and trait functions. I'm curious what you think

@rakita
Copy link
Member

rakita commented Jan 16, 2023

Here is database component idea: https://github.com/bluealloy/revm/blob/primitives/crates/revm/src/db/components.rs

will move it to revm-primitives with Database trait

@Wodann
Copy link
Contributor Author

Wodann commented Jan 16, 2023

I see you opted for code duplication between the Database and State/BlockHash traits. My fear was that not using the super-trait approach would leave room for them to go out-of-sync.

@rakita
Copy link
Member

rakita commented Jan 21, 2023

merged here: #334

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