Skip to content
This repository was archived by the owner on Sep 8, 2026. It is now read-only.
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 crates/contributor-rewards/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- fix(contributor-rewards): resolve the Solana epoch for a timestamp from real block times instead of dividing wall clock by a hardcoded 400ms slot duration. The old estimate drifted about 30k slots per day of lookback and picked the wrong epoch near a boundary, and no fixed constant survives the SIMD-0525 rollout. That epoch selects the leader schedule rewards are computed against, so the search now errors rather than returning a wrong answer: a backfill older than the endpoint's ledger retention fails on the `ingestor::demand` path instead of silently mis-estimating (malbeclabs/infra#2317)
- fix(contributor-rewards): `snapshot` validates before writing. It warns and continues when the leader schedule cannot be fetched, but every consumer rejects a snapshot without one, so the command exited 0 having written an unusable file under the canonical name and a `snapshot` then `export-shapley` chain failed a step late. Pre-existing, but reachable now that resolving the Solana epoch depends on block-time reads (malbeclabs/infra#2317)
- migrate to Solana 3.0: workspace `solana-*` crates and `solana-sdk` move to the 3.0 line, `solana-program-test` to 3.0.12, and the doublezero SDK git-deps repin from `client/v0.27.1` to the malbeclabs/doublezero#3830 merge revision (malbeclabs/infra#1853)
- release artifact now builds as a static `x86_64-unknown-linux-musl` binary (malbeclabs/infra#1853)
- TLS for HTTP clients moves from openssl to rustls; trust roots are the bundled webpki Mozilla set plus the host OS certificate store, so OS-installed private CAs remain trusted (malbeclabs/infra#1853)
Expand Down
34 changes: 15 additions & 19 deletions crates/contributor-rewards/src/cli/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,31 +252,22 @@ pub async fn create_snapshot(
fetcher.dz_rpc_client.clone(),
fetcher.solana_read_client.clone(),
);
let solana_epoch = match epoch_finder
.find_epoch_at_timestamp(fetch_data.start_us)
// fetch_leader_schedule resolves the Solana epoch itself and reports which
// one it used, so taking the epoch from its result avoids running the
// chain-verified epoch search twice over the same timestamp.
let leader_schedule = match epoch_finder
.fetch_leader_schedule(fetch_epoch, fetch_data.start_us)
.await
{
Ok(epoch) => Some(epoch),
Ok(schedule) => Some(schedule),
Err(e) => {
warn!("Failed to determine Solana epoch: {}", e);
warn!("Failed to get leader schedule: {}", e);
Comment thread
nikw9944 marked this conversation as resolved.
None
}
};

let leader_schedule = if solana_epoch.is_some() {
match epoch_finder
.fetch_leader_schedule(fetch_epoch, fetch_data.start_us)
.await
{
Ok(schedule) => Some(schedule),
Err(e) => {
warn!("Failed to get leader schedule: {}", e);
None
}
}
} else {
None
};
let solana_epoch = leader_schedule
.as_ref()
.map(|schedule| schedule.solana_epoch);

// Create metadata
let metadata = SnapshotMetadata {
Expand Down Expand Up @@ -327,6 +318,11 @@ pub async fn create_snapshot(
Network::Devnet => "dn",
};

// Refuse to write a snapshot no consumer can read. Without this the command
// exits 0 having left an unusable file under the canonical name, and the
// failure surfaces a step later in whatever reads it next.
snapshot.validate()?;

// Export: local override or configured storage
if local_file.is_some() || local_dir.is_some() {
// Save to local filesystem (ignores storage backend config)
Expand Down
Loading
Loading