Skip to content

Commit 97ac8b9

Browse files
kariyclaude
andauthored
feat(ops): rebuild mock TEE contracts from piltover submodule at build time (#61)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3c14c52 commit 97ac8b9

15 files changed

Lines changed: 246 additions & 11 deletions

File tree

.github/workflows/build.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ jobs:
1414
steps:
1515
- name: "Checkout source code"
1616
uses: "actions/checkout@v4"
17+
with:
18+
submodules: recursive
1719

1820
- name: "Setup Rust toolchain"
1921
uses: "dtolnay/rust-toolchain@master"
@@ -35,6 +37,16 @@ jobs:
3537
run: |
3638
pip install -r requirements.txt
3739
40+
- name: "Install scarb (version pinned by piltover/.tool-versions)"
41+
run: |
42+
SCARB_VERSION=$(awk '$1 == "scarb" { print $2 }' piltover/.tool-versions)
43+
if [ -z "$SCARB_VERSION" ]; then
44+
echo "Could not read scarb version from piltover/.tool-versions"; exit 1
45+
fi
46+
echo "Installing scarb $SCARB_VERSION"
47+
curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh -s -- -v "$SCARB_VERSION"
48+
echo "$HOME/.local/bin" >> $GITHUB_PATH
49+
3850
- name: "Cargo build (root workspace)"
3951
run: |
4052
cargo build --workspace --all-features

.github/workflows/lint.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ jobs:
1313
steps:
1414
- name: "Checkout source code"
1515
uses: "actions/checkout@v4"
16+
with:
17+
submodules: recursive
1618

1719
- name: "Setup Rust toolchain"
1820
uses: "dtolnay/rust-toolchain@master"
@@ -40,6 +42,8 @@ jobs:
4042
steps:
4143
- name: "Checkout source code"
4244
uses: "actions/checkout@v4"
45+
with:
46+
submodules: recursive
4347

4448
- name: "Setup Rust toolchain"
4549
uses: "dtolnay/rust-toolchain@master"
@@ -61,6 +65,16 @@ jobs:
6165
run: |
6266
pip install -r requirements.txt
6367
68+
- name: "Install scarb (version pinned by piltover/.tool-versions)"
69+
run: |
70+
SCARB_VERSION=$(awk '$1 == "scarb" { print $2 }' piltover/.tool-versions)
71+
if [ -z "$SCARB_VERSION" ]; then
72+
echo "Could not read scarb version from piltover/.tool-versions"; exit 1
73+
fi
74+
echo "Installing scarb $SCARB_VERSION"
75+
curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh -s -- -v "$SCARB_VERSION"
76+
echo "$HOME/.local/bin" >> $GITHUB_PATH
77+
6478
- name: "Run Clippy"
6579
run: |
6680
bash scripts/clippy.sh

.github/workflows/release-dispatch.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ jobs:
1717
VERSION: ""
1818
steps:
1919
- uses: actions/checkout@v4
20+
with:
21+
submodules: recursive
2022

2123
- name: "Setup toolchain"
2224
uses: "actions-rs/toolchain@v1"

.github/workflows/release.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ jobs:
6262

6363
steps:
6464
- uses: actions/checkout@v4
65+
with:
66+
submodules: recursive
6567

6668
- uses: dtolnay/rust-toolchain@master
6769
name: Rust Toolchain Setup
@@ -88,6 +90,17 @@ jobs:
8890
run: |
8991
pip install -r requirements.txt
9092
93+
- name: "Install scarb (version pinned by piltover/.tool-versions)"
94+
run: |
95+
SCARB_VERSION=$(awk '$1 == "scarb" { print $2 }' piltover/.tool-versions)
96+
if [ -z "$SCARB_VERSION" ]; then
97+
echo "Could not read scarb version from piltover/.tool-versions"; exit 1
98+
fi
99+
echo "Installing scarb $SCARB_VERSION"
100+
curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh -s -- -v "$SCARB_VERSION"
101+
echo "$HOME/.local/bin" >> $GITHUB_PATH
102+
shell: bash
103+
91104
- name: Apple M1 setup
92105
if: ${{ matrix.job.target == 'aarch64-apple-darwin' }}
93106
run: |

.github/workflows/test.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ jobs:
1616
steps:
1717
- name: "Checkout source code"
1818
uses: "actions/checkout@v4"
19+
with:
20+
submodules: recursive
1921

2022
- name: "Setup Rust toolchain"
2123
uses: "dtolnay/rust-toolchain@master"
@@ -37,6 +39,16 @@ jobs:
3739
run: |
3840
pip install -r requirements.txt
3941
42+
- name: "Install scarb (version pinned by piltover/.tool-versions)"
43+
run: |
44+
SCARB_VERSION=$(awk '$1 == "scarb" { print $2 }' piltover/.tool-versions)
45+
if [ -z "$SCARB_VERSION" ]; then
46+
echo "Could not read scarb version from piltover/.tool-versions"; exit 1
47+
fi
48+
echo "Installing scarb $SCARB_VERSION"
49+
curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | sh -s -- -v "$SCARB_VERSION"
50+
echo "$HOME/.local/bin" >> $GITHUB_PATH
51+
4052
- name: "Cargo test (root workspace)"
4153
run: |
4254
cargo test --workspace --all-features --exclude saya-e2e

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "piltover"]
2+
path = piltover
3+
url = https://github.com/cartridge-gg/piltover

