Skip to content

Add ephemeral history gossiper #1851

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

Merged
merged 8 commits into from
May 27, 2025

Conversation

KolbyML
Copy link
Member

@KolbyML KolbyML commented May 22, 2025

This PR depends on

What was wrong?

This is a chunk from #1846

We need a Gossiper which handles the unique cases fund in the Ephemeral History Bridge we do different functions

  • flushing proven headers once a period is provable
  • gossiping an ephemeral bundle which includes a chain of Header's, but it can include multiple bodies/receipts if a re-org happens

How was it fixed?

Add the code

Work for followup PR's

  • Add support for metrics for header series
  • Think about how the AcceptCodeList from HeaderSeries would make sense in terms of the census OfferResult algo

@KolbyML KolbyML requested a review from morph-dev May 22, 2025 17:35
@KolbyML KolbyML self-assigned this May 22, 2025
@KolbyML KolbyML force-pushed the add-ephemeral-history-gossiper branch from a8cb9ba to 860fd2f Compare May 23, 2025 15:51
@KolbyML
Copy link
Member Author

KolbyML commented May 23, 2025

@morph-dev this PR is ready to review now

Copy link
Collaborator

@morph-dev morph-dev left a comment

Choose a reason for hiding this comment

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

There is one/two high level comments that I would like to discuss.

But no major concerns and PR looks mostly ok.

.collect()
}

async fn send_offer_with_header_series(
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: maybe rename to send_multi_offer?

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 function assumes that the content is a header series. Multi offer would imply you could multi offer any set of data which isn't the case here

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's true for gossip_content_header_series (because of the select_random_peers), but how would this function be different for other type of content?

Copy link
Member Author

Choose a reason for hiding this comment

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

I will change it's input parameter's then to restrict it more

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I understand your arguments, can you elaborate more?

Multi offer would imply you could multi offer any set of data which isn't the case here

Why couldn't we offer any set of data usign this function? If you have a peer and you want to send it multiple content items, why can't you use this function?

I will change it's input parameter's then to restrict it more

No change was done to this function (or it's parameters). Why would you rectrict function if it can be broad (without downsides)?


Ultimately, it doesn't matter much, it's just a function name. I think it would be nice if we have both send_offer and send_multi_offer and they behave similarly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well this function can't be used to gossip not ephemeral content, and there is no justification to added support for that. It isn't a priority and this function can't be used on generic content. It requests for random peers. send_multi_offer would require logic we don't need now, this clearly isn't a 1 line change.


/// The finalized state root should be for a finalized period. The beacon blocks passed in must
/// be contained with that respective period's block roots to be provable.
pub async fn gossiped_proven_headers(
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I'm not mistaken, this would start close to 8192*2 gossiping tasks.
I'm worried that all these tasks might starve the offer permit semaphore, especially because semaphore is fair. Meaning, we might not be able to gossip ephemeral content for some time.

Instead, we could do one of the following:

  • send this content sequentially (it will take longer)
  • use different semaphore

What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

I did the third non-listed option of using a future stream then buffering the futures, which will basically rate limit it so only X futures can run at the same time.

  • sequentially
    this one isn't good
  • semaphore
    There are many ways this could be implemented using semaphore, so the suggestion is too broad for me to make any direct statements.

Anyways let me know what you think

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 that you implementation doesn't work as you want it. I tried it in simplified form here:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f9efd12f88caac5f19a20c9e2b8a1310

The reason is that because we have tokio::spawn, background task will start as soon as it's called (not when it's buffered).

Copy link
Member Author

@KolbyML KolbyML May 27, 2025

Choose a reason for hiding this comment

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

Your simplifed code isn't the same as our code hence it gives an invalid result. I updated it so that tokio::spawn is awaited in the function called and it works as expected https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=5acc022e9180d573e1e0b4769a2377da

Copy link
Member Author

Choose a reason for hiding this comment

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

We talked on the call, and I did have an oversight in my implementation. After discussion using a semaphore makes the most sense, so I implemented it.

@KolbyML
Copy link
Member Author

KolbyML commented May 25, 2025

@morph-dev ready for another look, let me know if I missed anything

Copy link
Collaborator

@morph-dev morph-dev left a comment

Choose a reason for hiding this comment

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

I think that buffered futures doesn't work as you are expecting. Happy to have a call and discuss it.

Other than that, no big concerns.

let mut bodies = vec![];
let mut receipts = vec![];
for (header, body, receipt) in ephemeral_bundle.blocks {
bodies.push((header.hash_slow(), body));
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: maybe calculate header.hash_slow() once and reuse?

.collect()
}

async fn send_offer_with_header_series(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I understand your arguments, can you elaborate more?

Multi offer would imply you could multi offer any set of data which isn't the case here

Why couldn't we offer any set of data usign this function? If you have a peer and you want to send it multiple content items, why can't you use this function?

I will change it's input parameter's then to restrict it more

No change was done to this function (or it's parameters). Why would you rectrict function if it can be broad (without downsides)?


Ultimately, it doesn't matter much, it's just a function name. I think it would be nice if we have both send_offer and send_multi_offer and they behave similarly.


/// The finalized state root should be for a finalized period. The beacon blocks passed in must
/// be contained with that respective period's block roots to be provable.
pub async fn gossiped_proven_headers(
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 that you implementation doesn't work as you want it. I tried it in simplified form here:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f9efd12f88caac5f19a20c9e2b8a1310

The reason is that because we have tokio::spawn, background task will start as soon as it's called (not when it's buffered).

@KolbyML
Copy link
Member Author

KolbyML commented May 27, 2025

@morph-dev ready for another review

Copy link
Collaborator

@morph-dev morph-dev left a comment

Choose a reason for hiding this comment

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

Looks good! 🚀

}

impl Gossiper {
pub fn new(
census: Census,
history_network: Arc<HistoryNetwork>,
metrics: BridgeMetricsReporter,
offer_limit: usize,
head_offer_limit: usize,
proven_headers_offer_limit: usize,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: maybe name the variable (proven_headers_offer_limit) and semaphore (non_ephemeral_headers_semaphore) in the same way? Meaning, non_ephemeral_offer_limit

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I missed this when renaming the variables, nice catch

Comment on lines 195 to 207
let offer_permit = if is_head_offer {
self.head_semaphore
.clone()
.acquire_owned()
.await
.expect("to be able to acquire semaphore")
} else {
self.non_ephemeral_headers_semaphore
.clone()
.acquire_owned()
.await
.expect("to be able to acquire semaphore")
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let offer_permit = if is_head_offer {
self.head_semaphore
.clone()
.acquire_owned()
.await
.expect("to be able to acquire semaphore")
} else {
self.non_ephemeral_headers_semaphore
.clone()
.acquire_owned()
.await
.expect("to be able to acquire semaphore")
};
let semaphore = if is_head_offer {
self.head_semaphore.clone()
} else {
self.non_ephemeral_headers_semaphore.clone()
};
let offer_permit = semaphore
.acquire_owned()
.await
.expect("to be able to acquire semaphore")

@KolbyML KolbyML merged commit 4c7897d into ethereum:master May 27, 2025
16 checks passed
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.

2 participants