1use alloy::primitives::Address;
4use eddsa_babyjubjub::EdDSAPublicKey;
5use ruint::aliases::U256;
6
7use crate::{
8 api_types::{
9 GatewayRequestId, GatewayRequestState, GatewayStatusResponse, InsertAuthenticatorRequest,
10 RemoveAuthenticatorRequest, UpdateAuthenticatorRequest,
11 },
12 authenticator::Authenticator,
13 error::AuthenticatorError,
14 traits::OnchainKeyRepresentable,
15};
16
17use world_id_registries::world_id::{
18 domain, sign_insert_authenticator, sign_remove_authenticator, sign_update_authenticator,
19};
20
21impl Authenticator {
22 pub async fn insert_authenticator(
32 &self,
33 new_authenticator_pubkey: EdDSAPublicKey,
34 new_authenticator_address: Address,
35 ) -> Result<GatewayRequestId, AuthenticatorError> {
36 let leaf_index = self.leaf_index();
37 let nonce = self.signing_nonce().await?;
38 let mut key_set = self.fetch_authenticator_pubkeys().await?;
39 let old_offchain_signer_commitment = key_set.leaf_hash();
40 let encoded_offchain_pubkey = new_authenticator_pubkey.to_ethereum_representation()?;
41 let index = key_set.insert_or_reuse(new_authenticator_pubkey)?;
42 let new_offchain_signer_commitment = key_set.leaf_hash();
43
44 let eip712_domain = domain(self.config.chain_id(), *self.config.registry_address());
45
46 #[allow(clippy::cast_possible_truncation)]
47 let signature = sign_insert_authenticator(
49 &self.signer.onchain_signer(),
50 leaf_index,
51 new_authenticator_address,
52 index as u32,
53 encoded_offchain_pubkey,
54 new_offchain_signer_commitment.into(),
55 nonce,
56 &eip712_domain,
57 )
58 .map_err(|e| {
59 AuthenticatorError::Generic(format!("Failed to sign insert authenticator: {e}"))
60 })?;
61
62 #[allow(clippy::cast_possible_truncation)]
63 let req = InsertAuthenticatorRequest {
65 leaf_index,
66 new_authenticator_address,
67 pubkey_id: index as u32,
68 new_authenticator_pubkey: encoded_offchain_pubkey,
69 old_offchain_signer_commitment: old_offchain_signer_commitment.into(),
70 new_offchain_signer_commitment: new_offchain_signer_commitment.into(),
71 signature,
72 nonce,
73 };
74
75 let body: GatewayStatusResponse = self
76 .gateway_client
77 .post_json(self.config.gateway_url(), "/insert-authenticator", &req)
78 .await?;
79 Ok(body.request_id)
80 }
81
82 pub async fn update_authenticator(
92 &self,
93 old_authenticator_address: Address,
94 new_authenticator_address: Address,
95 new_authenticator_pubkey: EdDSAPublicKey,
96 index: u32,
97 ) -> Result<GatewayRequestId, AuthenticatorError> {
98 let leaf_index = self.leaf_index();
99 let nonce = self.signing_nonce().await?;
100 let mut key_set = self.fetch_authenticator_pubkeys().await?;
101 let old_commitment: U256 = key_set.leaf_hash().into();
102 let encoded_offchain_pubkey = new_authenticator_pubkey.to_ethereum_representation()?;
103 key_set.try_set_at_index(index as usize, new_authenticator_pubkey)?;
104 let new_commitment: U256 = key_set.leaf_hash().into();
105
106 let eip712_domain = domain(self.config.chain_id(), *self.config.registry_address());
107
108 let signature = sign_update_authenticator(
109 &self.signer.onchain_signer(),
110 leaf_index,
111 old_authenticator_address,
112 new_authenticator_address,
113 index,
114 encoded_offchain_pubkey,
115 new_commitment,
116 nonce,
117 &eip712_domain,
118 )
119 .map_err(|e| {
120 AuthenticatorError::Generic(format!("Failed to sign update authenticator: {e}"))
121 })?;
122
123 let req = UpdateAuthenticatorRequest {
124 leaf_index,
125 old_authenticator_address,
126 new_authenticator_address,
127 old_offchain_signer_commitment: old_commitment,
128 new_offchain_signer_commitment: new_commitment,
129 signature,
130 nonce,
131 pubkey_id: index,
132 new_authenticator_pubkey: encoded_offchain_pubkey,
133 };
134
135 let gateway_resp: GatewayStatusResponse = self
136 .gateway_client
137 .post_json(self.config.gateway_url(), "/update-authenticator", &req)
138 .await?;
139 Ok(gateway_resp.request_id)
140 }
141
142 pub async fn remove_authenticator(
152 &self,
153 authenticator_address: Address,
154 index: u32,
155 ) -> Result<GatewayRequestId, AuthenticatorError> {
156 let leaf_index = self.leaf_index();
157 let nonce = self.signing_nonce().await?;
158 let mut key_set = self.fetch_authenticator_pubkeys().await?;
159 let old_commitment: U256 = key_set.leaf_hash().into();
160 let existing_pubkey = key_set
161 .get(index as usize)
162 .ok_or(AuthenticatorError::PublicKeyNotFound)?;
163
164 let encoded_old_offchain_pubkey = existing_pubkey.to_ethereum_representation()?;
165
166 key_set.try_clear_at_index(index as usize)?;
167 let new_commitment: U256 = key_set.leaf_hash().into();
168
169 let eip712_domain = domain(self.config.chain_id(), *self.config.registry_address());
170
171 let signature = sign_remove_authenticator(
172 &self.signer.onchain_signer(),
173 leaf_index,
174 authenticator_address,
175 index,
176 encoded_old_offchain_pubkey,
177 new_commitment,
178 nonce,
179 &eip712_domain,
180 )
181 .map_err(|e| {
182 AuthenticatorError::Generic(format!("Failed to sign remove authenticator: {e}"))
183 })?;
184
185 let req = RemoveAuthenticatorRequest {
186 leaf_index,
187 authenticator_address,
188 old_offchain_signer_commitment: old_commitment,
189 new_offchain_signer_commitment: new_commitment,
190 signature,
191 nonce,
192 pubkey_id: Some(index),
193 authenticator_pubkey: Some(encoded_old_offchain_pubkey),
194 };
195
196 let gateway_resp: GatewayStatusResponse = self
197 .gateway_client
198 .post_json(self.config.gateway_url(), "/remove-authenticator", &req)
199 .await?;
200 Ok(gateway_resp.request_id)
201 }
202
203 pub async fn poll_status(
213 &self,
214 request_id: &GatewayRequestId,
215 ) -> Result<GatewayRequestState, AuthenticatorError> {
216 let path = format!("/status/{request_id}");
217 let body: GatewayStatusResponse = self
218 .gateway_client
219 .get_json(self.config.gateway_url(), &path)
220 .await?;
221 Ok(body.status)
222 }
223}