sui_types/
authenticator_state.rs1use fastcrypto_zkp::bn254::zk_login::{JWK, JwkId};
5use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr};
6use mysten_common::ZipDebugEqIteratorExt;
7use serde::{Deserialize, Serialize};
8
9use crate::base_types::SequenceNumber;
10use crate::dynamic_field::get_dynamic_field_from_store;
11use crate::error::{SuiErrorKind, SuiResult};
12use crate::object::Owner;
13use crate::storage::ObjectStore;
14use crate::{SUI_AUTHENTICATOR_STATE_OBJECT_ID, SUI_FRAMEWORK_ADDRESS, id::UID};
15
16pub const AUTHENTICATOR_STATE_MODULE_NAME: &IdentStr = ident_str!("authenticator_state");
17pub const AUTHENTICATOR_STATE_STRUCT_NAME: &IdentStr = ident_str!("AuthenticatorState");
18pub const AUTHENTICATOR_STATE_UPDATE_FUNCTION_NAME: &IdentStr =
19 ident_str!("update_authenticator_state");
20pub const AUTHENTICATOR_STATE_CREATE_FUNCTION_NAME: &IdentStr = ident_str!("create");
21pub const AUTHENTICATOR_STATE_EXPIRE_JWKS_FUNCTION_NAME: &IdentStr = ident_str!("expire_jwks");
22pub const RESOLVED_SUI_AUTHENTICATOR_STATE: (&AccountAddress, &IdentStr, &IdentStr) = (
23 &SUI_FRAMEWORK_ADDRESS,
24 AUTHENTICATOR_STATE_MODULE_NAME,
25 AUTHENTICATOR_STATE_STRUCT_NAME,
26);
27
28pub const AUTHENTICATOR_STATE_VERSION: u64 = 1;
30
31#[derive(Debug, Serialize, Deserialize)]
32pub struct AuthenticatorState {
33 pub id: UID,
34 pub version: u64,
35}
36
37#[derive(Debug, Serialize, Deserialize)]
38pub struct AuthenticatorStateInner {
39 pub version: u64,
40
41 pub active_jwks: Vec<ActiveJwk>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
46pub struct ActiveJwk {
47 pub jwk_id: JwkId,
48 pub jwk: JWK,
49 pub epoch: u64,
51}
52
53fn string_bytes_ord(a: &str, b: &str) -> std::cmp::Ordering {
54 let a_bytes = a.as_bytes();
55 let b_bytes = b.as_bytes();
56
57 if a_bytes.len() < b_bytes.len() {
58 return std::cmp::Ordering::Less;
59 }
60 if a_bytes.len() > b_bytes.len() {
61 return std::cmp::Ordering::Greater;
62 }
63
64 for (a_byte, b_byte) in a_bytes.iter().zip_debug_eq(b_bytes.iter()) {
65 if a_byte < b_byte {
66 return std::cmp::Ordering::Less;
67 }
68 if a_byte > b_byte {
69 return std::cmp::Ordering::Greater;
70 }
71 }
72
73 std::cmp::Ordering::Equal
74}
75
76fn jwk_ord(a: &ActiveJwk, b: &ActiveJwk) -> std::cmp::Ordering {
78 if a.jwk_id.iss != b.jwk_id.iss {
80 string_bytes_ord(&a.jwk_id.iss, &b.jwk_id.iss)
81 } else if a.jwk_id.kid != b.jwk_id.kid {
82 string_bytes_ord(&a.jwk_id.kid, &b.jwk_id.kid)
83 } else if a.jwk.kty != b.jwk.kty {
84 string_bytes_ord(&a.jwk.kty, &b.jwk.kty)
85 } else if a.jwk.e != b.jwk.e {
86 string_bytes_ord(&a.jwk.e, &b.jwk.e)
87 } else if a.jwk.n != b.jwk.n {
88 string_bytes_ord(&a.jwk.n, &b.jwk.n)
89 } else {
90 string_bytes_ord(&a.jwk.alg, &b.jwk.alg)
91 }
92}
93
94#[allow(clippy::non_canonical_partial_ord_impl)]
95impl std::cmp::PartialOrd for ActiveJwk {
96 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
98 Some(jwk_ord(self, other))
99 }
100}
101
102impl std::cmp::Ord for ActiveJwk {
103 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
105 jwk_ord(self, other)
106 }
107}
108
109pub fn get_authenticator_state(
110 object_store: impl ObjectStore,
111) -> SuiResult<Option<AuthenticatorStateInner>> {
112 let outer = object_store.get_object(&SUI_AUTHENTICATOR_STATE_OBJECT_ID);
113 let Some(outer) = outer else {
114 return Ok(None);
115 };
116 let move_object = outer.data.try_as_move().ok_or_else(|| {
117 SuiErrorKind::SuiSystemStateReadError(
118 "AuthenticatorState object must be a Move object".to_owned(),
119 )
120 })?;
121 let outer = bcs::from_bytes::<AuthenticatorState>(move_object.contents())
122 .map_err(|err| SuiErrorKind::SuiSystemStateReadError(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 SuiErrorKind::DynamicFieldReadError(format!(
131 "Failed to load sui 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) -> SuiResult<Option<SequenceNumber>> {
142 Ok(object_store
143 .get_object(&SUI_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}