1use crate::room_state::member::MemberId;
2use crate::room_state::ChatRoomParametersV1;
3use crate::util::{sign_struct, truncated_base64, verify_struct};
4use crate::ChatRoomStateV1;
5use blake3::Hash;
6use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
7use freenet_scaffold::ComposableState;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
12pub struct OptionalUpgradeV1(pub Option<AuthorizedUpgradeV1>);
13
14#[derive(Serialize, Deserialize, Clone, PartialEq)]
15pub struct AuthorizedUpgradeV1 {
16 pub upgrade: UpgradeV1,
17 pub signature: Signature,
18}
19
20impl ComposableState for OptionalUpgradeV1 {
21 type ParentState = ChatRoomStateV1;
22 type Summary = Option<u8>;
23 type Delta = AuthorizedUpgradeV1;
24 type Parameters = ChatRoomParametersV1;
25
26 fn verify(
27 &self,
28 _parent_state: &Self::ParentState,
29 parameters: &Self::Parameters,
30 ) -> Result<(), String> {
31 if let Some(upgrade) = &self.0 {
32 upgrade
33 .validate(¶meters.owner)
34 .map_err(|e| format!("Invalid signature: {}", e))
35 } else {
36 Ok(())
37 }
38 }
39
40 fn summarize(
41 &self,
42 _parent_state: &Self::ParentState,
43 _parameters: &Self::Parameters,
44 ) -> Self::Summary {
45 self.0.as_ref().map(|u| u.upgrade.version)
46 }
47
48 fn delta(
49 &self,
50 _parent_state: &Self::ParentState,
51 _parameters: &Self::Parameters,
52 old_state_summary: &Self::Summary,
53 ) -> Option<Self::Delta> {
54 match &self.0 {
55 Some(upgrade) => {
56 if old_state_summary.is_none_or(|old_version| upgrade.upgrade.version > old_version)
59 {
60 Some(upgrade.clone())
61 } else {
62 None
63 }
64 }
65 None => None,
66 }
67 }
68
69 fn apply_delta(
70 &mut self,
71 _parent_state: &Self::ParentState,
72 parameters: &Self::Parameters,
73 delta: &Option<Self::Delta>,
74 ) -> Result<(), String> {
75 if let Some(delta) = delta {
76 delta
78 .validate(¶meters.owner)
79 .map_err(|e| format!("Invalid upgrade signature: {}", e))?;
80
81 *self = OptionalUpgradeV1(Some(delta.clone()));
82 }
83 Ok(())
84 }
85}
86
87impl AuthorizedUpgradeV1 {
88 pub fn new(upgrade: UpgradeV1, signing_key: &SigningKey) -> Self {
89 Self {
90 upgrade: upgrade.clone(),
91 signature: sign_struct(&upgrade, signing_key),
92 }
93 }
94
95 pub fn with_signature(upgrade: UpgradeV1, signature: Signature) -> Self {
98 Self { upgrade, signature }
99 }
100
101 pub fn validate(
102 &self,
103 verifying_key: &VerifyingKey,
104 ) -> Result<(), ed25519_dalek::SignatureError> {
105 verify_struct(&self.upgrade, &self.signature, verifying_key)
106 }
107}
108
109impl fmt::Debug for AuthorizedUpgradeV1 {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 f.debug_struct("AuthorizedUpgrade")
112 .field("upgrade", &self.upgrade)
113 .field(
114 "signature",
115 &format_args!("{}", truncated_base64(self.signature.to_bytes())),
116 )
117 .finish()
118 }
119}
120
121#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
122pub struct UpgradeV1 {
123 pub owner_member_id: MemberId,
124 pub version: u8,
125 pub new_chatroom_address: Hash,
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::room_state::member::MemberId;
132 use ed25519_dalek::SigningKey;
133 use freenet_scaffold::util::FastHash;
134 use rand::rngs::OsRng;
135
136 fn create_test_upgrade(owner_id: MemberId) -> UpgradeV1 {
137 UpgradeV1 {
138 owner_member_id: owner_id,
139 version: 1,
140 new_chatroom_address: Hash::from([0; 32]),
141 }
142 }
143
144 #[test]
145 fn test_authorized_upgrade_new_and_validate() {
146 let signing_key = SigningKey::generate(&mut OsRng);
147 let verifying_key = signing_key.verifying_key();
148 let owner_id = MemberId(FastHash(0));
149
150 let upgrade = create_test_upgrade(owner_id);
151 let authorized_upgrade = AuthorizedUpgradeV1::new(upgrade.clone(), &signing_key);
152
153 assert_eq!(authorized_upgrade.upgrade, upgrade);
154 assert!(authorized_upgrade.validate(&verifying_key).is_ok());
155
156 let wrong_key = SigningKey::generate(&mut OsRng).verifying_key();
158 assert!(authorized_upgrade.validate(&wrong_key).is_err());
159 }
160
161 #[test]
162 fn test_optional_upgrade_verify() {
163 let owner_signing_key = SigningKey::generate(&mut OsRng);
164 let owner_verifying_key = owner_signing_key.verifying_key();
165 let owner_id = MemberId::from(&owner_verifying_key);
166
167 let upgrade = create_test_upgrade(owner_id);
168 let authorized_upgrade = AuthorizedUpgradeV1::new(upgrade, &owner_signing_key);
169
170 let optional_upgrade = OptionalUpgradeV1(Some(authorized_upgrade));
171
172 let parent_state = ChatRoomStateV1::default();
173 let parameters = ChatRoomParametersV1 {
174 owner: owner_verifying_key,
175 };
176
177 assert!(
179 optional_upgrade.verify(&parent_state, ¶meters).is_ok(),
180 "Valid upgrade should pass verification"
181 );
182
183 let mut invalid_upgrade = optional_upgrade.clone();
185 if let Some(ref mut au) = invalid_upgrade.0 {
186 au.signature = Signature::from_bytes(&[0; 64]); }
188 assert!(
189 invalid_upgrade.verify(&parent_state, ¶meters).is_err(),
190 "Upgrade with invalid signature should fail verification"
191 );
192
193 let none_upgrade = OptionalUpgradeV1(None);
195 assert!(
196 none_upgrade.verify(&parent_state, ¶meters).is_ok(),
197 "None upgrade should pass verification"
198 );
199 }
200
201 #[test]
202 fn test_optional_upgrade_summarize() {
203 let signing_key = SigningKey::generate(&mut OsRng);
204 let owner_id = MemberId(FastHash(0));
205
206 let upgrade = create_test_upgrade(owner_id);
207 let authorized_upgrade = AuthorizedUpgradeV1::new(upgrade, &signing_key);
208
209 let optional_upgrade = OptionalUpgradeV1(Some(authorized_upgrade));
210
211 let parent_state = ChatRoomStateV1::default();
212 let parameters = ChatRoomParametersV1 {
213 owner: signing_key.verifying_key(),
214 };
215
216 let summary = optional_upgrade.summarize(&parent_state, ¶meters);
217 assert_eq!(summary, Some(1));
218
219 let none_upgrade = OptionalUpgradeV1(None);
220 let none_summary = none_upgrade.summarize(&parent_state, ¶meters);
221 assert_eq!(none_summary, None);
222 }
223
224 #[test]
225 fn test_optional_upgrade_delta() {
226 let signing_key = SigningKey::generate(&mut OsRng);
227 let owner_id = MemberId(FastHash(0));
228
229 let upgrade = create_test_upgrade(owner_id);
230 let authorized_upgrade = AuthorizedUpgradeV1::new(upgrade, &signing_key);
231
232 let optional_upgrade = OptionalUpgradeV1(Some(authorized_upgrade.clone()));
233
234 let parent_state = ChatRoomStateV1::default();
235 let parameters = ChatRoomParametersV1 {
236 owner: signing_key.verifying_key(),
237 };
238
239 let old_summary = None;
240 let delta = optional_upgrade.delta(&parent_state, ¶meters, &old_summary);
241
242 assert_eq!(delta, Some(authorized_upgrade));
243
244 let none_upgrade = OptionalUpgradeV1(None);
245 let none_delta = none_upgrade.delta(&parent_state, ¶meters, &old_summary);
246 assert_eq!(none_delta, None);
247 }
248
249 #[test]
250 fn test_optional_upgrade_apply_delta() {
251 let signing_key = SigningKey::generate(&mut OsRng);
252 let owner_id = MemberId(FastHash(0));
253
254 let upgrade = create_test_upgrade(owner_id);
255 let authorized_upgrade = AuthorizedUpgradeV1::new(upgrade, &signing_key);
256
257 let mut optional_upgrade = OptionalUpgradeV1(None);
258
259 let parent_state = ChatRoomStateV1::default();
260 let parameters = ChatRoomParametersV1 {
261 owner: signing_key.verifying_key(),
262 };
263
264 let delta = authorized_upgrade.clone();
265 assert!(optional_upgrade
266 .apply_delta(&parent_state, ¶meters, &Some(delta.clone()))
267 .is_ok());
268 assert_eq!(optional_upgrade, OptionalUpgradeV1(Some(delta)));
269 }
270}