Skip to content

State Forking

Ken Vu edited this page May 3, 2023 · 4 revisions

Introduction

This document explains how to get the state of our TestNet or MainNet. This can be useful in the following cases:

  • Testing runtime upgrades
  • Debugging issues that are not replicable with the Dev chain spec

The state from our networks come in two formats, as a chain spec or as a state snapshot. Both formats have their uses cases

Usage

Default usage

From root folder execute the following command:

./scripts/state_forking.sh

By default the script will checkout to the latest git tag and get Porcini's state.

Changing target network and tag

Getting Root's state and using a different tag can be done like this:

# We are forking root at the tag v1.32.0
./scripts/state_forking.sh -c root -t v1.32.0

Ignore tag switch

# Now it won't do a tag switch
./scripts/state_forking.sh -i

Help

./scripts/state_forking.sh --help

Explanation

Chain Specification State

The script generates two chain specifications. One untouched but unrunable and one modified but runable.

Original Untouched Chain Specification

This chain specification contains the full and true state of a network. Because it contains all the data, it is virtually unrunable and should not be used unless there is a specific need for it. You can find this file under the name porcini_chain_state.json or root_chain_state.json.

Modified Chain Specification

This chain specification was made by combining the Dev chain specification together with the previous mentioned untouched chain specification. The end result is a fully runable chain that contains most of the data. This chain specification is the one that should be used and you can find it under the name dev_chain_state.json.

Usage

Run the generate node binary together with the generate dev chain specification. Command:

./output/binary --chain ./output/dev_chain_state.json --alice --tmp

State Snapshot

Our tests are running in a controlled environment with mocked data. This is OK for most situations but there are cases where we need use actual data to properly debug an issue. This can be done by using the generated state snapshot together with writing tests inside the remote_tests namespace. This means that you can run and debug any test or arbitrary code with Porcini's or Root's data which is perfect for inspecting babe, staking or grandpa data since these things are not available when the chain spec is used.

Usage

Set the SNAP environment variable to point to the state.top file path and execute the following command:

cargo test --all-features

Currently we only test our migrations with the snapshot but you can easily add your own tests or custom logic by changing the run_migrations test inside the remote_tests namespace.

This is how it looks currently (./runtime/migrations/mod.rs):

#[cfg(all(test, feature = "try-runtime"))]
mod remote_tests {
	use super::*;
	use crate::{migrations::AllMigrations, Block};
	use remote_externalities::{Builder, Mode, OfflineConfig};
	use std::env::var;

	#[tokio::test]
	async fn run_migrations() {
		//std::env::set_var("SNAP", "/full/path/to/snap.top");
		let Some(state_snapshot) = var("SNAP").map(|s| s.into()).ok() else {
			return;
		};
		let mode = Mode::Offline(OfflineConfig { state_snapshot });
		let mut ext = Builder::<Block>::default().mode(mode).build().await.unwrap();
		ext.execute_with(|| {
			AllMigrations::pre_upgrade().unwrap();
			AllMigrations::on_runtime_upgrade();
			AllMigrations::post_upgrade().unwrap();
		});
	}
}

If you don't want to use the environment variable you can uncomment the set_var line and replace the default path with your one. Example:

- //std::env::set_var("SNAP", "/full/path/to/snap.top");
+ std::env::set_var("SNAP", "/home/marko/futureverse/seed/snap.top");