Makefile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
SUBMODULE_DIR := piltover
2+
3+
# Scarb version is read from the submodule's .tool-versions — single source of
4+
# truth, no drift between saya's Makefile and the piltover pin.
5+
SCARB_VERSION := $(shell if [ -f $(SUBMODULE_DIR)/.tool-versions ]; then \
6+
awk '$$1 == "scarb" { print $$2 }' $(SUBMODULE_DIR)/.tool-versions; \
7+
fi)
8+
9+
.PHONY: contracts install-scarb
10+
11+
install-scarb:
12+
@command -v asdf >/dev/null 2>&1 || { \
13+
echo "Error: asdf is required. Install from https://asdf-vm.com/"; exit 1; }
14+
@if [ ! -f $(SUBMODULE_DIR)/.tool-versions ]; then \
15+
echo "Initializing piltover submodule first (to read scarb version)..."; \
16+
git submodule update --init --recursive --force $(SUBMODULE_DIR); \
17+
fi
18+
@if [ -z "$(SCARB_VERSION)" ]; then \
19+
echo "Error: could not read scarb version from $(SUBMODULE_DIR)/.tool-versions"; \
20+
exit 1; \
21+
fi
22+
@asdf plugin list 2>/dev/null | grep -qx scarb || asdf plugin add scarb
23+
@asdf where scarb $(SCARB_VERSION) >/dev/null 2>&1 || asdf install scarb $(SCARB_VERSION)
24+
@echo "scarb $(SCARB_VERSION) ready (pinned by $(SUBMODULE_DIR)/.tool-versions)."
25+
26+
# Diagnostic / explicit-refresh target. `cargo build -p saya-ops` already runs
27+
# scarb via bin/ops/build.rs — this exists for cases where you want to warm
28+
# the scarb cache or verify contracts compile before a full cargo build.
29+
contracts: install-scarb
30+
@cd $(SUBMODULE_DIR) && asdf exec scarb build
31+
@echo "Piltover contracts built in $(SUBMODULE_DIR)/target/dev/"
32+
@echo "Note: bin/ops/build.rs re-runs scarb at cargo build time; nothing"
33+
@echo "needs to be copied manually."

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,30 @@ saya-tee tee start \
197197
## Building from source
198198

