Skip to main content

Archive Redundancy

The archive node stores its data in a PostgreSQL database that node operators host on a provider of their choice, including self-hosting. For redundancy, archive node data can also be stored to an object storage like Google Cloud Storage; soon S3 and others) or to a mina.log file that reside on your computer or be streamed to any typical logging service, for example, LogDNA.

Archive data is critical for applications that require historical lookup.

On the protocol side, archive data is important for disaster recovery to reconstruct a certain state. A single archive node set up might not be sufficient.

If the daemon that sends blocks to the archive process or if the archive process itself fails for some reason, there can be missing blocks in the database. To minimize the risk of archive data loss, employ the redundancy techniques described on this page; to detect and repair gaps once they have already formed, see Backfilling Missing Blocks.

A single archive node setup has a daemon sending blocks to an archive process that writes them to the database.

To connect multiple daemons to the archive process, specify the address of an archive process in multiple daemons to reduce the dependency on a single daemon to provide blocks to the archive process.

For example, the server port of an archive process is 3086. The daemons can connect to that port using the flag archive-address

mina daemon \
.....
--archive-address <Ip-address>:3086\

Similarly, it is possible to have multiple archive processes write to the same database. In this case, the postgres-uri passed to the archive process is the same across multiple archive processes.

However, multiple archive processes concurrently writing to a database could cause data inconsistencies (explained in https://github.com/MinaProtocol/mina/issues/7567). To avoid this, set the transaction isolation level of the archive database to Serializable with the following query:

ALTER DATABASE <DATABASE NAME> SET DEFAULT_TRANSACTION_ISOLATION TO SERIALIZABLE ;

Set the transaction level after you create the database and before you connect an archive process to it.

Back up block data

To ensure that archive data can be restored, use the following features to back up and restore block data.

A mechanism for logging a high-fidelity machine-readable representation of blocks using JSON includes some opaque information deep within.

These logs are used internally to quickly replay blocks to get to certain chain states for debugging. This information suffices to recreate exact states of the network.

Some of the internal data look like this:

{"data":["Signed_command",{"payload":{"common":{"fee":"100","fee_token":"1","fee_payer_pk":"B62qixbmBBmCmv1xH5SeF1hw6EqwSNVPi9B28epa3phqVMSyuZk9EoH","nonce":"340","valid_until":"4294967295","memo":"E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH"},"body":["Payment",{"source_pk":"B62qixbmBBmCmv1xH5SeF1hw6EqwSNVPi9B28epa3phqVMSyuZk9EoH","receiver_pk":"B62qm2GCuGCEK79mEjeyaeiFoukThuZLJCHGe9HAzuAnfbtS5FHtPnP","token_id":"1","amount":"100000000"}]},"signer":"B62qixbmBBmCmv1xH5SeF1hw6EqwSNVPi9B28epa3phqVMSyuZk9EoH","signature":"7mXGz8Df1gu92HVWGue24wcrGxDWkgQrDK59xQGXc627PKFQvVAPSzZn7JMkHtfdBUXavDHcgLBZy4iR4UmA5seRCPMkFDci"}],"status":["Applied",{"fee_payer_account_creation_fee_paid":null,"receiver_account_creation_fee_paid":null,"created_token":null},{"fee_payer_balance":"31866000100000","source_balance":"31866000100000","receiver_balance":"34099000000"}]}],"coinbase":["One",null],"internal_command_balances":[["Coinbase",{"coinbase_receiver_balance":"75477804514901","fee_transfer_receiver_balance":null}],["Fee_transfer",{"receiver1_balance":"65266376010003","receiver2_balance":"78601129170700"}],["Fee_transfer",{"receiver1_balance":"66001820000000","receiver2_balance":"76870784414900"}],["Fee_transfer",{"receiver1_balance":"71158365898775","receiver2_balance":"59264207944722"}],["Fee_transfer",{"receiver1_balance":"68546088449962","receiver2_balance":"66721919100000"}],["Fee_transfer",{"receiver1_balance":"67700798001000","receiver2_balance":"66372760000000"}],["Fee_transfer",{"receiver1_balance":"85383891400000","receiver2_balance":"107174952265469"}],["Fee_transfer",{"receiver1_balance":"65879310000000","receiver2_balance":"66282230000000"}]]}]},"delta_transition_chain_proof":["jxLZWooV57gKCmanzCHHt1CDbHfUpMu6MkynUdqN9ZkBUJi7B1W",[]]}

