Skip to content

Commit 00137c2

Browse files
committed
simple 2 step fin
1 parent 0cf9e61 commit 00137c2

File tree

5 files changed

+564
-309
lines changed

5 files changed

+564
-309
lines changed

crates/rbuilder/src/building/builders/block_building_helper.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ pub trait BlockBuildingHelper: Send + Sync {
8080
/// Name of the builder that pregenerated this block.
8181
/// BE CAREFUL: Might be ambiguous if several building parts were involved...
8282
fn builder_name(&self) -> &str;
83+
84+
fn prefinalize_block(
85+
&mut self,
86+
local_ctx: &mut ThreadBlockBuildingContext,
87+
) -> Result<(), BlockBuildingHelperError>;
88+
89+
fn finalize_prefinalized_block(
90+
&mut self,
91+
local_ctx: &mut ThreadBlockBuildingContext,
92+
payout_tx_value: U256,
93+
seen_competition_bid: Option<U256>,
94+
) -> Result<FinalizeBlockResult, BlockBuildingHelperError>;
8395
}
8496

8597
/// Wraps a BlockBuildingHelper with a valid true_block_value which makes it ready to bid.
@@ -134,6 +146,13 @@ pub struct BlockBuildingHelperFromProvider<
134146
built_block_trace: BuiltBlockTrace,
135147
/// Token to cancel in case of fatal error (if we believe that it's impossible to build for this block).
136148
cancel_on_fatal_error: CancellationToken,
149+
150+
prefinalize_state: Option<PrefinalizeState>,
151+
}
152+
153+
#[derive(Debug, Clone)]
154+
pub struct PrefinalizeState {
155+
builder_nonce: u64,
137156
}
138157

139158
#[derive(Debug, thiserror::Error)]
@@ -152,6 +171,8 @@ pub enum BlockBuildingHelperError {
152171
FinalizeError(#[from] FinalizeError),
153172
#[error("Provider historical block hashes error: {0}")]
154173
HistoricalBlockError(#[from] HistoricalBlockError),
174+
#[error("Block is not prefinalized")]
175+
BlockIsNotPrefinalized,
155176
}
156177

157178
impl BlockBuildingHelperError {
@@ -250,6 +271,7 @@ impl<
250271
building_ctx,
251272
built_block_trace,
252273
cancel_on_fatal_error,
274+
prefinalize_state: None,
253275
})
254276
}
255277

@@ -472,4 +494,109 @@ impl<
472494
self.built_block_trace
473495
.set_filtered_build_statistics(considered_orders_statistics, failed_orders_statistics);
474496
}
497+
498+
// prepare block for fast finalization
499+
// executes the most expensive part of finalization so that adding a bid transaction is fast
500+
fn prefinalize_block(
501+
&mut self,
502+
local_ctx: &mut ThreadBlockBuildingContext,
503+
) -> Result<(), BlockBuildingHelperError> {
504+
self.built_block_trace
505+
.verify_bundle_consistency(&self.building_ctx.blocklist)?;
506+
507+
let builder_nonce = self
508+
.partial_block
509+
.insert_combined_refunds_two_step_finalize(
510+
&self.building_ctx,
511+
local_ctx,
512+
&mut self.block_state,
513+
)?;
514+
515+
self.prefinalize_state = Some(PrefinalizeState { builder_nonce });
516+
517+
Ok(())
518+
}
519+
520+
fn finalize_prefinalized_block(
521+
&mut self,
522+
local_ctx: &mut ThreadBlockBuildingContext,
523+
payout_tx_value: U256,
524+
seen_competition_bid: Option<U256>,
525+
) -> Result<FinalizeBlockResult, BlockBuildingHelperError> {
526+
let start_time = Instant::now();
527+
528+
let prefinalize_state = if let Some(state) = self.prefinalize_state.as_mut() {
529+
state
530+
} else {
531+
return Err(BlockBuildingHelperError::BlockIsNotPrefinalized);
532+
};
533+
534+
let mut partial_block = self.partial_block.clone();
535+
let mut block_state = self.block_state.clone();
536+
537+
let payout_tx_gas = self.payout_tx_gas;
538+
partial_block.insert_refund_tx_two_step_finalize(
539+
&self.building_ctx,
540+
local_ctx,
541+
&mut block_state,
542+
payout_tx_gas,
543+
payout_tx_value,
544+
prefinalize_state.builder_nonce,
545+
)?;
546+
547+
let fee_recipient_balance_after = block_state.balance(
548+
self.building_ctx.attributes.suggested_fee_recipient,
549+
&self.building_ctx.shared_cached_reads,
550+
&mut local_ctx.cached_reads,
551+
)?;
552+
let fee_recipient_balance_diff = fee_recipient_balance_after
553+
.checked_sub(self._fee_recipient_balance_start)
554+
.unwrap_or_default();
555+
556+
self.built_block_trace.bid_value = max(payout_tx_value, fee_recipient_balance_diff);
557+
self.built_block_trace.true_bid_value =
558+
partial_block.get_proposer_payout_tx_value(payout_tx_gas, &self.building_ctx)?;
559+
560+
let sim_gas_used = partial_block.tracer.used_gas;
561+
let block_number = self.building_context().block();
562+
563+
let finalized_block =
564+
match partial_block.finalize(block_state, &self.building_ctx, local_ctx) {
565+
Ok(finalized_block) => finalized_block,
566+
Err(err) => {
567+
if err.is_consistent_db_view_err() {
568+
debug!(
569+
block_number,
570+
payload_id = self.building_ctx.payload_id,
571+
"Can't build on this head, cancelling slot"
572+
);
573+
self.cancel_on_fatal_error.cancel();
574+
}
575+
return Err(BlockBuildingHelperError::FinalizeError(err));
576+
}
577+
};
578+
579+
self.built_block_trace.update_orders_sealed_at();
580+
self.built_block_trace.root_hash_time = finalized_block.root_hash_time;
581+
self.built_block_trace.finalize_time = start_time.elapsed();
582+
self.built_block_trace.seen_competition_bid = seen_competition_bid;
583+
584+
Self::trace_finalized_block(
585+
&finalized_block,
586+
&self.builder_name,
587+
&self.building_ctx,
588+
&self.built_block_trace,
589+
sim_gas_used,
590+
);
591+
592+
let block = Block {
593+
trace: self.built_block_trace.clone(),
594+
sealed_block: finalized_block.sealed_block,
595+
txs_blobs_sidecars: finalized_block.txs_blob_sidecars,
596+
builder_name: self.builder_name.clone(),
597+
execution_requests: finalized_block.execution_requests,
598+
bid_adjustments: finalized_block.bid_adjustments,
599+
};
600+
Ok(FinalizeBlockResult { block })
601+
}
475602
}

