Skip to content

Commit

Permalink
Added backward and forward compatibility integration tests for forkle…
Browse files Browse the repository at this point in the history
…ss upgrades (#1895)

Closes #1622

The change adds integration tests for the forkless upgrade feature. The
tests verify that it is backward compatible and, where possible, forward
compatible.

The main idea is to use published `fuel-core` crates to produce blocks
with different state transition functions.

Tests are using Ignition testnet chain config as a main source to be
compatible with.


### Before requesting review
- [x] I have reviewed the code myself
  • Loading branch information
xgreenx authored May 29, 2024
1 parent 70dcde6 commit 8eeae5d
Show file tree
Hide file tree
Showing 20 changed files with 1,316 additions and 3 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,12 @@ jobs:
args: -p fuel-core-client --no-default-features
- command: test
args: -p fuel-core-chain-config --no-default-features
- command: test
args: --manifest-path version-compatibility/Cargo.toml --workspace
# Don't split this command; this is a workaround.
# We need to run `cargo check` first to fetch the locked dependencies
# for `fuel-core 0.26.0`(because of the bug with `--offline`
# and `--locked` when we build `fuel-core-wasm-executor 0.26.0`).
- command: check
args: --manifest-path version-compatibility/Cargo.toml --workspace && cargo test --manifest-path version-compatibility/Cargo.toml --workspace
- command: build
args: -p fuel-core-bin --no-default-features --features production

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Version 0.27.0]

### Added