This JSON evolves as the format of the block and transaction payloads evolve in the network.

Upload block data to Google Cloud Storage

The daemon generates a file for each block with the name <network-name>-<protocol-state-hash>.json . These files are called precomputed blocks and have all the fields of a block.

To specify a daemon to upload block data to Google Cloud Storage, pass the flag --upload-blocks-to-gcloud.

Set the following environment variables:

  • GCLOUD_KEYFILE: Key file for authentication

  • NETWORK_NAME: Network name to use in the filename to easily distinguish between blocks in different networks (Mainnet and Testnets)

  • GCLOUD_BLOCK_UPLOAD_BUCKET: Google Cloud Storage bucket where the files are uploaded

Save block data from logs

The daemon logs the block data if the flag -log-precomputed-blocks is passed.

The log to look for is Saw block with state hash $state_hash that contains precomputed_block in the metadata and has the block information. These precomputed blocks contain the same information that gets uploaded to Google Cloud Storage.

Generate block data from another archive database

From a fully synced archive database, you can generate block data for each block using the mina-extract-blocks tool.

The mina-extract-blocks tool generates a file for each block with name <protocol-state-hash>.json.

The tool takes an --archive-uri, an --end-state-hash, and an optional --start-state-hash, and writes all the blocks in the chain starting from start-state-hash and ending at end-state-hash (including start and end).

If only the end hash is provided, then the tool generates blocks starting with the unparented block closest to the end block. This would be the genesis block if there are no missing blocks in between. The block data in these files are called extensional blocks. Since these blocks are generated from the database, they have only the data stored in the archive database and do not contain any other information pertaining to a block (for example, blockchain SNARK) like the precomputed blocks and can only be used to restore blocks in the archive database.

Provide the flag --all-blocks to write out all blocks contained in the database.

Back up the archive database

In addition to retaining per-block files, you can snapshot the entire PostgreSQL archive database. A database dump is the fastest way to seed a fresh archive without replaying every block.

Use pg_dump to create a snapshot:

pg_dump -U <user> -Fp <database> | gzip > archive-$(date +%F).sql.gz

Schedule this regularly (a nightly cron is typical) and publish the dumps to object storage that your downstream consumers can reach.

o1Labs publishes nightly dumps of its public-network archives to the mina-archive-dumps GCS bucket as a convenience:

# Mainnet
curl -O https://storage.googleapis.com/mina-archive-dumps/mainnet-archive-dump-$(date +%F)_0000.sql.tar.gz
# Devnet — swap "mainnet" for "devnet".
Mirror the dumps you depend on

Hosting your own dumps (and your own per-block files) is what keeps the network from converging on a single provider. The o1Labs bucket is a convenience, not a guarantee.

Restoring missing blocks

Detecting gaps and restoring blocks from the backups described above is covered on a dedicated page: Backfilling Missing Blocks. That guide walks through mina-missing-blocks-auditor, mina-archive-blocks, and the missing-blocks guardian script.

Staking ledgers

Staking ledgers are used to determine slot winners for each epoch. Mina daemon stores staking ledger for the current and the next epoch after it is finalized. When transitioning to a new epoch, the "next" staking ledger from the previous epoch is used to determine slot winners of the new epoch and a new "next" staking ledger is chosen. Since staking ledgers for older epochs are no longer accessible, you can still keep them around for reporting or other purposes.

Export these ledgers using the mina cli command:

mina ledger export [current-staged-ledger|staking-epoch-ledger|next-epoch-ledger]

Epoch ledger transition happens once every 14 days (given slot-time = 3mins and slots-per-epoch = 7140).

The window to backup a staking ledger is ~27 days considering "next" staking ledger is finalized after k (currently 290) blocks in the current epoch and therefore is available for the rest of the current epoch and the entire next epoch.