Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# exclude changes from `git diff`
Cargo.lock binary
8 changes: 8 additions & 0 deletions bin/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ async fn main() -> eyre::Result<()> {

builder::config_from_env();

// Pre-load the KZG settings in a separate thread.
//
// This takes ~3 seconds, and we want to do it in parallel with the rest of
// the initialization.
std::thread::spawn(|| {
Copy link

Choose a reason for hiding this comment

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

Preface: not a rust expert, but just noting that I worry that an async version of this, if a call to _settings is made on the main thread could lazy init and create a race between the two. Perhaps it's worth a main thread call once and ensuring k8s readiness expires after we guarantee the initializations?

Copy link
Member Author

@prestwich prestwich Dec 31, 2025

Choose a reason for hiding this comment

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

so this is handled in the underlying c_kzg library, which uses a synchronization primitive to prevent this from happening

fn ethereum_kzg_settings_inner(precompute: u64) -> &'static Arc<KzgSettings> {
    static DEFAULT: OnceBox<Arc<KzgSettings>> = OnceBox::new();
    DEFAULT.get_or_init(|| {
        let settings = KzgSettings::load_trusted_setup(
            ETH_G1_MONOMIAL_POINTS,
            ETH_G1_LAGRANGE_POINTS,
            ETH_G2_MONOMIAL_POINTS,
            precompute,
        )
        .expect("failed to load default trusted setup");
        Box::new(Arc::new(settings))
    })
}

the OnceBox handles thread synchronization, allows only 1 entrant to the initialization. While initialization is occurring, threads trying to access the contents are blocked until it completes. No race condition allowed

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

oooh interesting, they explicitly use race

https://docs.rs/once_cell/latest/once_cell/race/index.html

Copy link
Member Author

Choose a reason for hiding this comment

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

🙄

Copy link
Member Author

Choose a reason for hiding this comment

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

on review, i am not concerned about this race and here's why:

  • we know that this process takes ~3 seconds
  • slot times are 12 seconds
  • the kzg build will not be invoked until the final 3 seconds of a slot
  • so this race will occur IFF all conditions hold
    • the builder is booted inside a slot that it controls
    • the builder is booted in the last half of the slot (as the 3s stretch started by init would have to last until the 3s in build, ie. initializtation is i the last 6 seconds of the slot)
    • the builder produces a block in that slot
      • this means the builder would have had to receive a header for the previous block
      • the builder will not receive a header from the WS subscription until the next block AFTER it boots
  • therefore there is no situation in which the builder boots in the last 6 seconds of a slot AND builds a block for that slot

let _settings = alloy::eips::eip4844::env_settings::EnvKzgSettings::default().get();
});

// Set up env and metrics tasks
let (env_task, metrics_task) = tokio::try_join!(EnvTask::new(), MetricsTask::new())?;

Expand Down