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
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions crates/rollup-boost-types/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ impl PayloadSource {

#[derive(Debug, Clone)]
pub struct PayloadTrace {
pub builder_has_payload: bool,
pub builder_payload_id: Option<PayloadId>,
pub trace_id: Option<tracing::Id>,
}

Expand Down Expand Up @@ -263,14 +263,14 @@ impl PayloadTraceContext {
&self,
payload_id: PayloadId,
parent_hash: B256,
builder_has_payload: bool,
builder_payload_id: Option<PayloadId>,
trace_id: Option<tracing::Id>,
) {
self.payload_id
.insert(
payload_id,
PayloadTrace {
builder_has_payload,
builder_payload_id,
trace_id,
},
)
Expand Down Expand Up @@ -312,12 +312,17 @@ impl PayloadTraceContext {
.and_then(|x| x.trace_id)
}

pub async fn has_builder_payload(&self, payload_id: &PayloadId) -> bool {
/// Returns the builder's payload id for the given local (L2) payload id, if any.
///
/// Builder and L2 may compute different payload ids (e.g. when the builder augments
/// attributes with flashblocks-specific fields). Callers that need to forward
/// `engine_getPayload` to the builder must translate the incoming (L2) id through
/// this map rather than passing it verbatim.
pub async fn builder_payload_id(&self, payload_id: &PayloadId) -> Option<PayloadId> {
self.payload_id
.get(payload_id)
.await
.map(|x| x.builder_has_payload)
.unwrap_or_default()
.and_then(|x| x.builder_payload_id)
}

pub async fn remove_by_parent_hash(&self, block_hash: &B256) {
Expand Down
111 changes: 109 additions & 2 deletions crates/rollup-boost/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub struct RollupBoostLibArgs {
pub ignore_unhealthy_builders: bool,

#[clap(flatten)]
pub flashblocks_ws: Option<FlashblocksWsArgs>,
pub flashblocks_ws: FlashblocksWsArgs,

#[clap(flatten)]
pub flashblocks_p2p: Option<FlashblocksP2PArgs>,
Expand Down Expand Up @@ -218,7 +218,7 @@ pub mod tests {
assert!(!args.metrics);
assert_eq!(args.rpc_host, "127.0.0.1");
assert_eq!(args.rpc_port, 8081);
assert!(args.lib.flashblocks_ws.is_none());
assert!(!args.lib.flashblocks_ws.flashblocks_ws);
assert!(args.lib.flashblocks_p2p.is_none());

Ok(())
Expand Down Expand Up @@ -354,6 +354,113 @@ pub mod tests {
Ok(())
}

#[test]
fn test_parse_args_with_flashblocks_ws_flag() -> Result<(), Box<dyn std::error::Error>> {
// `--flashblocks` must enable the WS client and materialize nested
// FlashblocksWebsocketConfig with defaults. Previously broken when
// FlashblocksWsArgs was wrapped in Option<> — clap's Option detection
// does not work with nested #[command(flatten)].
let args = RollupBoostServiceArgs::try_parse_from([
"rollup-boost",
"--builder-jwt-token",
SECRET,
"--l2-jwt-token",
SECRET,
"--flashblocks",
])?;

let ws = &args.lib.flashblocks_ws;
assert!(ws.flashblocks_ws, "--flashblocks should set bool true");
assert_eq!(ws.flashblocks_host, "127.0.0.1");
assert_eq!(ws.flashblocks_port, 1112);
assert_eq!(
ws.flashblocks_ws_config
.flashblock_builder_ws_ping_interval_ms,
500,
"nested config default must be populated"
);

Ok(())
}

#[test]
fn test_parse_args_flashblocks_ws_absent_defaults_false()
-> Result<(), Box<dyn std::error::Error>> {
let args = RollupBoostServiceArgs::try_parse_from([
"rollup-boost",
"--builder-jwt-token",
SECRET,
"--l2-jwt-token",
SECRET,
])?;

assert!(!args.lib.flashblocks_ws.flashblocks_ws);
Ok(())
}

#[test]
fn test_parse_args_flashblocks_ws_custom_config() -> Result<(), Box<dyn std::error::Error>> {
let args = RollupBoostServiceArgs::try_parse_from([
"rollup-boost",
"--builder-jwt-token",
SECRET,
"--l2-jwt-token",
SECRET,
"--flashblocks",
"--flashblocks-builder-url",
"ws://builder:9999",
"--flashblocks-host",
"0.0.0.0",
"--flashblocks-port",
"2222",
"--flashblock-builder-ws-ping-interval-ms",
"777",
"--flashblock-builder-ws-pong-timeout-ms",
"1234",
])?;

let ws = &args.lib.flashblocks_ws;
assert!(ws.flashblocks_ws);
assert_eq!(ws.flashblocks_builder_url.as_str(), "ws://builder:9999/");
assert_eq!(ws.flashblocks_host, "0.0.0.0");
assert_eq!(ws.flashblocks_port, 2222);
assert_eq!(
ws.flashblocks_ws_config
.flashblock_builder_ws_ping_interval_ms,
777
);
assert_eq!(
ws.flashblocks_ws_config
.flashblock_builder_ws_pong_timeout_ms,
1234
);

Ok(())
}

#[test]
fn test_parse_args_flashblocks_ws_conflicts_with_p2p() {
// `--flashblocks` and `--flashblocks-p2p` are mutually exclusive.
let result = RollupBoostServiceArgs::try_parse_from([
"rollup-boost",
"--builder-jwt-token",
SECRET,
"--l2-jwt-token",
SECRET,
"--flashblocks",
"--flashblocks-p2p",
"--flashblocks-authorizer-sk",
FLASHBLOCKS_SK,
"--flashblocks-builder-vk",
FLASHBLOCKS_VK,
]);

assert!(
result.is_err(),
"--flashblocks and --flashblocks-p2p must conflict"
);
}

#[test]
fn test_parse_args_missing_jwt_succeeds_at_parse_time() {
// JWT validation happens at runtime, not parse time, so this should succeed
Expand Down
7 changes: 3 additions & 4 deletions crates/rollup-boost/src/flashblocks/args.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use backoff::{ExponentialBackoff, ExponentialBackoffBuilder};
use clap::{Args, Parser};
use clap::Args;
use ed25519_dalek::{SigningKey, VerifyingKey};
use std::time::Duration;
use url::Url;

use hex::FromHex;

#[derive(Args, Clone, Debug)]
#[group(requires = "flashblocks_ws")]
pub struct FlashblocksWsArgs {
/// Enable Flashblocks Websocket client
#[arg(
Expand All @@ -16,7 +15,7 @@ pub struct FlashblocksWsArgs {
id = "flashblocks_ws",
conflicts_with = "flashblocks_p2p",
env,
default_value = "false"
required = false
)]
pub flashblocks_ws: bool,

Expand All @@ -37,7 +36,7 @@ pub struct FlashblocksWsArgs {
pub flashblocks_ws_config: FlashblocksWebsocketConfig,
}

#[derive(Parser, Debug, Clone, Copy)]
#[derive(Args, Debug, Clone, Copy)]
pub struct FlashblocksWebsocketConfig {
/// Minimum time for exponential backoff for timeout if builder disconnected
#[arg(long, env, default_value = "10")]
Expand Down
13 changes: 5 additions & 8 deletions crates/rollup-boost/src/flashblocks/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,9 @@ mod tests {
write.send(Message::Text(utf8_bytes)).await.expect("message sent");
},
msg = read.next() => {
match msg {
// we need to read for the library to handle pong messages
Some(Ok(Message::Ping(_))) => {
send_ping_tx.send(()).await.expect("ping notification sent");
},
_ => {}
// we need to read for the library to handle pong messages
if let Some(Ok(Message::Ping(_))) = msg {
send_ping_tx.send(()).await.expect("ping notification sent");
}
}
_ = term_rx.changed() => {
Expand Down Expand Up @@ -417,7 +414,7 @@ mod tests {
flashblock_builder_ws_connect_timeout_ms: 5000,
};
let service = FlashblocksReceiverService::new(url, tx, config);
let _ = tokio::spawn(async move {
tokio::spawn(async move {
service.run().await;
});

Expand Down Expand Up @@ -471,7 +468,7 @@ mod tests {

let (tx, _rx) = mpsc::channel(100);
let service = FlashblocksReceiverService::new(url, tx, config);
let _ = tokio::spawn(async move {
tokio::spawn(async move {
service.run().await;
});

Expand Down
2 changes: 1 addition & 1 deletion crates/rollup-boost/src/flashblocks/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ impl EngineApiExt for FlashblocksService {
if let Some(payload_id) = resp.payload_id {
let current_payload = *self.current_payload_id.read().await;
if current_payload != Some(payload_id) {
tracing::error!(
tracing::debug!(
message = "Payload id returned by builder differs from calculated. Using builder payload id",
builder_payload_id = %payload_id,
calculated_payload_id = %current_payload.unwrap_or_default(),
Expand Down
4 changes: 2 additions & 2 deletions crates/rollup-boost/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ mod tests {

// A JSON-RPC error is retriable if error.code ∉ (-32700, -32600]
fn is_retriable_code(code: i32) -> bool {
code < -32700 || code > -32600
!(-32700..=-32600).contains(&code)
}

struct TestHarness {
Expand Down Expand Up @@ -889,7 +889,7 @@ mod tests {
{
let l2_requests = l2.requests.lock().await;
assert!(
l2_requests.len() >= 1,
!l2_requests.is_empty(),
"L2 server should have received requests"
);
assert_eq!(l2_requests[0]["method"], "mock_forwardedMethod");
Expand Down
Loading
Loading