199199
Only needed if you want to modify Saya.
200-
Requires Rust `1.89` — install via [rustup](https://rustup.rs); `rust-toolchain.toml` pins the version automatically.
200+
201+
**One-time setup:**
202+
203+
```bash
204+
git clone --recursive https://github.com/dojoengine/saya
205+
# or, if you already cloned without --recursive:
206+
# git submodule update --init --recursive
207+
208+
make install-scarb
209+
```
210+
211+
`make install-scarb` uses [asdf](https://asdf-vm.com/) to install the `scarb`
212+
version pinned by `piltover/.tool-versions`. `saya-ops` rebuilds
213+
two Piltover mock contracts from that submodule on every cargo build via its
214+
`build.rs`, so scarb is a hard build-time dependency.
215+
216+
Rust `1.89` is also required — install via [rustup](https://rustup.rs);
217+
`rust-toolchain.toml` pins the version automatically.
218+
219+
**Build:**
201220

202221
```bash
203222
cd bin/persistent && cargo build --release
204223
cd bin/ops && cargo build --release
205-
206-
# TEE mode (requires SSH access to cartridge-gg/katana-tee)
207224
cd bin/persistent-tee && cargo build --release
208225
```
209226

bin/ops/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ version.workspace = true
1515
edition.workspace = true
1616
license.workspace = true
1717
repository.workspace = true
18+
build = "build.rs"
1819

1920
[dependencies]
2021
anyhow = { version = "1.0.95", default-features = false }

bin/ops/build.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! Rebuilds Piltover mock contracts from source via the `piltover`
2+
//! submodule + scarb. The submodule is the single source of truth for the
3+
//! Sierra bytecode embedded in saya-ops.
4+
//!
5+
//! Dev setup: run `make install-scarb` from the repo root before `cargo build`.
6+
//! This build script hard-fails if asdf/scarb is missing — there is no
7+
//! vendored fallback for the mock contracts.
8+
//!
9+
//! NOT rebuilt: `contracts/core_contract.json`. Its class hash is load-bearing
10+
//! for already-deployed Piltover on Sepolia/Mainnet, and the Cairo source in
11+
//! the pinned piltover rev has diverged from the rev it was originally
12+
//! compiled at. It remains vendored.
13+
14+
use std::env;
15+
use std::path::{Path, PathBuf};
16+
use std::process::Command;
17+
18+
/// Pairs of (piltover scarb output name, OUT_DIR name for include_bytes!).
19+
const CONTRACTS: &[(&str, &str)] = &[
20+
("piltover_fact_registry_mock.contract_class.json", "fact_registry_mock.json"),
21+
("piltover_mock_amd_tee_registry.contract_class.json", "tee_registry_mock.json"),
22+
];
23+
24+
fn main() {
25+
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
26+
let workspace_root = manifest_dir.join("../..");
27+
let piltover_dir = workspace_root.join("piltover");
28+
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
29+
30+
println!("cargo:rerun-if-changed=build.rs");
31+
println!("cargo:rerun-if-changed=../../piltover/src");
32+
println!("cargo:rerun-if-changed=../../piltover/Scarb.toml");
33+
println!("cargo:rerun-if-changed=../../piltover/Scarb.lock");
34+
println!("cargo:rerun-if-changed=../../piltover/.tool-versions");
35+
36+
// Auto-init the submodule if it's empty (user may have cloned without
37+
// `--recursive`). Mirrors katana's `initialize_submodule` helper in
38+
// `crates/contracts/build.rs`.
39+
initialize_submodule_if_empty(&piltover_dir);
40+
41+
// Hard-fail if scarb isn't on PATH. Dev must run `make install-scarb` first
42+
// (or install scarb directly). We invoke `scarb` directly rather than
43+
// `asdf exec scarb` so CI environments that install scarb via the official
44+
// installer (without asdf) also work. Local dev via asdf still works
45+
// transparently: asdf shims intercept the `scarb` call and route to the
46+
// version pinned by `piltover/.tool-versions`.
47+
let scarb_check = Command::new("scarb")
48+
.arg("--version")
49+
.current_dir(&piltover_dir)
50+
.output();
51+
if !scarb_check.as_ref().map(|o| o.status.success()).unwrap_or(false) {
52+
panic!(
53+
"scarb not found on PATH. Run `make install-scarb` from the repo root to \
54+
install the version pinned by `piltover/.tool-versions`, or install scarb \
55+
directly from https://docs.swmansion.com/scarb/download.html."
56+
);
57+
}
58+
59+
// Build piltover contracts via scarb.
60+
let status = Command::new("scarb")
61+
.arg("build")
62+
.current_dir(&piltover_dir)
63+
.status()
64+
.expect("`scarb build` failed to spawn");
65+
if !status.success() {
66+
panic!("scarb build failed in {}", piltover_dir.display());
67+
}
68+
69+
// Copy the two mock artifacts into OUT_DIR under stable names so the
70+
// corresponding `include_bytes!` sites in `core_contract/constants.rs`
71+
// can resolve them.
72+
for (scarb_name, out_name) in CONTRACTS {
73+
let src = piltover_dir.join("target/dev").join(scarb_name);
74+
let dst = out_dir.join(out_name);
75+
std::fs::copy(&src, &dst).unwrap_or_else(|e| {
76+
panic!(
77+
"failed to copy scarb output {} -> {}: {e}",
78+
src.display(),
79+
dst.display()
80+
)
81+
});
82+
}
83+
}
84+
85+
/// Auto-initializes the piltover submodule if the working tree is empty.
86+
/// Prints a `cargo:warning` so the user knows it's happening.
87+
fn initialize_submodule_if_empty(submodule_dir: &Path) {
88+
if submodule_dir.join("Scarb.toml").exists() {
89+
return;
90+
}
91+
println!(
92+
"cargo:warning=piltover submodule at {} not initialized, running `git submodule \
93+
update --init --recursive --force`...",
94+
submodule_dir.display()
95+
);
96+
let in_git_repo = Command::new("git")
97+
.args(["rev-parse", "--git-dir"])
98+
.output()
99+
.map(|o| o.status.success())
100+
.unwrap_or(false);
101+
if !in_git_repo {
102+
panic!(
103+
"Not in a git repo, and {} is empty. Clone saya with `--recursive`, or run \
104+
`git submodule update --init --recursive` from a proper checkout.",
105+
submodule_dir.display()
106+
);
107+
}
108+
let status = Command::new("git")
109+
.args(["submodule", "update", "--init", "--recursive", "--force"])
110+
.arg(submodule_dir)
111+
.status()
112+
.expect("`git submodule update` failed to spawn");
113+
if !status.success() {
114+
panic!("Failed to initialize piltover submodule at {}", submodule_dir.display());
115+
}
116+
}

0 commit comments

Comments
 (0)