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
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
target/
debug/
.git/
.github/
.local/
.claude/
.cursor/
.memsearch/
.idea/
.vscode/
docs/
*.md

# Test fixtures only, and large: the spec-test corpus and the LFS perf/e2e
# data are both irrelevant to `cargo build --bin silver`.
crates/beacon_state/tile/consensus-spec-tests/
crates/e2e/data/
kurtosis/el/
kurtosis/*.ssz
2 changes: 2 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ rustls = "0.23.37"
serde = { version = "1.0.228", features = ["derive"] }
clap = { version = "4", features = ["derive"] }
serde_json = "1.0.135"
serde_yml = "0.0.12"
simd-json = "0.17.0"
toml = "0.8"
sha2 = "0.10"
Expand Down
37 changes: 37 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# syntax=docker/dockerfile:1

# `blst`, `hashtree-rs` and `mimalloc` compile C (and hashtree, assembly), and
# both `build.rs` scripts shell out to `buf`.
FROM rust:1.91-bookworm AS builder

ARG BUF_VERSION=1.67.0
RUN apt-get update && \
apt-get install -y --no-install-recommends clang && \
rm -rf /var/lib/apt/lists/* && \
curl -fsSL -o /usr/local/bin/buf \
"https://github.com/bufbuild/buf/releases/download/v${BUF_VERSION}/buf-Linux-$(uname -m)" && \
chmod +x /usr/local/bin/buf

WORKDIR /silver
COPY . .

# `--locked`: the committed Cargo.lock is under a publish-age cooldown (see
# the justfile), so resolution must not drift in the image.
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/silver/target \
cargo build --release --locked --no-default-features --bin silver && \
cp target/release/silver /usr/local/bin/silver

FROM debian:bookworm-slim

RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/*

COPY --from=builder /usr/local/bin/silver /usr/local/bin/silver

# Without this the tracing subscriber writes to rolling files under LOG_PATH,
# where `docker logs` never sees them.
ENV LOG_STDOUT=1

ENTRYPOINT ["/usr/local/bin/silver"]
6 changes: 3 additions & 3 deletions crates/beacon_api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,10 @@ fn configured(spec: &SpecConfig) -> impl IntoIterator<Item = (&'static str, u64)
("MIN_GENESIS_TIME", spec.min_genesis_time),
("GENESIS_DELAY", spec.genesis_delay),
("TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH", spec.terminal_block_hash_activation_epoch),
("SECONDS_PER_SLOT", spec.seconds_per_slot),
("SECONDS_PER_SLOT", spec.seconds_per_slot()),
// Teku and Nimbus reject a body whose two spellings of the slot length
// disagree, so this is derived rather than a mainnet literal.
("SLOT_DURATION_MS", spec.seconds_per_slot * 1000),
("SLOT_DURATION_MS", spec.slot_duration_ms()),
("SECONDS_PER_ETH1_BLOCK", spec.seconds_per_eth1_block),
("ETH1_FOLLOW_DISTANCE", spec.eth1_follow_distance),
("SHARD_COMMITTEE_PERIOD", spec.shard_committee_period),
Expand Down Expand Up @@ -574,7 +574,7 @@ mod tests {
/// aborts unless they agree, and `SECONDS_PER_SLOT` is overridable.
#[test]
fn slot_duration_ms_follows_an_overridden_seconds_per_slot() {
let spec = spec_map(&SpecConfig { seconds_per_slot: 4, ..SpecConfig::mainnet() });
let spec = spec_map(&SpecConfig { seconds_per_slot: Some(4), ..SpecConfig::mainnet() });
assert_eq!(spec["SECONDS_PER_SLOT"], "4");
assert_eq!(spec["SLOT_DURATION_MS"], "4000");
}
Expand Down
2 changes: 1 addition & 1 deletion crates/beacon_state/tile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ silver_storage.workspace = true
criterion.workspace = true
rand.workspace = true
serde = { version = "1", features = ["derive"] }
serde_yml = "0.0.12"
serde_yml.workspace = true
snap = "0.1"

[[bench]]
Expand Down
5 changes: 1 addition & 4 deletions crates/beacon_state/tile/src/stf/gloas/bid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use crate::{
error::ExecutionPayloadBidError as E,
ssz_hash_gloas::hash_execution_payload_bid,
stf::get_beacon_proposer_index,
tile::get_blob_parameters,
};

/// Record the block's execution payload bid. The bid's
Expand Down Expand Up @@ -53,9 +52,7 @@ pub fn process_execution_payload_bid(
}
}

let max_blobs =
get_blob_parameters(current_epoch, &cfg.blob_schedule, cfg.default_blob_params())
.max_blobs_per_block as usize;
let max_blobs = cfg.blob_params_at(current_epoch).max_blobs_per_block as usize;
if bid.blob_kzg_commitments.len() > max_blobs {
return Err(E::TooManyBlobCommitments {
got: bid.blob_kzg_commitments.len(),
Expand Down
2 changes: 1 addition & 1 deletion crates/beacon_state/tile/src/stf/gloas/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub fn verify_execution_payload_envelope(
if *ExecutionPayloadView::parent_hash(payload) != state.latest_block_hash {
return Err(E::PayloadMismatch { field: "parent_hash" });
}
let expected_timestamp = rv.imm.genesis_time + state.slot * cfg.seconds_per_slot;
let expected_timestamp = rv.imm.genesis_time + state.slot * cfg.seconds_per_slot();
if ExecutionPayloadView::timestamp(payload) != expected_timestamp {
return Err(E::PayloadMismatch { field: "timestamp" });
}
Expand Down
44 changes: 4 additions & 40 deletions crates/beacon_state/tile/src/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use flux::{
};
use rustc_hash::FxHashMap;
use silver_beacon_state_data::{
B256, BeaconBlockHeader, BeaconState, BeaconStateOwner, BeaconStateReader, BlobParameters,
Checkpoint, Epoch, SLOTS_PER_EPOCH, Slot, SlotState, SpecConfig, StateId, Version,
B256, BeaconBlockHeader, BeaconState, BeaconStateOwner, BeaconStateReader, Checkpoint, Epoch,
SLOTS_PER_EPOCH, Slot, SlotState, SpecConfig, StateId,
};
use silver_common::{
BeaconStateEvent, BlockSource, DataColumnsEvent, DataKind, EngineResp, GossipTopic,
Expand All @@ -21,7 +21,7 @@ use silver_config::{PendingBounds, SyncingConfig};
use crate::{
bls,
fork_choice::{ExecutionStatus, FORK_CHOICE_NODES_HINT, ForkChoice, PayloadStatus},
merkle, ssz_hash, stf,
ssz_hash, stf,
tile::{
attestation_pool::AttestationPool, attestation_root_memo::AttestationRootMemo,
fork_data_roots::ForkDataRoots, orphan_pool::PendingBlock, seen_aggregates::SeenAggregates,
Expand Down Expand Up @@ -372,9 +372,7 @@ impl BeaconStateTile {
}

let gvr = self.state.state().immutable.genesis_validators_root;
let bp =
get_blob_parameters(epoch, &self.spec.blob_schedule, self.spec.default_blob_params());
let d = compute_fork_digest(self.spec.fork_version_at(epoch), &gvr, Some(bp));
let d = self.spec.fork_digest_at(epoch, &gvr);
self.cached_fork_digest = Some((epoch, d));
d
}
Expand Down Expand Up @@ -791,39 +789,5 @@ impl Tile<SilverSpine> for BeaconStateTile {
/// current_slot >= aggregate.slot`.
const ATTESTATION_PROPAGATION_SLOT_RANGE: u64 = 32;

/// Spec `compute_fork_digest` (Fulu EIP-7892). `blob_parameters` is `None`
/// pre-Fulu, `Some` from Fulu onward — the active BLOB_SCHEDULE entry.
fn compute_fork_digest(
fork_version: Version,
genesis_validators_root: &B256,
blob_parameters: Option<BlobParameters>,
) -> [u8; 4] {
let base = ssz_hash::hash_tree_root_fork_data(fork_version, genesis_validators_root);
let Some(bp) = blob_parameters else {
return base[..4].try_into().unwrap();
};
let mut input = [0u8; 16];
input[..8].copy_from_slice(&bp.epoch.to_le_bytes());
input[8..].copy_from_slice(&bp.max_blobs_per_block.to_le_bytes());
let mix = merkle::sha256(&input);
[base[0] ^ mix[0], base[1] ^ mix[1], base[2] ^ mix[2], base[3] ^ mix[3]]
}

/// Spec `get_blob_parameters`. `schedule` must be sorted ascending by epoch;
/// `default` is `BlobParameters(ELECTRA_FORK_EPOCH,
/// MAX_BLOBS_PER_BLOCK_ELECTRA)`.
pub(crate) fn get_blob_parameters(
epoch: Epoch,
schedule: &[BlobParameters],
default: BlobParameters,
) -> BlobParameters {
for entry in schedule.iter().rev() {
if epoch >= entry.epoch {
return *entry;
}
}
default
}

#[cfg(test)]
mod tests;
87 changes: 1 addition & 86 deletions crates/beacon_state/tile/src/tile/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use silver_ssz::ssz_view::EXECUTION_PAYLOAD_ENVELOPE_MIN;
use super::*;
use crate::{
fork_choice::{BlockImport, PayloadStatus},
merkle,
stf::AttestationVote,
test_signing,
};
Expand Down Expand Up @@ -2191,89 +2192,3 @@ fn finalize_promotes_every_tier_into_checkpoint_encode() {
);
}
}

// ── fork_digest (standalone `compute_fork_digest`, no tile state) ──

const FD_SENTINEL: BlobParameters = BlobParameters { epoch: u64::MAX, max_blobs_per_block: 0 };

fn fd_genesis_validators_root(b: u8) -> B256 {
[b; 32]
}

fn fd_ef_schedule() -> [BlobParameters; 6] {
[
BlobParameters { epoch: 9, max_blobs_per_block: 9 },
BlobParameters { epoch: 100, max_blobs_per_block: 100 },
BlobParameters { epoch: 150, max_blobs_per_block: 175 },
BlobParameters { epoch: 200, max_blobs_per_block: 200 },
BlobParameters { epoch: 250, max_blobs_per_block: 275 },
BlobParameters { epoch: 300, max_blobs_per_block: 300 },
]
}

fn fd_ef_digest(epoch: Epoch, fork_version: Version, gvr: &B256) -> [u8; 4] {
let schedule = fd_ef_schedule();
let bp = get_blob_parameters(epoch, &schedule, FD_SENTINEL);
compute_fork_digest(fork_version, gvr, Some(bp))
}

#[test]
fn ef_compute_fork_digest_vectors() {
let v6 = [0x06, 0x00, 0x00, 0x00];
let v61 = [0x06, 0x00, 0x00, 0x01];
let v7 = [0x07, 0x00, 0x00, 0x00];
let v71 = [0x07, 0x00, 0x00, 0x01];

let cases: &[(Epoch, Version, B256, [u8; 4])] = &[
(9, v6, fd_genesis_validators_root(0), [0xab, 0x3a, 0xe6, 0xc8]),
(10, v6, fd_genesis_validators_root(0), [0xab, 0x3a, 0xe6, 0xc8]),
(11, v6, fd_genesis_validators_root(0), [0xab, 0x3a, 0xe6, 0xc8]),
(99, v6, fd_genesis_validators_root(0), [0xab, 0x3a, 0xe6, 0xc8]),
(100, v6, fd_genesis_validators_root(0), [0xdf, 0x67, 0x55, 0x7b]),
(101, v6, fd_genesis_validators_root(0), [0xdf, 0x67, 0x55, 0x7b]),
(150, v6, fd_genesis_validators_root(0), [0x8a, 0xb3, 0x8b, 0x59]),
(199, v6, fd_genesis_validators_root(0), [0x8a, 0xb3, 0x8b, 0x59]),
(200, v6, fd_genesis_validators_root(0), [0xd9, 0xb8, 0x14, 0x38]),
(201, v6, fd_genesis_validators_root(0), [0xd9, 0xb8, 0x14, 0x38]),
(250, v6, fd_genesis_validators_root(0), [0x4e, 0xf3, 0x2a, 0x62]),
(299, v6, fd_genesis_validators_root(0), [0x4e, 0xf3, 0x2a, 0x62]),
(300, v6, fd_genesis_validators_root(0), [0xca, 0x10, 0x0d, 0x64]),
(301, v6, fd_genesis_validators_root(0), [0xca, 0x10, 0x0d, 0x64]),
(9, v6, fd_genesis_validators_root(1), [0x89, 0x67, 0x11, 0x11]),
(9, v6, fd_genesis_validators_root(2), [0xf4, 0x9b, 0x0e, 0x24]),
(9, v6, fd_genesis_validators_root(3), [0x86, 0x54, 0x4e, 0x4f]),
(100, v6, fd_genesis_validators_root(1), [0xfd, 0x3a, 0xa2, 0xa2]),
(100, v6, fd_genesis_validators_root(2), [0x80, 0xc6, 0xbd, 0x97]),
(100, v6, fd_genesis_validators_root(3), [0xf2, 0x09, 0xfd, 0xfc]),
(9, v61, fd_genesis_validators_root(0), [0x30, 0xf8, 0xc2, 0x5b]),
(9, v7, fd_genesis_validators_root(0), [0x04, 0x32, 0xf5, 0xa9]),
(9, v71, fd_genesis_validators_root(0), [0x6e, 0x69, 0xa6, 0x71]),
(100, v61, fd_genesis_validators_root(0), [0x44, 0xa5, 0x71, 0xe8]),
(100, v7, fd_genesis_validators_root(0), [0x70, 0x6f, 0x46, 0x1a]),
(100, v71, fd_genesis_validators_root(0), [0x1a, 0x34, 0x15, 0xc2]),
];

for (epoch, fv, g, expected) in cases {
let got = fd_ef_digest(*epoch, *fv, g);
assert_eq!(
got, *expected,
"epoch={epoch} fv={fv:02x?} gvr[0]={:#04x}: got {got:02x?}, want {expected:02x?}",
g[0]
);
}
}

#[test]
fn mainnet_fulu_fork_digest_419072() {
let mainnet_gvr: B256 = [
0x4b, 0x36, 0x3d, 0xb9, 0x4e, 0x28, 0x61, 0x20, 0xd7, 0x6e, 0xb9, 0x05, 0x34, 0x0f, 0xdd,
0x4e, 0x54, 0xbf, 0xe9, 0xf0, 0x6b, 0xf3, 0x3f, 0xf6, 0xcf, 0x5a, 0xd2, 0x7f, 0x51, 0x1b,
0xfe, 0x95,
];
let spec = SpecConfig::mainnet();
let bp = get_blob_parameters(419072, &spec.blob_schedule, spec.default_blob_params());
assert_eq!(bp, BlobParameters { epoch: 419072, max_blobs_per_block: 21 });

let digest = compute_fork_digest(spec.fulu_fork_version, &mainnet_gvr, Some(bp));
assert_eq!(digest, [0x8c, 0x9f, 0x62, 0xfe]);
}
2 changes: 1 addition & 1 deletion crates/beacon_state/tile/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ pub fn validate_execution_payload(
});
}

let expected_timestamp = view.imm.genesis_time + block_slot * cfg.seconds_per_slot;
let expected_timestamp = view.imm.genesis_time + block_slot * cfg.seconds_per_slot();
let got_timestamp = ExecutionPayloadView::timestamp(payload);
if got_timestamp != expected_timestamp {
return Err(ExecutionPayloadError::TimestampMismatch {
Expand Down
Loading
Loading