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: add new BlockchainProvider type #9656

Merged
merged 12 commits into from
Jul 23, 2024
Merged

Conversation

fgimenez
Copy link
Member

@fgimenez fgimenez commented Jul 19, 2024

Closes #9607

@fgimenez fgimenez force-pushed the fgimenez/blockchain-provider-type branch from f2b99de to c51f37a Compare July 19, 2024 16:46
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

I thought about this a bit, as you highlighted we're now running into cyclic dep issues.
we could first move the types to provider like this but I wonder if we could improve the engine/tree layout instead (or later)

Copy link
Collaborator

@mattsse mattsse Jul 20, 2024

Choose a reason for hiding this comment

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

I wonder if we should move these types to a new crate instead.

the provider crate is already a bit messy, so I wonder how to structure this.
I think the ideal solution here is that the engine/tree does not require the new provider type directly and instead operates on provider traits and has the Inmemorystate as a separate field, because otherwise we need to go through the provider to update state and exposing these functions publicly on the provider type sounds a bit dangerous

we should also consider splitting the content of engine/tree into multiple smaller crates for

  • persistence + database
  • orchestrator + handlers
  • state for the inmemory types

Copy link
Member Author

Choose a reason for hiding this comment

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

makes total sense, will prepare changes for this

Copy link
Collaborator

Choose a reason for hiding this comment

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

cool, I think we can have the orchestrator and related types in one crate and then the actual engineapi impl in another, and the db+persistence in an additional crate

so that
engineapiimpl imports orchestrator + persistence

@Rjected Rjected changed the title feat: add new BlockahainProvider type feat: add new BlockchainProvider type Jul 22, 2024
@fgimenez fgimenez force-pushed the fgimenez/blockchain-provider-type branch from c51f37a to 5847557 Compare July 22, 2024 15:45
Copy link
Member Author

@fgimenez fgimenez left a comment

Choose a reason for hiding this comment

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

Ok, I have removed some redundant tree trait impls that will be handled differently now in the communication of the beaconenggine and the tree, and changed some of them to use the in memory representation

I've also left some questions mainly regarding the pending state provider @mattsse ptal

trace!(target: "providers::blockchain", "Getting provider for pending state");

if let Some(block) = self.canonical_in_memory_state.pending_block_num_hash() {
if let Ok(pending) = self.tree.pending_state_provider(block.hash) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure what to do with this pending_state_provider, should we implement it in the in memory state?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we'd need to check if there's a pending block in the canonical memory state, if not this should fallback to latest

Copy link
Member Author

@fgimenez fgimenez Jul 23, 2024

Choose a reason for hiding this comment

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

this is similar to the previous question, this method returns a ProviderResult<StateProviderBox> how do we obtain a StateProvider including in memory information? I think this affects all the methods in the StateProviderFactory impl

}

