use ark_bn254::Fr;
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{PrimeField, Zero};
use eddsa_babyjubjub::EdDSAPublicKey;
use taceo_oprf::core::{dlog_equality::DLogEqualityProof, oprf::BlindingFactor};
use world_id_primitives::{
FieldElement,
authenticator::{AuthenticatorPublicKeySet, MAX_AUTHENTICATOR_KEYS},
circuit_inputs::{NullifierProofCircuitInput, QueryProofCircuitInput},
merkle::MerkleInclusionProof,
};
type BaseField = ark_babyjubjub::Fq;
type Affine = ark_babyjubjub::EdwardsAffine;
#[derive(Debug, thiserror::Error)]
pub enum ProofInputError {
#[error("The specified Merkle tree depth is invalid (expected: {expected}, got: {is}).")]
InvalidMerkleTreeDepth {
expected: usize,
is: BaseField,
},
#[error("The set of authenticator public keys is invalid.")]
InvalidAuthenticatorPublicKeySet,
#[error("The provided Merkle tree inclusion proof is invalid.")]
InvalidMerkleTreeInclusionProof,
#[error("The signature over the nonce and RP ID is invalid.")]
InvalidQuerySignature,
#[error("The provided blinding factor is invalid.")]
InvalidBlindingFactor,
#[error(
"The provided credential has expired (expires_at: {expires_at}, check_timestamp: {current_timestamp})."
)]
CredentialExpired {
current_timestamp: u64,
expires_at: u64,
},
#[error(
"The provided credential has a genesis issued at date that is too old (genesis_issued_at: {genesis_issued_at}, check_timestamp: {genesis_issued_at_min})."
)]
CredentialGenesisExpired {
genesis_issued_at_min: u64,
genesis_issued_at: u64,
},
#[error("The value '{name}' is out of bounds (got: {is}, limit: {limit}).")]
ValueOutOfBounds {
name: &'static str,
is: BaseField,
limit: BaseField,
},
#[error("The credential signature is invalid for the given issuer public key.")]
InvalidCredentialSignature,
#[error(
"The provided point '{name}' is not a valid point in the prime-order subgroup of the BabyJubJub curve."
)]
InvalidBabyJubJubPoint {
name: &'static str,
},
#[error("The provided OPRF DlogEquality proof is invalid.")]
InvalidOprfProof,
#[error("The provided unblinded OPRF response point is invalid.")]
InvalidOprfResponse,
#[error(
"The provided session ID commitment is invalid for the given id and session id randomness."
)]
InvalidSessionId,
}
pub fn check_query_input_validity<const TREE_DEPTH: usize>(
inputs: &QueryProofCircuitInput<TREE_DEPTH>,
) -> Result<Affine, ProofInputError> {
if inputs.depth != BaseField::new((TREE_DEPTH as u64).into()) {
return Err(ProofInputError::InvalidMerkleTreeDepth {
expected: TREE_DEPTH,
is: inputs.depth,
});
}
let idx_u64 = u64::try_from(FieldElement::from(inputs.mt_index)).map_err(|_| {
ProofInputError::ValueOutOfBounds {
name: "Merkle tree index",
is: inputs.mt_index,
limit: BaseField::new((1u64 << TREE_DEPTH).into()),
}
})?;
if idx_u64 >= (1u64 << TREE_DEPTH) {
return Err(ProofInputError::ValueOutOfBounds {
name: "Merkle tree index",
is: inputs.mt_index,
limit: BaseField::new((1u64 << TREE_DEPTH).into()),
});
}
let pk_set = AuthenticatorPublicKeySet::new(
inputs
.pk
.iter()
.map(|&x| EdDSAPublicKey { pk: x })
.collect(),
)
.map_err(|_| ProofInputError::InvalidAuthenticatorPublicKeySet)?;
let pk_set_hash = pk_set.leaf_hash();
let merkle_tree_inclusion_proof = MerkleInclusionProof::new(
FieldElement::from(inputs.merkle_root),
idx_u64,
inputs.siblings.map(FieldElement::from),
);
if !merkle_tree_inclusion_proof.is_valid(FieldElement::from(pk_set_hash)) {
return Err(ProofInputError::InvalidMerkleTreeInclusionProof);
}
let pk_index_usize = usize::try_from(FieldElement::from(inputs.pk_index)).map_err(|_| {
ProofInputError::ValueOutOfBounds {
name: "Authenticator PubKey index",
is: inputs.pk_index,
limit: BaseField::new((MAX_AUTHENTICATOR_KEYS as u64).into()),
}
})?;
let pk = pk_set
.get(pk_index_usize)
.ok_or_else(|| ProofInputError::ValueOutOfBounds {
name: "Authenticator PubKey index",
is: inputs.pk_index,
limit: BaseField::new((MAX_AUTHENTICATOR_KEYS as u64).into()),
})?;
if !inputs.r.is_on_curve() || !inputs.r.is_in_correct_subgroup_assuming_on_curve() {
return Err(ProofInputError::InvalidBabyJubJubPoint {
name: "Query Signature R",
});
}
if !pk.pk.is_on_curve() || !pk.pk.is_in_correct_subgroup_assuming_on_curve() {
return Err(ProofInputError::InvalidBabyJubJubPoint {
name: "Authenticator Public Key",
});
}
let _rp_id_u64 = u64::try_from(FieldElement::from(inputs.rp_id)).map_err(|_| {
ProofInputError::ValueOutOfBounds {
name: "RP Id",
is: inputs.pk_index,
limit: BaseField::new((MAX_AUTHENTICATOR_KEYS as u64).into()),
}
})?;
let query = world_id_primitives::authenticator::oprf_query_digest(
idx_u64,
FieldElement::from(inputs.action),
FieldElement::from(inputs.rp_id),
);
let signature = eddsa_babyjubjub::EdDSASignature {
r: inputs.r,
s: inputs.s,
};
if !pk.verify(*query, &signature) {
return Err(ProofInputError::InvalidQuerySignature);
}
let blinding_factor = BlindingFactor::from_scalar(inputs.beta)
.map_err(|_| ProofInputError::InvalidBlindingFactor)?;
let query_point = taceo_oprf::core::oprf::client::blind_query(*query, blinding_factor);
Ok(query_point.blinded_query())
}
#[expect(
clippy::too_many_lines,
reason = "necessary checks for input validity should be in one function"
)]
pub fn check_nullifier_input_validity<const TREE_DEPTH: usize>(
inputs: &NullifierProofCircuitInput<TREE_DEPTH>,
) -> Result<FieldElement, ProofInputError> {
let blinded_query = check_query_input_validity(&inputs.query_input)?;
let current_timestamp_u64 = u64::try_from(FieldElement::from(inputs.current_timestamp))
.map_err(|_| ProofInputError::ValueOutOfBounds {
name: "current timestamp",
is: inputs.current_timestamp,
limit: BaseField::new(u64::MAX.into()),
})?;
let credential_expires_at_u64 = u64::try_from(FieldElement::from(inputs.cred_expires_at))
.map_err(|_| ProofInputError::ValueOutOfBounds {
name: "credential expiry timestamp",
is: inputs.current_timestamp,
limit: BaseField::new(u64::MAX.into()),
})?;
if credential_expires_at_u64 <= current_timestamp_u64 {
return Err(ProofInputError::CredentialExpired {
current_timestamp: current_timestamp_u64,
expires_at: credential_expires_at_u64,
});
}
let genesis_issued_at_u64 = u64::try_from(FieldElement::from(inputs.cred_genesis_issued_at))
.map_err(|_| ProofInputError::ValueOutOfBounds {
name: "credential genesis issued at",
is: inputs.cred_genesis_issued_at,
limit: BaseField::new(u64::MAX.into()),
})?;
let genesis_issued_at_min_u64 =
u64::try_from(FieldElement::from(inputs.cred_genesis_issued_at_min)).map_err(|_| {
ProofInputError::ValueOutOfBounds {
name: "credential genesis issued at minimum bound",
is: inputs.cred_genesis_issued_at_min,
limit: BaseField::new(u64::MAX.into()),
}
})?;
if genesis_issued_at_min_u64 > genesis_issued_at_u64 {
return Err(ProofInputError::CredentialGenesisExpired {
genesis_issued_at_min: genesis_issued_at_min_u64,
genesis_issued_at: genesis_issued_at_u64,
});
}
let blinded_subject = sub(
FieldElement::from(inputs.query_input.mt_index),
FieldElement::from(inputs.cred_sub_blinding_factor),
);
let cred_hash = hash_credential(
FieldElement::from(inputs.issuer_schema_id),
blinded_subject,
FieldElement::from(inputs.cred_genesis_issued_at),
FieldElement::from(inputs.cred_expires_at),
FieldElement::from(inputs.cred_hashes[0]),
FieldElement::from(inputs.cred_hashes[1]),
FieldElement::from(inputs.cred_id),
);
let pk = EdDSAPublicKey { pk: inputs.cred_pk };
let signature = eddsa_babyjubjub::EdDSASignature {
r: inputs.cred_r,
s: inputs.cred_s,
};
if !inputs.cred_r.is_on_curve() || !inputs.cred_r.is_in_correct_subgroup_assuming_on_curve() {
return Err(ProofInputError::InvalidBabyJubJubPoint {
name: "Credential Signature R",
});
}
if !pk.pk.is_on_curve() || !pk.pk.is_in_correct_subgroup_assuming_on_curve() {
return Err(ProofInputError::InvalidBabyJubJubPoint {
name: "Credential Public Key",
});
}
if !pk.verify(*cred_hash, &signature) {
return Err(ProofInputError::InvalidCredentialSignature);
}
if !inputs.oprf_pk.is_on_curve() || !inputs.oprf_pk.is_in_correct_subgroup_assuming_on_curve() {
return Err(ProofInputError::InvalidBabyJubJubPoint {
name: "OPRF Public Key",
});
}
if !inputs.oprf_response_blinded.is_on_curve()
|| !inputs
.oprf_response_blinded
.is_in_correct_subgroup_assuming_on_curve()
{
return Err(ProofInputError::InvalidBabyJubJubPoint {
name: "OPRF Blinded Response",
});
}
let dlog_proof = DLogEqualityProof::new(inputs.dlog_e, inputs.dlog_s);
dlog_proof
.verify(
inputs.oprf_pk,
blinded_query,
inputs.oprf_response_blinded,
Affine::generator(),
)
.map_err(|_| ProofInputError::InvalidOprfProof)?;
if !inputs.oprf_response.is_on_curve()
|| !inputs
.oprf_response
.is_in_correct_subgroup_assuming_on_curve()
{
return Err(ProofInputError::InvalidBabyJubJubPoint {
name: "OPRF Unblinded Response",
});
}
let expected_blinded_response = (inputs.oprf_response * inputs.query_input.beta).into_affine();
if expected_blinded_response != inputs.oprf_response_blinded {
return Err(ProofInputError::InvalidOprfResponse);
}
if !inputs.id_commitment.is_zero() {
let expected_commitment = session_id_commitment(
FieldElement::from(inputs.query_input.mt_index),
FieldElement::from(inputs.id_commitment_r),
);
if expected_commitment != FieldElement::from(inputs.id_commitment) {
return Err(ProofInputError::InvalidSessionId);
}
}
let nullifier = oprf_finalize_hash(
*world_id_primitives::authenticator::oprf_query_digest(
#[expect(
clippy::missing_panics_doc,
reason = "checked in check_query_input_validity"
)]
u64::try_from(FieldElement::from(inputs.query_input.mt_index)).unwrap(),
FieldElement::from(inputs.query_input.action),
FieldElement::from(inputs.query_input.rp_id),
),
inputs.oprf_response,
);
Ok(nullifier)
}
fn sub(leaf_index: FieldElement, blinding_factor: FieldElement) -> FieldElement {
let sub_ds = Fr::from_be_bytes_mod_order(b"H_CS(id, r)");
let mut input = [sub_ds, *leaf_index, *blinding_factor];
poseidon2::bn254::t3::permutation_in_place(&mut input);
input[1].into()
}
fn oprf_finalize_hash(query: BaseField, oprf_response: Affine) -> FieldElement {
let finalize_ds = Fr::from_be_bytes_mod_order(super::OPRF_PROOF_DS);
let mut input = [finalize_ds, query, oprf_response.x, oprf_response.y];
poseidon2::bn254::t4::permutation_in_place(&mut input);
input[1].into()
}
fn session_id_commitment(user_id: FieldElement, commitment_rand: FieldElement) -> FieldElement {
let sub_ds = Fr::from_be_bytes_mod_order(b"H(id, r)");
let mut input = [sub_ds, *user_id, *commitment_rand];
poseidon2::bn254::t3::permutation_in_place(&mut input);
input[1].into()
}
fn hash_credential(
issuer_schema_id: FieldElement,
sub: FieldElement,
genesis_issued_at: FieldElement,
expires_at: FieldElement,
claims_hash: FieldElement,
associated_data_commitment: FieldElement,
id: FieldElement,
) -> FieldElement {
let cred_ds = Fr::from_be_bytes_mod_order(b"POSEIDON2+EDDSA-BJJ");
let mut input = [
cred_ds,
*issuer_schema_id,
*sub,
*genesis_issued_at,
*expires_at,
*claims_hash,
*associated_data_commitment,
*id,
];
poseidon2::bn254::t8::permutation_in_place(&mut input);
input[1].into()
}
#[cfg(test)]
mod tests {
use ark_ec::twisted_edwards::Affine;
use std::str::FromStr;
use world_id_primitives::circuit_inputs::{NullifierProofCircuitInput, QueryProofCircuitInput};
use crate::proof::errors::{check_nullifier_input_validity, check_query_input_validity};
fn get_valid_query_proof_input() -> QueryProofCircuitInput<30> {
QueryProofCircuitInput {
pk: [Affine {
x: ark_babyjubjub::Fq::from_str(
"19037598474602150174935475944965340829216795940473064039209388058233204431288",
).unwrap(),
y: ark_babyjubjub::Fq::from_str(
"3549932221586364715003722955756497910920276078443163728621283280434115857197",
).unwrap(),
},
Affine::zero(),
Affine::zero(),
Affine::zero(),
Affine::zero(),
Affine::zero(),
Affine::zero(),
],
pk_index: ark_bn254::Fr::from(0u64),
s: ark_babyjubjub::Fr::from_str(
"2692248185200295468055279425612708965310378163906753799023551825366269352327",
).unwrap(),
r: Affine {
x: ark_babyjubjub::Fq::from_str(
"14689596469778385278298478829656243946283084496217945909620117398922933730711",
).unwrap(),
y: ark_babyjubjub::Fq::from_str(
"4424830738973486800075394160997493242162871494907432163152597205147606706197",
).unwrap(),
},
merkle_root: ark_bn254::Fr::from_str("4959814736111706042728533661656003495359474679272202023690954858781105690707").unwrap(),
depth: ark_babyjubjub::Fq::from(30u64),
mt_index: ark_bn254::Fr::from(1u64),
siblings: [
ark_bn254::Fr::from_str("0").unwrap(),
ark_bn254::Fr::from_str("15621590199821056450610068202457788725601603091791048810523422053872049975191").unwrap(),
ark_bn254::Fr::from_str("15180302612178352054084191513289999058431498575847349863917170755410077436260").unwrap(),
ark_bn254::Fr::from_str("20846426933296943402289409165716903143674406371782261099735847433924593192150").unwrap(),
ark_bn254::Fr::from_str("19570709311100149041770094415303300085749902031216638721752284824736726831172").unwrap(),
ark_bn254::Fr::from_str("11737142173000203701607979434185548337265641794352013537668027209469132654026").unwrap(),
ark_bn254::Fr::from_str("11865865012735342650993929214218361747705569437250152833912362711743119784159").unwrap(),
ark_bn254::Fr::from_str("1493463551715988755902230605042557878234810673525086316376178495918903796315").unwrap(),
ark_bn254::Fr::from_str("18746103596419850001763894956142528089435746267438407061601783590659355049966").unwrap(),
ark_bn254::Fr::from_str("21234194473503024590374857258930930634542887619436018385581872843343250130100").unwrap(),
ark_bn254::Fr::from_str("14681119568252857310414189897145410009875739166689283501408363922419813627484").unwrap(),
ark_bn254::Fr::from_str("13243470632183094581890559006623686685113540193867211988709619438324105679244").unwrap(),
ark_bn254::Fr::from_str("19463898140191333844443019106944343282402694318119383727674782613189581590092").unwrap(),
ark_bn254::Fr::from_str("10565902370220049529800497209344287504121041033501189980624875736992201671117").unwrap(),
ark_bn254::Fr::from_str("5560307625408070902174028041423028597194394554482880015024167821933869023078").unwrap(),
ark_bn254::Fr::from_str("20576730574720116265513866548855226316241518026808984067485384181494744706390").unwrap(),
ark_bn254::Fr::from_str("11166760821615661136366651998133963805984915741187325490784169611245269155689").unwrap(),
ark_bn254::Fr::from_str("13692603500396323648417392244466291089928913430742736835590182936663435788822").unwrap(),
ark_bn254::Fr::from_str("11129674755567463025028188404867541558752927519269975708924528737249823830641").unwrap(),
ark_bn254::Fr::from_str("6673535049007525806710184801639542254440636510496168661971704157154828514023").unwrap(),
ark_bn254::Fr::from_str("7958154589163466663626421142270206662020519181323839780192984613274682930816").unwrap(),
ark_bn254::Fr::from_str("3739156991379607404516753076057250171966250101655747790592556040569841550790").unwrap(),
ark_bn254::Fr::from_str("1334107297020502384420211493664486465203492095766400031330900935069700302301").unwrap(),
ark_bn254::Fr::from_str("20357028769054354174264046872903423695314313082869184437966002491602414517674").unwrap(),
ark_bn254::Fr::from_str("19392290367394672558538719012722289280213395590510602524366987685302929990731").unwrap(),
ark_bn254::Fr::from_str("7360502715619830055199267117332475946442427205382059394111067387016428818088").unwrap(),
ark_bn254::Fr::from_str("9629177338475347225553791169746168712988898028547587350296027054067573957412").unwrap(),
ark_bn254::Fr::from_str("21877160135037839571797468541807904053886800340144060811298025652177410263004").unwrap(),
ark_bn254::Fr::from_str("7105691694342706282901391345307729036900705570482804586768449537652208350743").unwrap(),
ark_bn254::Fr::from_str("15888057581779748293164452094398990053773731478520540058125130669204703869637").unwrap(),
],
beta: ark_babyjubjub::Fr::from_str("1277277022932719396321614946989807194659268059729440522321681213750340643042").unwrap(),
rp_id: ark_bn254::Fr::from_str("14631649082411674499").unwrap(),
action: ark_bn254::Fr::from_str("8982441576518976929447725179565370305223105654688049122733783421407497941726").unwrap(),
nonce: ark_bn254::Fr::from_str("8530676162050357218814694371816107906694725175836943927290214963954696613748").unwrap(),
}
}
#[test]
fn test_valid_query_proof_input() {
let inputs = get_valid_query_proof_input();
let _ = check_query_input_validity(&inputs).unwrap();
}
#[test]
fn test_invalid_query_proof_input() {
let inputs = get_valid_query_proof_input();
{
let mut inputs = inputs.clone();
inputs.depth = ark_babyjubjub::Fq::from(29u64); assert!(matches!(
check_query_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidMerkleTreeDepth { .. }
));
}
{
let mut inputs = inputs.clone();
inputs.mt_index = ark_bn254::Fr::from(1073741824u64);
assert!(matches!(
check_query_input_validity(&inputs).unwrap_err(),
super::ProofInputError::ValueOutOfBounds {
name: "Merkle tree index",
..
}
));
}
{
let mut inputs = inputs.clone();
inputs.merkle_root = ark_bn254::Fr::from(12345u64);
assert!(matches!(
check_query_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidMerkleTreeInclusionProof
));
}
{
let mut inputs = inputs.clone();
inputs.pk_index = ark_bn254::Fr::from(7u64); assert!(matches!(
check_query_input_validity(&inputs).unwrap_err(),
super::ProofInputError::ValueOutOfBounds {
name: "Authenticator PubKey index",
..
}
));
}
{
let mut inputs = inputs.clone();
inputs.r = Affine {
x: ark_babyjubjub::Fq::from(1u64),
y: ark_babyjubjub::Fq::from(2u64),
};
assert!(matches!(
check_query_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidBabyJubJubPoint {
name: "Query Signature R"
}
));
}
{
let mut inputs = inputs.clone();
inputs.pk[0] = Affine {
x: ark_babyjubjub::Fq::from(1u64),
y: ark_babyjubjub::Fq::from(2u64),
};
let pk_set = world_id_primitives::authenticator::AuthenticatorPublicKeySet::new(
inputs
.pk
.iter()
.map(|&x| eddsa_babyjubjub::EdDSAPublicKey { pk: x })
.collect(),
)
.unwrap();
let mut current = pk_set.leaf_hash();
let idx =
u64::try_from(world_id_primitives::FieldElement::from(inputs.mt_index)).unwrap();
for (i, sibling) in inputs.siblings.iter().enumerate() {
let sibling_fr = *world_id_primitives::FieldElement::from(*sibling);
if (idx >> i) & 1 == 0 {
let mut state = poseidon2::bn254::t2::permutation(&[current, sibling_fr]);
state[0] += current;
current = state[0];
} else {
let mut state = poseidon2::bn254::t2::permutation(&[sibling_fr, current]);
state[0] += sibling_fr;
current = state[0];
}
}
inputs.merkle_root = current;
assert!(matches!(
check_query_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidBabyJubJubPoint {
name: "Authenticator Public Key"
}
));
}
{
let mut inputs = inputs.clone();
inputs.action = ark_bn254::Fr::from(12345u64);
assert!(matches!(
check_query_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidQuerySignature
));
}
}
fn get_valid_nullifier_proof_input() -> NullifierProofCircuitInput<30> {
NullifierProofCircuitInput {
query_input: get_valid_query_proof_input(),
issuer_schema_id: ark_bn254::Fr::from(1u64),
cred_pk: Affine {
x: ark_babyjubjub::Fq::from_str(
"15406775215557320288232407896017344573719706795510112309920214099347968981892",
)
.unwrap(),
y: ark_babyjubjub::Fq::from_str(
"486388649729314270871358770861421181497883381447163109744630700259216042819",
)
.unwrap(),
},
cred_hashes: [
ark_bn254::Fr::from_str(
"14272087287699568472569351444185311392108883722570788958733484799744115401870",
)
.unwrap(),
ark_bn254::Fr::from_str("0").unwrap(),
],
cred_genesis_issued_at: ark_bn254::Fr::from(1770125923u64),
cred_expires_at: ark_bn254::Fr::from(1770125983u64),
cred_s: ark_babyjubjub::Fr::from_str(
"1213918488111680600555111454085490191981091366153388773926786471247948539005",
)
.unwrap(),
cred_r: Affine {
x: ark_babyjubjub::Fq::from_str(
"15844586803954862856390946258558419582000810449135704981677693963391564067969",
)
.unwrap(),
y: ark_babyjubjub::Fq::from_str(
"592710378120172403096018676235519447487818389124797234601458948988041235710",
)
.unwrap(),
},
current_timestamp: ark_bn254::Fr::from(1770125908u64),
cred_genesis_issued_at_min: ark_bn254::Fr::from(0u64),
cred_sub_blinding_factor: ark_bn254::Fr::from_str(
"12170146734368267085913078854954627576787934009906407554611507307540342380837",
)
.unwrap(),
cred_id: ark_bn254::Fr::from(3198767490419873482u64),
id_commitment_r: ark_bn254::Fr::from_str(
"11722352184830287916674945948108962396487445899741105828127518108056503126019",
)
.unwrap(),
id_commitment: ark_bn254::Fr::from(0u64),
dlog_e: ark_bn254::Fr::from_str(
"20738873297635092620048980552264360096607713029337408079647701591795211132447",
)
.unwrap(),
dlog_s: ark_babyjubjub::Fr::from_str(
"409914485496464180245985942628922659137136006706846380135829705769429965654",
)
.unwrap(),
oprf_pk: Affine {
x: ark_babyjubjub::Fq::from_str(
"2124016492737602714904869498047199181102594928943726277329982080254326092458",
)
.unwrap(),
y: ark_babyjubjub::Fq::from_str(
"13296886400185574560491768605341786437896334271868835545571935419923854148448",
)
.unwrap(),
},
oprf_response_blinded: Affine {
x: ark_babyjubjub::Fq::from_str(
"186021305824089989598292966483056363224488147240980559441958002546059602483",
)
.unwrap(),
y: ark_babyjubjub::Fq::from_str(
"16813058203546508924422863380215026034284821141284206571184467783067057954778",
)
.unwrap(),
},
oprf_response: Affine {
x: ark_babyjubjub::Fq::from_str(
"10209445202057032226639052993170591937356545068582397532992536070677055126187",
)
.unwrap(),
y: ark_babyjubjub::Fq::from_str(
"21877375411477040679486668720099554257785799784699842830375906922948306109699",
)
.unwrap(),
},
signal_hash: ark_bn254::Fr::from_str(
"37938388892362834151584770384290207919364301626797345218722464515205243407",
)
.unwrap(),
}
}
#[test]
fn test_valid_nullifier_proof_input() {
let inputs = get_valid_nullifier_proof_input();
let _ = check_nullifier_input_validity(&inputs).unwrap();
}
#[test]
fn test_invalid_nullifier_proof_input() {
let inputs = get_valid_nullifier_proof_input();
{
let mut inputs = inputs.clone();
inputs.current_timestamp =
ark_babyjubjub::Fq::from_str("123465723894591324701234982134000070").unwrap(); assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::ValueOutOfBounds {
name: "current timestamp",
..
}
));
}
{
let mut inputs = inputs.clone();
inputs.current_timestamp = inputs.cred_expires_at;
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::CredentialExpired { .. }
));
}
{
let mut inputs = inputs.clone();
inputs.cred_genesis_issued_at_min = ark_bn254::Fr::from(1770125924u64);
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::CredentialGenesisExpired { .. }
));
}
{
let mut inputs = inputs.clone();
inputs.cred_r = Affine {
x: ark_babyjubjub::Fq::from(1u64),
y: ark_babyjubjub::Fq::from(2u64),
};
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidBabyJubJubPoint {
name: "Credential Signature R"
}
));
}
{
let mut inputs = inputs.clone();
inputs.cred_pk = Affine {
x: ark_babyjubjub::Fq::from(1u64),
y: ark_babyjubjub::Fq::from(2u64),
};
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidBabyJubJubPoint {
name: "Credential Public Key"
}
));
}
{
let mut inputs = inputs.clone();
inputs.cred_s = ark_babyjubjub::Fr::from(12345u64);
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidCredentialSignature
));
}
{
let mut inputs = inputs.clone();
inputs.oprf_pk = Affine {
x: ark_babyjubjub::Fq::from(1u64),
y: ark_babyjubjub::Fq::from(2u64),
};
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidBabyJubJubPoint {
name: "OPRF Public Key"
}
));
}
{
let mut inputs = inputs.clone();
inputs.oprf_response_blinded = Affine {
x: ark_babyjubjub::Fq::from(1u64),
y: ark_babyjubjub::Fq::from(2u64),
};
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidBabyJubJubPoint {
name: "OPRF Blinded Response"
}
));
}
{
let mut inputs = inputs.clone();
inputs.dlog_s = ark_babyjubjub::Fr::from(12345u64);
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidOprfProof
));
}
{
let mut inputs = inputs.clone();
inputs.oprf_response = Affine {
x: ark_babyjubjub::Fq::from(1u64),
y: ark_babyjubjub::Fq::from(2u64),
};
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidBabyJubJubPoint {
name: "OPRF Unblinded Response"
}
));
}
{
let mut inputs = inputs.clone();
use ark_ec::AffineRepr;
inputs.oprf_response = ark_babyjubjub::EdwardsAffine::generator();
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidOprfResponse
));
}
{
let mut inputs = inputs.clone();
inputs.id_commitment = ark_bn254::Fr::from(12345u64);
assert!(matches!(
check_nullifier_input_validity(&inputs).unwrap_err(),
super::ProofInputError::InvalidSessionId
));
}
}
}