From 05f33efa72f36c9ad6f897978b2e59a1952ba6e5 Mon Sep 17 00:00:00 2001 From: etiti Date: Sun, 29 Mar 2026 17:15:42 +0300 Subject: [PATCH] feat: add Mithril snapshot restoration script for fast Cardano node --- .envrc | 22 +++++++- README.md | 26 +++++++++ restore-cardano-snapshot.sh | 105 ++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 2 deletions(-) create mode 100755 restore-cardano-snapshot.sh diff --git a/.envrc b/.envrc index 243aa7d..cd97d29 100644 --- a/.envrc +++ b/.envrc @@ -39,7 +39,7 @@ export APPEND_ARGS="--allow-private-ip --pool-limit 10 --trie-cache-size 0 --pro # Validator Values: -if [ ! -f node.privatekey ]; then +if [ ! -f midnight-node.privatekey ]; then # generate node key like this: DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -it docker.io/parity/subkey:latest generate-node-key | sed -n '2p' > midnight-node.privatekey # Use the second line of output for NODE_KEY (that's what sed -n '2p' does) @@ -50,6 +50,24 @@ export NODE_KEY="$(cat ./midnight-node.privatekey)" # Cardano chain config: # export CARDANO_NETWORK=preview -export CARDANO_IMAGE="ghcr.io/intersectmbo/cardano-node:10.5.3" +export CARDANO_IMAGE="ghcr.io/intersectmbo/cardano-node:10.6.2" export CARDANO_DATA_DIR=./cardano-data export CARDANO_CONFIG_DIR=./cardano-config/${CARDANO_NETWORK} + +# +# Mithril (Fast Cardano Sync): +# +export MITHRIL_IMAGE="ghcr.io/input-output-hk/mithril-client:latest" +if [ "$CARDANO_NETWORK" = "preview" ]; then + export MITHRIL_AGGREGATOR_ENDPOINT="https://aggregator.pre-release-preview.api.mithril.network/aggregator" + export MITHRIL_GENESIS_VERIFICATION_KEY="5b3132372c37332c3132342c3136312c362c3133372c3133312c3231332c3230372c3131372c3139382c38352c3137362c3139392c3136322c3234312c36382c3132332c3131392c3134352c31332c3233322c3234332c34392c3232392c322c3234392c3230352c3230352c33392c3233352c34345d" + export MITHRIL_ANCILLARY_VERIFICATION_KEY="5b3138392c3139322c3231362c3135302c3131342c3231362c3233372c3231302c34352c31382c32312c3139362c3230382c3234362c3134362c322c3235322c3234332c3235312c3139372c32382c3135372c3230342c3134352c33302c31342c3232382c3136382c3132392c38332c3133362c33365d" +elif [ "$CARDANO_NETWORK" = "preprod" ]; then + export MITHRIL_AGGREGATOR_ENDPOINT="https://aggregator.pre-release-preprod.api.mithril.network/aggregator" + export MITHRIL_GENESIS_VERIFICATION_KEY="5b3132372c37332c3132342c3136312c362c3133372c3133312c3231332c3230372c3131372c3139382c38352c3137362c3139392c3136322c3234312c36382c3132332c3131392c3134352c31332c3233322c3234332c34392c3232392c322c3234392c3230352c3230352c33392c3233352c34345d" + export MITHRIL_ANCILLARY_VERIFICATION_KEY="5b3138392c3139322c3231362c3135302c3131342c3231362c3233372c3231302c34352c31382c32312c3139362c3230382c3234362c3134362c322c3235322c3234332c3235312c3139372c32382c3135372c3230342c3134352c33302c31342c3232382c3136382c3132392c38332c3133362c33365d" +elif [ "$CARDANO_NETWORK" = "mainnet" ]; then + export MITHRIL_AGGREGATOR_ENDPOINT="https://aggregator.release-mainnet.api.mithril.network/aggregator" + export MITHRIL_GENESIS_VERIFICATION_KEY="5b3139312c36362c3134302c3138352c3133382c31312c3233372c3230372c3235302c3134342c32372c322c3138382c33302c31322c38312c3135352c3230342c31302c3137392c37352c3233322c3133382c3139362c3231372c352c31342c32302c35372c37392c33392c3137365d" + export MITHRIL_ANCILLARY_VERIFICATION_KEY="5b32332c37312c39362c3133332c34372c3235332c3232362c3133362c3233352c35372c3136342c3130362c3138362c322c32312c32392c3132302c3136332c38392c3132312c3137372c3133382c3230382c3133382c3231342c39392c35382c32322c302c35382c332c36395d" +fi diff --git a/README.md b/README.md index 2a68a1e..222a757 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,32 @@ rm -R ./cardano-data docker volume rm midnight-node-docker_midnight-data-testnet ``` +### Fast Sync with Mithril (Cardano) + +Syncing a Cardano node from scratch can take several days. To speed this up, you can use **Mithril** to download a verified snapshot of the blockchain. This usually takes between 30 minutes and a couple of hours. + +The restoration script is fully automated and will: +- Download the latest immutable blocks (approx 14GB). +- **Automatically inject the latest ledger state** (ancillary files) to bypass the lengthy block replay phase. +- Configure the correct `protocolMagicId` for the network. + +#### Prerequisites +Ensure you have the following tools installed: +- `jq`, `wget`, `zstd`, `curl` +- `docker` and `docker compose` + +#### Instructions +1. Ensure `direnv allow` has been run so the Mithril configuration is loaded (or source `.envrc`). +2. Ensure you are starting from a clean state (no `./cardano-data/db` directory). +3. Run the restoration script: + ```sh + ./restore-cardano-snapshot.sh + ``` +4. Once complete, start your nodes as usual: + ```sh + docker compose -f ./compose-partner-chains.yml up -d cardano-node + ``` + #### Env vars not setup If you get warnings like this then likely `direnv` is not setup or `direnv allow` has not been run: diff --git a/restore-cardano-snapshot.sh b/restore-cardano-snapshot.sh new file mode 100755 index 0000000..2852119 --- /dev/null +++ b/restore-cardano-snapshot.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +set -euo pipefail + +# This file is part of https://github.com/midnightntwrk/midnight-node-docker +# Copyright (C) 2025 Midnight Foundation +# SPDX-License-Identifier: Apache-2.0 + +# Load environment if direnv is not used +if [ -z "${MITHRIL_IMAGE:-}" ]; then + if [ -f .envrc ]; then + echo "Loading environment from .envrc..." + source .envrc + fi +fi + +# Fallback defaults if still not set +NETWORK=${CARDANO_NETWORK:-preview} +DATA_DIR=${CARDANO_DATA_DIR:-./cardano-data} +MITHRIL_IMAGE=${MITHRIL_IMAGE:-ghcr.io/input-output-hk/mithril-client:latest} + +echo "─── Cardano snapshot restoration (Mithril) ───" +echo "Network: $NETWORK" +echo "Data directory: $DATA_DIR" + +if [ ! -d "$DATA_DIR" ]; then + echo "Creating data directory $DATA_DIR..." + mkdir -p "$DATA_DIR" +fi + +# Check if DB is already populated +if [ -d "$DATA_DIR/db" ] && [ "$(ls -A "$DATA_DIR/db" 2>/dev/null)" ]; then + echo "Error: $DATA_DIR/db is not empty." + echo "If you want to restore a fresh snapshot, please remove the existing database first:" + echo " rm -rf $DATA_DIR/db" + exit 1 +fi + +# Ensure we have the necessary Mithril variables +if [ -z "${MITHRIL_AGGREGATOR_ENDPOINT:-}" ] || [ -z "${MITHRIL_GENESIS_VERIFICATION_KEY:-}" ]; then + echo "Error: MITHRIL_AGGREGATOR_ENDPOINT or MITHRIL_GENESIS_VERIFICATION_KEY not set." + echo "Please ensure you have run 'direnv allow' or populated these variables in .envrc" + exit 1 +fi + +echo "Pulling Mithril client image ($MITHRIL_IMAGE)..." +docker pull "$MITHRIL_IMAGE" + +echo "Downloading latest snapshot for $NETWORK..." + +# Check for required tools +for tool in jq wget zstd curl; do + if ! command -v "$tool" &> /dev/null; then + echo "Error: $tool is not installed. Please install it first." + exit 1 + fi +done + +# Execute the main restoration +docker run --rm \ + -e AGGREGATOR_ENDPOINT="$MITHRIL_AGGREGATOR_ENDPOINT" \ + -e GENESIS_VERIFICATION_KEY="$MITHRIL_GENESIS_VERIFICATION_KEY" \ + -e ANCILLARY_VERIFICATION_KEY="$MITHRIL_ANCILLARY_VERIFICATION_KEY" \ + -v "$(pwd)/$DATA_DIR:/data" \ + "$MITHRIL_IMAGE" \ + cardano-db download latest --download-dir /data --include-ancillary + +echo "─── Post-Restoration Fixes ───" + +# Fix 1: Ensure protocolMagicId exists (required by cardano-node to identify the network) +if [ "$NETWORK" == "preview" ]; then MAGIC=2; elif [ "$NETWORK" == "preprod" ]; then MAGIC=1; else MAGIC=764824073; fi +echo -n "$MAGIC" > "$DATA_DIR/db/protocolMagicId" +echo "✅ Created protocolMagicId: $MAGIC" + +# Fix 2: Manual Ledger Injection (Mithril often skips these or they fail to extract) +# We detect if ledger is empty or small and inject if necessary. +if [ -d "$DATA_DIR/db/ledger" ] && [ "$(ls -A "$DATA_DIR/db/ledger" 2>/dev/null)" ]; then + echo "✅ Ledger state found." +else + echo "⚠️ Ledger state missing. Injecting manual ancillary snapshot to bypass block replay..." + # We fetch the ancillary URL dynamically from the aggregator for the 'latest' snapshot + DIGEST=$(docker run --rm -e AGGREGATOR_ENDPOINT="$MITHRIL_AGGREGATOR_ENDPOINT" "$MITHRIL_IMAGE" cardano-db snapshot list --json | jq -r '.[0].hash') + ANCILLARY_URL=$(curl -s "$MITHRIL_AGGREGATOR_ENDPOINT/artifact/cardano-database/$DIGEST" | jq -r '.ancillary.locations[0].uri') + + if [ "$ANCILLARY_URL" != "null" ]; then + echo "Downloading ancillary files from $ANCILLARY_URL..." + wget -q -O ancillary.tar.zst "$ANCILLARY_URL" + zstd -d -q ancillary.tar.zst + tar -xf ancillary.tar -C "$DATA_DIR/db/" + rm -f ancillary.tar ancillary.tar.zst + echo "✅ Ledger injection complete." + else + echo "❌ Could not find ancillary URL in aggregator metadata. Node will perform fallback replay." + fi +fi + +# Fix 3: Permissions (ensure Docker can read the files) +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # On Linux, we might need to chown to 1000 (standard for cardano-node image) + sudo chown -R 1000:1000 "$DATA_DIR/db" 2>/dev/null || true +fi +chmod -R 777 "$DATA_DIR/db" 2>/dev/null || true + +echo "─── Fast Sync Complete ───" +echo "You can now start your Cardano node:" +echo " docker compose -f compose-partner-chains.yml up -d cardano-node"