Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
57c2e8b
.
DSharifi Feb 27, 2026
b2621f8
wip
DSharifi Feb 27, 2026
f08c453
wip
DSharifi Feb 27, 2026
315a833
.
DSharifi Feb 27, 2026
07b3be5
wip
DSharifi Feb 27, 2026
cbf5afa
chore: enforce docker_content_trust at compile time
DSharifi Mar 2, 2026
8044407
created json
DSharifi Mar 2, 2026
34302f6
it compiles
DSharifi Mar 2, 2026
2965cde
wip
DSharifi Mar 2, 2026
795576a
wip
DSharifi Mar 3, 2026
c1ca0b2
wip
DSharifi Mar 3, 2026
d2ddedc
use const MPC_IMAGE_HASH_EVENT
DSharifi Mar 4, 2026
391be02
add deref to bounded vec
DSharifi Mar 4, 2026
1253c03
compiles
DSharifi Mar 4, 2026
9fe5b9a
wip
DSharifi Mar 4, 2026
422178a
wip
DSharifi Mar 5, 2026
cba7733
wip
DSharifi Mar 5, 2026
4b2f887
fix bug
DSharifi Mar 5, 2026
70c6fb1
.
DSharifi Mar 5, 2026
1b9bc08
fix sha256 prefix
DSharifi Mar 5, 2026
9b36896
fix docker args
DSharifi Mar 5, 2026
8a7c676
add some json examples of new config
DSharifi Mar 5, 2026
5815988
allow dynamically passthrough envs
DSharifi Mar 5, 2026
a357365
remove todo panics
DSharifi Mar 5, 2026
107a166
use backon for retries
DSharifi Mar 5, 2026
3913f97
inline the backon
DSharifi Mar 5, 2026
5e69675
add snapshot tests
DSharifi Mar 5, 2026
055f0af
use DockerSha256Digest
DSharifi Mar 5, 2026
4bde6c6
cleanup
DSharifi Mar 5, 2026
40289e1
fix bugs
DSharifi Mar 5, 2026
110d985
remove dead constants
DSharifi Mar 5, 2026
58b4421
rename to constants
DSharifi Mar 5, 2026
6b78e67
update image hashes watcher tests
DSharifi Mar 5, 2026
8294a99
cargo clippy on launcher tests
DSharifi Mar 5, 2026
86811bd
cargo clippy launcher interface tests
DSharifi Mar 5, 2026
dcbe19a
let claude add tests
DSharifi Mar 5, 2026
e658af4
add more tests
DSharifi Mar 5, 2026
4281065
rename to ApprovedHashes
DSharifi Mar 6, 2026
154e27b
update docker build launcher
DSharifi Mar 6, 2026
06dc89d
Merge remote-tracking branch 'origin/main' into 2262-port-node-launch…
DSharifi Mar 6, 2026
a696478
update cargo lock
DSharifi Mar 6, 2026
98d85fb
undo hash.rs change
DSharifi Mar 6, 2026
9d5d521
use assert_matches!
DSharifi Mar 6, 2026
b25072f
claude first pass
DSharifi Mar 6, 2026
af78d2c
testing manually with localnet works
DSharifi Mar 6, 2026
ae8e459
use envsubst
DSharifi Mar 6, 2026
a989b04
make it portable for fish
DSharifi Mar 6, 2026
62efec6
remove section comments
DSharifi Mar 6, 2026
50f27e0
cargo fmt
DSharifi Mar 6, 2026
b5aedee
Have run as standalone function
DSharifi Mar 6, 2026
0636c92
rename to run_mpc_node
DSharifi Mar 6, 2026
599d999
use pathbuf
DSharifi Mar 6, 2026
ffa3497
add todo issue links
DSharifi Mar 6, 2026
0b53d1c
fix pathbuf issue
DSharifi Mar 6, 2026
34515c2
update pytests
DSharifi Mar 6, 2026
ca80bca
fix: test config was overwriting neard config
DSharifi Mar 6, 2026
44b9a0e
reset nearcore change
DSharifi Mar 6, 2026
bc9b2fc
fix yml failure
DSharifi Mar 6, 2026
99084d1
redact secrets
DSharifi Mar 6, 2026
6f90606
fmt
DSharifi Mar 6, 2026
2867315
Merge remote-tracking branch 'origin/main' into 2262-port-node-launch…
DSharifi Mar 6, 2026
a210ce2
Merge branch '2331-remove-usage-of-env-variables-and-cli-flags-from-m…
DSharifi Mar 6, 2026
79c001d
change to pass forward path instead
DSharifi Mar 6, 2026
3812aa4
fmt
DSharifi Mar 6, 2026
5fa48e5
shear and sort
DSharifi Mar 6, 2026
a5cbac2
make check all fast pass
DSharifi Mar 6, 2026
3138c55
undo nearcore
DSharifi Mar 6, 2026
d8f44a6
undo launcher
DSharifi Mar 6, 2026
2ebb35f
oneshot change to toml
DSharifi Mar 9, 2026
c23fb69
Merge remote-tracking branch 'origin/main' into 2331-remove-usage-of-…
DSharifi Mar 9, 2026
7d5cc09
sort toml declaration
DSharifi Mar 9, 2026
ee45ca9
revert nearcore change
DSharifi Mar 9, 2026
3b4dcbb
pytest, dont incldue None fields
DSharifi Mar 9, 2026
b8dd3ce
Merge remote-tracking branch 'origin/2331-remove-usage-of-env-variabl…
DSharifi Mar 13, 2026
92d5c94
Merge remote-tracking branch 'origin/main' into 2262-port-node-launch…
DSharifi Mar 13, 2026
7d45c50
remove extra hosts functionality, see #2438
DSharifi Mar 13, 2026
4fcfdc6
use compose tempalte file
DSharifi Mar 13, 2026
eca6da3
create file and forward content in the file
DSharifi Mar 13, 2026
7534392
remove rewrite comment
DSharifi Mar 13, 2026
515d425
use write_all
DSharifi Mar 13, 2026
12d7e47
dont use tempfile for passed config, since it gets dropped
DSharifi Mar 13, 2026
1e7ec24
change to toml and forward toml file
DSharifi Mar 13, 2026
8c5c586
Merge remote-tracking branch 'origin/main' into 2262-port-node-launch…
DSharifi Mar 13, 2026
e7f69b7
update launcher hash :)
DSharifi Mar 13, 2026
7098969
sort deps
DSharifi Mar 13, 2026
f5d9d5c
chore: add frodo.toml file
DSharifi Mar 15, 2026
1ee4ba4
fix: install openssl
DSharifi Mar 15, 2026
104eaf7
update compose file to use new image and binary with config file acce…
DSharifi Mar 15, 2026
de02ec0
update localnet scripts to generate TOML config for Rust launcher
barakeinav1 Mar 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:

