1use crate::serde_utils;
2use alloy_primitives::U256;
3use ark_bn254::Bn254;
4use circom_types::groth16::Proof;
5use serde::{Deserialize, Serialize};
6use taceo_oprf::types::api::{CloseFrameMessage, OprfRequestAuthenticatorError};
7
8use crate::{FieldElement, rp::RpId};
9
10#[repr(u8)]
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum OprfPrefix {
21 Uniqueness = 0x00,
28 SessionOprfSeed = 0x01,
30 SessionAction = 0x02,
32}
33
34pub trait OprfPrefixedFieldElement {
36 fn random_with_prefix<R: rand::CryptoRng + rand::RngCore>(
38 rng: &mut R,
39 prefix: OprfPrefix,
40 ) -> FieldElement;
41
42 fn has_prefix(&self, prefix: OprfPrefix) -> bool;
44}
45
46impl OprfPrefixedFieldElement for FieldElement {
47 fn random_with_prefix<R: rand::CryptoRng + rand::RngCore>(
48 rng: &mut R,
49 prefix: OprfPrefix,
50 ) -> FieldElement {
51 let mut bytes = [0u8; 32];
52 rng.fill_bytes(&mut bytes);
53 bytes[0] = prefix as u8;
54 Self::from_be_bytes(&bytes).expect(
55 "should always fit in the field because with 0x02 or lower as the MSB, the field element < babyjubjub modulus",
56 )
57 }
58
59 fn has_prefix(&self, prefix: OprfPrefix) -> bool {
60 self.to_be_bytes()[0] == prefix as u8
61 }
62}
63
64#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
66pub enum OprfModule {
67 Nullifier,
69 CredentialBlindingFactor,
71 Session,
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78pub enum RpSignatureVerification {
79 UniquenessAction {
84 action: FieldElement,
86 },
87}
88
89impl std::fmt::Display for OprfModule {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 match self {
92 Self::Nullifier => write!(f, "nullifier"),
93 Self::CredentialBlindingFactor => write!(f, "credential_blinding_factor"),
94 Self::Session => write!(f, "session"),
95 }
96 }
97}
98
99#[derive(Clone, Serialize, Deserialize)]
101pub struct NullifierOprfRequestAuthV1 {
102 pub proof: Proof<Bn254>,
104 #[serde(with = "ark_serde_compat::field")]
106 pub action: ark_babyjubjub::Fq,
107 #[serde(with = "ark_serde_compat::field")]
109 pub nonce: ark_babyjubjub::Fq,
110 #[serde(with = "ark_serde_compat::field")]
112 pub merkle_root: ark_babyjubjub::Fq,
113 #[serde(alias = "current_time_stamp")]
115 pub created_at: u64,
116 #[serde(alias = "expiration_timestamp")]
118 pub expires_at: u64,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub signature: Option<alloy_primitives::Signature>,
124 pub rp_id: RpId,
126 #[serde(
132 default,
133 skip_serializing_if = "Option::is_none",
134 with = "serde_utils::hex_bytes_opt"
135 )]
136 pub wip101_data: Option<Vec<u8>>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub rp_signature_verification: Option<RpSignatureVerification>,
143}
144
145#[derive(Clone, Serialize, Deserialize)]
147pub struct CredentialBlindingFactorOprfRequestAuthV1 {
148 pub proof: Proof<Bn254>,
150 #[serde(with = "ark_serde_compat::field")]
152 pub action: ark_babyjubjub::Fq,
153 #[serde(with = "ark_serde_compat::field")]
155 pub nonce: ark_babyjubjub::Fq,
156 #[serde(with = "ark_serde_compat::field")]
158 pub merkle_root: ark_babyjubjub::Fq,
159 pub issuer_schema_id: u64,
161}
162
163#[derive(Copy, Clone, Debug, thiserror::Error)]
168#[non_exhaustive]
169pub enum WorldIdRequestAuthError {
170 #[error("unknown_rp")]
172 UnknownRp,
173 #[error("inactive_rp")]
176 InactiveRp,
177 #[error("unknown_schema_issuer_id")]
183 UnknownSchemaIssuerId,
184 #[error("timestamp_too_old")]
187 CreatedAtTooOld,
188 #[error("timestamp_too_far_in_future")]
191 CreatedAtTooFarInFuture,
192 #[error("expires_at_too_far_in_future")]
195 ExpiresAtTooFarInFuture,
196 #[error("invalid_timestamp")]
198 InvalidTimestamp,
199 #[error("rp_signature_expired")]
202 RpSignatureExpired,
203 #[error("invalid_rp_signature")]
206 InvalidRpSignature,
207 #[error("rp_signature_missing")]
211 RpSignatureMissing,
212 #[error("wip101_aux_data_on_eoa")]
214 Wip101AuxDataOnEoa,
215 #[error("duplicate_nonce")]
218 DuplicateNonce,
219 #[error("invalid_merkle_root")]
222 InvalidMerkleRoot,
223 #[error("invalid_query_proof")]
226 InvalidQueryProof,
227 #[error("invalid_action_for_blinding_factor")]
231 InvalidActionSchemaIssuer,
232 #[error("invalid_action_for_nullifier")]
235 InvalidActionNullifier,
236 #[error("invalid_action_for_session")]
241 InvalidActionSession,
242 #[error("invalid_rp_signature_verification")]
247 InvalidRpSignatureVerification,
248 #[error("wip101_incompatible_rp_signer")]
250 Wip101IncompatibleRpSigner,
251 #[error("wip101_verification_failed")]
257 Wip101VerificationFailed(Option<U256>),
258 #[error("wip101_custom_revert")]
262 Wip101CustomRevert,
263 #[error("wip101_aux_data_too_large")]
267 Wip101AuxDataTooLarge,
268 #[error("wip101_verification_timeout")]
270 Wip101VerificationTimeout,
271 #[error("wip101_account_check_timeout")]
273 Wip101AccountCheckTimeout,
274 #[error("internal_server_error")]
276 Internal,
277 #[error("unknown_error_{0}")]
279 Unknown(u16),
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum ErrorActor {
286 Rp,
288 Issuer,
290 Authenticator,
292 OprfNode,
294}
295
296impl WorldIdRequestAuthError {
297 #[must_use]
299 pub const fn as_actor(&self) -> ErrorActor {
300 match self {
301 Self::UnknownRp
302 | Self::InactiveRp
303 | Self::CreatedAtTooOld
304 | Self::CreatedAtTooFarInFuture
305 | Self::ExpiresAtTooFarInFuture
306 | Self::InvalidTimestamp
307 | Self::RpSignatureExpired
308 | Self::InvalidRpSignature
309 | Self::DuplicateNonce
310 | Self::InvalidActionNullifier
311 | Self::Wip101IncompatibleRpSigner
312 | Self::Wip101VerificationFailed(_)
313 | Self::Wip101CustomRevert
314 | Self::Wip101VerificationTimeout
315 | Self::Wip101AuxDataOnEoa
316 | Self::Wip101AuxDataTooLarge
317 | Self::Wip101AccountCheckTimeout => ErrorActor::Rp,
318 Self::UnknownSchemaIssuerId => ErrorActor::Issuer,
319 Self::InvalidMerkleRoot
320 | Self::InvalidQueryProof
321 | Self::InvalidActionSchemaIssuer
322 | Self::InvalidActionSession
323 | Self::InvalidRpSignatureVerification
324 | Self::RpSignatureMissing => ErrorActor::Authenticator,
325 Self::Internal | Self::Unknown(_) => ErrorActor::OprfNode,
326 }
327 }
328}
329
330impl From<u16> for WorldIdRequestAuthError {
331 fn from(value: u16) -> Self {
332 match value {
333 error_codes::UNKNOWN_RP => Self::UnknownRp,
334 error_codes::INACTIVE_RP => Self::InactiveRp,
335 error_codes::CREATED_AT_TOO_OLD => Self::CreatedAtTooOld,
336 error_codes::INVALID_RP_SIGNATURE => Self::InvalidRpSignature,
337 error_codes::DUPLICATE_NONCE => Self::DuplicateNonce,
338 error_codes::INVALID_MERKLE_ROOT => Self::InvalidMerkleRoot,
339 error_codes::INVALID_QUERY_PROOF => Self::InvalidQueryProof,
340 error_codes::INVALID_ACTION_SCHEMA_ISSUER => Self::InvalidActionSchemaIssuer,
341 error_codes::UNKNOWN_SCHEMA_ISSUER => Self::UnknownSchemaIssuerId,
342 error_codes::INVALID_ACTION_NULLIFIER => Self::InvalidActionNullifier,
343 error_codes::INVALID_ACTION_SESSION => Self::InvalidActionSession,
344 error_codes::INVALID_RP_SIGNATURE_VERIFICATION => Self::InvalidRpSignatureVerification,
345 error_codes::RP_SIGNATURE_EXPIRED => Self::RpSignatureExpired,
346 error_codes::RP_SIGNATURE_MISSING => Self::RpSignatureMissing,
347 error_codes::INVALID_TIMESTAMP => Self::InvalidTimestamp,
348 error_codes::CREATED_AT_TOO_FAR_IN_FUTURE => Self::CreatedAtTooFarInFuture,
349 error_codes::EXPIRES_AT_TOO_FAR_IN_FUTURE => Self::ExpiresAtTooFarInFuture,
350 error_codes::WIP101_INCOMPATIBLE_RP_SIGNER => Self::Wip101IncompatibleRpSigner,
351 error_codes::WIP101_VERIFICATION_TIMEOUT => Self::Wip101VerificationTimeout,
352 error_codes::WIP101_ACCOUNT_CHECK_TIMEOUT => Self::Wip101AccountCheckTimeout,
353 error_codes::WIP101_VERIFICATION_FAILED => Self::Wip101VerificationFailed(None),
355 error_codes::WIP101_CUSTOM_REVERT => Self::Wip101CustomRevert,
356 error_codes::INTERNAL => Self::Internal,
357 other => Self::Unknown(other),
358 }
359 }
360}
361
362impl From<WorldIdRequestAuthError> for u16 {
363 fn from(value: WorldIdRequestAuthError) -> Self {
364 match value {
365 WorldIdRequestAuthError::UnknownRp => error_codes::UNKNOWN_RP,
366 WorldIdRequestAuthError::InactiveRp => error_codes::INACTIVE_RP,
367 WorldIdRequestAuthError::CreatedAtTooOld => error_codes::CREATED_AT_TOO_OLD,
368 WorldIdRequestAuthError::ExpiresAtTooFarInFuture => {
369 error_codes::EXPIRES_AT_TOO_FAR_IN_FUTURE
370 }
371 WorldIdRequestAuthError::InvalidTimestamp => error_codes::INVALID_TIMESTAMP,
372 WorldIdRequestAuthError::InvalidRpSignature => error_codes::INVALID_RP_SIGNATURE,
373 WorldIdRequestAuthError::RpSignatureMissing => error_codes::RP_SIGNATURE_MISSING,
374 WorldIdRequestAuthError::DuplicateNonce => error_codes::DUPLICATE_NONCE,
375 WorldIdRequestAuthError::InvalidMerkleRoot => error_codes::INVALID_MERKLE_ROOT,
376 WorldIdRequestAuthError::InvalidQueryProof => error_codes::INVALID_QUERY_PROOF,
377 WorldIdRequestAuthError::InvalidActionSchemaIssuer => {
378 error_codes::INVALID_ACTION_SCHEMA_ISSUER
379 }
380 WorldIdRequestAuthError::UnknownSchemaIssuerId => error_codes::UNKNOWN_SCHEMA_ISSUER,
381 WorldIdRequestAuthError::InvalidActionNullifier => {
382 error_codes::INVALID_ACTION_NULLIFIER
383 }
384 WorldIdRequestAuthError::InvalidActionSession => error_codes::INVALID_ACTION_SESSION,
385 WorldIdRequestAuthError::InvalidRpSignatureVerification => {
386 error_codes::INVALID_RP_SIGNATURE_VERIFICATION
387 }
388 WorldIdRequestAuthError::RpSignatureExpired => error_codes::RP_SIGNATURE_EXPIRED,
389 WorldIdRequestAuthError::CreatedAtTooFarInFuture => {
390 error_codes::CREATED_AT_TOO_FAR_IN_FUTURE
391 }
392 WorldIdRequestAuthError::Wip101IncompatibleRpSigner => {
393 error_codes::WIP101_INCOMPATIBLE_RP_SIGNER
394 }
395 WorldIdRequestAuthError::Wip101VerificationFailed(_) => {
396 error_codes::WIP101_VERIFICATION_FAILED
397 }
398 WorldIdRequestAuthError::Wip101VerificationTimeout => {
399 error_codes::WIP101_VERIFICATION_TIMEOUT
400 }
401 WorldIdRequestAuthError::Wip101CustomRevert => error_codes::WIP101_CUSTOM_REVERT,
402 WorldIdRequestAuthError::Wip101AuxDataOnEoa => error_codes::WIP101_AUX_DATA_ON_EOA,
403 WorldIdRequestAuthError::Wip101AuxDataTooLarge => {
404 error_codes::WIP101_AUX_DATA_TOO_LARGE
405 }
406 WorldIdRequestAuthError::Wip101AccountCheckTimeout => {
407 error_codes::WIP101_ACCOUNT_CHECK_TIMEOUT
408 }
409 WorldIdRequestAuthError::Internal => error_codes::INTERNAL,
410 WorldIdRequestAuthError::Unknown(other) => other,
411 }
412 }
413}
414
415pub mod error_codes {
417 pub const UNKNOWN_RP: u16 = 4500;
419 pub const CREATED_AT_TOO_OLD: u16 = 4501;
421 pub const INVALID_RP_SIGNATURE: u16 = 4502;
423 pub const DUPLICATE_NONCE: u16 = 4503;
425 pub const INVALID_MERKLE_ROOT: u16 = 4504;
427 pub const INVALID_QUERY_PROOF: u16 = 4505;
429 pub const INVALID_ACTION_SCHEMA_ISSUER: u16 = 4506;
431 pub const UNKNOWN_SCHEMA_ISSUER: u16 = 4507;
433 pub const INVALID_ACTION_NULLIFIER: u16 = 4508;
435 pub const INVALID_ACTION_SESSION: u16 = 4509;
437 pub const INACTIVE_RP: u16 = 4510;
439 pub const RP_SIGNATURE_EXPIRED: u16 = 4511;
441 pub const INVALID_TIMESTAMP: u16 = 4512;
443 pub const CREATED_AT_TOO_FAR_IN_FUTURE: u16 = 4513;
445 pub const WIP101_INCOMPATIBLE_RP_SIGNER: u16 = 4514;
447 pub const WIP101_VERIFICATION_FAILED: u16 = 4515;
449 pub const WIP101_CUSTOM_REVERT: u16 = 4516;
451 pub const WIP101_AUX_DATA_TOO_LARGE: u16 = 4517;
453 pub const RP_SIGNATURE_MISSING: u16 = 4518;
455 pub const WIP101_AUX_DATA_ON_EOA: u16 = 4519;
457 pub const WIP101_VERIFICATION_TIMEOUT: u16 = 4520;
459 pub const WIP101_ACCOUNT_CHECK_TIMEOUT: u16 = 4521;
461 pub const EXPIRES_AT_TOO_FAR_IN_FUTURE: u16 = 4523;
463 pub const INVALID_RP_SIGNATURE_VERIFICATION: u16 = 4524;
465 pub const INTERNAL: u16 = 1011;
467}
468
469impl From<WorldIdRequestAuthError> for OprfRequestAuthenticatorError {
470 fn from(value: WorldIdRequestAuthError) -> Self {
471 let code = u16::from(value);
472 let msg = match value {
473 WorldIdRequestAuthError::UnknownRp => {
474 taceo_oprf::types::close_frame_message!("unknown RP")
475 }
476 WorldIdRequestAuthError::CreatedAtTooOld => {
477 taceo_oprf::types::close_frame_message!("created_at too old")
478 }
479 WorldIdRequestAuthError::CreatedAtTooFarInFuture => {
480 taceo_oprf::types::close_frame_message!("created_at too far in future")
481 }
482 WorldIdRequestAuthError::ExpiresAtTooFarInFuture => {
483 taceo_oprf::types::close_frame_message!("expires_at too far in the future")
484 }
485 WorldIdRequestAuthError::InvalidRpSignature => {
486 taceo_oprf::types::close_frame_message!("signature from RP cannot be verified")
487 }
488 WorldIdRequestAuthError::RpSignatureMissing => {
489 taceo_oprf::types::close_frame_message!("RP signature missing but signer is an EOA")
490 }
491 WorldIdRequestAuthError::DuplicateNonce => {
492 taceo_oprf::types::close_frame_message!("signature nonce already used")
493 }
494 WorldIdRequestAuthError::InvalidMerkleRoot => {
495 taceo_oprf::types::close_frame_message!("invalid merkle root")
496 }
497 WorldIdRequestAuthError::InvalidQueryProof => {
498 taceo_oprf::types::close_frame_message!("cannot verify query proof")
499 }
500 WorldIdRequestAuthError::InvalidActionSchemaIssuer => {
501 taceo_oprf::types::close_frame_message!(
502 "invalid action for credential sub blinding factor"
503 )
504 }
505 WorldIdRequestAuthError::UnknownSchemaIssuerId => {
506 taceo_oprf::types::close_frame_message!("unknown schema issuer id")
507 }
508 WorldIdRequestAuthError::InvalidActionNullifier => {
509 taceo_oprf::types::close_frame_message!("invalid action for nullifier")
510 }
511 WorldIdRequestAuthError::InvalidActionSession => {
512 taceo_oprf::types::close_frame_message!("invalid action for session proofs")
513 }
514 WorldIdRequestAuthError::InactiveRp => {
515 taceo_oprf::types::close_frame_message!("inactive RP")
516 }
517 WorldIdRequestAuthError::RpSignatureExpired => {
518 taceo_oprf::types::close_frame_message!("RP signature expired")
519 }
520 WorldIdRequestAuthError::InvalidTimestamp => {
521 taceo_oprf::types::close_frame_message!("cannot parse timestamp on request")
522 }
523 WorldIdRequestAuthError::Wip101IncompatibleRpSigner => {
524 taceo_oprf::types::close_frame_message!(
525 "RP has a contract backed signer but doesn't conform to WIP101"
526 )
527 }
528 WorldIdRequestAuthError::Wip101CustomRevert => {
529 taceo_oprf::types::close_frame_message!(
530 "RP signer contract reverted with custom error (and not error RpInvalidRequest(uint256 code);)"
531 )
532 }
533 WorldIdRequestAuthError::Wip101VerificationFailed(None) => {
534 taceo_oprf::types::close_frame_message!("")
536 }
537 WorldIdRequestAuthError::Wip101VerificationTimeout => {
538 taceo_oprf::types::close_frame_message!("WIP101 verification ran into timeout")
539 }
540 WorldIdRequestAuthError::Wip101VerificationFailed(Some(code)) => {
541 CloseFrameMessage::new_truncate(format!("{:#x}", code))
543 }
544 WorldIdRequestAuthError::InvalidRpSignatureVerification => {
545 taceo_oprf::types::close_frame_message!("Invalid RP signature verification data")
546 }
547 WorldIdRequestAuthError::Wip101AuxDataOnEoa => taceo_oprf::types::close_frame_message!(
548 "Auxiliary data must be empty with EOA backed signer"
549 ),
550 WorldIdRequestAuthError::Wip101AuxDataTooLarge => {
551 taceo_oprf::types::close_frame_message!(
552 "Auxiliary data for WIP101 contract too large - max 1024 bytes"
553 )
554 }
555 WorldIdRequestAuthError::Wip101AccountCheckTimeout => {
556 taceo_oprf::types::close_frame_message!(
557 "Ran into timeout while doing WIP101/ERC165 check on RP's signer"
558 )
559 }
560 WorldIdRequestAuthError::Internal => {
561 taceo_oprf::types::close_frame_message!("internal server error")
562 }
563 WorldIdRequestAuthError::Unknown(_) => {
564 taceo_oprf::types::close_frame_message!("unknown")
565 }
566 };
567 Self::with_message(code, msg)
568 }
569}
570
571#[cfg(test)]
572mod tests {
573 use super::*;
574
575 const ALL_PREFIXES: [OprfPrefix; 3] = [
576 OprfPrefix::Uniqueness,
577 OprfPrefix::SessionOprfSeed,
578 OprfPrefix::SessionAction,
579 ];
580
581 #[test]
582 fn random_with_prefix_is_recognized_only_by_its_own_prefix() {
583 let mut rng = rand::rngs::OsRng;
584 for prefix in ALL_PREFIXES {
585 let field_element = FieldElement::random_with_prefix(&mut rng, prefix);
586 assert_eq!(field_element.to_be_bytes()[0], prefix as u8);
587 for other in ALL_PREFIXES {
588 assert_eq!(field_element.has_prefix(other), other == prefix);
589 }
590 }
591 }
592
593 fn test_proof() -> Proof<Bn254> {
595 serde_json::from_value(serde_json::json!({
596 "pi_a": ["1", "2", "1"],
597 "pi_b": [
598 [
599 "10857046999023057135944570762232829481370756359578518086990519993285655852781",
600 "11559732032986387107991004021392285783925812861821192530917403151452391805634"
601 ],
602 [
603 "8495653923123431417604973247489272438418190587263600148770280649306958101930",
604 "4082367875863433681332203403145435568316851327593401208105741076214120093531"
605 ],
606 ["1", "0"]
607 ],
608 "pi_c": ["1", "2", "1"],
609 "protocol": "groth16",
610 "curve": "bn128"
611 }))
612 .expect("valid test proof")
613 }
614
615 fn test_auth(
616 rp_signature_verification: Option<RpSignatureVerification>,
617 ) -> NullifierOprfRequestAuthV1 {
618 NullifierOprfRequestAuthV1 {
619 proof: test_proof(),
620 action: ark_babyjubjub::Fq::from(1u64),
621 nonce: ark_babyjubjub::Fq::from(2u64),
622 merkle_root: ark_babyjubjub::Fq::from(3u64),
623 created_at: 4,
624 expires_at: 5,
625 signature: None,
626 rp_id: RpId::new(6),
627 wip101_data: None,
628 rp_signature_verification,
629 }
630 }
631
632 #[test]
633 fn nullifier_auth_rp_signature_verification_json_roundtrip() {
634 let verification = RpSignatureVerification::UniquenessAction {
635 action: FieldElement::from(42u64),
636 };
637 let auth = test_auth(Some(verification));
638 let json = serde_json::to_string(&auth).unwrap();
639 let parsed: NullifierOprfRequestAuthV1 = serde_json::from_str(&json).unwrap();
640 assert_eq!(parsed.rp_signature_verification, Some(verification));
641 }
642
643 #[test]
644 fn nullifier_auth_rp_signature_verification_cbor_roundtrip() {
645 let verification = RpSignatureVerification::UniquenessAction {
646 action: FieldElement::from(42u64),
647 };
648 let auth = test_auth(Some(verification));
649 let mut bytes = Vec::new();
650 ciborium::into_writer(&auth, &mut bytes).unwrap();
651 let parsed: NullifierOprfRequestAuthV1 = ciborium::from_reader(bytes.as_slice()).unwrap();
652 assert_eq!(parsed.rp_signature_verification, Some(verification));
653 }
654
655 #[test]
656 fn error_code_roundtrip() {
657 let codes: &[u16] = &[
658 error_codes::UNKNOWN_RP,
659 error_codes::CREATED_AT_TOO_OLD,
660 error_codes::CREATED_AT_TOO_FAR_IN_FUTURE,
661 error_codes::INVALID_RP_SIGNATURE,
662 error_codes::DUPLICATE_NONCE,
663 error_codes::INVALID_MERKLE_ROOT,
664 error_codes::INVALID_QUERY_PROOF,
665 error_codes::INVALID_ACTION_SCHEMA_ISSUER,
666 error_codes::UNKNOWN_SCHEMA_ISSUER,
667 error_codes::INVALID_ACTION_NULLIFIER,
668 error_codes::INVALID_ACTION_SESSION,
669 error_codes::INVALID_RP_SIGNATURE_VERIFICATION,
670 error_codes::INACTIVE_RP,
671 error_codes::RP_SIGNATURE_EXPIRED,
672 error_codes::INVALID_TIMESTAMP,
673 error_codes::WIP101_INCOMPATIBLE_RP_SIGNER,
674 error_codes::WIP101_VERIFICATION_FAILED,
675 error_codes::WIP101_CUSTOM_REVERT,
676 error_codes::WIP101_AUX_DATA_TOO_LARGE,
677 error_codes::RP_SIGNATURE_MISSING,
678 error_codes::WIP101_AUX_DATA_ON_EOA,
679 error_codes::WIP101_VERIFICATION_TIMEOUT,
680 error_codes::WIP101_ACCOUNT_CHECK_TIMEOUT,
681 error_codes::INTERNAL,
682 ];
683 for &code in codes {
684 let error = WorldIdRequestAuthError::from(code);
685 let back: u16 = error.into();
686 assert_eq!(code, back, "roundtrip failed for code {code}");
687 }
688 }
689}