1use serde::{Deserialize, Serialize};
8
9use super::types::{Prefix, Said};
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17pub struct KeyState {
18 pub prefix: Prefix,
20
21 pub current_keys: Vec<String>,
24
25 pub next_commitment: Vec<String>,
28
29 pub sequence: u64,
31
32 pub last_event_said: Said,
34
35 pub is_abandoned: bool,
37 pub threshold: u64,
39 pub next_threshold: u64,
41}
42
43impl KeyState {
44 pub fn from_inception(
54 prefix: Prefix,
55 keys: Vec<String>,
56 next: Vec<String>,
57 threshold: u64,
58 next_threshold: u64,
59 said: Said,
60 ) -> Self {
61 Self {
62 prefix,
63 current_keys: keys,
64 next_commitment: next.clone(),
65 sequence: 0,
66 last_event_said: said,
67 is_abandoned: next.is_empty(),
68 threshold,
69 next_threshold,
70 }
71 }
72
73 pub fn apply_rotation(
80 &mut self,
81 new_keys: Vec<String>,
82 new_next: Vec<String>,
83 threshold: u64,
84 next_threshold: u64,
85 sequence: u64,
86 said: Said,
87 ) {
88 self.current_keys = new_keys;
89 self.next_commitment = new_next.clone();
90 self.threshold = threshold;
91 self.next_threshold = next_threshold;
92 self.sequence = sequence;
93 self.last_event_said = said;
94 self.is_abandoned = new_next.is_empty();
95 }
96
97 pub fn apply_interaction(&mut self, sequence: u64, said: Said) {
101 self.sequence = sequence;
102 self.last_event_said = said;
103 }
104
105 pub fn current_key(&self) -> Option<&str> {
109 self.current_keys.first().map(|s| s.as_str())
110 }
111
112 pub fn can_rotate(&self) -> bool {
116 !self.is_abandoned && !self.next_commitment.is_empty()
117 }
118
119 pub fn did(&self) -> String {
121 format!("did:keri:{}", self.prefix.as_str())
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn key_state_from_inception() {
131 let state = KeyState::from_inception(
132 Prefix::new_unchecked("EPrefix".to_string()),
133 vec!["DKey1".to_string()],
134 vec!["ENext1".to_string()],
135 1,
136 1,
137 Said::new_unchecked("ESAID".to_string()),
138 );
139 assert_eq!(state.sequence, 0);
140 assert!(!state.is_abandoned);
141 assert!(state.can_rotate());
142 assert_eq!(state.current_key(), Some("DKey1"));
143 assert_eq!(state.did(), "did:keri:EPrefix");
144 }
145
146 #[test]
147 fn key_state_apply_rotation() {
148 let mut state = KeyState::from_inception(
149 Prefix::new_unchecked("EPrefix".to_string()),
150 vec!["DKey1".to_string()],
151 vec!["ENext1".to_string()],
152 1,
153 1,
154 Said::new_unchecked("ESAID1".to_string()),
155 );
156
157 state.apply_rotation(
158 vec!["DKey2".to_string()],
159 vec!["ENext2".to_string()],
160 1,
161 1,
162 1,
163 Said::new_unchecked("ESAID2".to_string()),
164 );
165
166 assert_eq!(state.sequence, 1);
167 assert_eq!(state.current_keys[0], "DKey2");
168 assert_eq!(state.next_commitment[0], "ENext2");
169 assert_eq!(state.last_event_said, "ESAID2");
170 assert!(state.can_rotate());
171 }
172
173 #[test]
174 fn key_state_apply_interaction() {
175 let mut state = KeyState::from_inception(
176 Prefix::new_unchecked("EPrefix".to_string()),
177 vec!["DKey1".to_string()],
178 vec!["ENext1".to_string()],
179 1,
180 1,
181 Said::new_unchecked("ESAID1".to_string()),
182 );
183
184 state.apply_interaction(1, Said::new_unchecked("ESAID_IXN".to_string()));
185
186 assert_eq!(state.sequence, 1);
187 assert_eq!(state.current_keys[0], "DKey1");
189 assert_eq!(state.last_event_said, "ESAID_IXN");
190 }
191
192 #[test]
193 fn abandoned_identity_cannot_rotate() {
194 let state = KeyState::from_inception(
195 Prefix::new_unchecked("EPrefix".to_string()),
196 vec!["DKey1".to_string()],
197 vec![], 1,
199 0,
200 Said::new_unchecked("ESAID".to_string()),
201 );
202 assert!(state.is_abandoned);
203 assert!(!state.can_rotate());
204 }
205
206 #[test]
207 fn key_state_serializes() {
208 let state = KeyState::from_inception(
209 Prefix::new_unchecked("EPrefix".to_string()),
210 vec!["DKey1".to_string()],
211 vec!["ENext1".to_string()],
212 1,
213 1,
214 Said::new_unchecked("ESAID".to_string()),
215 );
216
217 let json = serde_json::to_string(&state).unwrap();
218 let parsed: KeyState = serde_json::from_str(&json).unwrap();
219 assert_eq!(state, parsed);
220 }
221}