1use crate::{
12 crypto::{
13 ecdsa::{ECDSASignature, ECDSAVerifier},
14 KeyType, PrivateKey, PublicKey, Signature,
15 },
16 error::{ChaincraftError, Result},
17 shared::{MessageType, SharedMessage, SharedObjectId},
18 shared_object::ApplicationObject,
19};
20use async_trait::async_trait;
21use serde::{Deserialize, Serialize};
22use serde_json::Value;
23use sha2::{Digest, Sha256};
24use std::any::Any;
25use std::collections::HashMap;
26use std::time::{SystemTime, UNIX_EPOCH};
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30#[serde(tag = "message_type")]
31pub enum ChatroomMessageType {
32 #[serde(rename = "CREATE_CHATROOM")]
33 CreateChatroom {
34 chatroom_name: String,
35 public_key_pem: String,
36 #[serde(default)]
37 timestamp: f64,
38 #[serde(default)]
39 signature: String,
40 },
41 #[serde(rename = "REQUEST_JOIN")]
42 RequestJoin {
43 chatroom_name: String,
44 public_key_pem: String,
45 #[serde(default)]
46 timestamp: f64,
47 #[serde(default)]
48 signature: String,
49 },
50 #[serde(rename = "ACCEPT_MEMBER")]
51 AcceptMember {
52 chatroom_name: String,
53 public_key_pem: String,
54 requester_key_pem: String,
55 #[serde(default)]
56 timestamp: f64,
57 #[serde(default)]
58 signature: String,
59 },
60 #[serde(rename = "POST_MESSAGE")]
61 PostMessage {
62 chatroom_name: String,
63 public_key_pem: String,
64 text: String,
65 #[serde(default)]
66 timestamp: f64,
67 #[serde(default)]
68 signature: String,
69 },
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct Chatroom {
75 pub name: String,
76 pub admin: String, pub members: Vec<String>, pub messages: Vec<ChatMessage>, }
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ChatMessage {
84 pub message_type: String,
85 pub chatroom_name: String,
86 pub public_key_pem: String,
87 pub text: Option<String>,
88 pub requester_key_pem: Option<String>,
89 pub timestamp: f64,
90 pub signature: String,
91}
92
93#[derive(Debug, Clone)]
95pub struct ChatroomObject {
96 id: SharedObjectId,
97 chatrooms: HashMap<String, Chatroom>,
98 users: HashMap<String, String>,
99 verifier: ECDSAVerifier,
100}
101
102impl ChatroomObject {
103 pub fn new() -> Self {
104 Self {
105 id: SharedObjectId::new(),
106 chatrooms: HashMap::new(),
107 users: HashMap::new(),
108 verifier: ECDSAVerifier::new(),
109 }
110 }
111
112 pub fn get_chatrooms(&self) -> &HashMap<String, Chatroom> {
114 &self.chatrooms
115 }
116
117 pub fn get_chatroom(&self, name: &str) -> Option<&Chatroom> {
119 self.chatrooms.get(name)
120 }
121
122 fn validate_signature(
124 &self,
125 msg_data: &Value,
126 signature: &str,
127 public_key_pem: &str,
128 ) -> Result<bool> {
129 let mut msg_for_verification = msg_data.clone();
131 if let Some(obj) = msg_for_verification.as_object_mut() {
132 obj.remove("signature");
133 }
134
135 let payload = serde_json::to_string(&msg_for_verification).map_err(|e| {
136 ChaincraftError::Serialization(crate::error::SerializationError::Json(e))
137 })?;
138
139 let signature_bytes = hex::decode(signature)
141 .map_err(|_| ChaincraftError::validation("Invalid signature hex"))?;
142
143 let ecdsa_sig = ECDSASignature::from_bytes(&signature_bytes)
145 .map_err(|_| ChaincraftError::validation("Invalid signature format"))?;
146
147 self.verifier
148 .verify(payload.as_bytes(), &ecdsa_sig, public_key_pem)
149 }
150
151 fn is_timestamp_recent(&self, timestamp: f64) -> bool {
153 let now = SystemTime::now()
154 .duration_since(UNIX_EPOCH)
155 .unwrap()
156 .as_secs_f64();
157
158 let diff = (now - timestamp).abs();
159 diff <= 15.0
160 }
161
162 async fn process_create_chatroom(
164 &mut self,
165 msg: ChatroomMessageType,
166 msg_data: &Value,
167 ) -> Result<bool> {
168 if let ChatroomMessageType::CreateChatroom {
169 chatroom_name,
170 public_key_pem,
171 timestamp,
172 signature,
173 } = msg
174 {
175 if !self.validate_signature(msg_data, &signature, &public_key_pem)? {
177 return Ok(false);
178 }
179
180 if !self.is_timestamp_recent(timestamp) {
182 return Ok(false);
183 }
184
185 if self.chatrooms.contains_key(&chatroom_name) {
187 return Ok(false);
188 }
189
190 let chatroom = Chatroom {
192 name: chatroom_name.clone(),
193 admin: public_key_pem.clone(),
194 members: vec![public_key_pem.clone()], messages: Vec::new(),
196 };
197
198 self.chatrooms.insert(chatroom_name, chatroom);
199 tracing::info!("Created chatroom with admin: {}", public_key_pem);
200
201 Ok(true)
202 } else {
203 Ok(false)
204 }
205 }
206
207 async fn process_request_join(
209 &mut self,
210 msg: ChatroomMessageType,
211 msg_data: &Value,
212 ) -> Result<bool> {
213 if let ChatroomMessageType::RequestJoin {
214 chatroom_name,
215 public_key_pem,
216 timestamp,
217 signature,
218 } = msg
219 {
220 if !self.validate_signature(msg_data, &signature, &public_key_pem)? {
222 return Ok(false);
223 }
224
225 if !self.is_timestamp_recent(timestamp) {
227 return Ok(false);
228 }
229
230 if !self.chatrooms.contains_key(&chatroom_name) {
232 return Ok(false);
233 }
234
235 tracing::info!(
237 "Join request for chatroom '{}' from: {}",
238 chatroom_name,
239 public_key_pem
240 );
241
242 if let Some(chatroom) = self.chatrooms.get_mut(&chatroom_name) {
244 let chat_msg = ChatMessage {
245 message_type: "REQUEST_JOIN".to_string(),
246 chatroom_name: chatroom_name.clone(),
247 public_key_pem: public_key_pem.clone(),
248 text: None,
249 requester_key_pem: None,
250 timestamp,
251 signature,
252 };
253 chatroom.messages.push(chat_msg);
254 }
255
256 Ok(true)
257 } else {
258 Ok(false)
259 }
260 }
261
262 async fn process_accept_member(
264 &mut self,
265 msg: ChatroomMessageType,
266 msg_data: &Value,
267 ) -> Result<bool> {
268 if let ChatroomMessageType::AcceptMember {
269 chatroom_name,
270 public_key_pem,
271 requester_key_pem,
272 timestamp,
273 signature,
274 } = msg
275 {
276 if !self.validate_signature(msg_data, &signature, &public_key_pem)? {
278 return Ok(false);
279 }
280
281 if !self.is_timestamp_recent(timestamp) {
283 return Ok(false);
284 }
285
286 if let Some(chatroom) = self.chatrooms.get_mut(&chatroom_name) {
288 if chatroom.admin != public_key_pem {
289 return Ok(false); }
291
292 if !chatroom.members.contains(&requester_key_pem) {
294 chatroom.members.push(requester_key_pem.clone());
295 tracing::info!(
296 "Added member {} to chatroom '{}'",
297 requester_key_pem,
298 chatroom_name
299 );
300 }
301
302 let chat_msg = ChatMessage {
304 message_type: "ACCEPT_MEMBER".to_string(),
305 chatroom_name: chatroom_name.clone(),
306 public_key_pem: public_key_pem.clone(),
307 text: None,
308 requester_key_pem: Some(requester_key_pem),
309 timestamp,
310 signature,
311 };
312 chatroom.messages.push(chat_msg);
313
314 Ok(true)
315 } else {
316 Ok(false)
317 }
318 } else {
319 Ok(false)
320 }
321 }
322
323 async fn process_post_message(
325 &mut self,
326 msg: ChatroomMessageType,
327 msg_data: &Value,
328 ) -> Result<bool> {
329 if let ChatroomMessageType::PostMessage {
330 chatroom_name,
331 public_key_pem,
332 text,
333 timestamp,
334 signature,
335 } = msg
336 {
337 if !self.validate_signature(msg_data, &signature, &public_key_pem)? {
339 return Ok(false);
340 }
341
342 if !self.is_timestamp_recent(timestamp) {
344 return Ok(false);
345 }
346
347 if let Some(chatroom) = self.chatrooms.get_mut(&chatroom_name) {
349 if !chatroom.members.contains(&public_key_pem) {
350 return Ok(false); }
352
353 let chat_msg = ChatMessage {
355 message_type: "POST_MESSAGE".to_string(),
356 chatroom_name: chatroom_name.clone(),
357 public_key_pem: public_key_pem.clone(),
358 text: Some(text),
359 requester_key_pem: None,
360 timestamp,
361 signature,
362 };
363 chatroom.messages.push(chat_msg);
364
365 tracing::info!("Message posted to '{}' by: {}", chatroom_name, public_key_pem);
366
367 Ok(true)
368 } else {
369 Ok(false)
370 }
371 } else {
372 Ok(false)
373 }
374 }
375}
376
377impl Default for ChatroomObject {
378 fn default() -> Self {
379 Self::new()
380 }
381}
382
383#[async_trait]
384impl ApplicationObject for ChatroomObject {
385 fn id(&self) -> &SharedObjectId {
386 &self.id
387 }
388
389 fn type_name(&self) -> &'static str {
390 "ChatroomObject"
391 }
392
393 async fn is_valid(&self, message: &SharedMessage) -> Result<bool> {
394 let msg_result: std::result::Result<ChatroomMessageType, _> =
396 serde_json::from_value(message.data.clone());
397 Ok(msg_result.is_ok())
398 }
399
400 async fn add_message(&mut self, message: SharedMessage) -> Result<()> {
401 let msg: ChatroomMessageType =
402 serde_json::from_value(message.data.clone()).map_err(|e| {
403 ChaincraftError::Serialization(crate::error::SerializationError::Json(e))
404 })?;
405
406 let processed = match &msg {
407 ChatroomMessageType::CreateChatroom { .. } => {
408 self.process_create_chatroom(msg.clone(), &message.data)
409 .await?
410 },
411 ChatroomMessageType::RequestJoin { .. } => {
412 self.process_request_join(msg.clone(), &message.data)
413 .await?
414 },
415 ChatroomMessageType::AcceptMember { .. } => {
416 self.process_accept_member(msg.clone(), &message.data)
417 .await?
418 },
419 ChatroomMessageType::PostMessage { .. } => {
420 self.process_post_message(msg.clone(), &message.data)
421 .await?
422 },
423 };
424
425 if processed {
426 tracing::debug!("Successfully processed chatroom message: {:?}", msg);
427 } else {
428 tracing::warn!("Failed to process chatroom message: {:?}", msg);
429 }
430
431 Ok(())
432 }
433
434 fn is_merkleized(&self) -> bool {
435 false
436 }
437
438 async fn get_latest_digest(&self) -> Result<String> {
439 let mut hasher = Sha256::new();
440
441 for (room_name, chatroom) in &self.chatrooms {
442 hasher.update(room_name.as_bytes());
443 hasher.update(chatroom.messages.len().to_le_bytes());
444 }
445
446 Ok(hex::encode(hasher.finalize()))
447 }
448
449 async fn has_digest(&self, _digest: &str) -> Result<bool> {
450 Ok(false) }
452
453 async fn is_valid_digest(&self, _digest: &str) -> Result<bool> {
454 Ok(true)
455 }
456
457 async fn add_digest(&mut self, _digest: String) -> Result<bool> {
458 Ok(true)
459 }
460
461 async fn gossip_messages(&self, _digest: Option<&str>) -> Result<Vec<SharedMessage>> {
462 Ok(Vec::new()) }
464
465 async fn get_messages_since_digest(&self, _digest: &str) -> Result<Vec<SharedMessage>> {
466 Ok(Vec::new()) }
468
469 async fn get_state(&self) -> Result<Value> {
470 let state = serde_json::json!({
471 "chatroom_count": self.chatrooms.len(),
472 "chatrooms": self.chatrooms.keys().collect::<Vec<_>>(),
473 "total_messages": self.chatrooms.values().map(|c| c.messages.len()).sum::<usize>()
474 });
475 Ok(state)
476 }
477
478 async fn reset(&mut self) -> Result<()> {
479 self.chatrooms.clear();
480 Ok(())
481 }
482
483 fn clone_box(&self) -> Box<dyn ApplicationObject> {
484 Box::new(self.clone())
485 }
486
487 fn as_any(&self) -> &dyn Any {
488 self
489 }
490
491 fn as_any_mut(&mut self) -> &mut dyn Any {
492 self
493 }
494}
495
496pub mod helpers {
498 use super::*;
499 use crate::crypto::ecdsa::ECDSASigner;
500
501 pub fn create_chatroom_message(chatroom_name: String, signer: &ECDSASigner) -> Result<Value> {
503 let timestamp = SystemTime::now()
504 .duration_since(UNIX_EPOCH)
505 .unwrap()
506 .as_secs_f64();
507
508 let public_key_pem = signer.get_public_key_pem()?;
509
510 let mut msg = serde_json::json!({
511 "message_type": "CREATE_CHATROOM",
512 "chatroom_name": chatroom_name,
513 "public_key_pem": public_key_pem,
514 "timestamp": timestamp
515 });
516
517 let payload = serde_json::to_string(&msg).map_err(|e| {
519 ChaincraftError::Serialization(crate::error::SerializationError::Json(e))
520 })?;
521 let signature = signer.sign(payload.as_bytes())?;
522
523 msg["signature"] = serde_json::Value::String(hex::encode(signature.to_bytes()));
524
525 Ok(msg)
526 }
527
528 pub fn create_post_message(
530 chatroom_name: String,
531 text: String,
532 signer: &ECDSASigner,
533 ) -> Result<Value> {
534 let timestamp = SystemTime::now()
535 .duration_since(UNIX_EPOCH)
536 .unwrap()
537 .as_secs_f64();
538
539 let public_key_pem = signer.get_public_key_pem()?;
540
541 let mut msg = serde_json::json!({
542 "message_type": "POST_MESSAGE",
543 "chatroom_name": chatroom_name,
544 "public_key_pem": public_key_pem,
545 "text": text,
546 "timestamp": timestamp
547 });
548
549 let payload = serde_json::to_string(&msg).map_err(|e| {
551 ChaincraftError::Serialization(crate::error::SerializationError::Json(e))
552 })?;
553 let signature = signer.sign(payload.as_bytes())?;
554
555 msg["signature"] = serde_json::Value::String(hex::encode(signature.to_bytes()));
556
557 Ok(msg)
558 }
559}