Use the deterministically built wasm

Has the Dockerfile output to a volume. Has the node use the wasm from the
volume, if it exists.
This commit is contained in:
Luke Parker 2024-03-22 02:19:09 -04:00
parent 84cee06ac1
commit fab7a0a7cb
No known key found for this signature in database
6 changed files with 117 additions and 18 deletions

View file

@ -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 # Move to a Debian package snapshot
RUN rm -rf /etc/apt/sources.list.d/debian.sources && \ RUN rm -rf /etc/apt/sources.list.d/debian.sources && \
rm -rf /var/lib/apt/lists/* && \ 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 apt update
# Install dependencies # Install dependencies
RUN apt install clang -y RUN apt update && apt upgrade && apt install clang -y
# Add the wasm toolchain # Add the wasm toolchain
RUN rustup target add wasm32-unknown-unknown RUN rustup target add wasm32-unknown-unknown
FROM deterministic
# Add files for build # Add files for build
ADD patches /serai/patches ADD patches /serai/patches
ADD common /serai/common ADD common /serai/common
@ -30,3 +33,8 @@ ADD Cargo.lock /serai
ADD AGPL-3.0 /serai ADD AGPL-3.0 /serai
WORKDIR /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

View file

@ -325,6 +325,87 @@ fn start(network: Network, services: HashSet<String>) {
_ => panic!("starting unrecognized service"), _ => 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 // Build it
println!("Building {service}"); println!("Building {service}");
docker::build(&orchestration_path(network), network, name); docker::build(&orchestration_path(network), network, name);
@ -367,6 +448,7 @@ fn start(network: Network, services: HashSet<String>) {
assert_eq!(network, Network::Dev, "monero-wallet-rpc is only for dev"); assert_eq!(network, Network::Dev, "monero-wallet-rpc is only for dev");
command.arg("-p").arg("18082:18082") command.arg("-p").arg("18082:18082")
} }
"serai" => command.arg("--volume").arg(format!("{serai_runtime_volume}:/runtime")),
_ => command, _ => command,
}; };
assert!( assert!(

View file

@ -21,7 +21,7 @@ EXPOSE 30333 9615 9933 9944
ADD /orchestration/{}/serai/run.sh / ADD /orchestration/{}/serai/run.sh /
CMD ["/run.sh"] CMD ["/run.sh"]
"#, "#,
network.label() network.label(),
); );
let run = os(Os::Debian, "", "serai") + &run_serai; let run = os(Os::Debian, "", "serai") + &run_serai;

View file

@ -15,6 +15,14 @@ fn account_from_name(name: &'static str) -> PublicKey {
insecure_pair_from_name(name).public() insecure_pair_from_name(name).public()
} }
fn wasm_binary() -> Vec<u8> {
// 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( fn testnet_genesis(
wasm_binary: &[u8], wasm_binary: &[u8],
validators: &[&'static str], validators: &[&'static str],
@ -64,18 +72,18 @@ fn testnet_genesis(
} }
} }
pub fn development_config() -> Result<ChainSpec, &'static str> { pub fn development_config() -> ChainSpec {
let wasm_binary = WASM_BINARY.ok_or("Development wasm not available")?; let wasm_binary = wasm_binary();
Ok(ChainSpec::from_genesis( ChainSpec::from_genesis(
// Name // Name
"Development Network", "Development Network",
// ID // ID
"devnet", "devnet",
ChainType::Development, ChainType::Development,
|| { move || {
testnet_genesis( testnet_genesis(
wasm_binary, &wasm_binary,
&["Alice"], &["Alice"],
vec![ vec![
account_from_name("Alice"), account_from_name("Alice"),
@ -99,21 +107,21 @@ pub fn development_config() -> Result<ChainSpec, &'static str> {
None, None,
// Extensions // Extensions
None, None,
)) )
} }
pub fn testnet_config() -> Result<ChainSpec, &'static str> { pub fn testnet_config() -> ChainSpec {
let wasm_binary = WASM_BINARY.ok_or("Testnet wasm not available")?; let wasm_binary = wasm_binary();
Ok(ChainSpec::from_genesis( ChainSpec::from_genesis(
// Name // Name
"Local Test Network", "Local Test Network",
// ID // ID
"local", "local",
ChainType::Local, ChainType::Local,
|| { move || {
testnet_genesis( testnet_genesis(
wasm_binary, &wasm_binary,
&["Alice", "Bob", "Charlie", "Dave"], &["Alice", "Bob", "Charlie", "Dave"],
vec![ vec![
account_from_name("Alice"), account_from_name("Alice"),
@ -137,5 +145,5 @@ pub fn testnet_config() -> Result<ChainSpec, &'static str> {
None, None,
// Extensions // Extensions
None, None,
)) )
} }

View file

@ -39,8 +39,8 @@ impl SubstrateCli for Cli {
fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> { fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
match id { match id {
"dev" | "devnet" => Ok(Box::new(chain_spec::development_config()?)), "dev" | "devnet" => Ok(Box::new(chain_spec::development_config())),
"local" => Ok(Box::new(chain_spec::testnet_config()?)), "local" => Ok(Box::new(chain_spec::testnet_config())),
_ => panic!("Unknown network ID"), _ => panic!("Unknown network ID"),
} }
} }

View file

@ -142,6 +142,7 @@ pub mod pallet {
} }
// 80% threshold // 80% threshold
// TODO: Use 34% for halting a set (not 80%)
const REQUIREMENT_NUMERATOR: u64 = 4; const REQUIREMENT_NUMERATOR: u64 = 4;
const REQUIREMENT_DIVISOR: u64 = 5; const REQUIREMENT_DIVISOR: u64 = 5;