use alloy::{
primitives::Address,
providers::Provider,
};
use crate::{
artifacts::Artifacts,
deploy::deploy_contract_from,
error::{
Error,
Result,
},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Circuit {
BearerLink,
OidcGoogle,
}
impl Circuit {
pub const ALL: [Self; 2] = [Self::BearerLink, Self::OidcGoogle];
pub const fn name(self) -> &'static str {
match self {
Self::BearerLink => "bearer-link",
Self::OidcGoogle => "oidc-google",
}
}
pub const fn contract(self) -> &'static str {
match self {
Self::BearerLink => "BearerLinkHonkVerifier",
Self::OidcGoogle => "OidcGoogleHonkVerifier",
}
}
}
pub const LIBRARIES: [&str; 2] = ["RelationsLib", "ZKTranscriptLib"];
pub fn version(artifacts: &Artifacts) -> Result<String> {
let pin: serde_json::Value = artifacts.read_json("circuits.json")?;
pin["version"]
.as_str()
.map(str::to_owned)
.ok_or_else(|| Error::Artifact {
detail: "circuits.json has no version".into(),
})
}
pub async fn deploy_honk_verifier<P: Provider>(
provider: &P,
artifacts: &Artifacts,
circuit: Circuit,
sender: Option<Address>,
) -> Result<Address> {
let contract = circuit.contract();
let bytecode = artifacts
.linked_bytecode(provider, contract, contract, sender)
.await?;
deploy_contract_from(
provider,
bytecode,
&format!("{contract} ({} circuit)", circuit.name()),
sender,
)
.await
}
#[cfg(test)]
mod tests {
use super::*;
use crate::artifacts::COVERED;
#[test]
fn every_verifier_and_its_libraries_are_covered() {
let artifacts = Artifacts::embedded();
for circuit in Circuit::ALL {
let contract = circuit.contract();
assert!(
COVERED.contains(&(contract, contract)),
"{contract} is not in COVERED"
);
for library in LIBRARIES {
assert!(
COVERED.contains(&(contract, library)),
"{contract}.sol:{library} is not in COVERED"
);
}
let refs = artifacts.link_references(contract, contract).unwrap();
let mut linked: Vec<(String, String)> = refs
.iter()
.flat_map(|(path, libs)| {
let stem = std::path::Path::new(path)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or_else(|| panic!("{contract}: bad library path {path}"))
.to_owned();
libs.as_object()
.into_iter()
.flatten()
.map(move |(name, _)| (stem.clone(), name.clone()))
})
.collect();
linked.sort();
let mut expected: Vec<(String, String)> = LIBRARIES
.iter()
.map(|lib| (contract.to_owned(), (*lib).to_owned()))
.collect();
expected.sort();
assert_eq!(linked, expected, "{contract} links something else");
}
}
#[test]
fn the_two_circuits_are_different_artifacts() {
let artifacts = Artifacts::embedded();
let [bearer, oidc] = Circuit::ALL;
assert_ne!(bearer.contract(), oidc.contract());
assert_ne!(
artifacts
.bytecode_hex(bearer.contract(), bearer.contract())
.unwrap(),
artifacts
.bytecode_hex(oidc.contract(), oidc.contract())
.unwrap()
);
}
#[test]
fn the_enum_matches_the_pin() {
let artifacts = Artifacts::embedded();
let pin: serde_json::Value = artifacts.read_json("circuits.json").unwrap();
let circuits = pin["circuits"].as_object().expect("circuits object");
assert_eq!(circuits.len(), Circuit::ALL.len());
for circuit in Circuit::ALL {
let entry = &circuits[circuit.name()];
assert_eq!(entry["contract"].as_str(), Some(circuit.contract()));
let digest = entry["sha256"].as_str().expect("sha256 string");
assert_eq!(digest.len(), 64, "{}: not a sha256", circuit.name());
}
let version = version(&artifacts).unwrap();
assert!(
version.split('.').count() == 3,
"circuits.json version '{version}' is not major.minor.patch"
);
}
}