- [#1895](https://github.com/FuelLabs/fuel-core/pull/1895): Added backward and forward compatibility integration tests for forkless upgrades.
- [#1898](https://github.com/FuelLabs/fuel-core/pull/1898): Enforce increasing of the `Executor::VERSION` on each release.

### Changed
Expand Down
1 change: 1 addition & 0 deletions ci_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ cargo sort -w --check &&
source .github/workflows/scripts/verify_openssl.sh &&
cargo clippy -p fuel-core-wasm-executor --target wasm32-unknown-unknown --no-default-features &&
cargo clippy --all-targets --all-features &&
cargo clippy --manifest-path version-compatibility/Cargo.toml --workspace &&
cargo doc --all-features --workspace --no-deps &&
cargo make check --locked &&
FUEL_ALWAYS_USE_WASM=true cargo make check --all-features --locked &&
Expand Down
6 changes: 6 additions & 0 deletions crates/fuel-core/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ pub struct FuelService {
pub bound_address: SocketAddr,
}

impl Drop for FuelService {
fn drop(&mut self) {
self.stop();
}
}

impl FuelService {
/// Creates a `FuelService` instance from service config
#[tracing::instrument(skip_all, fields(name = %config.name))]
Expand Down
15 changes: 14 additions & 1 deletion crates/services/importer/src/importer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub enum Error {
ExecuteGenesis,
#[display(fmt = "The database already contains the data at the height {_0}.")]
NotUnique(BlockHeight),
#[display(fmt = "The previous block processing is not finished yet.")]
PreviousBlockProcessingNotFinished,
#[from]
StorageError(StorageError),
UnsupportedConsensusVariant(String),
Expand Down Expand Up @@ -204,7 +206,18 @@ where

// Await until all receivers of the notification process the result.
if let Some(channel) = previous_block_result {
let _ = channel.await;
const TIMEOUT: u64 = 20;
let result =
tokio::time::timeout(tokio::time::Duration::from_secs(TIMEOUT), channel)
.await;

if result.is_err() {
tracing::error!(
"The previous block processing \
was not finished for {TIMEOUT} seconds."
);
return Err(Error::PreviousBlockProcessingNotFinished)
}
}
let mut guard = self
.database
Expand Down
1 change: 1 addition & 0 deletions tests/tests/node_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ async fn test_peer_info() {
.shared
.config
.p2p
.as_ref()
.unwrap()
.keypair
.public()
Expand Down
1 change: 1 addition & 0 deletions version-compatibility/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ resolver = "2"
members = [
# Add new versions here when testing backwards compatibility between fuel-core-client and fuel-core
"placeholder",
"forkless-upgrade",
]

# exclude previous versions no longer maintained
Expand Down
40 changes: 40 additions & 0 deletions version-compatibility/forkless-upgrade/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
edition = "2021"
license = "BUSL-1.1"
name = "forkless-upgrade"
publish = false
version = "0.0.0"
build = "build.rs"

[dev-dependencies]
anyhow = "1.0"
clap = "4.4"
libp2p = "0.53.2"
hex = "0.4.3"
rand = "0.8"
tempfile = "3.4"
tokio = { version = "1.37.0", features = ["rt-multi-thread"] }

# Neutral deps
fuel-core-trace = { path = "../../crates/trace" }
fuel-crypto = { version = "0.49.0" }
fuel-tx = { version = "0.49.0", features = ["random"] }

# Latest fuel-core
latest-fuel-core-bin = { path = "../../bin/fuel-core", package = "fuel-core-bin", features = [
"parquet",
"p2p",
] }
latest-fuel-core-client = { path = "../../crates/client", package = "fuel-core-client" }
latest-fuel-core-services = { path = "../../crates/services", package = "fuel-core-services" }
latest-fuel-core-upgradable-executor = { path = "../../crates/services/upgradable-executor", package = "fuel-core-upgradable-executor", features = [
"wasm-executor",
] }

# Genesis fuel-core
genesis-fuel-core-bin = { version = "0.26.0", package = "fuel-core-bin", features = [
"parquet",
"p2p",
] }
genesis-fuel-core-client = { version = "0.26.0", package = "fuel-core-client" }
genesis-fuel-core-services = { version = "0.26.0", package = "fuel-core-services" }
15 changes: 15 additions & 0 deletions version-compatibility/forkless-upgrade/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Forkless upgrade tests

This crate tests that state transition functions for all releases of `fuel-core` are backward compatible.

In addition, we also test that releases are forward-compatible unless we introduce a breaking change in the API.

## Adding new test

We need to add a new backward compatibility test for each new release. To add tests, we need to duplicate tests that are using the latest `fuel-core` and replace usage of the latest crate with a new release.

## Forward compatibility

If the forward compatibility test fails after your changes, it usually means that the change breaks a WASM API, and the network first must upgrade the binary before performing an upgrade of the network.

In the case of breaking API, we need to remove old tests(usually, we need to create a new test per each release) and write a new test(only one) to track new forward compatibility.
58 changes: 58 additions & 0 deletions version-compatibility/forkless-upgrade/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use std::{
env,
path::{
Path,
PathBuf,
},
process::Command,
};

fn main() {
// It only forces a rerun of the build when `build.rs` is changed.
println!("cargo:rerun-if-changed=build.rs");
// Because `fuel-core-wasm-executor 0.26.0` is using `--offline` flag,
// we need to download all dependencies before building the WASM executor.
// This is a workaround specifically for `fuel-core 0.26.0`.
// Future version of the `fuel-core` will not use `--offline` flag.
build_fuel_core_26()
}

fn build_fuel_core_26() {
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
let args = vec![
"install".to_owned(),
"fuel-core-wasm-executor".to_owned(),
"--target=wasm32-unknown-unknown".to_owned(),
"--no-default-features".to_owned(),
"--locked".to_owned(),
"--version".to_owned(),
"0.26.0".to_owned(),
];

let mut cargo = Command::new(cargo);
cargo.current_dir(project_root()).args(args);

let output = cargo.output();

match output {
Ok(output) => {
if !output.status.success() {
panic!(
"Got an error status during compiling WASM executor: \n{:#?}",
output
);
}
}
Err(err) => {
panic!("\n{:#?}", err);
}
}
}

fn project_root() -> PathBuf {
Path::new(&env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(1)
.unwrap()
.to_path_buf()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The configuration of the Ignition network

This configuration is copy of [this](https://github.com/FuelLabs/chain-configuration/tree/master/ignition) repository.

With modifications:
- PoA Key: `{"address":"3ba3b213c8d5ec4d4901ac34b0e924d635384a8b0a5df6e116bce9a783da8e02","secret":"e3d6eb39607650e22f0befa26d52e921d2e7924d0e165f38ffa8d9d0ac73de93","type":"block_production"}`
- Privileged Key: `{"address":"f034f7859dbf1d775cba16fc175eef249a045d6484a8b9388c9e280267125b73","secret":"dcbe36d8e890d7489b6e1be442eab98ae2fdbb5c7d77e1f9e1e12a545852304f","type":"block_production"}`
Loading

0 comments on commit 8eeae5d

Please sign in to comment.