docker-launcher-build-and-verify:
name: "Build MPC Launcher Docker image and verify"
runs-on: warp-ubuntu-2404-x64-2x
runs-on: warp-ubuntu-2404-x64-8x
timeout-minutes: 60
permissions:
contents: read
Expand All @@ -75,10 +75,16 @@ jobs:
with:
persist-credentials: false

- name: Install skopeo
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y skopeo
sudo apt-get install -y skopeo liblzma-dev

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "warpbuild"

- name: Build launcher docker image and verify its hash
shell: bash
Expand Down
18 changes: 12 additions & 6 deletions .github/workflows/docker_build_launcher.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
jobs:
build-and-push-images:
name: "Build and push Docker launcher image with commit hash"
runs-on: warp-ubuntu-2404-x64-2x
runs-on: warp-ubuntu-2404-x64-8x
permissions:
contents: read

Expand All @@ -23,17 +23,23 @@ jobs:
with:
persist-credentials: false

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y skopeo liblzma-dev

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "warpbuild"

- name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Install skopeo
run: |
sudo apt-get update
sudo apt-get install -y skopeo

- name: Build and push launcher image
run: |
export LAUNCHER_IMAGE_NAME=mpc-launcher
Expand Down
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ members = [
"crates/foreign-chain-inspector",
"crates/foreign-chain-rpc-interfaces",
"crates/include-measurements",
"crates/launcher-interface",
"crates/mpc-attestation",
"crates/near-mpc-bounded-collections",
"crates/near-mpc-contract-interface",
Expand All @@ -23,6 +24,7 @@ members = [
"crates/node-types",
"crates/primitives",
"crates/tee-authority",
"crates/tee-launcher",
"crates/test-migration-contract",
"crates/test-parallel-contract",
"crates/test-utils",
Expand All @@ -44,6 +46,7 @@ contract-history = { path = "crates/contract-history" }
foreign-chain-inspector = { path = "crates/foreign-chain-inspector" }
foreign-chain-rpc-interfaces = { path = "crates/foreign-chain-rpc-interfaces" }
include-measurements = { path = "crates/include-measurements" }
launcher-interface = { path = "crates/launcher-interface" }
mpc-attestation = { path = "crates/mpc-attestation" }
mpc-contract = { path = "crates/contract", features = ["dev-utils"] }
mpc-node = { path = "crates/node" }
Expand Down
20 changes: 20 additions & 0 deletions crates/launcher-interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "launcher-interface"
version.workspace = true
edition.workspace = true
license.workspace = true

[dependencies]
derive_more = { workspace = true }
mpc-primitives = { workspace = true }
near-mpc-bounded-collections = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
assert_matches = { workspace = true }
insta = { workspace = true }
serde_json = { workspace = true }

[lints]
workspace = true
176 changes: 176 additions & 0 deletions crates/launcher-interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
pub const MPC_IMAGE_HASH_EVENT: &str = "mpc-image-digest";

pub mod types {
use std::fmt;
use std::str::FromStr;

use mpc_primitives::hash::MpcDockerImageHash;
use serde::{Deserialize, Serialize};

/// JSON structure for the approved hashes file written by the MPC node, and read by the launcher.
#[derive(Debug, Serialize, Deserialize)]
pub struct ApprovedHashes {
pub approved_hashes: near_mpc_bounded_collections::NonEmptyVec<DockerSha256Digest>,
}

impl ApprovedHashes {
pub fn newest_approved_hash(&self) -> &DockerSha256Digest {
self.approved_hashes.first()
}
}

const SHA256_PREFIX: &str = "sha256:";

#[derive(Debug, Clone, PartialEq, Eq, derive_more::From)]
pub struct DockerSha256Digest(MpcDockerImageHash);

impl fmt::Display for DockerSha256Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{SHA256_PREFIX}{}", self.0.as_hex())
}
}

#[derive(Debug, thiserror::Error)]
pub enum DockerDigestParseError {
#[error("missing {SHA256_PREFIX} prefix")]
MissingPrefix,
#[error(transparent)]
InvalidHash(#[from] mpc_primitives::hash::Hash32ParseError),
}

impl DockerSha256Digest {
pub fn as_raw_hex(&self) -> String {
self.0.as_hex()
}
}

impl FromStr for DockerSha256Digest {
type Err = DockerDigestParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let hex_str = s
.strip_prefix(SHA256_PREFIX)
.ok_or(DockerDigestParseError::MissingPrefix)?;
Ok(DockerSha256Digest(hex_str.parse()?))
}
}

impl Serialize for DockerSha256Digest {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_string().serialize(serializer)
}
}