crates/rbuilder/src/building/builders/block_building_helper_stats_logger.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ use std::{
77
use alloy_primitives::{utils::format_ether, U256};
88

99
use crate::{
10-
building::builders::block_building_helper::BlockBuildingHelper,
10+
building::{builders::block_building_helper::BlockBuildingHelper, ThreadBlockBuildingContext},
1111
live_builder::order_input::mempool_txs_detector::MempoolTxsDetector,
1212
primitives::{order_statistics::OrderStatistics, OrderId, SimulatedOrder},
1313
};
1414

15+
use super::block_building_helper::{BlockBuildingHelperError, FinalizeBlockResult};
16+
1517
/// Wraps a BlockBuildingHelper and stores info about every commit_order as lightweight as possible.
1618
pub struct BlockBuildingHelperStatsLogger<'a> {
1719
block_building_helper: &'a mut dyn BlockBuildingHelper,
@@ -162,7 +164,7 @@ impl BlockBuildingHelper for BlockBuildingHelperStatsLogger<'_> {
162164

163165
fn commit_order(
164166
&mut self,
165-
local_ctx: &mut crate::building::ThreadBlockBuildingContext,
167+
local_ctx: &mut ThreadBlockBuildingContext,
166168
order: &crate::primitives::SimulatedOrder,
167169
result_filter: &dyn Fn(
168170
&crate::primitives::SimValue,
@@ -207,10 +209,7 @@ impl BlockBuildingHelper for BlockBuildingHelperStatsLogger<'_> {
207209
_local_ctx: &mut crate::building::ThreadBlockBuildingContext,
208210
_payout_tx_value: alloy_primitives::U256,
209211
_seen_competition_bid: Option<alloy_primitives::U256>,
210-
) -> Result<
211-
super::block_building_helper::FinalizeBlockResult,
212-
super::block_building_helper::BlockBuildingHelperError,
213-
> {
212+
) -> Result<FinalizeBlockResult, BlockBuildingHelperError> {
214213
panic!("finalize_block not implemented. This is only for testing.");
215214
}
216215

@@ -234,4 +233,20 @@ impl BlockBuildingHelper for BlockBuildingHelperStatsLogger<'_> {
234233
self.block_building_helper
235234
.set_filtered_build_statistics(considered_orders_statistics, failed_orders_statistics);
236235
}
236+
237+
fn prefinalize_block(
238+
&mut self,
239+
_local_ctx: &mut ThreadBlockBuildingContext,
240+
) -> Result<(), BlockBuildingHelperError> {
241+
unimplemented!()
242+
}
243+
244+
fn finalize_prefinalized_block(
245+
&mut self,
246+
_local_ctx: &mut ThreadBlockBuildingContext,
247+
_payout_tx_value: U256,
248+
_seen_competition_bid: Option<U256>,
249+
) -> Result<FinalizeBlockResult, BlockBuildingHelperError> {
250+
unimplemented!()
251+
}
237252
}