fn pending_state_by_hash(&self, block_hash: B256) -> ProviderResult<Option<StateProviderBox>> {
if let Some(state) = self.tree.find_pending_state_provider(block_hash) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Same, also related to pending state provider

DB: Send + Sync,
{
fn subscribe_to_canonical_state(&self) -> CanonStateNotifications {
self.tree.subscribe_to_canonical_state()
Copy link
Member Author

Choose a reason for hiding this comment

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

Should we impl this trait CanonStateSubscriptions?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah, we need this in rpc and other components, what we need to do is, move the broadcast channel into the canonicalinmemory type, as a replacement for

/// Broadcast channel for canon state changes notifications.
canon_state_notification_sender: CanonStateNotificationSender,

Copy link
Member Author

Choose a reason for hiding this comment

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

makes sense, done here 09e51ed, already being used in the CanonStateSubscriptions impl here 26bc11b

&self.0.execution_outcome().receipts
}

/// Returns a vector of `Receipt` of executed block that determines the state.
pub fn flattened_receipts(&self) -> Vec<Receipt> {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is required by a method in BlockReader is it ok to flatten like this the Vec<Vec<Option<Receipt>>> that Receipts contains?

Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately the Receipts type refers to multiple blocks' worth of receipts because the ExecutionOutcome is for both single and multiple blocks. In this case we can expect there to be only one element in the outer Vec. One thing we could do is debug_assert that the list is length one, and .first().unwrap() until we find a better way to handle this, does that make sense?

Copy link
Member Author

Choose a reason for hiding this comment

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

makes total sense, done here ef07c86 ptal

Copy link
Member

Choose a reason for hiding this comment

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

makes sense to me, I like the comment

Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

awesome, finding the corresponding values will be a bit more tricky because now that we store canonical blocks in two locations we also need to check both of them when we try to find blocks.

similar situation as with the existing blockchainprovider but now the separation is not pending vs canonical but memory vs disk, for now we can simply check both locations, the inmemory lookup should be super cheap because this is just an rwlock.

functionality wise this pr looks complete and we can iterate on this and the inmemorycanonical state in more smaller scoped prs

I believe we also need to keep track of the block range we currently have in memory and think about race conditions a bit.
since we flush to disko before we update the memory state, we have a guarantee that the state is present somewhere, so we should likely always check inmemory first and then Hit disk if we couldn't find it

Comment on lines 244 to 248
BlockSource::Pending => self
.canonical_in_memory_state
.state_by_hash(hash)
.map(|block_state| block_state.block().block().clone().unseal()),
BlockSource::Canonical => self.database.block_by_hash(hash)?,
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is now a bit blurry, because the canonical in memory state is also canonical, so we need to handle this like any.
and pending should just check the pending block

Copy link
Member Author

Choose a reason for hiding this comment

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

very good point, done here 6a00ec8, now we handle BlockSource::Canonical and BlockSource::Any with the same code, I've changed it a bit to check the in memory representation before hitting the db, let me know wdyt. And for BlockSource::Pending we just return the pending block if present.

Comment on lines +592 to +598
self.ensure_canonical_block(block_number)?;
self.database.history_by_block_number(block_number)
}

fn history_by_block_hash(&self, block_hash: BlockHash) -> ProviderResult<StateProviderBox> {
trace!(target: "providers::blockchain", ?block_hash, "Getting history by block hash");
self.database.history_by_block_hash(block_hash)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

these would also need to check the in memory provider as well because from caller pov all blocks in there are also historical blocks, part of the canonical chain

Copy link
Member Author

Choose a reason for hiding this comment

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

hmm but these methods are returning a StateProviderBox , which is Box<dyn StateProvider> and in memory we are only storing a set of blocks, how can we impl StateProvider for the in memory rep. of a few blocks?

Copy link
Collaborator

Choose a reason for hiding this comment

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

we're still lacking this feature: #9614
to unblock this pr we can ignore the memory state for now and track in an issue instead

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, I've left todo's about this

trace!(target: "providers::blockchain", "Getting provider for pending state");

if let Some(block) = self.canonical_in_memory_state.pending_block_num_hash() {
if let Ok(pending) = self.tree.pending_state_provider(block.hash) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we'd need to check if there's a pending block in the canonical memory state, if not this should fallback to latest

DB: Send + Sync,
{
fn subscribe_to_canonical_state(&self) -> CanonStateNotifications {
self.tree.subscribe_to_canonical_state()
Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah, we need this in rpc and other components, what we need to do is, move the broadcast channel into the canonicalinmemory type, as a replacement for

/// Broadcast channel for canon state changes notifications.
canon_state_notification_sender: CanonStateNotificationSender,

@fgimenez fgimenez force-pushed the fgimenez/blockchain-provider-type branch from 7669fdd to ef07c86 Compare July 23, 2024 08:28
@fgimenez fgimenez force-pushed the fgimenez/blockchain-provider-type branch from 6a00ec8 to 3afd7f3 Compare July 23, 2024 15:18
@fgimenez fgimenez marked this pull request as ready for review July 23, 2024 15:30
Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

lets send it

@mattsse mattsse added this pull request to the merge queue Jul 23, 2024
Merged via the queue into main with commit 2209381 Jul 23, 2024
32 checks passed
@mattsse mattsse deleted the fgimenez/blockchain-provider-type branch July 23, 2024 16:30
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.

Add new BlockchainProvider type
3 participants