diff --git a/orchestration/runtime/Dockerfile b/orchestration/runtime/Dockerfile index 21da0a75..2801f070 100644 --- a/orchestration/runtime/Dockerfile +++ b/orchestration/runtime/Dockerfile @@ -1,17 +1,20 @@ -FROM --platform=linux/amd64 rust:1.77.0-slim-bookworm as builder +# rust:1.77.0-slim-bookworm as of March 22nd, 2024 (GMT) +FROM --platform=linux/amd64 rust@sha256:e785e4aa81f87bc1ee02fa2026ffbc491e0410bdaf6652cea74884373f452664 as deterministic # Move to a Debian package snapshot RUN rm -rf /etc/apt/sources.list.d/debian.sources && \ rm -rf /var/lib/apt/lists/* && \ - echo "deb [arch=amd64] http://snapshot.debian.org/archive/debian/20240201T000000Z bookworm main" > /etc/apt/sources.list && \ + echo "deb [arch=amd64] http://snapshot.debian.org/archive/debian/20240301T000000Z bookworm main" > /etc/apt/sources.list && \ apt update # Install dependencies -RUN apt install clang -y +RUN apt update && apt upgrade && apt install clang -y # Add the wasm toolchain RUN rustup target add wasm32-unknown-unknown +FROM deterministic + # Add files for build ADD patches /serai/patches ADD common /serai/common @@ -30,3 +33,8 @@ ADD Cargo.lock /serai ADD AGPL-3.0 /serai WORKDIR /serai + +# Build the runtime, copying it to the volume if it exists +CMD cargo build --release -p serai-runtime && \ + mkdir -p /volume && \ + cp /serai/target/release/wbuild/serai-runtime/serai_runtime.wasm /volume/serai.wasm diff --git a/orchestration/src/main.rs b/orchestration/src/main.rs index 0ec5913f..a2533c49 100644 --- a/orchestration/src/main.rs +++ b/orchestration/src/main.rs @@ -325,6 +325,87 @@ fn start(network: Network, services: HashSet) { _ => panic!("starting unrecognized service"), }; + // If we're building the Serai service, first build the runtime + let serai_runtime_volume = format!("serai-{}-runtime-volume", network.label()); + if name == "serai" { + // Check if it's built by checking if the volume has the expected runtime file + let built = || { + if let Ok(path) = Command::new("docker") + .arg("volume") + .arg("inspect") + .arg("-f") + .arg("{{ .Mountpoint }}") + .arg(&serai_runtime_volume) + .output() + { + if let Ok(path) = String::from_utf8(path.stdout) { + if let Ok(iter) = std::fs::read_dir(PathBuf::from(path.trim())) { + for item in iter.flatten() { + if item.file_name() == "serai.wasm" { + return true; + } + } + } + } + } + false + }; + + if !built() { + let mut repo_path = env::current_exe().unwrap(); + repo_path.pop(); + if repo_path.as_path().ends_with("deps") { + repo_path.pop(); + } + assert!(repo_path.as_path().ends_with("debug") || repo_path.as_path().ends_with("release")); + repo_path.pop(); + assert!(repo_path.as_path().ends_with("target")); + repo_path.pop(); + + // Build the image to build the runtime + if !Command::new("docker") + .current_dir(&repo_path) + .arg("build") + .arg("-f") + .arg("orchestration/runtime/Dockerfile") + .arg(".") + .arg("-t") + .arg(format!("serai-{}-runtime-img", network.label())) + .spawn() + .unwrap() + .wait() + .unwrap() + .success() + { + panic!("failed to build runtime image"); + } + + // Run the image, building the runtime + println!("Building the Serai runtime"); + let container_name = format!("serai-{}-runtime", network.label()); + let _ = + Command::new("docker").arg("rm").arg("-f").arg(&container_name).spawn().unwrap().wait(); + let _ = Command::new("docker") + .arg("run") + .arg("--name") + .arg(container_name) + .arg("--volume") + .arg(format!("{serai_runtime_volume}:/volume")) + .arg(format!("serai-{}-runtime-img", network.label())) + .spawn(); + + // Wait until its built + let mut ticks = 0; + while !built() { + std::thread::sleep(core::time::Duration::from_secs(60)); + ticks += 1; + if ticks > 6 * 60 { + panic!("couldn't build the runtime after 6 hours") + } + } + } + } + // Build it println!("Building {service}"); docker::build(&orchestration_path(network), network, name); @@ -367,6 +448,7 @@ fn start(network: Network, services: HashSet) { assert_eq!(network, Network::Dev, "monero-wallet-rpc is only for dev"); command.arg("-p").arg("18082:18082") } + "serai" => command.arg("--volume").arg(format!("{serai_runtime_volume}:/runtime")), _ => command, }; assert!( diff --git a/orchestration/src/serai.rs b/orchestration/src/serai.rs index a3382acb..74fa78e6 100644 --- a/orchestration/src/serai.rs +++ b/orchestration/src/serai.rs @@ -21,7 +21,7 @@ EXPOSE 30333 9615 9933 9944 ADD /orchestration/{}/serai/run.sh / CMD ["/run.sh"] "#, - network.label() + network.label(), ); let run = os(Os::Debian, "", "serai") + &run_serai; diff --git a/substrate/node/src/chain_spec.rs b/substrate/node/src/chain_spec.rs index 042f5178..b630c00b 100644 --- a/substrate/node/src/chain_spec.rs +++ b/substrate/node/src/chain_spec.rs @@ -15,6 +15,14 @@ fn account_from_name(name: &'static str) -> PublicKey { insecure_pair_from_name(name).public() } +fn wasm_binary() -> Vec { + // TODO: Accept a config of runtime path + if let Ok(binary) = std::fs::read("/runtime/serai.wasm") { + return binary; + } + WASM_BINARY.ok_or("compiled in wasm not available").unwrap().to_vec() +} + fn testnet_genesis( wasm_binary: &[u8], validators: &[&'static str], @@ -64,18 +72,18 @@ fn testnet_genesis( } } -pub fn development_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or("Development wasm not available")?; +pub fn development_config() -> ChainSpec { + let wasm_binary = wasm_binary(); - Ok(ChainSpec::from_genesis( + ChainSpec::from_genesis( // Name "Development Network", // ID "devnet", ChainType::Development, - || { + move || { testnet_genesis( - wasm_binary, + &wasm_binary, &["Alice"], vec![ account_from_name("Alice"), @@ -99,21 +107,21 @@ pub fn development_config() -> Result { None, // Extensions None, - )) + ) } -pub fn testnet_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or("Testnet wasm not available")?; +pub fn testnet_config() -> ChainSpec { + let wasm_binary = wasm_binary(); - Ok(ChainSpec::from_genesis( + ChainSpec::from_genesis( // Name "Local Test Network", // ID "local", ChainType::Local, - || { + move || { testnet_genesis( - wasm_binary, + &wasm_binary, &["Alice", "Bob", "Charlie", "Dave"], vec![ account_from_name("Alice"), @@ -137,5 +145,5 @@ pub fn testnet_config() -> Result { None, // Extensions None, - )) + ) } diff --git a/substrate/node/src/command.rs b/substrate/node/src/command.rs index 3588f95f..2f7ea0f7 100644 --- a/substrate/node/src/command.rs +++ b/substrate/node/src/command.rs @@ -39,8 +39,8 @@ impl SubstrateCli for Cli { fn load_spec(&self, id: &str) -> Result, String> { match id { - "dev" | "devnet" => Ok(Box::new(chain_spec::development_config()?)), - "local" => Ok(Box::new(chain_spec::testnet_config()?)), + "dev" | "devnet" => Ok(Box::new(chain_spec::development_config())), + "local" => Ok(Box::new(chain_spec::testnet_config())), _ => panic!("Unknown network ID"), } } diff --git a/substrate/signals/pallet/src/lib.rs b/substrate/signals/pallet/src/lib.rs index 3fad27c9..54d6086a 100644 --- a/substrate/signals/pallet/src/lib.rs +++ b/substrate/signals/pallet/src/lib.rs @@ -142,6 +142,7 @@ pub mod pallet { } // 80% threshold + // TODO: Use 34% for halting a set (not 80%) const REQUIREMENT_NUMERATOR: u64 = 4; const REQUIREMENT_DIVISOR: u64 = 5;