use std::path::Path;
#[allow(dead_code)]
#[path = "support/plumb_v2.rs"]
mod plumb_vectors;
fn assert_plumb_v2_path_authentication(tasm: &str, case: plumb_vectors::Case) {
use triton_vm::prelude::*;
assert!(tasm.contains("os_neptune_standards_plumb__update_leaf:"));
assert!(
tasm.contains("assert_vector"),
"missing full digest assertion"
);
let program = Program::from_code(tasm).unwrap();
let run = |public: &[u64], secret: &[u64]| {
VM::run(
program.clone(),
PublicInput::new(
public
.iter()
.copied()
.map(BFieldElement::new)
.collect::<Vec<_>>(),
),
NonDeterminism::new(
secret
.iter()
.copied()
.map(BFieldElement::new)
.collect::<Vec<_>>(),
),
)
};
let output =
run(&case.public, &case.secret).expect("independent same-path transition must execute");
assert_eq!(
output.iter().map(|x| x.value()).collect::<Vec<_>>(),
case.output
);
for root_start in [1, 6] {
for coordinate in 0..5 {
let mut public = case.public.clone();
public[root_start + coordinate] ^= 1;
assert!(
run(&public, &case.secret).is_err(),
"unauthenticated root coordinate accepted"
);
}
}
let mut secret = case.secret.clone();
*secret.last_mut().unwrap() ^= 1;
assert!(
run(&case.public, &secret).is_err(),
"changed shared path accepted"
);
secret.pop();
assert!(
run(&case.public, &secret).is_err(),
"truncated shared path accepted"
);
}
#[allow(dead_code)]
fn compile_triton(source: &str, filename: &str) -> Result<String, String> {
let dir = tempfile::tempdir().map_err(|e| e.to_string())?;
let path = dir.path().join(filename);
std::fs::write(&path, source).map_err(|e| e.to_string())?;
trisha_rs::build_tasm(
&path,
if source.contains("os.neptune") {
"neptune"
} else {
"triton"
},
"debug",
)
}
#[allow(dead_code)]
fn compile_triton_profile(source: &str, filename: &str, profile: &str) -> Result<String, String> {
let dir = tempfile::tempdir().map_err(|e| e.to_string())?;
let path = dir.path().join(filename);
std::fs::write(&path, source).map_err(|e| e.to_string())?;
trisha_rs::build_tasm(&path, "triton", profile)
}
#[allow(dead_code)]
fn compile_project_triton(path: &Path) -> Result<String, String> {
trisha_rs::build_tasm(
path,
if path.components().any(|part| part.as_os_str() == "neptune") {
"neptune"
} else {
"triton"
},
"debug",
)
}
#[allow(dead_code)]
fn trident_repo_path(rel: &str) -> std::path::PathBuf {
if let Some(name) = rel.strip_prefix("os/neptune/") {
if name.starts_with("locks/")
|| name.starts_with("types/")
|| matches!(name, "standards/coin.tri" | "standards/card.tri")
{
return Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../examples/neptune")
.join(name);
}
}
if let Some(name) = rel.strip_prefix("os/neptune/programs/") {
return Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../examples/experimental/neptune")
.join(name);
}
if rel.starts_with("os/neptune/") || rel.starts_with("vm/triton/") {
return Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../lib")
.join(rel);
}
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../trident/lib")
.join(rel)
}
#[test]
fn test_compile_valid_program() {
let source =
"program test\nfn main() {\n let x: Field = pub_read()\n pub_write(x + 1)\n}";
let result = compile_triton(source, "test.tri");
assert!(result.is_ok());
let tasm = result.unwrap();
assert!(tasm.contains("read_io 1"));
assert!(tasm.contains("write_io 1"));
}
#[test]
fn test_compile_type_error_returns_err() {
let source = "program test\nfn main() {\n let x: U32 = pub_read()\n}";
let result = compile_triton(source, "test.tri");
assert!(result.is_err());
}
#[test]
fn test_deeply_nested_if() {
let source = r#"program test
fn main() {
let x: Field = pub_read()
if x == 0 {
if x == 1 {
if x == 2 {
if x == 3 {
if x == 4 {
pub_write(x)
}
}
}
}
}
}
"#;
assert!(compile_triton(source, "test.tri").is_ok());
}
#[test]
fn test_deeply_nested_for() {
let source = r#"program test
fn main() {
let mut s: Field = 0
for i in 0..3 bounded 3 {
for j in 0..3 bounded 3 {
for k in 0..3 bounded 3 {
s = s + 1
}
}
}
pub_write(s)
}
"#;
let result = compile_triton(source, "test.tri");
assert!(result.is_ok());
let tasm = result.unwrap();
assert!(tasm.contains("write_io 1"));
}
#[test]
fn test_many_variables_spill() {
let source = r#"program test
fn main() {
let a: Field = pub_read()
let b: Field = pub_read()
let c: Field = pub_read()
let d: Field = pub_read()
let e: Field = pub_read()
let f: Field = pub_read()
let g: Field = pub_read()
let h: Field = pub_read()
let i: Field = pub_read()
let j: Field = pub_read()
let k: Field = pub_read()
let l: Field = pub_read()
let m: Field = pub_read()
let n: Field = pub_read()
let o: Field = pub_read()
let p: Field = pub_read()
let q: Field = pub_read()
let r: Field = pub_read()
pub_write(a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r)
}
"#;
let result = compile_triton(source, "test.tri");
assert!(
result.is_ok(),
"should handle 18 live variables with spilling"
);
let tasm = result.unwrap();
assert!(
tasm.contains("write_io 1"),
"18-variable sum should produce write_io"
);
}
#[test]
fn test_chain_of_function_calls() {
let source = r#"program test
fn add1(x: Field) -> Field {
x + 1
}
fn add2(x: Field) -> Field {
add1(add1(x))
}
fn add4(x: Field) -> Field {
add2(add2(x))
}
fn main() {
let x: Field = pub_read()
pub_write(add4(add4(x)))
}
"#;
let result = compile_triton(source, "test.tri");
assert!(result.is_ok());
}
#[test]
fn test_all_binary_operators() {
let source = r#"program test
fn main() {
let a: Field = pub_read()
let b: Field = pub_read()
let sum: Field = a + b
let prod: Field = a * b
let eq: Bool = a == b
let (hi, lo) = split(a)
let lt: Bool = hi < lo
let band: U32 = hi & lo
let bxor: U32 = hi ^ lo
let (q, r) = hi /% lo
pub_write(sum)
pub_write(prod)
}
"#;
assert!(compile_triton(source, "test.tri").is_ok());
}
#[test]
fn test_struct_with_digest_field() {
let source = r#"program test
struct AuthData {
owner: Digest,
nonce: Field,
}
fn main() {
let d: Digest = divine5()
let auth: AuthData = AuthData { owner: d, nonce: 42 }
pub_write(auth.nonce)
}
"#;
assert!(compile_triton(source, "test.tri").is_ok());
}
#[test]
fn test_xfield_operations() {
let source = r#"program test
fn main() {
let a: XField = xfield(1, 2, 3)
let s: Field = pub_read()
let c: XField = a *. s
let d: XField = xinvert(c)
pub_write(0)
}
"#;
assert!(compile_triton(source, "test.tri").is_ok());
}
#[test]
fn test_tail_expression() {
let source = r#"program test
fn double(x: Field) -> Field {
x + x
}
fn main() {
pub_write(double(pub_read()))
}
"#;
assert!(compile_triton(source, "test.tri").is_ok());
}
#[test]
fn test_multiple_return_paths() {
let source = r#"program test
fn abs_diff(a: Field, b: Field) -> Field {
if a == b {
return 0
}
a + b
}
fn main() {
pub_write(abs_diff(pub_read(), pub_read()))
}
"#;
assert!(compile_triton(source, "test.tri").is_ok());
}
#[test]
fn test_events_emit_and_seal() {
let source = r#"program test
event Transfer {
from: Field,
to: Field,
amount: Field,
}
event Commitment {
value: Field,
}
fn main() {
let a: Field = pub_read()
let b: Field = pub_read()
let c: Field = pub_read()
// Open reveal: tag + 3 fields written directly
reveal Transfer { from: a, to: b, amount: c }
// Sealed: hash(tag, value, 0...) written as digest
seal Commitment { value: a }
}
"#;
let tasm = compile_triton(source, "events.tri").expect("events program should compile");
let write_io_1 = tasm.lines().filter(|l| l.trim() == "write_io 1").count();
assert!(
write_io_1 >= 4,
"expected >= 4 write_io 1 (reveal tag + 3 fields), got {}",
write_io_1
);
assert!(tasm.contains("hash"), "seal should contain hash");
assert!(tasm.contains("write_io 5"), "seal should write_io 5");
eprintln!("Events TASM:\n{}", tasm);
}
#[test]
fn test_error_max_nesting_depth() {
let handle = std::thread::Builder::new()
.stack_size(16 * 1024 * 1024)
.spawn(|| {
let depth = 260u32;
let mut src = String::from("program t\nfn main() {\n");
for _ in 0..depth {
src.push_str("if true {\n");
}
src.push_str("pub_write(0)\n");
for _ in 0..depth {
src.push_str("}\n");
}
src.push_str("}\n");
let result = trident::parse_source_silent(&src, "t.tri");
assert!(
result.is_err(),
"deeply nested input should produce an error"
);
let diags = result.unwrap_err();
let has_depth = diags.iter().any(|d| d.message.contains("nesting depth"));
assert!(
has_depth,
"should report nesting depth exceeded, got: {:?}",
diags.iter().map(|d| &d.message).collect::<Vec<_>>()
);
})
.expect("failed to spawn test thread");
handle.join().expect("test thread panicked");
}
#[test]
fn test_coin_compiles() {
let path = trident_repo_path("os/neptune/standards/coin.tri");
if !path.exists() {
return;
}
let tasm = compile_project_triton(&path).expect("coin program should compile");
assert!(tasm.contains("__pay:"), "missing pay function");
assert!(tasm.contains("__mint:"), "missing mint function");
assert!(tasm.contains("__burn:"), "missing burn function");
assert!(tasm.contains("__lock:"), "missing lock function");
assert!(tasm.contains("__update:"), "missing update function");
assert!(tasm.contains("__hash_leaf:"), "missing hash_leaf function");
assert!(
tasm.contains("__hash_config:"),
"missing hash_config function"
);
assert!(
tasm.contains("__verify_auth:"),
"missing verify_auth function"
);
assert!(
tasm.contains("__verify_config:"),
"missing verify_config function"
);
let hash_count = tasm.lines().filter(|l| l.trim() == "hash").count();
assert!(
hash_count >= 5,
"expected at least 5 hash ops, got {}",
hash_count
);
assert!(
tasm.contains("write_io 5"),
"seal should produce write_io 5"
);
let assert_count = tasm
.lines()
.filter(|l| l.trim().starts_with("assert"))
.count();
assert!(
assert_count >= 6,
"expected at least 6 assertions, got {}",
assert_count
);
assert_plumb_v2_path_authentication(&tasm, plumb_vectors::coin_cases().remove(0));
eprintln!(
"Token TASM: {} lines, {} instructions",
tasm.lines().count(),
tasm.lines()
.filter(|l| l.starts_with(" ") && !l.trim().is_empty())
.count()
);
}
#[test]
fn test_card_compiles() {
let path = trident_repo_path("os/neptune/standards/card.tri");
if !path.exists() {
return;
}
let tasm = compile_project_triton(&path).expect("card program should compile");
assert!(tasm.contains("__pay:"), "missing pay function");
assert!(tasm.contains("__mint:"), "missing mint function");
assert!(tasm.contains("__burn:"), "missing burn function");
assert!(tasm.contains("__lock:"), "missing lock function");
assert!(tasm.contains("__update:"), "missing update function");
assert!(tasm.contains("__hash_leaf:"), "missing hash_leaf function");
assert!(
tasm.contains("__hash_config:"),
"missing hash_config function"
);
assert!(
tasm.contains("__verify_auth:"),
"missing verify_auth function"
);
assert!(
tasm.contains("__verify_config:"),
"missing verify_config function"
);
assert_plumb_v2_path_authentication(&tasm, plumb_vectors::card_cases().remove(0));
}