From 415c914e4e5e77067cc8d7d478fe84a08ccbeb3f Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 14 Oct 2026 01:54:19 -0400 Subject: [PATCH 1/3] ci: wai-quantum's byte-pinning tests on nine more instruction sets A new workflow runs bit_identity (23 digests), the frame_reference fixtures and the byte-pinning library modules for s390x, sparc64, powerpc64 and powerpc64le, riscv64, armv7, i686, aarch64 and loongarch64 Linux, under user-mode QEMU on the hardware host (i686 runs on the x86_64 host directly). Every leg passed by hand before this commit. Each leg runs one at a time, in a container capped at 6 GiB, with a target directory of its own. armv7's container is pinned to four CPUs: QEMU 5.1's 32-bit ARM guest grows the emulator's own memory with the guest's thread count and passed 6 GiB with twenty. loongarch64 uses cross's development image, pinned by digest. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-quantum-processors.yml | 116 +++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 .github/workflows/wai-quantum-processors.yml diff --git a/.github/workflows/wai-quantum-processors.yml b/.github/workflows/wai-quantum-processors.yml new file mode 100644 index 000000000..160fb152f --- /dev/null +++ b/.github/workflows/wai-quantum-processors.yml @@ -0,0 +1,116 @@ +name: wai-quantum on every processor + +# The crate promises the same bits on every processor. Native legs run elsewhere +# (x86_64 Linux, aarch64 macOS, the arm64 Linux VM, wasm32, wasip2); this +# workflow runs the crate's byte-pinning tests under user-mode QEMU on the +# instruction sets no runner has, the big-endian ones among them: +# +# bit_identity 23 digests across every numeric layer, taken natively and +# confirmed under wasip2; +# frame_reference the noisy-Clifford fixtures written by an independent tool; +# the library the modules that pin exact bytes: the core and its corpora, +# the fabric and its certificates, the strict JSON reader, +# receipts, energy labels and the bill of materials. +# +# These are emulator results: they prove what each target's binary computes +# under QEMU, not what that silicon does. i686 is the exception: cross runs it +# directly on the x86_64 host in 32-bit mode, so that leg is silicon. +# +# Each target builds in a target directory of its own (a build script compiled +# in one cross image fails in another with an older glibc), each container is +# capped at 6 GiB (an uncapped emulated run once took the 15 GiB host down), and +# the legs run one at a time on the hardware host, which has QEMU's binfmt +# handlers and Docker. + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: [main] + paths: + - 'wai/wai-quantum/**' + - 'rust-toolchain.toml' + - '.github/workflows/wai-quantum-processors.yml' + pull_request: + paths: + - 'wai/wai-quantum/**' + - 'rust-toolchain.toml' + - '.github/workflows/wai-quantum-processors.yml' + +jobs: + emulated: + name: emulated · ${{ matrix.target }} + runs-on: linux-hw + timeout-minutes: 120 + strategy: + fail-fast: false + max-parallel: 1 + matrix: + target: + - s390x-unknown-linux-gnu + - sparc64-unknown-linux-gnu + - powerpc64-unknown-linux-gnu + - powerpc64le-unknown-linux-gnu + - riscv64gc-unknown-linux-gnu + - armv7-unknown-linux-gnueabihf + - i686-unknown-linux-gnu + - aarch64-unknown-linux-gnu + - loongarch64-unknown-linux-gnu + env: + CROSS_CONTAINER_OPTS: --memory=6g --memory-swap=6g + steps: + - name: Fetch the source + shell: bash + run: | + set -euo pipefail + want="${{ github.event.pull_request.head.sha || github.sha }}" + rm -rf src && mkdir src && cd src + git init -q + # Fetch the commit itself, not the ref: a later commit on the ref that this + # workflow's paths do not match would otherwise be fetched instead. + git fetch -q --depth 1 http://100.64.224.0:3400/Transaction-Science/open-standards.git "$want" + git checkout -q FETCH_HEAD + got="$(git rev-parse HEAD)" + [ "$got" = "$want" ] || { echo "::error::fetched $got, not $want"; exit 1; } + - name: The byte-pinning tests under QEMU + shell: bash + working-directory: src/wai/wai-quantum + run: | + set -euo pipefail + export PATH="$HOME/.cargo/bin:$PATH" + command -v cross >/dev/null || cargo install cross --locked --version 0.2.5 + t="${{ matrix.target }}" + # QEMU 5.1's 32-bit ARM guest grows its own memory with the guest's thread + # count: with all 20 host CPUs, bit_identity's threaded state-vector run + # took qemu-arm past the 6 GiB cap (a 32-bit guest addresses at most + # 4 GiB, so the excess is the emulator's). Four CPUs keep it under 1 GiB + # and still take the threaded paths. + # cross 0.2.5 ships no LoongArch image; its development image, pinned by + # digest, carries the toolchain and qemu-loongarch64. + if [ "$t" = loongarch64-unknown-linux-gnu ]; then + rustup target add "$t" + export CROSS_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_IMAGE=ghcr.io/cross-rs/loongarch64-unknown-linux-gnu@sha256:8968bd3b2c3b7b6d73c48e10faa605e14b40ce0080579a06402ea9fdfbc2a5c0 + fi + if [ "$t" = armv7-unknown-linux-gnueabihf ]; then + export CROSS_CONTAINER_OPTS="$CROSS_CONTAINER_OPTS --cpuset-cpus=0-3" + fi + case "$t" in + i686-*) how="silicon, x86_64 host in 32-bit mode" ;; + *) how="emulated, qemu-user" ;; + esac + # A target directory per target, kept between runs; cross mounts it. + export CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-cross/$t" + mkdir -p "$CARGO_TARGET_DIR" + run() { + local name="$1"; shift + timeout 2700 nice -n 19 cross test --locked --release -j 6 --target "$t" --features full "$@" 2>&1 | tee "$RUNNER_TEMP/$name.log" + } + run bit_identity --test bit_identity + grep -qE '^test result: ok\. 23 passed; 0 failed' "$RUNNER_TEMP/bit_identity.log" || { echo "::error::$t: the 23 bit-identity digests did not all pass"; exit 1; } + run frame --test frame_reference + grep -qE '^test result: ok\. 2 passed; 0 failed' "$RUNNER_TEMP/frame.log" || { echo "::error::$t: the fixtures did not pass"; exit 1; } + run lib --lib -- quantum:: quantum_fabric:: ijson:: quantum_receipt quantum_energy quantum_qbom merkle + grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/lib.log" || { echo "::error::$t: the byte-pinning library tests did not pass"; exit 1; } + echo "processor: $t ($how) bit_identity 23/23, fixtures 2/2, $(grep -E '^test result' "$RUNNER_TEMP/lib.log" | tail -1)" -- 2.54.0 (Apple Git-157) From 6b1708549c848c6c3abdf35b2401b15135de1ece Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 14 Oct 2026 02:14:36 -0400 Subject: [PATCH 2/3] ci: run each processor leg in its target's image on the container runner The hardware runner's jobs run as a user without Docker, by design, so cross could not start its containers there. Each leg's job container is now that target's cross image, pinned by digest and capped at 6 GiB (armv7 also pinned to four CPUs). The job installs the pinned Rust into the runner's persistent cache, runs cargo directly with the image's linker and QEMU runner, and holds a host-wide lock so legs never overlap. The source comes as the forge's archive of the exact commit, its embedded commit id checked (the images' git is too old to fetch a bare commit). s390x, armv7, i686 and loongarch64 passed this way by hand before the push. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-quantum-processors.yml | 132 +++++++++++-------- 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/.github/workflows/wai-quantum-processors.yml b/.github/workflows/wai-quantum-processors.yml index 160fb152f..2a71696ba 100644 --- a/.github/workflows/wai-quantum-processors.yml +++ b/.github/workflows/wai-quantum-processors.yml @@ -13,14 +13,16 @@ name: wai-quantum on every processor # receipts, energy labels and the bill of materials. # # These are emulator results: they prove what each target's binary computes -# under QEMU, not what that silicon does. i686 is the exception: cross runs it +# under QEMU, not what that silicon does. i686 is the exception: it runs # directly on the x86_64 host in 32-bit mode, so that leg is silicon. # -# Each target builds in a target directory of its own (a build script compiled -# in one cross image fails in another with an older glibc), each container is -# capped at 6 GiB (an uncapped emulated run once took the 15 GiB host down), and -# the legs run one at a time on the hardware host, which has QEMU's binfmt -# handlers and Docker. +# Each leg's job container is that target's cross image (linker, sysroot and +# QEMU, the runner preset in its environment), pinned by digest, capped at +# 6 GiB: an uncapped emulated run once took the 15 GiB host down. The job +# installs the pinned Rust into the runner's persistent cache, builds in a +# target directory of its own there (a build script compiled in one image fails +# in another with an older glibc), and holds a host-wide lock, so one leg runs +# at a time across every run of this workflow. concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -42,75 +44,95 @@ on: jobs: emulated: name: emulated · ${{ matrix.target }} - runs-on: linux-hw + runs-on: linux timeout-minutes: 120 + container: + image: ${{ matrix.image }} + options: --memory=6g --memory-swap=6g ${{ matrix.options }} strategy: fail-fast: false max-parallel: 1 matrix: - target: - - s390x-unknown-linux-gnu - - sparc64-unknown-linux-gnu - - powerpc64-unknown-linux-gnu - - powerpc64le-unknown-linux-gnu - - riscv64gc-unknown-linux-gnu - - armv7-unknown-linux-gnueabihf - - i686-unknown-linux-gnu - - aarch64-unknown-linux-gnu - - loongarch64-unknown-linux-gnu - env: - CROSS_CONTAINER_OPTS: --memory=6g --memory-swap=6g + include: + - target: s390x-unknown-linux-gnu + image: ghcr.io/cross-rs/s390x-unknown-linux-gnu@sha256:0d8edc92c39158abe211fddfc9617bf5b7865c2ae0f58873f9cf0c58bb315271 + how: emulated, qemu-user + - target: sparc64-unknown-linux-gnu + image: ghcr.io/cross-rs/sparc64-unknown-linux-gnu@sha256:2d04e507ee8bbb1ba61d483d22cf5839a0f138678f71facb40a08a9dffecf316 + how: emulated, qemu-user + - target: powerpc64-unknown-linux-gnu + image: ghcr.io/cross-rs/powerpc64-unknown-linux-gnu@sha256:db02ecb2429912a6e939cc3d5cfe00aa20d7423ff484980f1fbfee4a3f2683cf + how: emulated, qemu-user + - target: powerpc64le-unknown-linux-gnu + image: ghcr.io/cross-rs/powerpc64le-unknown-linux-gnu@sha256:de5114adcd786f6a41eb07379459aaae94de73670303e202ea3603d250c19091 + how: emulated, qemu-user + - target: riscv64gc-unknown-linux-gnu + image: ghcr.io/cross-rs/riscv64gc-unknown-linux-gnu@sha256:67ee97dd4719390d299edfcf39f226360ccfb1539e08b078ec187d4848415794 + how: emulated, qemu-user + # QEMU 5.1's 32-bit ARM guest grows the emulator's own memory with the + # guest's thread count: with all 20 host CPUs, bit_identity's threaded + # state-vector run took qemu-arm past 6 GiB (a 32-bit guest addresses at + # most 4 GiB, so the excess is the emulator's). Four CPUs keep it under + # 1 GiB and still take the threaded paths. + - target: armv7-unknown-linux-gnueabihf + image: ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf@sha256:4a08bebb60f08d52c1885b643a768f163c7318c05f91cf119f6c02855ea52699 + options: --cpuset-cpus=0-3 + how: emulated, qemu-user + # Runs directly on the x86_64 host in 32-bit mode. + - target: i686-unknown-linux-gnu + image: ghcr.io/cross-rs/i686-unknown-linux-gnu@sha256:2647400f884dd399a3ce6f6d918fec0776d43e73f471f52e310cbe664ac1671f + how: silicon, x86_64 host in 32-bit mode + - target: aarch64-unknown-linux-gnu + image: ghcr.io/cross-rs/aarch64-unknown-linux-gnu@sha256:7f8308a8734d9fcd2ebbe9a3e4bdea74af293f0799d80c3cc341e340cda49a4c + how: emulated, qemu-user + # cross 0.2.5 has no LoongArch image; this is cross's development image. + - target: loongarch64-unknown-linux-gnu + image: ghcr.io/cross-rs/loongarch64-unknown-linux-gnu@sha256:8968bd3b2c3b7b6d73c48e10faa605e14b40ce0080579a06402ea9fdfbc2a5c0 + how: emulated, qemu-user steps: - name: Fetch the source shell: bash run: | set -euo pipefail want="${{ github.event.pull_request.head.sha || github.sha }}" - rm -rf src && mkdir src && cd src - git init -q - # Fetch the commit itself, not the ref: a later commit on the ref that this - # workflow's paths do not match would otherwise be fetched instead. - git fetch -q --depth 1 http://100.64.224.0:3400/Transaction-Science/open-standards.git "$want" - git checkout -q FETCH_HEAD - got="$(git rev-parse HEAD)" - [ "$got" = "$want" ] || { echo "::error::fetched $got, not $want"; exit 1; } - - name: The byte-pinning tests under QEMU + # The commit itself, not the ref: a later commit on the ref that this + # workflow's paths do not match would otherwise be tested instead. The + # archive carries its commit id, checked before anything is built. + rm -rf src src.tar.gz && mkdir src + curl -sSf -o src.tar.gz "http://100.64.224.0:3400/Transaction-Science/open-standards/archive/$want.tar.gz" + got="$(gzip -dc src.tar.gz | git get-tar-commit-id)" + [ "$got" = "$want" ] || { echo "::error::the archive is of $got, not $want"; exit 1; } + tar -xzf src.tar.gz -C src --strip-components=1 + rm src.tar.gz + - name: The byte-pinning tests shell: bash working-directory: src/wai/wai-quantum run: | set -euo pipefail - export PATH="$HOME/.cargo/bin:$PATH" - command -v cross >/dev/null || cargo install cross --locked --version 0.2.5 t="${{ matrix.target }}" - # QEMU 5.1's 32-bit ARM guest grows its own memory with the guest's thread - # count: with all 20 host CPUs, bit_identity's threaded state-vector run - # took qemu-arm past the 6 GiB cap (a 32-bit guest addresses at most - # 4 GiB, so the excess is the emulator's). Four CPUs keep it under 1 GiB - # and still take the threaded paths. - # cross 0.2.5 ships no LoongArch image; its development image, pinned by - # digest, carries the toolchain and qemu-loongarch64. - if [ "$t" = loongarch64-unknown-linux-gnu ]; then - rustup target add "$t" - export CROSS_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_IMAGE=ghcr.io/cross-rs/loongarch64-unknown-linux-gnu@sha256:8968bd3b2c3b7b6d73c48e10faa605e14b40ce0080579a06402ea9fdfbc2a5c0 + cache=/ci-cache/wai-quantum-processors + mkdir -p "$cache" + # One leg at a time on this host, across every run of this workflow: the + # emulated tests are heavy, and the toolchain cache is shared. + exec 9>"$cache/.lock" + flock 9 + export RUSTUP_HOME="$cache/rustup" CARGO_HOME="$cache/cargo" + export PATH="$CARGO_HOME/bin:$PATH" + if ! command -v rustup >/dev/null; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --profile minimal --default-toolchain none fi - if [ "$t" = armv7-unknown-linux-gnueabihf ]; then - export CROSS_CONTAINER_OPTS="$CROSS_CONTAINER_OPTS --cpuset-cpus=0-3" - fi - case "$t" in - i686-*) how="silicon, x86_64 host in 32-bit mode" ;; - *) how="emulated, qemu-user" ;; - esac - # A target directory per target, kept between runs; cross mounts it. - export CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-cross/$t" - mkdir -p "$CARGO_TARGET_DIR" + rustup toolchain install + rustup target add "$t" + export CARGO_TARGET_DIR="$cache/target/$t" + logs="$(mktemp -d)" run() { local name="$1"; shift - timeout 2700 nice -n 19 cross test --locked --release -j 6 --target "$t" --features full "$@" 2>&1 | tee "$RUNNER_TEMP/$name.log" + timeout 2700 cargo test --locked --release -j 6 --target "$t" --features full "$@" 2>&1 | tee "$logs/$name.log" } run bit_identity --test bit_identity - grep -qE '^test result: ok\. 23 passed; 0 failed' "$RUNNER_TEMP/bit_identity.log" || { echo "::error::$t: the 23 bit-identity digests did not all pass"; exit 1; } + grep -qE '^test result: ok\. 23 passed; 0 failed' "$logs/bit_identity.log" || { echo "::error::$t: the 23 bit-identity digests did not all pass"; exit 1; } run frame --test frame_reference - grep -qE '^test result: ok\. 2 passed; 0 failed' "$RUNNER_TEMP/frame.log" || { echo "::error::$t: the fixtures did not pass"; exit 1; } + grep -qE '^test result: ok\. 2 passed; 0 failed' "$logs/frame.log" || { echo "::error::$t: the fixtures did not pass"; exit 1; } run lib --lib -- quantum:: quantum_fabric:: ijson:: quantum_receipt quantum_energy quantum_qbom merkle - grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/lib.log" || { echo "::error::$t: the byte-pinning library tests did not pass"; exit 1; } - echo "processor: $t ($how) bit_identity 23/23, fixtures 2/2, $(grep -E '^test result' "$RUNNER_TEMP/lib.log" | tail -1)" + grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$logs/lib.log" || { echo "::error::$t: the byte-pinning library tests did not pass"; exit 1; } + echo "processor: $t (${{ matrix.how }}) bit_identity 23/23, fixtures 2/2, $(grep -E '^test result' "$logs/lib.log" | tail -1)" -- 2.54.0 (Apple Git-157) From 57240f7ac0f372b91f35886b2c608147c175964a Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 14 Oct 2026 08:56:17 -0400 Subject: [PATCH 3/3] ci: decompress the source archive to a file before reading its commit id git get-tar-commit-id stops reading at the archive's header, so gzip, writing into the pipe, died of a broken pipe (exit 141), and pipefail failed every leg's fetch step before any test ran. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/wai-quantum-processors.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/wai-quantum-processors.yml b/.github/workflows/wai-quantum-processors.yml index 2a71696ba..0a1cdd7ce 100644 --- a/.github/workflows/wai-quantum-processors.yml +++ b/.github/workflows/wai-quantum-processors.yml @@ -98,12 +98,15 @@ jobs: # The commit itself, not the ref: a later commit on the ref that this # workflow's paths do not match would otherwise be tested instead. The # archive carries its commit id, checked before anything is built. - rm -rf src src.tar.gz && mkdir src + rm -rf src src.tar.gz src.tar && mkdir src curl -sSf -o src.tar.gz "http://100.64.224.0:3400/Transaction-Science/open-standards/archive/$want.tar.gz" - got="$(gzip -dc src.tar.gz | git get-tar-commit-id)" + # Decompressed to a file, not a pipe: get-tar-commit-id stops reading at the + # header, and the writer's broken pipe would fail the step under pipefail. + gzip -dc src.tar.gz > src.tar + got="$(git get-tar-commit-id < src.tar)" [ "$got" = "$want" ] || { echo "::error::the archive is of $got, not $want"; exit 1; } - tar -xzf src.tar.gz -C src --strip-components=1 - rm src.tar.gz + tar -xf src.tar -C src --strip-components=1 + rm src.tar.gz src.tar - name: The byte-pinning tests shell: bash working-directory: src/wai/wai-quantum -- 2.54.0 (Apple Git-157)