Skip to content

RISC-V no-panic (part 2) #2176

RISC-V no-panic (part 2)

RISC-V no-panic (part 2) #2176

Workflow file for this run

name: Rust
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
concurrency:
group: rust-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# Not needed in CI, should make things a bit faster
CARGO_INCREMENTAL: 0
CARGO_TERM_COLOR: always
CARGO_BUILD_WARNINGS: deny
# Build smaller artifacts to avoid running out of space in CI and make it a bit faster.
# TODO: `-Znext-solver=globally` is a requirement for `generic_const_args`, `-Zmin-recursion-limit=256` is in turn
# needed for some crates due to `-Znext-solver=globally`
RUSTFLAGS: -Znext-solver=globally -Zmin-recursion-limit=256 -C strip=symbols
# Needed for things like file system access
# TODO: `-Znext-solver=globally` is a requirement for `generic_const_args`, `-Zmin-recursion-limit=256` is in turn
# needed for some crates due to `-Znext-solver=globally`
MIRIFLAGS: -Znext-solver=globally -Zmin-recursion-limit=256 -Zmiri-disable-isolation
RUST_BACKTRACE: full
jobs:
cargo-fmt:
runs-on: ubuntu-26.04
# Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead
needs: act4
steps:
- &checkout-step
name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0
- &cache-restore-step
name: Restore cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
with:
path: &cache-path |
~/.cargo/registry
~/.cargo/git
${{ env.RUST_GPU_CACHE }}
key: &cache-key ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-cargo-
- name: cargo fmt
run: cargo fmt --all -- --check
cargo-clippy:
strategy:
matrix:
os:
- ubuntu-26.04
- ubuntu-26.04-arm
- macos-26
- windows-11-vs2026-arm
- windows-2025
type:
- together
- individually
- features
target:
- ""
include:
- os: ubuntu-26.04
type: together
target: riscv64gc-unknown-linux-gnu
- os: ubuntu-26.04
type: features
target: riscv64gc-unknown-linux-gnu
- os: ubuntu-26.04
type: individually
target: riscv64gc-unknown-linux-gnu
- os: ubuntu-26.04
type: together
target: riscv64a23-unknown-linux-gnu
- os: ubuntu-26.04
type: features
target: riscv64a23-unknown-linux-gnu
- os: ubuntu-26.04
type: individually
target: riscv64a23-unknown-linux-gnu
fail-fast: false
runs-on: ${{ matrix.os }}
env:
# TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally`
command: >-
cargo -Zgitoxide -Zgit clippy
${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }}
-Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]'
# Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead
needs: act4
steps:
- &rust-gpu-cache
name: Set rust-gpu cache path
shell: bash
run: |
if [[ "$RUNNER_OS" == "macOS" ]]; then
echo "RUST_GPU_CACHE=$HOME/Library/Caches/rust-gpu" >> "$GITHUB_ENV"
elif [[ "$RUNNER_OS" == "Windows" ]]; then
echo "RUST_GPU_CACHE=$LOCALAPPDATA/rust-gpu" >> "$GITHUB_ENV"
else
echo "RUST_GPU_CACHE=$HOME/.cache/rust-gpu" >> "$GITHUB_ENV"
fi
- &shared-test-steps
parallel:
- *checkout-step
- *cache-restore-step
- name: RISC-V support
run: |
echo "command=${command} -Zbuild-std" >> $GITHUB_ENV
echo "RUSTFLAGS=${RUSTFLAGS} -C linker=riscv64-linux-gnu-gcc" >> $GITHUB_ENV
# TODO: Temporary workaround for https://github.com/rust-lang/cc-rs/issues/1654
echo "CC_riscv64_unknown_none_abundance=riscv64-linux-gnu-gcc" >> $GITHUB_ENV
echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV
sudo apt-get update
sudo apt-get install --no-install-recommends --yes g++-riscv64-linux-gnu
if: contains(matrix.target, 'riscv64')
- uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1
if: runner.os == 'Linux' && matrix.target == ''
# Use Mold linker for shorter build times
- name: Use Mold linker on Linux
shell: bash
run: |
echo "RUSTFLAGS=${RUSTFLAGS} -C link-arg=-fuse-ld=mold" >> $GITHUB_ENV
if: runner.os == 'Linux' && matrix.target == ''
# Use LLD linker for shorter build times
- name: Use LLD linker on Windows
shell: bash
run: |
echo "RUSTFLAGS=${RUSTFLAGS} -C linker=rust-lld" >> $GITHUB_ENV
if: runner.os == 'Windows'
- name: cargo clippy
run: |
${{ env.command }} --locked --all-targets
if: matrix.type == 'together'
- name: cargo clippy (each crate individually)
shell: bash
run: |
cargo metadata --no-deps --format-version 1 \
| jq -r '.packages[].name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --all-targets --package \"${crate}\"" >> "${{ runner.temp }}/queue"
done
if: matrix.type == 'individually'
- name: cargo clippy (various features)
shell: bash
run: |
metadata=$(cargo metadata --no-deps --format-version 1)
for feature in alloc parallel scale-codec serde; do
printf '%s' "$metadata" \
| jq -r --arg feature "$feature" '.packages[] | select(
(.features | has($feature)) and
(.manifest_path | gsub("\\\\"; "/") | test("/crates/(execution|farmer|node|shared)/"))
) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`${feature}\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --all-targets --package \"${crate}\" --features \"${crate}/${feature}\"" >> "${{ runner.temp }}/queue"
done
done
# Ensure `clippy` is happy with `guest` feature
printf '%s' "$metadata" \
| jq -r '.packages[] | select(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/")) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --all-targets --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue"
done
printf '%s' "$metadata" \
| jq -r '.packages[] | select(
(.features | has("guest")) and
(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/core/"))
) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --all-targets --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue"
done
echo "Checking \`executor\` in \`ab-contracts-common\`" >> "${{ runner.temp }}/queue"
echo "$command --all-targets --package ab-contracts-common --features ab-contracts-common/executor" >> "${{ runner.temp }}/queue"
echo "Checking \`payload-builder\` in \`ab-system-contract-simple-wallet-base\`" >> "${{ runner.temp }}/queue"
echo "$command --all-targets --package ab-system-contract-simple-wallet-base --features ab-system-contract-simple-wallet-base/payload-builder" >> "${{ runner.temp }}/queue"
if: matrix.type == 'features'
- parallel:
- shell: bash
env:
WORKER_ID: "1"
run: &parallel_worker |
set -e
mkdir -p "${{ runner.temp }}/claims"
export CARGO_TARGET_DIR="target_${WORKER_ID}"
while IFS= read -r message && IFS= read -r command; do
hash=$(printf '%s\n%s\n' "$message" "$command" | sha256sum | cut -d' ' -f1)
if mkdir "${{ runner.temp }}/claims/${hash}" 2>/dev/null; then
echo "$message"
eval "$command"
fi
done < "${{ runner.temp }}/queue"
if: matrix.type == 'individually' || matrix.type == 'features'
- shell: bash
env:
WORKER_ID: "2"
run: *parallel_worker
if: matrix.type == 'individually' || matrix.type == 'features'
- shell: bash
env:
WORKER_ID: "3"
run: *parallel_worker
if: matrix.type == 'individually' || matrix.type == 'features'
- shell: bash
env:
WORKER_ID: "4"
run: *parallel_worker
if: matrix.type == 'individually' || matrix.type == 'features'
- name: Save cache
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
with:
path: *cache-path
key: *cache-key
# Only save on `main` branch and only from one canonical matrix variant per OS
if: github.ref == 'refs/heads/main' && matrix.type == 'together' && matrix.target == ''
cargo-test:
strategy:
matrix:
os:
- ubuntu-26.04
- ubuntu-26.04-arm
- macos-26
- windows-11-vs2026-arm
- windows-2025
miri:
- true
- false
target:
- ""
type:
# `together` variant is running in a separate job, see `cargo-test-slow`
# - together
- features
# Only a few select targets are explicitly included with `guest-feature` below
# - guest-feature
# Test a few RISC-V targets (both Linux and one used for contracts) using Miri
include:
- os: ubuntu-26.04
miri: true
type: guest-feature
target: ""
- os: ubuntu-26.04
miri: false
type: guest-feature
target: ""
- os: ubuntu-26.04
miri: true
type: features
target: riscv64a23-unknown-linux-gnu
- os: ubuntu-26.04
miri: true
type: guest-feature
target: riscv64a23-unknown-linux-gnu
# TODO: Tests will need custom allocator to work under this target
# - os: ubuntu-26.04
# miri: true
# type: guest-feature
# target: crates/contracts/core/ab-contracts-tooling/src/riscv64-unknown-none-abundance.json
fail-fast: false
runs-on: ${{ matrix.os }}
# Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead
needs: act4
env:
# NOTE: Running tests under Miri with nextest is SLOWER than without it:
# https://github.com/nextest-rs/nextest/issues/27#issuecomment-4187292411
# TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally`
command: >-
${{ matrix.miri == true && 'miri test --bins --lib --tests' || 'nextest run --no-tests pass' }}
${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }}
-Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]'
steps: &test-steps
- name: Install cargo-nextest
uses: taiki-e/install-action@a6b2e2dcd845ddd7f509ce4f3ed3d922b80cc5d9 # 2.84.0
with:
tool: cargo-nextest
- *rust-gpu-cache
- *shared-test-steps
- name: Install Vulkan runtime (Linux)
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends --yes mesa-vulkan-drivers
if: matrix.miri == false && runner.os == 'Linux' && matrix.type == 'together'
- name: Install Vulkan runtime (Windows)
run: |
& {
# See https://vulkan.lunarg.com/sdk/home for updates
$sdkVersion = "1.4.350.0"
if ("${{ runner.arch }}" -eq "ARM64") {
$arch = "ARM64"
$urlPath = "warm"
$expectedHash = "1845c336ca17180ca4e4f6f52542ff9998aa39a76bc6ed75961eef67aad1a811"
} else {
$arch = "X64"
$urlPath = "windows"
$expectedHash = "23ce69f32cef3e2799617e2b1776cd0c71030d23a91f8375821cc40d76b185b9"
}
$url = "https://sdk.lunarg.com/sdk/download/$sdkVersion/$urlPath/VulkanRT-$arch-$sdkVersion-Components.zip"
Invoke-WebRequest -Uri $url -OutFile VulkanRT.zip
$actualHash = (Get-FileHash VulkanRT.zip -Algorithm SHA256).Hash.ToLower()
if ($actualHash -ne $expectedHash.ToLower()) {
throw "SHA256 mismatch: expected $expectedHash, got $actualHash"
}
7z e VulkanRT.zip -o"${{ runner.temp }}\vulkan" -r "vulkan-1.*"
Remove-Item VulkanRT.zip
$vulkanPath = "${{ runner.temp }}\vulkan"
"$vulkanPath" >> $env:GITHUB_PATH
}
& {
# See https://github.com/mmozeiko/build-mesa/releases for updates
$version = "26.1.4"
if ("${{ runner.arch }}" -eq "ARM64") {
$arch = "arm64"
$archJson = "aarch64"
$expectedHash = "b77f5ea72e442c5424b85eb55199bf9bf9404809e1d832ed88183b36cf3a440e"
} else {
$arch = "x64"
$archJson = "x86_64"
$expectedHash = "18f2c6a3eb2f36b669d68b7a888647aeaf0c007a61487f8cf606df4365126e85"
}
$url = "https://github.com/mmozeiko/build-mesa/releases/download/$version/mesa-lavapipe-$arch-$version.7z"
Invoke-WebRequest -Uri $url -OutFile mesa-lavapipe.7z
$actualHash = (Get-FileHash mesa-lavapipe.7z -Algorithm SHA256).Hash.ToLower()
if ($actualHash -ne $expectedHash.ToLower()) {
throw "SHA256 mismatch: expected $expectedHash, got $actualHash"
}
7z x mesa-lavapipe.7z -o"${{ runner.temp }}\mesa"
Remove-Item mesa-lavapipe.7z
$mesaPath = "${{ runner.temp }}\mesa"
"$mesaPath" >> $env:GITHUB_PATH
$icdPath = "$mesaPath\lvp_icd.$archJson.json"
New-Item -Path "HKLM:\SOFTWARE\Khronos\Vulkan\Drivers" -Force| Out-Null
Set-ItemProperty -Path "HKLM:\SOFTWARE\Khronos\Vulkan\Drivers" -Name $icdPath -Value 0 -Type DWord
}
if: matrix.miri == false && runner.os == 'Windows' && matrix.type == 'together'
- name: cargo ${{ env.command }} (together)
run: |
cargo -Zgitoxide -Zgit ${{ env.command }} --locked
if: matrix.miri == false && matrix.type == 'together'
# The whole app is only needed on Windows since it doesn't support shebang
- name: Install Miri capture wrapper
shell: bash
run: |
miri_bin="$(rustup which miri)"
miri_dir="$(dirname "$miri_bin")"
mv "$miri_bin" "$miri_dir/miri.real"
mkdir -p "${{ runner.temp }}/miri_invocations"
cat > "${{ runner.temp }}/miri_wrapper.rs" << 'RUST_EOF'
use std::fmt::Write as FmtWrite;
use std::io::Write;
use std::{env, fs, iter, process};
fn main() {
let exe = env::current_exe().unwrap();
let miri_real = exe.parent().unwrap().join("miri.real");
if env::var_os("MIRI_BE_RUSTC").is_some() {
let status = process::Command::new(&miri_real)
.args(env::args_os().skip(1))
.status()
.expect("failed to exec miri.real");
process::exit(status.code().unwrap_or(1));
}
if let Ok(inv_path) = env::var("MIRI_REPLAY") {
let data = fs::read_to_string(&inv_path).unwrap();
let mut lines = data.lines();
let cwd = lines.next().unwrap();
let n_args = lines.next().unwrap().parse().unwrap();
let args = iter::repeat_with(|| lines.next().unwrap())
.take(n_args)
.collect::<Vec<_>>();
let n_envs = lines.next().unwrap().parse().unwrap();
let envs = iter::repeat_with(|| lines.next().unwrap().split_once('=').unwrap())
.take(n_envs)
.collect::<Vec<_>>();
let status = process::Command::new(&miri_real)
.args(&args)
.env_clear()
.envs(envs)
.current_dir(cwd)
.status()
.expect("failed to exec miri.real in replay");
process::exit(status.code().unwrap_or(1));
}
let args = env::args().skip(1).collect::<Vec<_>>();
let crate_name = args
.windows(2)
.find_map(|w| (w[0] == "--crate-name").then(|| w[1].as_str()))
.unwrap_or("unknown");
let inv_dir = env::var("MIRI_INVOCATIONS_DIR").expect("MIRI_INVOCATIONS_DIR not set");
let queue_path = env::var("MIRI_QUEUE_PATH").expect("MIRI_QUEUE_PATH not set");
let inv_path = format!("{inv_dir}/{crate_name}-{}", process::id());
let cwd = env::current_dir().unwrap();
let envs = env::vars().collect::<Vec<_>>();
let mut data = String::new();
writeln!(data, "{}", cwd.display()).unwrap();
writeln!(data, "{}", args.len()).unwrap();
for arg in &args {
writeln!(data, "{arg}").unwrap();
}
writeln!(data, "{}", envs.len()).unwrap();
for (k, v) in &envs {
writeln!(data, "{k}={v}").unwrap();
}
fs::write(&inv_path, &data).unwrap();
let mut queue = fs::OpenOptions::new()
.create(true)
.append(true)
.open(&queue_path)
.unwrap();
let name = std::path::Path::new(&inv_path)
.file_name()
.unwrap()
.to_string_lossy();
writeln!(queue, "Miri: running {name}").unwrap();
writeln!(queue, "MIRI_REPLAY=\"{inv_path}\" \"{}\"", exe.display()).unwrap();
}
RUST_EOF
rustc "${{ runner.temp }}/miri_wrapper.rs" -o "$miri_bin"
if: matrix.miri == true && matrix.type == 'together'
- name: cargo ${{ env.command }} --capture (together)
shell: bash
env:
MIRI_INVOCATIONS_DIR: ${{ runner.temp }}/miri_invocations
MIRI_QUEUE_PATH: ${{ runner.temp }}/queue
run: |
cargo -Zgitoxide -Zgit ${{ env.command }} --locked
if: matrix.miri == true && matrix.type == 'together'
- name: cargo test (various features)
shell: bash
run: |
metadata=$(cargo metadata --no-deps --format-version 1)
for feature in alloc parallel scale-codec serde; do
printf '%s' "$metadata" \
| jq -r --arg feature "$feature" '.packages[] | select(
(.features | has($feature)) and
(.manifest_path | gsub("\\\\"; "/") | test("/crates/(execution|farmer|node|shared)/"))
) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Testing \`${feature}\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "cargo -Zgitoxide -Zgit $command --package \"${crate}\" --features \"${crate}/${feature}\"" >> "${{ runner.temp }}/queue"
done
done
echo "Checking \`executor\` in \`ab-contracts-common\`" >> "${{ runner.temp }}/queue"
echo "cargo -Zgitoxide -Zgit $command --package ab-contracts-common --features ab-contracts-common/executor" >> "${{ runner.temp }}/queue"
echo "Testing \`payload-builder\` in \`ab-system-contract-simple-wallet-base\`" >> "${{ runner.temp }}/queue"
echo "cargo -Zgitoxide -Zgit $command --package ab-system-contract-simple-wallet-base --features ab-system-contract-simple-wallet-base/payload-builder" >> "${{ runner.temp }}/queue"
if: matrix.type == 'features'
- name: cargo ${{ env.command }} (guest feature)
shell: bash
run: |
metadata=$(cargo metadata --no-deps --format-version 1)
# Ensure tests pass with `guest` feature
printf '%s' "$metadata" \
| jq -r '.packages[] | select(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/")) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Testing \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "cargo -Zgitoxide -Zgit $command --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue"
done
printf '%s' "$metadata" \
| jq -r '.packages[] | select(
(.features | has("guest")) and
(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/core/"))
) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Testing \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "cargo -Zgitoxide -Zgit $command --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue"
done
if: matrix.type == 'guest-feature' && runner.os == 'Linux'
- parallel:
- shell: bash
env:
WORKER_ID: "1"
run: *parallel_worker
if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature'
- shell: bash
env:
WORKER_ID: "2"
run: *parallel_worker
if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature'
- shell: bash
env:
WORKER_ID: "3"
run: *parallel_worker
if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature'
- shell: bash
env:
WORKER_ID: "4"
run: *parallel_worker
if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature'
# This is a hack to start slower jobs before the rest, see `cargo-test` job for full definition
cargo-test-slow:
strategy:
matrix:
os:
- ubuntu-26.04
- ubuntu-26.04-arm
- macos-26
- windows-11-vs2026-arm
- windows-2025
miri:
- true
- false
target:
- ""
type:
- together
# - features
# - guest-feature
include:
- os: ubuntu-26.04
miri: true
type: together
target: riscv64a23-unknown-linux-gnu
fail-fast: false
runs-on: ${{ matrix.os }}
env:
# NOTE: Running tests under Miri with nextest is SLOWER than without it:
# https://github.com/nextest-rs/nextest/issues/27#issuecomment-4187292411
# TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally`
command: >-
${{ matrix.miri == true && 'miri test --bins --lib --tests' || 'nextest run --no-tests pass' }}
${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }}
-Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]'
steps: *test-steps
no-panic:
strategy:
matrix:
os:
- ubuntu-26.04
- ubuntu-26.04-arm
- macos-26
- windows-11-vs2026-arm
- windows-2025
type:
- default
- features
# Only one target is explicitly included with `guest-feature` below
# - guest-feature
target:
- ""
include:
- os: ubuntu-26.04
type: default
target: riscv64a23-unknown-linux-gnu
- os: ubuntu-26.04
type: features
target: riscv64a23-unknown-linux-gnu
# TODO: Tests will need custom allocator to work under this target
# - os: ubuntu-26.04
# type: guest-feature
# target: crates/contracts/core/ab-contracts-tooling/src/riscv64-unknown-none-abundance.json
fail-fast: false
runs-on: ${{ matrix.os }}
env:
# TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally`
command: >-
cargo -Zgitoxide -Zgit build
${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }}
-Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]'
steps:
- *rust-gpu-cache
- *shared-test-steps
# Increase the inlining threshold to make sure the compiler can see that some functions do not panic.
# Native CPU and LTO to allow the compiler to apply more optimizations and prove lack of panics in more cases.
- name: Set up RUSTFLAGS
shell: bash
run: |
echo "RUSTFLAGS=${RUSTFLAGS} -Cllvm-args=--inline-threshold=10000 -C embed-bitcode -C lto -Z dylib-lto ${{ matrix.target == '' && '-C target-cpu=native' || '' }}" >> $GITHUB_ENV
- name: Ensure no panics in annotated code
shell: bash
run: |
# TODO: This doesn't seem to work for now: https://users.rust-lang.org/t/compiler-optimizations-are-different-for-crate-itself-vs-dependency/130187?u=nazar-pc
# So we end up building individual crates instead, which is unfortunate
# ${{ env.command }} --release --all-targets --features no-panic
cargo metadata --no-deps --format-version 1 \
| jq -r '.packages[] | select(.features | has("no-panic")) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --release --all-targets --features no-panic --package \"${crate}\"" >> "${{ runner.temp }}/queue"
done
if: matrix.type == 'default'
- name: Ensure no panics in annotated code (various features)
shell: bash
run: |
cargo metadata --no-deps --format-version 1 \
| jq -r '.packages[] | select(
(.features | has("no-panic")) and
(.features | has("alloc"))
) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`alloc\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --release --all-targets --features no-panic --package \"${crate}\" --features \"${crate}/alloc\"" >> "${{ runner.temp }}/queue"
done
echo "Checking \`executor\` in \`ab-contracts-common\`" >> "${{ runner.temp }}/queue"
echo "$command --release --all-targets --features no-panic --package ab-contracts-common --features ab-contracts-common/executor" >> "${{ runner.temp }}/queue"
echo "Checking \`payload-builder\` in \`ab-system-contract-simple-wallet-base\`" >> "${{ runner.temp }}/queue"
echo "$command --release --all-targets --features no-panic --package ab-system-contract-simple-wallet-base --features ab-system-contract-simple-wallet-base/payload-builder" >> "${{ runner.temp }}/queue"
if: matrix.type == 'features'
- name: Ensure no panics in annotated code (guest feature)
shell: bash
run: |
metadata=$(cargo metadata --no-deps --format-version 1)
# Not all example/system contracts have the `no-panic` feature yet
printf '%s' "$metadata" \
| jq -r '.packages[] | select(
(.features | has("no-panic")) and
(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/"))
) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --release --all-targets --features no-panic --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue"
done
# Not all core contracts have both `no-panic` and `guest` features yet
printf '%s' "$metadata" \
| jq -r '.packages[] | select(
(.features | has("no-panic")) and
(.features | has("guest")) and
(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/core/"))
) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue"
echo "$command --release --all-targets --features no-panic --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue"
done
if: matrix.type == 'guest-feature' && runner.os == 'Linux'
- &parallel-workers
parallel:
- shell: bash
env:
WORKER_ID: "1"
run: *parallel_worker
- shell: bash
env:
WORKER_ID: "2"
run: *parallel_worker
- shell: bash
env:
WORKER_ID: "3"
run: *parallel_worker
- shell: bash
env:
WORKER_ID: "4"
run: *parallel_worker
contracts:
runs-on: ubuntu-26.04
# Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead
needs: act4
steps:
- *checkout-step
- *cache-restore-step
# TODO: Run clippy and `no-panic` on custom target for contracts too
- name: Ensure contracts compile for a custom target
shell: bash
run: |
cargo metadata --no-deps --format-version 1 \
| jq -r '.packages[] | select(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/")) | .name' \
| tr -d '\r' \
| while IFS= read -r crate; do
echo "Checking \`${crate}\` (release profile)" >> "${{ runner.temp }}/queue"
echo "cargo run --bin cargo-ab-contract -- build --package \"${crate}\"" >> "${{ runner.temp }}/queue"
echo "Checking \`${crate}\` (contract profile)" >> "${{ runner.temp }}/queue"
echo "cargo run --bin cargo-ab-contract -- build --package \"${crate}\" --profile contract" >> "${{ runner.temp }}/queue"
done
- *parallel-workers
act4:
env:
ACT4_REPO_REVISION: df4516ebd254eccbefdb5e395ac04f158390e178
strategy:
matrix:
os:
- ubuntu-26.04
- ubuntu-26.04-arm
runs-on: ${{ matrix.os }}
steps:
- *checkout-step
# Compute the hashes of inputs to avoid cloning and building ELFs when nothing has changed, see:
# https://github.com/riscv/riscv-arch-test/issues/1654
- name: Compute sources hash
id: sources-hash
working-directory: crates/execution/ab-riscv-act4-runner
run: |
RES_HASH=$(find res -type f -print0 \
| sort -z \
| xargs -0 sha256sum \
| sha256sum \
| cut -d' ' -f1)
COMBINED=$(printf '%s\n%s\n' "${ACT4_REPO_REVISION}" "${RES_HASH}" \
| sha256sum \
| cut -d' ' -f1)
echo "value=${COMBINED}" >> $GITHUB_OUTPUT
- name: Restore cache
id: restore-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
with:
path: &act4-work-cache-path |
crates/execution/ab-riscv-act4-runner/work/*/elfs
key: &act4-work-cache-key act4-work-${{ steps.sources-hash.outputs.value }}
# TODO: Workaround for https://github.com/rust-lang/rustup/issues/988
- name: Download toolchain
run: rustc --version
- name: Clone ACT4 repo
run: |
git clone --depth 1 --revision="${ACT4_REPO_REVISION}" \
https://github.com/riscv/riscv-arch-test \
riscv-arch-test
if: steps.restore-cache.outputs.cache-hit != 'true'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # 4.2.0
if: steps.restore-cache.outputs.cache-hit != 'true'
# TODO: Use official image once it is available: https://github.com/riscv/riscv-arch-test/pull/1161
- name: Build act4-build image
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # 7.3.0
with:
context: riscv-arch-test
file: crates/execution/ab-riscv-act4-runner/res/Dockerfile
cache-from: type=gha,scope=act4-build-${{ runner.arch }}
# Only save on `main` branch
cache-to: ${{ github.ref == 'refs/heads/main' && format('type=gha,mode=min,scope=act4-build-{0}', runner.arch) || '' }}
load: true
if: steps.restore-cache.outputs.cache-hit != 'true'
- name: Build ELFs
working-directory: crates/execution/ab-riscv-act4-runner
run: |
mkdir -p work
docker run --rm \
-u $(id -u):$(id -g) \
-v ./work:/act4/work \
-v ./res/abundance:/act4/config/abundance:ro \
-e CONFIG_FILES="config/abundance/abundance-rv32i-max/test_config.yaml config/abundance/abundance-rv64i-max/test_config.yaml" \
-e FAST=True \
${{ steps.build.outputs.imageid }} \
make
if: steps.restore-cache.outputs.cache-hit != 'true'
- parallel:
- name: ACT4 tests RV32
working-directory: crates/execution/ab-riscv-act4-runner
env:
CARGO_TARGET_DIR: target_rv32
run: |
cargo run --release -- rv32 work/abundance-rv32i-max/elfs
- name: ACT4 tests RV64 (native)
working-directory: crates/execution/ab-riscv-act4-runner
env:
CARGO_TARGET_DIR: target_rv64_native
# Native CPU will use AES intrinsics for Zknd/Zkne implementation
run: |
RUSTFLAGS="$RUSTFLAGS -C target-cpu=native" cargo run --release -- rv64 work/abundance-rv64i-max/elfs
# Force-disable AES intrinsics to test software implementation
- name: ACT4 tests RV64 (-aes)
working-directory: crates/execution/ab-riscv-act4-runner
env:
CARGO_TARGET_DIR: target_rv64_aes_disabled
run: |
RUSTFLAGS="$RUSTFLAGS -C target-feature=-aes" cargo run --release -- rv64 work/abundance-rv64i-max/elfs
- name: Save cache
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0
with:
path: *act4-work-cache-path
key: *act4-work-cache-key
# Only save on `main` branch and only from one OS since the contents will be identical for all OSes
if: steps.restore-cache.outputs.cache-hit != 'true' && github.ref == 'refs/heads/main' && matrix.os == 'ubuntu-26.04'
rust-all:
# Hack for buggy GitHub Actions behavior with skipped checks: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
if: ${{ always() }}
runs-on: ubuntu-slim
needs:
- cargo-clippy
- cargo-fmt
- cargo-test
- cargo-test-slow
- contracts
- no-panic
- act4
steps:
- name: Job status check
env:
RESULTS: ${{ join(needs.*.result, ' ') }}
run: |
for result in $RESULTS; do
[[ "$result" == "success" ]] || exit 1
done