Block/column timing
Analysis of block to column propagation timing on Ethereum mainnet.
Show code
display_sql("block_production_timeline", target_date)
View query
Show code
df = load_parquet("block_production_timeline", target_date)
# Flag MEV vs local blocks
df["has_mev"] = df["winning_bid_value"].notna()
df["block_type"] = df["has_mev"].map({True: "MEV", False: "Local"})
# Filter to slots with blobs
df["has_blobs"] = df["blob_count"] > 0
df_blobs = df[df["has_blobs"]].copy()
# Calculate block to first column delay
df_blobs = df_blobs.dropna(subset=["block_first_seen_ms", "first_column_first_seen_ms"])
df_blobs["block_to_column_ms"] = df_blobs["first_column_first_seen_ms"] - df_blobs["block_first_seen_ms"]
print(f"Total slots: {len(df):,}")
print(f"Slots with blobs: {len(df_blobs):,} ({len(df_blobs)/len(df)*100:.1f}%)")
print(f" MEV: {df_blobs['has_mev'].sum():,} ({df_blobs['has_mev'].mean()*100:.1f}%)")
print(f" Local: {(~df_blobs['has_mev']).sum():,} ({(~df_blobs['has_mev']).mean()*100:.1f}%)")
Block to column delay¶
Time from block first seen to first column first seen. Shows how quickly columns start propagating after the block arrives.
Note on negative values: A negative delay means a column was observed before the block. This can happen due to how data propagates through the network: columns may reach certain parts of the network before the block does.
Show code
if len(df_blobs) > 0:
fig = px.histogram(
df_blobs,
x="block_to_column_ms",
color="block_type",
category_orders={"block_type": ["MEV", "Local"]},
nbins=60,
barmode="overlay",
opacity=0.7,
color_discrete_map={"MEV": "#AB63FA", "Local": "#19D3F3"},
)
fig.update_layout(
margin=dict(l=60, r=30, t=30, b=60),
xaxis=dict(title="Block to first column (ms)"),
yaxis=dict(title="Slots"),
legend_title="Block type",
height=400,
)
fig.show(config={"responsive": True})
else:
print("No block-to-column timing data available.")
Show code
# Summary statistics
if len(df_blobs) > 0:
stats = df_blobs["block_to_column_ms"].describe(percentiles=[0.5, 0.9, 0.95, 0.99])
print("Block to first column (ms):")
print(f" Median: {stats['50%']:.0f}")
print(f" P90: {stats['90%']:.0f}")
print(f" P95: {stats['95%']:.0f}")
print(f" P99: {stats['99%']:.0f}")
print(f" Max: {stats['max']:.0f}")
Block to column delay over time¶
How the block-to-column delay varies throughout the day.
Show code
if len(df_blobs) > 0:
df_plot = df_blobs.copy()
df_plot["blob_count_f"] = df_plot["blob_count"].astype(float) # Force continuous color
max_blobs = df_plot["blob_count"].max()
fig = px.scatter(
df_plot,
x="slot_start_date_time",
y="block_to_column_ms",
color="blob_count_f",
color_continuous_scale="Plasma",
range_color=[0, max_blobs],
opacity=0.5,
hover_data={"slot": True, "blob_count": True, "block_to_column_ms": ":.0f", "slot_start_date_time": False, "blob_count_f": False},
)
fig.update_layout(
margin=dict(l=60, r=30, t=30, b=60),
xaxis=dict(title="Time (UTC)", tickformat="%H:%M"),
yaxis=dict(title="Block to first column (ms)"),
coloraxis_colorbar=dict(title="Blobs"),
height=400,
)
fig.show(config={"responsive": True})
Column spread by blob count (MEV vs local)¶
Does MEV vs local block production affect how columns spread at each blob count?
Box: 25th-75th percentile. Line: median. Whiskers: min/max excluding outliers.
Show code
# Filter to slots with blobs (column_spread only exists for blob slots)
df_col_spread = df[df["blob_count"] > 0].dropna(subset=["column_spread_ms"])
if len(df_col_spread) > 0:
fig = px.box(
df_col_spread,
x="blob_count",
y="column_spread_ms",
color="block_type",
category_orders={"block_type": ["MEV", "Local"]},
)
fig.update_layout(
margin=dict(l=60, r=30, t=30, b=60),
xaxis=dict(title="Blob count", dtick=1),
yaxis=dict(title="Column spread (ms)"),
legend_title="Block type",
height=450,
)
fig.show(config={"responsive": True})
Block-to-column delay by blob count¶
How much additional delay per blob for column propagation to begin after block arrival?
Box: 25th-75th percentile. Line: median. Whiskers: min/max excluding outliers.
Show code
df_delay = df[df["blob_count"] > 0].dropna(subset=["block_first_seen_ms", "first_column_first_seen_ms"])
df_delay["block_to_column_ms"] = df_delay["first_column_first_seen_ms"] - df_delay["block_first_seen_ms"]
if len(df_delay) > 0:
fig = px.box(
df_delay,
x="blob_count",
y="block_to_column_ms",
color="block_type",
category_orders={"block_type": ["MEV", "Local"]},
)
fig.update_layout(
margin=dict(l=60, r=30, t=30, b=60),
xaxis=dict(title="Blob count", dtick=1),
yaxis=dict(title="Block to first column (ms)"),
legend_title="Block type",
height=450,
)
fig.show(config={"responsive": True})