1#![allow(dead_code)]
2use crate::scw_verifier::SmartContractSignatureVerifier;
3
4use super::{
5 AccountId, Action, AddAssociation, ClaimHandle, CreateInbox, IdentityUpdate, RevokeAssociation,
6 SignatureError,
7 unsigned_actions::{
8 SignatureTextCreator, UnsignedAction, UnsignedAddAssociation,
9 UnsignedChangeRecoveryAddress, UnsignedClaimHandle, UnsignedCreateInbox,
10 UnsignedIdentityUpdate, UnsignedRevokeAssociation,
11 },
12 verified_signature::VerifiedSignature,
13};
14use futures::future::try_join_all;
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct UnverifiedIdentityUpdate {
18 pub inbox_id: String,
19 pub client_timestamp_ns: u64,
20 pub actions: Vec<UnverifiedAction>,
21}
22
23impl UnverifiedIdentityUpdate {
24 pub fn new(inbox_id: String, client_timestamp_ns: u64, actions: Vec<UnverifiedAction>) -> Self {
25 Self {
26 inbox_id,
27 client_timestamp_ns,
28 actions,
29 }
30 }
31
32 fn signature_text(&self) -> String {
33 let unsigned_actions = self
34 .actions
35 .iter()
36 .map(|action| action.unsigned_action())
37 .collect();
38 let unsigned_identity_update = UnsignedIdentityUpdate::new(
39 unsigned_actions,
40 self.inbox_id.clone(),
41 self.client_timestamp_ns,
42 );
43
44 unsigned_identity_update.signature_text()
45 }
46
47 fn signatures(&self) -> Vec<UnverifiedSignature> {
48 self.actions
49 .iter()
50 .flat_map(|action| action.signatures())
51 .collect()
52 }
53
54 pub async fn to_verified(
56 &self,
57 scw_verifier: impl SmartContractSignatureVerifier,
58 ) -> Result<IdentityUpdate, SignatureError> {
59 let signature_text = self.signature_text();
60
61 let actions: Vec<Action> = try_join_all(
62 self.actions
63 .iter()
64 .map(|action| action.to_verified(&signature_text, &scw_verifier)),
65 )
66 .await?;
67
68 Ok(IdentityUpdate::new(
69 actions,
70 self.inbox_id.clone(),
71 self.client_timestamp_ns,
72 ))
73 }
74}
75
76#[derive(Debug, Clone, PartialEq)]
77pub enum UnverifiedAction {
78 CreateInbox(UnverifiedCreateInbox),
79 AddAssociation(UnverifiedAddAssociation),
80 RevokeAssociation(UnverifiedRevokeAssociation),
81 ChangeRecoveryAddress(UnverifiedChangeRecoveryAddress),
82 ClaimHandle(UnverifiedClaimHandle),
83}
84
85impl UnverifiedAction {
86 fn unsigned_action(&self) -> UnsignedAction {
87 match self {
88 UnverifiedAction::CreateInbox(action) => {
89 UnsignedAction::CreateInbox(action.unsigned_action.clone())
90 }
91 UnverifiedAction::AddAssociation(action) => {
92 UnsignedAction::AddAssociation(action.unsigned_action.clone())
93 }
94 UnverifiedAction::RevokeAssociation(action) => {
95 UnsignedAction::RevokeAssociation(action.unsigned_action.clone())
96 }
97 UnverifiedAction::ChangeRecoveryAddress(action) => {
98 UnsignedAction::ChangeRecoveryAddress(action.unsigned_action.clone())
99 }
100 UnverifiedAction::ClaimHandle(action) => {
101 UnsignedAction::ClaimHandle(action.unsigned_action.clone())
102 }
103 }
104 }
105
106 fn signatures(&self) -> Vec<UnverifiedSignature> {
107 match self {
108 UnverifiedAction::CreateInbox(action) => {
109 vec![action.initial_identifier_signature.clone()]
110 }
111 UnverifiedAction::AddAssociation(action) => vec![
112 action.existing_member_signature.clone(),
113 action.new_member_signature.clone(),
114 ],
115 UnverifiedAction::RevokeAssociation(action) => {
116 vec![action.recovery_identifier_signature.clone()]
117 }
118 UnverifiedAction::ChangeRecoveryAddress(action) => {
119 vec![action.recovery_identifier_signature.clone()]
120 }
121 UnverifiedAction::ClaimHandle(action) => vec![action.signature.clone()],
122 }
123 }
124
125 pub async fn to_verified<Text: AsRef<str>>(
126 &self,
127 signature_text: Text,
128 scw_verifier: impl SmartContractSignatureVerifier,
129 ) -> Result<Action, SignatureError> {
130 let action = match self {
131 UnverifiedAction::CreateInbox(action) => Action::CreateInbox(CreateInbox {
132 nonce: action.unsigned_action.nonce,
133 account_identifier: action.unsigned_action.account_identifier.clone(),
134 initial_identifier_signature: action
135 .initial_identifier_signature
136 .to_verified(signature_text.as_ref(), &scw_verifier)
137 .await?,
138 }),
139 UnverifiedAction::AddAssociation(action) => Action::AddAssociation(AddAssociation {
140 new_member_signature: action
141 .new_member_signature
142 .to_verified(signature_text.as_ref(), &scw_verifier)
143 .await?,
144 new_member_identifier: action.unsigned_action.new_member_identifier.clone(),
145 existing_member_signature: action
146 .existing_member_signature
147 .to_verified(signature_text.as_ref(), &scw_verifier)
148 .await?,
149 }),
150 UnverifiedAction::RevokeAssociation(action) => {
151 Action::RevokeAssociation(RevokeAssociation {
152 recovery_identifier_signature: action
153 .recovery_identifier_signature
154 .to_verified(signature_text.as_ref(), &scw_verifier)
155 .await?,
156 revoked_member: action.unsigned_action.revoked_member.clone(),
157 })
158 }
159 UnverifiedAction::ChangeRecoveryAddress(action) => {
160 Action::ChangeRecoveryIdentity(super::ChangeRecoveryIdentity {
161 recovery_identifier_signature: action
162 .recovery_identifier_signature
163 .to_verified(signature_text.as_ref(), &scw_verifier)
164 .await?,
165 new_recovery_identifier: action.unsigned_action.new_recovery_identifier.clone(),
166 })
167 }
168 UnverifiedAction::ClaimHandle(action) => Action::ClaimHandle(ClaimHandle {
169 handle: action.unsigned_action.handle.clone(),
170 signature: action
171 .signature
172 .to_verified(signature_text.as_ref(), &scw_verifier)
173 .await?,
174 }),
175 };
176
177 Ok(action)
178 }
179}
180
181#[derive(Debug, Clone, PartialEq)]
182pub struct UnverifiedCreateInbox {
183 pub(crate) unsigned_action: UnsignedCreateInbox,
184 pub(crate) initial_identifier_signature: UnverifiedSignature,
185}
186
187impl UnverifiedCreateInbox {
188 pub fn new(
189 unsigned_action: UnsignedCreateInbox,
190 initial_identifier_signature: UnverifiedSignature,
191 ) -> Self {
192 Self {
193 unsigned_action,
194 initial_identifier_signature,
195 }
196 }
197}
198
199#[derive(Debug, Clone, PartialEq)]
200pub struct UnverifiedAddAssociation {
201 pub(crate) unsigned_action: UnsignedAddAssociation,
202 pub(crate) new_member_signature: UnverifiedSignature,
203 pub(crate) existing_member_signature: UnverifiedSignature,
204}
205
206impl UnverifiedAddAssociation {
207 pub fn new(
208 unsigned_action: UnsignedAddAssociation,
209 new_member_signature: UnverifiedSignature,
210 existing_member_signature: UnverifiedSignature,
211 ) -> Self {
212 Self {
213 unsigned_action,
214 new_member_signature,
215 existing_member_signature,
216 }
217 }
218}
219#[derive(Debug, Clone, PartialEq)]
220pub struct UnverifiedRevokeAssociation {
221 pub(crate) recovery_identifier_signature: UnverifiedSignature,
222 pub(crate) unsigned_action: UnsignedRevokeAssociation,
223}
224
225impl UnverifiedRevokeAssociation {
226 pub fn new(
227 unsigned_action: UnsignedRevokeAssociation,
228 recovery_identifier_signature: UnverifiedSignature,
229 ) -> Self {
230 Self {
231 unsigned_action,
232 recovery_identifier_signature,
233 }
234 }
235}
236
237#[derive(Debug, Clone, PartialEq)]
238pub struct UnverifiedChangeRecoveryAddress {
239 pub(crate) recovery_identifier_signature: UnverifiedSignature,
240 pub(crate) unsigned_action: UnsignedChangeRecoveryAddress,
241}
242
243impl UnverifiedChangeRecoveryAddress {
244 pub fn new(
245 unsigned_action: UnsignedChangeRecoveryAddress,
246 recovery_identifier_signature: UnverifiedSignature,
247 ) -> Self {
248 Self {
249 unsigned_action,
250 recovery_identifier_signature,
251 }
252 }
253}
254
255#[derive(Debug, Clone, PartialEq)]
256pub struct UnverifiedClaimHandle {
257 pub(crate) unsigned_action: UnsignedClaimHandle,
258 pub(crate) signature: UnverifiedSignature,
259}
260
261impl UnverifiedClaimHandle {
262 pub fn new(unsigned_action: UnsignedClaimHandle, signature: UnverifiedSignature) -> Self {
263 Self {
264 unsigned_action,
265 signature,
266 }
267 }
268}
269
270#[derive(Debug, Clone, PartialEq)]
271pub enum UnverifiedSignature {
272 InstallationKey(UnverifiedInstallationKeySignature),
273 RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature),
274 SmartContractWallet(UnverifiedSmartContractWalletSignature),
275 Passkey(UnverifiedPasskeySignature),
276}
277
278impl UnverifiedSignature {
279 pub async fn to_verified<Text: AsRef<str>>(
281 &self,
282 signature_text: Text,
283 scw_verifier: impl SmartContractSignatureVerifier,
284 ) -> Result<VerifiedSignature, SignatureError> {
285 match self {
286 UnverifiedSignature::InstallationKey(sig) => VerifiedSignature::from_installation_key(
287 signature_text,
288 &sig.signature_bytes,
289 *sig.verifying_key(),
290 ),
291 UnverifiedSignature::RecoverableEcdsa(sig) => {
292 VerifiedSignature::from_recoverable_ecdsa(signature_text, &sig.signature_bytes)
293 }
294 UnverifiedSignature::SmartContractWallet(sig) => {
295 VerifiedSignature::from_smart_contract_wallet(
296 signature_text,
297 scw_verifier,
298 &sig.signature_bytes,
299 sig.account_id.clone(),
300 &mut Some(sig.block_number),
301 )
302 .await
303 }
304 UnverifiedSignature::Passkey(sig) => VerifiedSignature::from_passkey(
305 signature_text,
306 &sig.public_key,
307 &sig.signature,
308 &sig.authenticator_data,
309 &sig.client_data_json,
310 ),
311 }
312 }
313
314 pub fn new_recoverable_ecdsa(signature: Vec<u8>) -> Self {
315 Self::RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature::new(signature))
316 }
317
318 pub fn new_installation_key(
319 signature: Vec<u8>,
320 verifying_key: ed25519_dalek::VerifyingKey,
321 ) -> Self {
322 Self::InstallationKey(UnverifiedInstallationKeySignature::new(
323 signature,
324 verifying_key,
325 ))
326 }
327
328 pub fn new_passkey(
329 public_key: Vec<u8>,
330 signature: Vec<u8>,
331 authenticator_data: Vec<u8>,
332 client_data_json: Vec<u8>,
333 ) -> Self {
334 Self::Passkey(UnverifiedPasskeySignature {
335 client_data_json,
336 authenticator_data,
337 signature,
338 public_key,
339 })
340 }
341
342 pub fn new_smart_contract_wallet(
343 signature: Vec<u8>,
344 account_id: AccountId,
345 block_number: u64,
346 ) -> Self {
347 Self::SmartContractWallet(UnverifiedSmartContractWalletSignature::new(
348 signature,
349 account_id,
350 block_number,
351 ))
352 }
353}
354
355#[derive(Debug, Clone, PartialEq)]
356pub struct UnverifiedInstallationKeySignature {
357 pub(crate) signature_bytes: Vec<u8>,
358 pub(crate) verifying_key: Box<ed25519_dalek::VerifyingKey>,
361}
362
363impl UnverifiedInstallationKeySignature {
364 pub fn new(signature_bytes: Vec<u8>, verifying_key: ed25519_dalek::VerifyingKey) -> Self {
365 Self {
366 signature_bytes,
367 verifying_key: Box::new(verifying_key),
368 }
369 }
370
371 pub fn verifying_key(&self) -> &ed25519_dalek::VerifyingKey {
372 self.verifying_key.as_ref()
373 }
374
375 pub fn verifying_key_bytes(&self) -> Vec<u8> {
376 self.verifying_key.as_ref().as_ref().to_vec()
377 }
378}
379
380#[derive(Debug, Clone, PartialEq)]
381pub struct UnverifiedPasskeySignature {
382 pub public_key: Vec<u8>,
384 pub signature: Vec<u8>,
385 pub authenticator_data: Vec<u8>,
386 pub client_data_json: Vec<u8>,
387}
388
389#[derive(Debug, Clone, PartialEq)]
390pub struct UnverifiedRecoverableEcdsaSignature {
391 pub(crate) signature_bytes: Vec<u8>,
392}
393
394impl UnverifiedRecoverableEcdsaSignature {
395 pub fn new(signature_bytes: Vec<u8>) -> Self {
396 Self { signature_bytes }
397 }
398 pub fn signature_bytes(&self) -> &[u8] {
399 &self.signature_bytes
400 }
401}
402
403#[derive(Debug, Clone, PartialEq)]
404pub struct UnverifiedSmartContractWalletSignature {
405 pub(crate) signature_bytes: Vec<u8>,
406 pub(crate) account_id: AccountId,
407 pub(crate) block_number: u64,
408}
409
410#[derive(Debug, Clone, PartialEq)]
411pub struct NewUnverifiedSmartContractWalletSignature {
412 pub(crate) signature_bytes: Vec<u8>,
413 pub(crate) account_id: AccountId,
414 pub(crate) block_number: Option<u64>,
415}
416
417impl NewUnverifiedSmartContractWalletSignature {
418 pub fn new(signature_bytes: Vec<u8>, account_id: AccountId, block_number: Option<u64>) -> Self {
419 Self {
420 account_id,
421 block_number,
422 signature_bytes,
423 }
424 }
425}
426
427impl UnverifiedSmartContractWalletSignature {
428 pub fn new(signature_bytes: Vec<u8>, account_id: AccountId, block_number: u64) -> Self {
429 Self {
430 signature_bytes,
431 account_id,
432 block_number,
433 }
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use crate::associations::{member::Identifier, unsigned_actions::UnsignedCreateInbox};
440
441 use super::{
442 UnverifiedAction, UnverifiedCreateInbox, UnverifiedIdentityUpdate,
443 UnverifiedRecoverableEcdsaSignature, UnverifiedSignature,
444 };
445
446 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
447 #[cfg_attr(not(target_arch = "wasm32"), test)]
448 fn create_identity_update() {
449 let account_identifier = Identifier::rand_ethereum();
450 let nonce = 1;
451 let update = UnverifiedIdentityUpdate {
452 inbox_id: account_identifier.inbox_id(nonce).unwrap(),
453 client_timestamp_ns: 10,
454 actions: vec![UnverifiedAction::CreateInbox(UnverifiedCreateInbox {
455 unsigned_action: UnsignedCreateInbox {
456 account_identifier: account_identifier.clone(),
457 nonce,
458 },
459 initial_identifier_signature: UnverifiedSignature::RecoverableEcdsa(
460 UnverifiedRecoverableEcdsaSignature {
461 signature_bytes: vec![1, 2, 3],
462 },
463 ),
464 })],
465 };
466 assert!(
467 update
468 .signature_text()
469 .contains(format!("(Owner: {})", account_identifier).as_str()),
470 "could not find account address in signature text: {}",
471 update.signature_text()
472 );
473 }
474}