#![allow(clippy::disallowed_names)]
use essential_types::{ContentAddress, Word};
use pint_pkg::{
build::{build_plan, BuiltPkg},
manifest::PackageKind,
};
use std::collections::HashMap;
use util::{edit_manifest, insert_dep, new_pkg, with_temp_dir};
mod util;
#[test]
fn build_default_contract() {
with_temp_dir(|dir| {
let foo = new_pkg(&dir.join("foo"), PackageKind::Contract);
let members = [(foo.pkg.name.to_string(), foo)].into_iter().collect();
let plan = pint_pkg::plan::from_members(&members).unwrap();
let _built = build_plan(&plan).build_all(&Default::default()).unwrap();
});
}
#[test]
fn build_default_library() {
with_temp_dir(|dir| {
let foo = new_pkg(&dir.join("foo"), PackageKind::Library);
let members = [(foo.pkg.name.to_string(), foo)].into_iter().collect();
let plan = pint_pkg::plan::from_members(&members).unwrap();
let _built = build_plan(&plan).build_all(&Default::default()).unwrap();
});
}
#[test]
fn build_default_contract_with_salt() {
with_temp_dir(|dir| {
let foo = new_pkg(&dir.join("foo"), PackageKind::Contract);
let members = [(foo.pkg.name.to_string(), foo.clone())]
.into_iter()
.collect();
let plan = pint_pkg::plan::from_members(&members).unwrap();
let built = build_plan(&plan)
.build_all(&pint_pkg::build::BuildOptions {
salts: HashMap::from_iter([(foo, [42; 32])]),
..Default::default()
})
.unwrap();
let order = plan.compilation_order();
let BuiltPkg::Contract(ref contract) = built[&order[0]] else {
panic!("expected last built package `foo` to be a contract");
};
assert!(contract.contract.salt == [42; 32]);
});
}
#[test]
fn build_contract_one_lib_dep() {
const BAR_SRC: &str = r#"type Age = int;"#;
const FOO_SRC: &str = r#"
use bar::Age;
predicate test(bob_age: Age) {
constraint bob_age == 42;
constraint bob_age == 6 * 7;
}
"#;
with_temp_dir(|dir| {
let mut foo = new_pkg(&dir.join("foo"), PackageKind::Contract);
let bar = new_pkg(&dir.join("bar"), PackageKind::Library);
std::fs::write(foo.entry_point(), FOO_SRC.as_bytes()).unwrap();
std::fs::write(bar.entry_point(), BAR_SRC.as_bytes()).unwrap();
edit_manifest(&mut foo, |m| insert_dep(m, &bar));
let members = [(foo.pkg.name.to_string(), foo)].into_iter().collect();
let plan = pint_pkg::plan::from_members(&members).unwrap();
let built_pkgs = match build_plan(&plan).build_all(&Default::default()) {
Ok(built) => built,
Err(err) => {
err.pkg_err.print_diagnostics();
panic!()
}
};
let order = plan.compilation_order();
assert_eq!(order.len(), 2, "expected two nodes in the plan");
let bar_n = order[0];
let built_bar = &built_pkgs[&bar_n];
assert!(
matches!(built_bar, &pint_pkg::build::BuiltPkg::Library(_)),
"expected first built package `bar` to be a library"
);
let foo_n = order[1];
let BuiltPkg::Contract(contract) = &built_pkgs[&foo_n] else {
panic!("expected last built package `foo` to be a contract");
};
assert_eq!(&contract.predicate_metadata[0].name, "::test");
let predicate = &contract.contract.predicates[0];
assert!(
!predicate.nodes.is_empty(),
"built foo should have at least one compute node"
);
});
}
#[test]
fn build_contract_with_transitive_dep() {
const BAZ_SRC: &str = "type Age = int;";
const BAR_SRC: &str = r#"use baz::Age;
type Person = {
age: Age,
};"#;
const FOO_SRC: &str = r#"
use bar::Person;
predicate test(bob: { age: int }) {
constraint bob == {
age: 42,
};
constraint bob.age == 6 * 7;
}
"#;
with_temp_dir(|dir| {
let mut foo = new_pkg(&dir.join("foo"), PackageKind::Contract);
let mut bar = new_pkg(&dir.join("bar"), PackageKind::Library);
let baz = new_pkg(&dir.join("baz"), PackageKind::Library);
std::fs::write(foo.entry_point(), FOO_SRC.as_bytes()).unwrap();
std::fs::write(bar.entry_point(), BAR_SRC.as_bytes()).unwrap();
std::fs::write(baz.entry_point(), BAZ_SRC.as_bytes()).unwrap();
edit_manifest(&mut foo, |m| insert_dep(m, &bar));
edit_manifest(&mut bar, |m| insert_dep(m, &baz));
let members = [(foo.pkg.name.to_string(), foo)].into_iter().collect();
let plan = pint_pkg::plan::from_members(&members).unwrap();
let built_pkgs = match build_plan(&plan).build_all(&Default::default()) {
Ok(built) => built,
Err(err) => {
err.pkg_err.print_diagnostics();
panic!()
}
};
let order = plan.compilation_order();
assert_eq!(order.len(), 3, "expected three nodes in the plan");
let baz_n = order[0];
let built_baz = &built_pkgs[&baz_n];
assert!(
matches!(built_baz, &pint_pkg::build::BuiltPkg::Library(_)),
"expected first built package `baz` to be a library"
);
let bar_n = order[1];
let built_bar = &built_pkgs[&bar_n];
assert!(
matches!(built_bar, &pint_pkg::build::BuiltPkg::Library(_)),
"expected second built package `bar` to be a library"
);
let foo_n = order[2];
let BuiltPkg::Contract(contract) = &built_pkgs[&foo_n] else {
panic!("expected last built package `foo` to be a contract");
};
assert_eq!(&contract.predicate_metadata[0].name, "::test");
let predicate = &contract.contract.predicates[0];
assert!(
!predicate.nodes.is_empty(),
"built foo should have at least one compute node"
);
});
}
#[test]
fn build_contract_one_contract_dep() {
const BAR_SRC: &str = r#"storage {
counter: int,
}
predicate Init(value: int) {
constraint storage::counter := value;
}
predicate Increment() {
constraint storage::counter := storage::counter! + 1;
}
"#;
const FOO_SRC: &str = r#"
predicate test() {
// The top-level contract address.
constraint bar::ADDRESS != 0x0000000000000000000000000000000000000000000000000000000000000000;
// The predicate addresses.
constraint bar::Init::ADDRESS != 0x0000000000000000000000000000000000000000000000000000000000000000;
constraint bar::Increment::ADDRESS != 0x0000000000000000000000000000000000000000000000000000000000000000;
}
"#;
with_temp_dir(|dir| {
let bar = new_pkg(&dir.join("bar"), PackageKind::Contract);
std::fs::write(bar.entry_point(), BAR_SRC.as_bytes()).unwrap();
let mut foo = new_pkg(&dir.join("foo"), PackageKind::Contract);
std::fs::write(foo.entry_point(), FOO_SRC.as_bytes()).unwrap();
edit_manifest(&mut foo, |m| insert_dep(m, &bar));
let members = [(foo.pkg.name.to_string(), foo)].into_iter().collect();
let plan = pint_pkg::plan::from_members(&members).unwrap();
let built_pkgs = match build_plan(&plan).build_all(&pint_pkg::build::BuildOptions {
skip_optimize: true,
..Default::default()
}) {
Ok(built) => built,
Err(err) => {
err.pkg_err.print_diagnostics();
panic!()
}
};
let order = plan.compilation_order();
assert_eq!(order.len(), 2, "expected two nodes in the plan");
let bar_n = order[0];
let built_bar = &built_pkgs[&bar_n];
let BuiltPkg::Contract(bar_contract) = &built_bar else {
panic!("expected first built package `bar` to be a library");
};
fn predicate_ca<'a>(
contract: &'a pint_pkg::build::BuiltContract,
name: &str,
) -> &'a ContentAddress {
&contract
.predicate_metadata
.iter()
.find(|i| i.name.contains(name))
.unwrap()
.ca
}
let bar_ca = &bar_contract.ca;
let bar_init_ca = predicate_ca(bar_contract, "Init");
let bar_increment_ca = predicate_ca(bar_contract, "Increment");
let foo_n = order[1];
let built_foo = &built_pkgs[&foo_n];
let BuiltPkg::Contract(foo_contract) = built_foo else {
panic!("expected last built package `foo` to be a contract");
};
fn constraints_contain_ca(
contract: &pint_pkg::build::BuiltContract,
ca: &ContentAddress,
) -> bool {
let get_program = contract
.programs
.iter()
.map(|program| (essential_hash::content_addr(program), program))
.collect::<HashMap<_, _>>();
for ca_chunk in ca.0.chunks(std::mem::size_of::<Word>()) {
let mut contains_chunk = false;
for predicate in &contract.contract.predicates {
for node in &predicate.nodes {
if get_program
.get(&node.program_address)
.unwrap()
.0
.windows(ca_chunk.len())
.any(|w| w == ca_chunk)
{
contains_chunk |= true;
}
}
}
if !contains_chunk {
return false;
}
}
true
}
assert!(constraints_contain_ca(foo_contract, bar_ca));
assert!(constraints_contain_ca(foo_contract, bar_init_ca));
assert!(constraints_contain_ca(foo_contract, bar_increment_ca));
});
}