impl<'de> Deserialize<'de> for DockerSha256Digest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
}

mod paths {}

#[cfg(test)]
mod tests {
use assert_matches::assert_matches;

use super::types::{ApprovedHashes, DockerDigestParseError, DockerSha256Digest};
use mpc_primitives::hash::MpcDockerImageHash;

fn sample_digest() -> DockerSha256Digest {
let hash: MpcDockerImageHash = [0xab; 32].into();
DockerSha256Digest::from(hash)
}

#[test]
fn serialize_docker_digest() {
let digest = sample_digest();
let json = serde_json::to_value(&digest).unwrap();
insta::assert_json_snapshot!("docker_digest", json);
}

#[test]
fn roundtrip_docker_digest() {
let digest = sample_digest();
let serialized = serde_json::to_string(&digest).unwrap();
let deserialized: DockerSha256Digest = serde_json::from_str(&serialized).unwrap();
insta::assert_json_snapshot!(
"docker_digest_roundtrip",
serde_json::to_value(&deserialized).unwrap()
);
}

#[test]
fn deserialize_rejects_missing_prefix() {
let json = serde_json::json!(
"abababababababababababababababababababababababababababababababababab"
);
assert_matches!(
serde_json::from_value::<DockerSha256Digest>(json),
Err(ref e) if e.to_string().contains("missing sha256: prefix")
);
}

#[test]
fn deserialize_rejects_invalid_hex() {
let json = serde_json::json!("sha256:not_valid_hex!");
assert_matches!(serde_json::from_value::<DockerSha256Digest>(json), Err(_));
}

#[test]
fn deserialize_rejects_wrong_length() {
let json = serde_json::json!("sha256:abab");
assert_matches!(serde_json::from_value::<DockerSha256Digest>(json), Err(_));
}

#[test]
fn display_docker_digest() {
let digest = sample_digest();
insta::assert_snapshot!("docker_digest_display", digest.to_string());
}

#[test]
fn parse_docker_digest() {
let input = "sha256:abababababababababababababababababababababababababababababababab";
let parsed: DockerSha256Digest = input.parse().unwrap();
assert_eq!(parsed.to_string(), input);
}

#[test]
fn parse_rejects_missing_prefix() {
let result = "abababababababababababababababababababababababababababababababababab"
.parse::<DockerSha256Digest>();
assert_matches!(result, Err(DockerDigestParseError::MissingPrefix));
}

#[test]
fn parse_rejects_invalid_hex() {
let result = "sha256:not_valid_hex!".parse::<DockerSha256Digest>();
assert_matches!(result, Err(DockerDigestParseError::InvalidHash(_)));
}

#[test]
fn parse_rejects_wrong_length() {
let result = "sha256:abab".parse::<DockerSha256Digest>();
assert_matches!(result, Err(DockerDigestParseError::InvalidHash(_)));
}

#[test]
fn serialize_approved_hashes_file() {
let file = ApprovedHashes {
approved_hashes: near_mpc_bounded_collections::NonEmptyVec::from_vec(vec![
sample_digest(),
])
.unwrap(),
};
let json = serde_json::to_value(&file).unwrap();
insta::assert_json_snapshot!("approved_hashes_file", json);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/launcher-interface/src/lib.rs
expression: json
---
{
"approved_hashes": [
"sha256:abababababababababababababababababababababababababababababababab"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/launcher-interface/src/lib.rs
expression: json
---
"sha256:abababababababababababababababababababababababababababababababab"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/launcher-interface/src/lib.rs
expression: digest.to_string()
---
sha256:abababababababababababababababababababababababababababababababab
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
source: crates/launcher-interface/src/lib.rs
expression: "serde_json::to_value(&deserialized).unwrap()"
---
"sha256:abababababababababababababababababababababababababababababababab"
1 change: 1 addition & 0 deletions crates/mpc-attestation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ borsh = { workspace = true }
derive_more = { workspace = true }
hex = { workspace = true }
include-measurements = { workspace = true }
launcher-interface = { workspace = true }
mpc-primitives = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down
3 changes: 1 addition & 2 deletions crates/mpc-attestation/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@ pub use attestation::measurements::ExpectedMeasurements;
use mpc_primitives::hash::{LauncherDockerComposeHash, MpcDockerImageHash};

use borsh::{BorshDeserialize, BorshSerialize};
use launcher_interface::MPC_IMAGE_HASH_EVENT;
use serde::{Deserialize, Serialize};
use sha2::{Digest as _, Sha256};

use crate::alloc::format;
use crate::alloc::string::ToString;

const MPC_IMAGE_HASH_EVENT: &str = "mpc-image-digest";

// TODO(#1639): extract timestamp from certificate itself
pub const DEFAULT_EXPIRATION_DURATION_SECONDS: u64 = 60 * 60 * 24 * 7; // 7 days

Expand Down
Loading
Loading