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