use trident::tir::TIROp;
use trisha_rs::lower::{StackLowering, TritonLowering};
use triton_vm::prelude::*;
fn execute(ops: Vec<TIROp>, input: Vec<u64>) -> Vec<u64> {
let assembly = TritonLowering::new().lower(&ops).join("\n") + "\nhalt";
let program = Program::from_code(&assembly).expect("legal Triton instructions");
VM::run(
program,
PublicInput::new(input.into_iter().map(BFieldElement::new).collect()),
NonDeterminism::default(),
)
.expect("legalized program executes")
.into_iter()
.map(|v| v.value())
.collect()
}
#[test]
fn deep_stack_operations_preserve_every_word() {
for depth in [16, 17, 31, 64] {
for duplicate in [false, true] {
let mut ops: Vec<_> = (0..=depth).map(|v| TIROp::Push(u64::from(v))).collect();
ops.push(if duplicate {
TIROp::Dup(depth)
} else {
TIROp::Swap(depth)
});
ops.push(TIROp::WriteIo(depth + 1 + u32::from(duplicate)));
let mut expected: Vec<_> = (0..=u64::from(depth)).rev().collect();
if duplicate {
expected.insert(0, 0);
} else {
expected.swap(0, depth as usize);
}
assert_eq!(execute(ops, vec![]), expected);
}
}
}
#[test]
fn wide_io_and_pop_are_legalized() {
let input: Vec<_> = (0..13).collect();
assert_eq!(
execute(vec![TIROp::ReadIo(13), TIROp::WriteIo(13)], input),
(0..13).rev().collect::<Vec<_>>()
);
assert_eq!(
execute(
vec![
TIROp::Push(99),
TIROp::ReadIo(13),
TIROp::Pop(13),
TIROp::WriteIo(1)
],
(0..13).collect()
),
vec![99]
);
let mut memory: Vec<_> = (0..13).map(TIROp::Push).collect();
memory.extend([
TIROp::Push(200),
TIROp::WriteMem(13),
TIROp::Pop(1),
TIROp::Push(212),
TIROp::ReadMem(13),
TIROp::Pop(1),
TIROp::WriteIo(13),
]);
assert_eq!(execute(memory, vec![]), (0..13).rev().collect::<Vec<_>>());
}
#[test]
fn embedded_package_owns_network_and_modules() {
let package = trisha_rs::target::package("neptune").expect("embedded package");
package.validate().expect("package integrity");
assert_eq!(package.terrain.digest_width, 5);
assert_eq!(package.states.len(), 3);
let local = package
.states
.iter()
.find(|state| state.name == "local-testnet1")
.expect("explicit local real-proof state");
assert_eq!(local.chain_id, "4");
assert_eq!(local.rpc_url, "http://127.0.0.1:29799");
assert!(!local.is_default);
assert!(package.modules.contains_key("os.neptune.kernel"));
assert!(!package.runtime.deploy);
let actual: std::collections::BTreeSet<_> = triton_vm::isa::instruction::ALL_INSTRUCTION_NAMES
.into_iter()
.map(str::to_owned)
.collect();
assert_eq!(
package
.instructions
.into_iter()
.collect::<std::collections::BTreeSet<_>>(),
actual
);
assert!(trisha_rs::target::package("nox").is_err());
}
#[test]
fn incorrect_intrinsic_abi_is_rejected_before_lowering() {
let directory = tempfile::tempdir().unwrap();
let source = directory.path().join("wrong.tri");
std::fs::write(
&source,
"module wrong\n#[intrinsic(hash)]\npub fn wrong(x: Field) -> Digest\n",
)
.unwrap();
let error = trisha_rs::build_tasm(&source, "triton", "debug").unwrap_err();
assert!(
error.contains("intrinsic") && error.contains("hash"),
"{error}"
);
}
#[test]
fn neptune_sdk_requires_neptune_target() {
let bare = trisha_rs::target::package("triton").unwrap();
assert!(bare.modules.keys().all(|name| !name.starts_with("os.")));
assert!(bare.union.is_none() && bare.states.is_empty());
let directory = tempfile::tempdir().unwrap();
let path = directory.path().join("network.tri");
std::fs::write(
&path,
"program network\nuse os.neptune.xfield\nfn main() { pub_write(1) }\n",
)
.unwrap();
assert!(trisha_rs::build_tasm(&path, "triton", "debug").is_err());
assert!(trisha_rs::build_tasm(&path, "neptune", "debug").is_ok());
}
#[test]
fn typed_extension_dot_steps_execute_with_both_pointers() {
let directory = tempfile::tempdir().unwrap();
for (operation, next_b) in [("xx_dot_step", 303), ("xb_dot_step", 301)] {
let path = directory.path().join("dot.tri");
let source = format!("program dot\nuse os.neptune.xfield\nfn main() {{\nram_write(200,2)\nram_write(201,3)\nram_write(202,4)\nram_write(300,5)\nram_write(301,0)\nram_write(302,0)\nlet (acc,a,b) = xfield.{operation}(xfield.new(1,2,3),200,300)\nlet (c0,c1,c2) = acc\npub_write(c0)\npub_write(c1)\npub_write(c2)\npub_write(a)\npub_write(b)\n}}\n");
std::fs::write(&path, source).unwrap();
let assembly = trisha_rs::build_tasm(&path, "neptune", "debug").unwrap();
let output = VM::run(
Program::from_code(&assembly).unwrap(),
PublicInput::default(),
NonDeterminism::default(),
)
.unwrap();
assert_eq!(
output.into_iter().map(|v| v.value()).collect::<Vec<_>>(),
vec![11, 17, 23, 203, next_b]
);
}
}
#[test]
fn extension_inverse_preserves_coefficient_order() {
let directory = tempfile::tempdir().unwrap();
let path = directory.path().join("inverse.tri");
std::fs::write(&path, "program inverse\nuse os.neptune.xfield\nfn main() {\nlet (a,b,c) = xfield.inv(xfield.new(2,3,4))\npub_write(a)\npub_write(b)\npub_write(c)\n}\n").unwrap();
let assembly = trisha_rs::build_tasm(&path, "neptune", "debug").unwrap();
let output = VM::run(
Program::from_code(&assembly).unwrap(),
PublicInput::default(),
NonDeterminism::default(),
)
.unwrap();
let expected = XFieldElement::new([2, 3, 4].map(BFieldElement::new)).inverse();
assert_eq!(output, expected.coefficients);
}
#[test]
fn local_functions_shadow_imported_and_builtin_names_at_execution() {
let directory = tempfile::tempdir().unwrap();
for (imports, name, increment, expected) in [
("use vm.crypto.hash\n", "native", 7, 11),
("", "ram_read", 9, 13),
] {
let path = directory.path().join("shadow.tri");
let source = format!("program shadow\n{imports}fn {name}(x: Field) -> Field {{ x + {increment} }}\nfn main() {{ pub_write({name}(4)) }}\n");
std::fs::write(&path, source).unwrap();
let assembly = trisha_rs::build_tasm(&path, "triton", "debug").unwrap();
let output = VM::run(
Program::from_code(&assembly).unwrap(),
PublicInput::default(),
NonDeterminism::default(),
)
.unwrap();
assert_eq!(
output,
vec![BFieldElement::new(expected)],
"local function {name} must be called"
);
}
}
#[test]
fn checked_u32_wrapper_rejects_high_limbs() {
let directory = tempfile::tempdir().unwrap();
let path = directory.path().join("checked.tri");
std::fs::write(&path, "program checked\nfn checked(x: Field) -> U32 { as_u32(x) }\nfn field(x: U32) -> Field { as_field(x) }\nfn main() { pub_write(field(checked(pub_read()))) }\n").unwrap();
let assembly = trisha_rs::build_tasm(&path, "triton", "release").unwrap();
let program = Program::from_code(&assembly).unwrap();
let maximum = BFieldElement::new(u64::from(u32::MAX));
assert_eq!(
VM::run(
program.clone(),
PublicInput::new(vec![maximum]),
NonDeterminism::default()
)
.unwrap(),
vec![maximum]
);
assert!(VM::run(
program,
PublicInput::new(vec![BFieldElement::new(1 << 32)]),
NonDeterminism::default()
)
.is_err());
}
#[test]
fn power_uses_base_then_exponent_source_order() {
for (base, exponent, expected) in [(3, 5, 243), (5, 3, 125), (7, 0, 1)] {
assert_eq!(
execute(
vec![
TIROp::Push(base),
TIROp::Push(exponent),
TIROp::Pow,
TIROp::WriteIo(1)
],
vec![]
),
vec![expected]
);
}
}
#[test]
fn owner_legalizes_wide_source_frames_in_both_profiles() {
let width = 40;
let reverse = (0..width)
.rev()
.map(|i| format!("words[{i}]"))
.collect::<Vec<_>>()
.join(", ");
let reads = vec!["pub_read()"; width].join(", ");
let mut source = format!("program deep_frames\nfn reverse(words: [Field; {width}]) -> [Field; {width}] {{ [{reverse}] }}\nfn main() {{ let sentinel: Field = 997\nlet words: [Field; {width}] = [{reads}]\nlet reversed = reverse(words)\n");
for name in ["reversed", "words"] {
for i in 0..width {
source.push_str(&format!("pub_write({name}[{i}])\n"));
}
}
source.push_str("pub_write(sentinel)\n}");
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("deep_frames.tri");
std::fs::write(&path, source).unwrap();
let input: Vec<u64> = (1..=width as u64).collect();
let expected: Vec<u64> = input
.iter()
.rev()
.chain(input.iter())
.copied()
.chain([997])
.collect();
for profile in ["debug", "release"] {
let assembly = trisha_rs::build_tasm(&path, "triton", profile).unwrap();
assert!(
assembly.contains("stack_scratch"),
"owner scratch must handle deep access"
);
let actual = VM::run(
Program::from_code(&assembly).unwrap(),
PublicInput::new(input.iter().copied().map(BFieldElement::new).collect()),
NonDeterminism::default(),
)
.unwrap();
assert_eq!(
actual.iter().map(|v| v.value()).collect::<Vec<_>>(),
expected,
"{profile}"
);
}
}