-
Notifications
You must be signed in to change notification settings - Fork 37
Feat: continuous flashblock building #311
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
base: main
Are you sure you want to change the base?
Conversation
40d2e53 to
e5c72dc
Compare
| FlashblocksPayloadV1, | ||
| ExecutionInfo<FlashblocksExecutionInfo>, | ||
| CacheState, | ||
| Option<TransitionState>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think you just want to keep bundle state in here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't found a way to apply BundleState to a &mut State.
Yes we can create a new state with the bundle state as prestate but:
- &mut state holds ownership of the underlying db, so it's not possible to move db to new state easily
- bundle/cache prestate are not compatible, and we use the cache of the state in other part such as when simulating builder transactions
| }; | ||
|
|
||
| // Check if we can build a better block by comparing execution results | ||
| let is_better_candidate = |prev: &ExecutionInfo<_>, new: &ExecutionInfo<_>| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's implement compare function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can but where? Comparing flashblock payloads can means very different things depending on the context. Here it's still isolated in a closure and within context of comparing candidates for the same interval.
|
i suggest following refactor: |
| if block_cancel.is_cancelled() { | ||
| self.record_flashblocks_metrics( | ||
| ctx, | ||
| info, | ||
| ctx.target_flashblock_count(), | ||
| span, | ||
| "Payload building complete, channel closed or job cancelled", | ||
| ); | ||
| return Ok(None); | ||
| } | ||
| // interval end: abort worker and publish current best immediately (below) | ||
| if ctx.cancel.is_cancelled() { | ||
| break; | ||
| } | ||
|
|
||
| // Build one candidate | ||
| best = self.refresh_best_flashblock_candidate( | ||
| best, | ||
| ctx, | ||
| &*info, | ||
| state, | ||
| &state_provider, | ||
| best_txs, | ||
| block_cancel, | ||
| target_gas_for_batch, | ||
| target_da_for_batch, | ||
| )?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if block_cancel.is_cancelled() { | |
| self.record_flashblocks_metrics( | |
| ctx, | |
| info, | |
| ctx.target_flashblock_count(), | |
| span, | |
| "Payload building complete, channel closed or job cancelled", | |
| ); | |
| return Ok(None); | |
| } | |
| // interval end: abort worker and publish current best immediately (below) | |
| if ctx.cancel.is_cancelled() { | |
| break; | |
| } | |
| // Build one candidate | |
| best = self.refresh_best_flashblock_candidate( | |
| best, | |
| ctx, | |
| &*info, | |
| state, | |
| &state_provider, | |
| best_txs, | |
| block_cancel, | |
| target_gas_for_batch, | |
| target_da_for_batch, | |
| )?; | |
| let handle = tokio::task::spawn_blocking(|| { | |
| refresh_best_flashblock_candidate( | |
| best, | |
| ctx, | |
| info, | |
| state, | |
| &state_provider, | |
| best_txs, | |
| block_cancel, | |
| target_gas_for_batch, | |
| target_da_for_batch, | |
| ) | |
| }); | |
| tokio::select! { | |
| biased; | |
| _ = block_cancel.cancelled() => { | |
| break; | |
| } | |
| result = handle => { | |
| match result { | |
| Ok(Ok(Some(new_best))) => { | |
| best = Some(new_best); | |
| } | |
| Ok(Ok(None)) => { | |
| return Ok(None); | |
| } | |
| Ok(Err(e)) => { | |
| return Err(e); | |
| } | |
| Err(e) => { | |
| return Err(eyre::eyre!("flashblock candidate building task panicked: {}", e)); | |
| } | |
| } | |
| } | |
| } |
something like this would address the issue in your description where refresh_best_flashblock_candidate might take too long, would need to refactor refresh_best_flashblock_candidate to not be a method on self, and update type param DB to be Sync.
📝 Summary
Add a
enable_continuous_buildingflag for flashblock builder enabled by default.When continuous building is enabled, at each interval the builder keep trying building flashblock payloads corresponding to the given interval (a FlashblockCandidate). This is done in a sequential loop so at any time we have one best candidate (after building the first one).
This is implemented in the
build_next_flashblock_continuous, which should provides similar behaviors as the non-continuousbuild_next_flashblock(where we build one flashblock and directly send it).The logic of building a candidate is defined in
refresh_best_flashblock_candidate. Because each candidate can have completely different transactions/ordering, they all need to be built from the same base state. All further mutations are done in a separatesimulation_statebased on the fb base state and saved within the candidate as a coupleCacheState, Option<TransitionState>, which can be used to apply the state of the best candidate and move forward in the building process.This whole process is blocking. However between each candidate building we check for
block_cancelandfb_cancel(and also check forblock_cancelwithinrefresh_best_flashblock_candidatebefore building the final block). This replicates current behavior, however I think that this could cause some isues in specific situations when therefresh_best_flashblock_candidatetakes too much time afterfb_cancelis triggered and could introduce slight delays in payload distribution/start of next flashblock. This could be improved with more careful asynchronous model IMO.It is only useful to try to build a new candidate when the mempool is updated, which is not checked currently and just naively try to build candidates. This can also be improved.