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
7 changes: 7 additions & 0 deletions docs/docs/users/guides/advanced/archival_snapshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ There are 10 diff snapshots between consecutive lite snapshots (30,000 / 3,000

## Figuring out which snapshots you need

:::tip

Use the interactive [Snapshot Calculator](./snapshot_calculator.mdx) to
automatically determine which snapshots you need and generate download commands.

:::

Given a target epoch `E` and a network, follow these steps:

1. **Find the base lite snapshot.** Take the largest lite epoch that is `≤ E`.
Expand Down
28 changes: 28 additions & 0 deletions docs/docs/users/guides/advanced/snapshot_calculator.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: Snapshot Calculator
sidebar_position: 3
---

import SnapshotCalculator from "@site/src/components/SnapshotCalculator";

# Snapshot Calculator

Use this tool to figure out which archival snapshots you need to download for a
given epoch. It checks the [Forest Archive](https://forest-archive.chainsafe.dev/list)
for availability and generates a download script.

For background on how lite and diff snapshots work together, see the
[Archival Snapshots guide](./archival_snapshots.md).

## Modes

**Use state** — read historical state (e.g., actor balances via `StateGetActor`).
Needs the base lite snapshot and diffs up to the target epoch.

**Validate / Recompute** — re-execute messages and verify state roots
(`forest-dev state validate`). Validation looks back up to 2000 epochs, but
lite snapshots only contain 900 epochs of state. When the target is close to
the base lite, the calculator adds the previous segment to cover the full
lookback. See [the validation caveat](./archival_snapshots.md#step-4-validate-optional).

<SnapshotCalculator />
321 changes: 321 additions & 0 deletions docs/src/components/SnapshotCalculator/SnapshotCalculator.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
.calculator {
width: 100%;
margin: 0 auto;
}

.controls {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 2rem;
padding: 1.5rem;
background: var(--ifm-background-surface-color);
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: 8px;
}

.row {
display: flex;
gap: 1rem;
align-items: flex-end;
flex-wrap: wrap;
}

.field {
display: flex;
flex-direction: column;
gap: 0.25rem;
}

.field label {
font-size: 0.85rem;
font-weight: 600;
color: var(--ifm-color-emphasis-800);
}

.epochInput {
padding: 0.5rem 0.75rem;
font-size: 1rem;
border: 2px solid var(--ifm-color-emphasis-300);
border-radius: 6px;
background: var(--ifm-background-color);
color: var(--ifm-font-color-base);
font-family: var(--ifm-font-family-monospace);
width: 200px;
}

.epochInput:focus {
outline: none;
border-color: var(--ifm-color-primary);
}

.networkSelect {
padding: 0.5rem 0.75rem;
font-size: 1rem;
border: 2px solid var(--ifm-color-emphasis-300);
border-radius: 6px;
background: var(--ifm-background-color);
color: var(--ifm-font-color-base);
cursor: pointer;
min-width: 140px;
}

.networkSelect:focus {
outline: none;
border-color: var(--ifm-color-primary);
}

.modeToggle {
display: flex;
gap: 0;
border: 2px solid var(--ifm-color-emphasis-300);
border-radius: 6px;
overflow: hidden;
}

.modeButton {
padding: 0.5rem 1rem;
font-size: 0.9rem;
border: none;
background: var(--ifm-background-color);
color: var(--ifm-font-color-base);
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}

.modeButton:not(:last-child) {
border-right: 1px solid var(--ifm-color-emphasis-300);
}

.modeButtonActive {
background: var(--ifm-color-primary);
color: #fff;
font-weight: 600;
}

.modeDescription {
font-size: 0.85rem;
color: var(--ifm-color-emphasis-600);
margin: 0;
line-height: 1.4;
}

.results {
margin-top: 1.5rem;
}

.segmentTitle {
font-size: 1.1rem;
font-weight: 700;
margin: 1.5rem 0 0.75rem 0;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--ifm-color-primary);
color: var(--ifm-color-primary);
}

.segmentNote {
font-size: 0.85rem;
color: var(--ifm-color-emphasis-600);
margin: 0 0 0.75rem 0;
font-style: italic;
}

.snapshotList {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1rem;
}

.snapshotItem {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
border: 1px solid var(--ifm-color-emphasis-200);
border-radius: 6px;
background: var(--ifm-background-surface-color);
font-family: var(--ifm-font-family-monospace);
font-size: 0.85rem;
word-break: break-all;
}

.snapshotBadge {
font-size: 0.7rem;
font-weight: 700;
padding: 0.2rem 0.5rem;
border-radius: 4px;
white-space: nowrap;
flex-shrink: 0;
text-transform: uppercase;
}

.badgeLite {
background: var(--ifm-color-primary-lightest);
color: var(--ifm-color-primary-darkest);
border: 1px solid var(--ifm-color-primary);
}

.badgeDiff {
background: var(--ifm-color-success-contrast-background);
color: var(--ifm-color-success-contrast-foreground);
border: 1px solid var(--ifm-color-success);
}

.snapshotLink {
flex: 1;
color: var(--ifm-color-primary);
text-decoration: none;
transition: color 0.2s;
}

.snapshotLink:hover {
color: var(--ifm-color-primary-dark);
text-decoration: underline;
}

.snapshotMissingName {
flex: 1;
color: var(--ifm-color-emphasis-600);
font-style: italic;
}

.statusIndicator {
flex-shrink: 0;
width: 1.5rem;
text-align: center;
font-size: 1rem;
}

.statusAvailable {
color: var(--ifm-color-success);
font-weight: 700;
}

.statusMissing {
color: var(--ifm-color-danger);
font-weight: 700;
}

.snapshotMissing {
border-color: var(--ifm-color-warning);
background: var(--ifm-color-warning-contrast-background);
}

.warningBanner {
padding: 0.75rem 1rem;
background: var(--ifm-color-warning-contrast-background);
color: var(--ifm-color-warning-contrast-foreground);
border: 1px solid var(--ifm-color-warning);
border-radius: 6px;
font-size: 0.9rem;
margin-bottom: 1rem;
}

.missingLabel {
color: var(--ifm-color-danger);
font-weight: 600;
}

.checkingLabel {
color: var(--ifm-color-emphasis-600);
font-style: italic;
}

.summary {
margin-top: 1.5rem;
padding: 1rem 1.25rem;
background: var(--ifm-color-emphasis-100);
border-radius: 8px;
font-size: 0.9rem;
}

.summary p {
margin: 0.25rem 0;
}

.summaryLabel {
font-weight: 600;
color: var(--ifm-color-emphasis-800);
}

.copyBlock {
margin-top: 1rem;
}

.copyButton {
padding: 0.5rem 1rem;
font-size: 0.85rem;
border: 1px solid var(--ifm-color-emphasis-400);
border-radius: 6px;
background: var(--ifm-background-color);
color: var(--ifm-font-color-base);
cursor: pointer;
transition: all 0.2s;
}

.copyButton:hover {
background: var(--ifm-color-emphasis-200);
border-color: var(--ifm-color-emphasis-600);
}

.downloadTitle {
font-size: 1rem;
font-weight: 700;
margin: 0 0 0.5rem 0;
}

.codeBlock {
padding: 1rem;
background: var(--ifm-code-background);
border: 1px solid var(--ifm-color-emphasis-300);
border-radius: 6px;
font-size: 0.8rem;
overflow-x: auto;
white-space: pre;
margin: 0 0 0.75rem 0;
}

.buttonRow {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}

.error {
color: var(--ifm-color-danger);
font-size: 0.9rem;
margin: 0.5rem 0 0 0;
}

@media (max-width: 768px) {
.controls {
padding: 1rem;
}

.row {
flex-direction: column;
align-items: stretch;
}

.epochInput,
.networkSelect {
width: 100%;
}

.modeToggle {
flex-direction: column;
}

.modeButton:not(:last-child) {
border-right: none;
border-bottom: 1px solid var(--ifm-color-emphasis-300);
}

.snapshotItem {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
}
Loading
Loading