iota_types/
authenticator_state.rs1pub(crate) use fastcrypto_zkp::bn254::zk_login::{JWK, JwkId};
6use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_FRAMEWORK_ADDRESS,
11 base_types::SequenceNumber,
12 dynamic_field::get_dynamic_field_from_store,
13 error::{IotaError, IotaResult},
14 id::UID,
15 object::Owner,
16 storage::ObjectStore,
17};
18
19pub const AUTHENTICATOR_STATE_MODULE_NAME: &IdentStr = ident_str!("authenticator_state");
20pub const AUTHENTICATOR_STATE_STRUCT_NAME: &IdentStr = ident_str!("AuthenticatorState");
21pub const AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME: &IdentStr =
22 ident_str!("update_authenticator_state");
23pub const AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME: &IdentStr = ident_str!("create");
24pub const AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME: &IdentStr = ident_str!("expire_jwks");
25pub const RESOLVED_IOTA_AUTHENTICATOR_STATE: (&AccountAddress, &IdentStr, &IdentStr) = (
26 &IOTA_FRAMEWORK_ADDRESS,
27 AUTHENTICATOR_STATE_MODULE_NAME,
28 AUTHENTICATOR_STATE_STRUCT_NAME,
29);
30
31pub const AUTHENTICATOR_STATE_VERSION: u64 = 1;
33
34#[derive(Debug, Serialize, Deserialize)]
35pub struct AuthenticatorState {
36 pub id: UID,
37 pub version: u64,
38}
39
40#[derive(Debug, Serialize, Deserialize)]
41pub struct AuthenticatorStateInner {
42 pub version: u64,
43
44 pub active_jwks: Vec<ActiveJwk>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
49pub struct ActiveJwk {
50 pub jwk_id: JwkId,
51 pub jwk: JWK,
52 pub epoch: u64,
54}
55
56fn string_bytes_ord(a: &str, b: &str) -> std::cmp::Ordering {
57 let a_bytes = a.as_bytes();
58 let b_bytes = b.as_bytes();
59
60 if a_bytes.len() < b_bytes.len() {
61 return std::cmp::Ordering::Less;
62 }
63 if a_bytes.len() > b_bytes.len() {
64 return std::cmp::Ordering::Greater;
65 }
66
67 for (a_byte, b_byte) in a_bytes.iter().zip(b_bytes.iter()) {
68 if a_byte < b_byte {
69 return std::cmp::Ordering::Less;
70 }
71 if a_byte > b_byte {
72 return std::cmp::Ordering::Greater;
73 }
74 }
75
76 std::cmp::Ordering::Equal
77}
78
79fn jwk_ord(a: &ActiveJwk, b: &ActiveJwk) -> std::cmp::Ordering {
81 if a.jwk_id.iss != b.jwk_id.iss {
83 string_bytes_ord(&a.jwk_id.iss, &b.jwk_id.iss)
84 } else if a.jwk_id.kid != b.jwk_id.kid {
85 string_bytes_ord(&a.jwk_id.kid, &b.jwk_id.kid)
86 } else if a.jwk.kty != b.jwk.kty {
87 string_bytes_ord(&a.jwk.kty, &b.jwk.kty)
88 } else if a.jwk.e != b.jwk.e {
89 string_bytes_ord(&a.jwk.e, &b.jwk.e)
90 } else if a.jwk.n != b.jwk.n {
91 string_bytes_ord(&a.jwk.n, &b.jwk.n)
92 } else {
93 string_bytes_ord(&a.jwk.alg, &b.jwk.alg)
94 }
95}
96
97impl std::cmp::PartialOrd for ActiveJwk {
98 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
100 Some(self.cmp(other))
101 }
102}
103
104impl std::cmp::Ord for ActiveJwk {
105 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
107 jwk_ord(self, other)
108 }
109}
110
111pub fn get_authenticator_state(
112 object_store: impl ObjectStore,
113) -> IotaResult<Option<AuthenticatorStateInner>> {
114 let outer = object_store.get_object(&IOTA_AUTHENTICATOR_STATE_OBJECT_ID)?;
115 let Some(outer) = outer else {
116 return Ok(None);
117 };
118 let move_object = outer.data.try_as_move().ok_or_else(|| {
119 IotaError::IotaSystemStateRead("AuthenticatorState object must be a Move object".to_owned())
120 })?;
121 let outer = bcs::from_bytes::<AuthenticatorState>(move_object.contents())
122 .map_err(|err| IotaError::IotaSystemStateRead(err.to_string()))?;
123
124 assert_eq!(outer.version, AUTHENTICATOR_STATE_VERSION);
126
127 let id = outer.id.id.bytes;
128 let inner: AuthenticatorStateInner =
129 get_dynamic_field_from_store(&object_store, id, &outer.version).map_err(|err| {
130 IotaError::DynamicFieldRead(format!(
131 "Failed to load iota system state inner object with ID {:?} and version {:?}: {:?}",
132 id, outer.version, err
133 ))
134 })?;
135
136 Ok(Some(inner))
137}
138
139pub fn get_authenticator_state_obj_initial_shared_version(
140 object_store: &dyn ObjectStore,
141) -> IotaResult<Option<SequenceNumber>> {
142 Ok(object_store
143 .get_object(&IOTA_AUTHENTICATOR_STATE_OBJECT_ID)?
144 .map(|obj| match obj.owner {
145 Owner::Shared {
146 initial_shared_version,
147 } => initial_shared_version,
148 _ => unreachable!("Authenticator state object must be shared"),
149 }))
150}