Skip to content

fix: [v2][contracts] Canonical deployment manifest and per-networ (#1… #6

fix: [v2][contracts] Canonical deployment manifest and per-networ (#1…

fix: [v2][contracts] Canonical deployment manifest and per-networ (#1… #6

Workflow file for this run

name: Contracts
on:
push:
branches: [main]
# Also run on tags so the build-manifest is published with the release
tags:
- "v*"
paths:
- "contracts/**"
pull_request:
branches: [main]
paths:
- "contracts/**"
schedule:
- cron: "0 2 * * *"
permissions:
contents: read
concurrency:
group: contracts-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-contracts:
name: Build & verify contracts
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: contracts
steps:
- uses: actions/checkout@v4
# rust-toolchain.toml in contracts/ is automatically picked up by rustup.
# We install it explicitly here so the cache key matches the pinned version.
- name: Install pinned Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
# Read the channel straight from the toolchain file so this step
# stays in sync automatically when the file is updated.
toolchain: "1.81.0"
targets: wasm32-unknown-unknown
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: contracts
# ── First build ─────────────────────────────────────────────────────────
- name: Build contracts (pass 1)
run: |
cargo build --target wasm32-unknown-unknown --release --locked
# Capture first-pass hashes immediately after the build
mkdir -p /tmp/wasm-pass1
cp target/wasm32-unknown-unknown/release/*.wasm /tmp/wasm-pass1/
# ── Emit hashes after first build ───────────────────────────────────────
- name: Compute and print WASM hashes (pass 1)
shell: bash
run: |
set -euo pipefail
echo "### WASM sha256 hashes (pass 1)" | tee /tmp/wasm-hashes-pass1.txt
echo "" | tee -a /tmp/wasm-hashes-pass1.txt
for f in /tmp/wasm-pass1/*.wasm; do
name=$(basename "$f")
hash=$(sha256sum "$f" | awk '{print $1}')
echo "${hash} ${name}" | tee -a /tmp/wasm-hashes-pass1.txt
done
# ── Second build (determinism check) ────────────────────────────────────
# Clean only the WASM output — keep the incremental cache so this doesn't
# double the build time — then rebuild from source.
- name: Wipe WASM output directory
run: rm -f target/wasm32-unknown-unknown/release/*.wasm
- name: Run contract fuzz tests

Check failure on line 79 in .github/workflows/contracts.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/contracts.yml

Invalid workflow file

You have an error in your yaml syntax on line 79
run: cargo test -p subscription_renewal -p escrow -p payment-channel -p virtual-card fuzz_
env:
PROPTEST_CASES: "8"
# Fixed seed so the property/state-machine tests are
# reproducible on every PR run.
PROPTEST_SEED: "0x1234567890abcdef1234567890abcdef"
- name: Measure contract resource budgets
shell: bash
run: |
set -euo pipefail
echo "Recording resource budgets for all contract entrypoints..."
# Run tests with budget instrumentation
cargo test --release -- --nocapture --test-threads=1 \
-Z unstable-options --unstable-features 2>&1 | tee budget_output.log
# Check if any entrypoints exceeded their budgets
if grep -q "REGRESSION\|❌" budget_output.log; then
echo "⚠️ Budget regression detected!"
grep "REGRESSION\|❌" budget_output.log || true
echo "Review the output above and update budgets.json if changes are intentional"
echo "(Current tolerance: 5%)"
fi
echo "✓ Budget check complete"
- name: Validate budgets.json structure
shell: bash
run: |
set -euo pipefail
echo "Validating budgets.json structure..."
# Check that budgets.json is valid JSON
if ! jq empty budgets.json; then
echo "❌ budgets.json is not valid JSON"
exit 1
fi
# Verify all required fields
jq -e '.metadata.version' budgets.json > /dev/null || exit 1
jq -e '.budgets | length > 0' budgets.json > /dev/null || exit 1
echo "✓ budgets.json is valid"
echo "Tracked entrypoints: $(jq '.budgets | length' budgets.json)"
- name: Upload WASM artifacts
uses: actions/upload-artifact@v7
with:
name: contract-wasm
path: contracts/target/wasm32-unknown-unknown/release/*.wasm
retention-days: 1
- name: Compute WASM hashes (pass 2)
shell: bash
run: |
set -euo pipefail
echo "### WASM sha256 hashes (pass 2)" | tee /tmp/wasm-hashes-pass2.txt
echo "" | tee -a /tmp/wasm-hashes-pass2.txt
for f in target/wasm32-unknown-unknown/release/*.wasm; do
name=$(basename "$f")
hash=$(sha256sum "$f" | awk '{print $1}')
echo "${hash} ${name}" | tee -a /tmp/wasm-hashes-pass2.txt
done
# ── Determinism gate ────────────────────────────────────────────────────
- name: Verify determinism (pass 1 == pass 2)
shell: bash
run: |
set -euo pipefail
mismatch=0
while IFS= read -r line; do
[[ -z "$line" || "$line" == \#* ]] && continue
hash1=$(echo "$line" | awk '{print $1}')
name=$(echo "$line" | awk '{print $2}')
hash2=$(grep -F " ${name}" /tmp/wasm-hashes-pass2.txt | awk '{print $1}' || true)
if [ "$hash1" != "$hash2" ]; then
echo "DETERMINISM FAILURE: ${name}"
echo " pass-1: ${hash1}"
echo " pass-2: ${hash2}"
mismatch=1
fi
done < /tmp/wasm-hashes-pass1.txt
if [ "$mismatch" -eq 1 ]; then
echo ""
echo "ERROR: Non-deterministic build detected. The toolchain, compiler flags,"
echo "or source may embed timestamps or random seeds. Investigate before merging."
exit 1
fi
echo "All WASM artifacts are deterministic."
if [ "${#wasm_files[@]}" -eq 0 ]; then
echo "No WASM artifacts found in target/wasm32-unknown-unknown/release"
exit 1
fi
for wasm in "${wasm_files[@]}"; do
size=$(wc -c < "$wasm")
echo "$wasm: ${size} bytes"
- name: Upload hash file
uses: actions/upload-artifact@v4
with:
name: wasm-hashes-${{ github.sha }}
path: /tmp/wasm-hashes-pass1.txt
retention-days: 90
- name: Verify mainnet promotion gates
working-directory: ..
run: npx -y tsx deploy/verify-gates.ts
fuzz-nightly:
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
timeout-minutes: 60
defaults:
run:
working-directory: contracts
steps:
- uses: actions/checkout@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: contracts
- name: Run extended property/fuzz runs
run: cargo test -p subscription_renewal -p escrow -p payment-channel -p virtual-card fuzz_
env:
# Extended case count for the nightly soak (PR runs use a
# small, fixed count via PROPTEST_CASES=8 + PROPTEST_SEED).
PROPTEST_CASES: "512"