crates/rbuilder/src/building/builders/mock_block_building_helper.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,22 @@ impl BlockBuildingHelper for MockBlockBuildingHelper {
122122
self.built_block_trace
123123
.set_filtered_build_statistics(considered_orders_statistics, failed_orders_statistics);
124124
}
125+
126+
fn prefinalize_block(
127+
&mut self,
128+
_local_ctx: &mut ThreadBlockBuildingContext,
129+
) -> Result<(), BlockBuildingHelperError> {
130+
unimplemented!()
131+
}
132+
133+
fn finalize_prefinalized_block(
134+
&mut self,
135+
_local_ctx: &mut ThreadBlockBuildingContext,
136+
_payout_tx_value: U256,
137+
_seen_competition_bid: Option<U256>,
138+
) -> Result<FinalizeBlockResult, BlockBuildingHelperError> {
139+
unimplemented!()
140+
}
125141
}
126142

127143
#[derive(Debug)]

crates/rbuilder/src/building/mod.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,100 @@ impl<Tracer: SimulationTracer, PartialBlockExecutionTracerType: PartialBlockExec
910910
Ok(())
911911
}
912912

913+
pub fn insert_combined_refunds_two_step_finalize(
914+
&mut self,
915+
ctx: &BlockBuildingContext,
916+
local_ctx: &mut ThreadBlockBuildingContext,
917+
state: &mut BlockState,
918+
) -> Result<u64, InsertPayoutTxErr> {
919+
let builder_signer = &ctx.builder_signer;
920+
self.free_reserved_block_space();
921+
let mut nonce = state
922+
.nonce(
923+
builder_signer.address,
924+
&ctx.shared_cached_reads,
925+
&mut local_ctx.cached_reads,
926+
)
927+
.map_err(CriticalCommitOrderError::Reth)?;
928+
929+
let mut fork = PartialBlockFork::new(state, ctx, local_ctx).with_tracer(&mut self.tracer);
930+
931+
for (refund_recipient, refund_amount) in &self.combined_refunds {
932+
let refund_recipient_code_hash = fork
933+
.state
934+
.code_hash(
935+
*refund_recipient,
936+
&ctx.shared_cached_reads,
937+
&mut fork.local_ctx.cached_reads,
938+
)
939+
.map_err(CriticalCommitOrderError::Reth)?;
940+
if refund_recipient_code_hash != KECCAK_EMPTY {
941+
error!(%refund_recipient_code_hash, %refund_recipient, %refund_amount, "Refund recipient has code, skipping refund");
942+
continue;
943+
}
944+
945+
let refund_tx = TransactionSignedEcRecoveredWithBlobs::new_no_blobs(create_payout_tx(
946+
ctx.chain_spec.as_ref(),
947+
ctx.evm_env.block_env.basefee,
948+
builder_signer,
949+
nonce,
950+
*refund_recipient,
951+
BASE_TX_GAS,
952+
*refund_amount,
953+
)?)
954+
.unwrap();
955+
let refund_result = fork.commit_tx(&refund_tx, self.space_state)??;
956+
if !refund_result.tx_info.receipt.success {
957+
return Err(InsertPayoutTxErr::CombinedRefundTxReverted);
958+
}
959+
960+
self.space_state
961+
.use_space(refund_result.space_used(), refund_result.blob_gas_used);
962+
self.executed_tx_infos.push(refund_result.tx_info);
963+
964+
nonce += 1;
965+
}
966+
967+
Ok(nonce)
968+
}
969+
970+
pub fn insert_refund_tx_two_step_finalize(
971+
&mut self,
972+
ctx: &BlockBuildingContext,
973+
local_ctx: &mut ThreadBlockBuildingContext,
974+
state: &mut BlockState,
975+
gas_limit: u64,
976+
value: U256,
977+
nonce: u64,
978+
) -> Result<(), InsertPayoutTxErr> {
979+
let builder_signer = &ctx.builder_signer;
980+
981+
let mut fork = PartialBlockFork::new(state, ctx, local_ctx).with_tracer(&mut self.tracer);
982+
983+
let tx = create_payout_tx(
984+
ctx.chain_spec.as_ref(),
985+
ctx.evm_env.block_env.basefee,
986+
builder_signer,
987+
nonce,
988+
ctx.attributes.suggested_fee_recipient,
989+
gas_limit,
990+
value,
991+
)?;
992+
// payout tx has no blobs so it's safe to unwrap
993+
let tx = TransactionSignedEcRecoveredWithBlobs::new_no_blobs(tx).unwrap();
994+
let exec_result = fork.commit_tx(&tx, self.space_state)?;
995+
let ok_result = exec_result?;
996+
if !ok_result.tx_info.receipt.success {
997+
return Err(InsertPayoutTxErr::PayoutTxReverted);
998+
}
999+
1000+
self.space_state
1001+
.use_space(ok_result.space_used(), ok_result.blob_gas_used);
1002+
self.executed_tx_infos.push(ok_result.tx_info);
1003+
1004+
Ok(())
1005+
}
1006+
9131007
/// returns (requests, withdrawals_root)
9141008
pub fn process_requests(
9151009
&self,

0 commit comments

Comments
 (0)