Skip to content

Commit b395010

Browse files
committed
build: pin canonical Registry Script artifact
1 parent 9f51fc3 commit b395010

15 files changed

Lines changed: 155 additions & 36 deletions

contracts/registry-type-script/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contracts/registry-type-script/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,20 @@ doctest = false
1313
bench = false
1414
required-features = ["ckb-script"]
1515

16+
[[bin]]
17+
name = "cellscript-registry-type-script-hash"
18+
path = "src/bin/ckb_data_hash.rs"
19+
test = false
20+
doctest = false
21+
bench = false
22+
required-features = ["hash-tool"]
23+
1624
[features]
1725
ckb-script = []
26+
hash-tool = ["dep:ckb-hash"]
1827

1928
[dependencies]
29+
ckb-hash = { version = "=1.1.1", optional = true }
2030
ckb-std = { version = "=1.1.0", default-features = false, features = ["allocator"] }
2131

2232
[dev-dependencies]

contracts/registry-type-script/README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,28 @@ contracts/registry-type-script/build_reproducible_release.sh
2929
cargo test --locked --manifest-path contracts/registry-type-script/Cargo.toml
3030
```
3131

32-
The release build disables `ckb-std` default features and enables only its Rust
32+
Reproduce the canonical Linux artifact with the pinned container digest:
33+
34+
```bash
35+
contracts/registry-type-script/build_canonical_container.sh
36+
```
37+
38+
The deployable artifact is tracked under `artifacts/v0.22.0` and was produced
39+
for the `x86_64-unknown-linux-gnu` host with the builder image digest recorded
40+
in `release-manifest.json`. Rust/LLVM may order identical RISC-V functions
41+
differently on another build host, so the script claims a byte-for-byte
42+
reproduction only on that canonical host. On every other host it still builds
43+
the source, reports the host artifact hash, verifies the tracked canonical
44+
identity, and places the canonical bytes at the normal target path for
45+
downstream tooling. The CKB-VM suite always executes those deployable bytes.
46+
47+
The build disables `ckb-std` default features and enables only its Rust
3348
allocator. Fixed-size data, Script, and lock-hash buffers call the official
3449
syscall layer directly; the contract does not carry the higher-level Molecule
35-
type graph. Consequently the canonical artifact does not depend on a host C
36-
compiler or the bundled `libc.c`; the pinned Rust toolchain plus
37-
`llvm-tools-preview` is the complete compiler toolchain used by this contract
38-
build.
50+
type graph or depend on a host C compiler and bundled `libc.c`.
51+
The small host-side hash utility in the same crate computes CKB's personalized
52+
Blake2b-256 identity without depending on the root compiler workspace or a
53+
sibling SDK checkout.
3954

4055
The test suite executes the stripped RISC-V binary in CKB-VM through
4156
`ckb-testtool`, covering authorized creation, replacement, destruction,
Binary file not shown.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
contract_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
repository_root="$(cd "$contract_dir/../.." && pwd)"
6+
canonical_target_dir="${CARGO_TARGET_DIR:-$contract_dir/target/canonical-container}"
7+
builder_image="rust@sha256:77fac8b98f9f46062bb680b6d25d5bcaabfc400143952ebc572e924bcbedc3fa"
8+
9+
mkdir -p "$canonical_target_dir"
10+
canonical_target_dir="$(cd "$canonical_target_dir" && pwd)"
11+
12+
docker run --rm --platform linux/amd64 \
13+
--user "$(id -u):$(id -g)" \
14+
--env CARGO_HOME=/contract-target/cargo-home \
15+
--env RUSTUP_HOME=/usr/local/rustup \
16+
--mount "type=bind,src=$repository_root,dst=/workspace,readonly" \
17+
--mount "type=bind,src=$canonical_target_dir,dst=/contract-target" \
18+
--workdir /workspace \
19+
"$builder_image" \
20+
bash -c '
21+
set -euo pipefail
22+
toolchain_bin=/usr/local/rustup/toolchains/1.97.1-x86_64-unknown-linux-gnu/bin
23+
export PATH="$toolchain_bin:/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
24+
export RUSTC="$toolchain_bin/rustc"
25+
export LD_LIBRARY_PATH="$toolchain_bin/../lib"
26+
test "$(rustc -vV | sed -n "s/^host: //p")" = x86_64-unknown-linux-gnu
27+
test -x "$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/rust-objcopy"
28+
rustc --print target-libdir --target riscv64imac-unknown-none-elf >/dev/null
29+
CARGO_TARGET_DIR=/contract-target \
30+
CELLSCRIPT_HASH_TARGET_DIR=/contract-target/cellc \
31+
/workspace/contracts/registry-type-script/build_reproducible_release.sh
32+
'

contracts/registry-type-script/build_reproducible_release.sh

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ contract_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
55
repository_root="$(cd "$contract_dir/../.." && pwd)"
66
cargo_home_dir="${CARGO_HOME:-${HOME}/.cargo}"
77
target_dir="${CARGO_TARGET_DIR:-$contract_dir/target}"
8+
hash_target_dir="${CELLSCRIPT_HASH_TARGET_DIR:-$repository_root/target}"
89
rust_sysroot="$(rustc --print sysroot)"
910
host_triple="$(rustc -vV | awk '/^host: / { print $2 }')"
1011
rust_objcopy="$rust_sysroot/lib/rustlib/$host_triple/bin/rust-objcopy"
@@ -34,17 +35,26 @@ env -u RUSTFLAGS \
3435
--bin cellscript-registry-type-script
3536

3637
artifact="$target_dir/riscv64imac-unknown-none-elf/release/cellscript-registry-type-script"
37-
stripped_artifact="$artifact.stripped"
38-
"$rust_objcopy" --strip-all "$artifact" "$stripped_artifact"
39-
mv "$stripped_artifact" "$artifact"
40-
41-
sha256_hash="$(shasum -a 256 "$artifact" | awk '{ print $1 }')"
42-
artifact_bytes="$(wc -c < "$artifact" | tr -d ' ')"
43-
ckb_hash_json="$(CARGO_TARGET_DIR="$repository_root/target" cargo run --quiet --locked \
44-
--manifest-path "$repository_root/Cargo.toml" \
45-
-p cellscript --bin cellc -- ckb-hash --file "$artifact" --json)"
46-
ckb_data_hash="$(printf '%s\n' "$ckb_hash_json" | sed -n 's/.*"hash": "\([0-9a-f]*\)".*/\1/p')"
38+
host_artifact="$artifact.$host_triple.stripped"
39+
"$rust_objcopy" --strip-all "$artifact" "$host_artifact"
40+
4741
release_manifest="$contract_dir/release-manifest.json"
42+
canonical_relative_path="$(sed -n 's/.*"artifact": "\([^"]*\)".*/\1/p' "$release_manifest")"
43+
canonical_artifact="$contract_dir/$canonical_relative_path"
44+
if [[ -z "$canonical_relative_path" || ! -f "$canonical_artifact" ]]; then
45+
printf 'canonical Registry Type Script artifact is missing: %s\n' "$canonical_artifact" >&2
46+
exit 1
47+
fi
48+
49+
sha256_hash="$(shasum -a 256 "$canonical_artifact" | awk '{ print $1 }')"
50+
artifact_bytes="$(wc -c < "$canonical_artifact" | tr -d ' ')"
51+
ckb_data_hash="$(CARGO_TARGET_DIR="$hash_target_dir" cargo run --quiet --locked \
52+
--manifest-path "$contract_dir/Cargo.toml" \
53+
--features hash-tool \
54+
--bin cellscript-registry-type-script-hash \
55+
-- "$canonical_artifact")"
56+
ckb_hash_json="$(printf '{\n "algorithm": "blake2b-256",\n "hash": "%s",\n "input_bytes": %s,\n "personalization": "ckb-default-hash",\n "status": "ok"\n}' \
57+
"$ckb_data_hash" "$artifact_bytes")"
4858
expected_sha256="$(sed -n 's/.*"sha256": "\([0-9a-f]*\)".*/\1/p' "$release_manifest")"
4959
expected_artifact_bytes="$(sed -n 's/.*"artifact_bytes": \([0-9]*\).*/\1/p' "$release_manifest")"
5060
expected_ckb_data_hash="$(sed -n 's/.*"ckb_data_hash": "0x\([0-9a-f]*\)".*/\1/p' "$release_manifest")"
@@ -55,6 +65,22 @@ if [[ "$artifact_bytes" != "$expected_artifact_bytes" || "$sha256_hash" != "$exp
5565
exit 1
5666
fi
5767

68+
host_sha256="$(shasum -a 256 "$host_artifact" | awk '{ print $1 }')"
69+
if [[ "$host_triple" == "x86_64-unknown-linux-gnu" ]]; then
70+
if ! cmp -s "$host_artifact" "$canonical_artifact"; then
71+
printf 'canonical x86_64 Linux rebuild does not match the tracked Registry Type Script artifact\n' >&2
72+
printf 'expected sha256=%s actual sha256=%s\n' "$sha256_hash" "$host_sha256" >&2
73+
exit 1
74+
fi
75+
printf 'canonical_rebuild=matched\n'
76+
else
77+
printf 'canonical_rebuild=not_claimed host=%s host_sha256=%s\n' "$host_triple" "$host_sha256"
78+
fi
79+
80+
# Downstream tools always execute the exact tracked deployable bytes. A
81+
# non-canonical host build is retained beside this path for inspection.
82+
cp "$canonical_artifact" "$artifact"
83+
5884
printf 'artifact=%s\n' "$artifact"
5985
printf 'artifact_bytes=%s\n' "$artifact_bytes"
6086
printf 'sha256=%s\n' "$sha256_hash"

contracts/registry-type-script/release-manifest.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
"schema": "cellscript-registry-type-script-release-v1",
33
"version": "0.22.0",
44
"target": "riscv64imac-unknown-none-elf",
5-
"artifact": "cellscript-registry-type-script",
5+
"artifact": "artifacts/v0.22.0/cellscript-registry-type-script",
6+
"canonical_build_host": "x86_64-unknown-linux-gnu",
7+
"canonical_builder_image": "rust@sha256:77fac8b98f9f46062bb680b6d25d5bcaabfc400143952ebc572e924bcbedc3fa",
68
"artifact_bytes": 3352,
7-
"sha256": "0f48a8736360c121f6ae0f04ab4b0496834f6715d47e3284a0a07add609dede9",
8-
"ckb_data_hash": "0x0dd596ade29e06e5bcc00f56abf36ecbe9afaa09f1b26a64436aa37854da622b",
9+
"sha256": "6a7ab8eaa2281fe77ca8c7b092006c52f96006ac2c7e4b013f8f88b7bf1f742a",
10+
"ckb_data_hash": "0x8b6de99567accdca438818a55c16534ed10fc335f117709b1487fd2666808bfb",
911
"script_template": {
10-
"code_hash": "0x0dd596ade29e06e5bcc00f56abf36ecbe9afaa09f1b26a64436aa37854da622b",
12+
"code_hash": "0x8b6de99567accdca438818a55c16534ed10fc335f117709b1487fd2666808bfb",
1113
"hash_type": "data1",
1214
"args_schema": "ckb_script_hash(custody_lock)",
1315
"args_bytes": 32
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use std::{env, fs, process::ExitCode};
2+
3+
fn main() -> ExitCode {
4+
let mut args = env::args_os();
5+
let _program = args.next();
6+
let Some(path) = args.next() else {
7+
eprintln!("usage: cellscript-registry-type-script-hash <artifact>");
8+
return ExitCode::from(2);
9+
};
10+
if args.next().is_some() {
11+
eprintln!("expected exactly one artifact path");
12+
return ExitCode::from(2);
13+
}
14+
let bytes = match fs::read(&path) {
15+
Ok(bytes) => bytes,
16+
Err(error) => {
17+
eprintln!("failed to read {}: {error}", path.to_string_lossy());
18+
return ExitCode::FAILURE;
19+
}
20+
};
21+
for byte in ckb_hash::blake2b_256(bytes) {
22+
print!("{byte:02x}");
23+
}
24+
println!();
25+
ExitCode::SUCCESS
26+
}

contracts/registry-type-script/tests/ckb_vm.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,8 @@ struct Scripts {
2222
}
2323

2424
fn contract_binary() -> Bytes {
25-
let path =
26-
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target/riscv64imac-unknown-none-elf/release/cellscript-registry-type-script");
27-
std::fs::read(&path)
28-
.unwrap_or_else(|error| panic!("read {}: {error}; run build_reproducible_release.sh first", path.display()))
29-
.into()
25+
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("artifacts/v0.22.0/cellscript-registry-type-script");
26+
std::fs::read(&path).unwrap_or_else(|error| panic!("read tracked canonical artifact {}: {error}", path.display())).into()
3027
}
3128

3229
fn scripts(args: Option<Bytes>) -> Scripts {

docs/CELLSCRIPT_GATE_POLICY.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,13 @@ same auditable resolver policy.
8282

8383
Both `dev` and `ci` also build the independent
8484
`contracts/registry-type-script` crate for
85-
`riscv64imac-unknown-none-elf`, strip it with the pinned toolchain, compare its
86-
SHA-256 and CKB data hash to the tracked release manifest, and execute its
85+
`riscv64imac-unknown-none-elf`, strip it with the pinned toolchain, verify the
86+
tracked canonical ELF's SHA-256 and CKB data hash, and execute that ELF's
8787
positive and negative lifecycle matrix in CKB-VM through `ckb-testtool`.
88+
Linux x86_64 additionally requires the fresh build to match the tracked ELF
89+
byte-for-byte. Other build hosts record their host artifact hash and make no
90+
cross-host reproduction claim; the pinned container builder provides that
91+
canonical check there.
8892
Passing this local boundary proves the deployed bytes' behavior and identity;
8993
it does not prove that the code Cell or custody Lock CellDep is live on
9094
mainnet. Production readiness still performs live RPC and confirmation checks.

0 commit comments

Comments
 (0)