1use ringkernel_core::message::{CorrelationId, MessageId};
16use ringkernel_derive::RingMessage;
17use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
18
19#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
27#[message(type_id = 300)]
28#[archive(check_bytes)]
29pub struct MonitorTransactionRing {
30 #[message(id)]
32 pub id: MessageId,
33 #[message(correlation)]
35 pub correlation_id: CorrelationId,
36 pub tx_id: u64,
38 pub source_id: u64,
40 pub dest_id: u64,
42 pub amount: i64,
44 pub timestamp: u64,
46 pub tx_type: u8,
48 pub currency: [u8; 4],
50}
51
52impl MonitorTransactionRing {
53 pub fn new(
55 tx_id: u64,
56 source_id: u64,
57 dest_id: u64,
58 amount: f64,
59 timestamp: u64,
60 tx_type: u8,
61 currency: &str,
62 ) -> Self {
63 let mut curr = [0u8; 4];
64 let bytes = currency.as_bytes();
65 let len = bytes.len().min(3);
66 curr[..len].copy_from_slice(&bytes[..len]);
67
68 Self {
69 id: MessageId::generate(),
70 correlation_id: CorrelationId::generate(),
71 tx_id,
72 source_id,
73 dest_id,
74 amount: (amount * 100_000_000.0) as i64,
75 timestamp,
76 tx_type,
77 currency: curr,
78 }
79 }
80}
81
82#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
86#[message(type_id = 301)]
87#[archive(check_bytes)]
88pub struct MonitorTransactionResponse {
89 #[message(correlation)]
91 pub correlation_id: CorrelationId,
92 pub tx_id: u64,
94 pub alert_count: u32,
96 pub risk_score: u8,
98 pub alert_flags: u64,
100}
101
102#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
106#[message(type_id = 302)]
107#[archive(check_bytes)]
108pub struct TransactionAlert {
109 #[message(id)]
111 pub id: MessageId,
112 pub tx_id: u64,
114 pub alert_type: u16,
116 pub severity: u8,
118 pub entity_id: u64,
120 pub threshold: i64,
122 pub actual_value: i64,
124 pub timestamp: u64,
126}
127
128#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
136#[message(type_id = 310)]
137#[archive(check_bytes)]
138pub struct AddGraphEdgeRing {
139 #[message(id)]
141 pub id: MessageId,
142 #[message(correlation)]
144 pub correlation_id: CorrelationId,
145 pub source_id: u64,
147 pub dest_id: u64,
149 pub amount: i64,
151 pub timestamp: u64,
153}
154
155impl AddGraphEdgeRing {
156 pub fn new(source_id: u64, dest_id: u64, amount: f64, timestamp: u64) -> Self {
158 Self {
159 id: MessageId::generate(),
160 correlation_id: CorrelationId::generate(),
161 source_id,
162 dest_id,
163 amount: (amount * 100_000_000.0) as i64,
164 timestamp,
165 }
166 }
167}
168
169#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
173#[message(type_id = 311)]
174#[archive(check_bytes)]
175pub struct AddGraphEdgeResponse {
176 #[message(correlation)]
178 pub correlation_id: CorrelationId,
179 pub cycle_detected: bool,
181 pub cycle_size: u32,
183 pub source_ratio: f32,
185}
186
187#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
191#[message(type_id = 312)]
192#[archive(check_bytes)]
193pub struct QueryCircularRatioRing {
194 #[message(id)]
196 pub id: MessageId,
197 #[message(correlation)]
199 pub correlation_id: CorrelationId,
200 pub entity_id: u64,
202}
203
204#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
208#[message(type_id = 313)]
209#[archive(check_bytes)]
210pub struct QueryCircularRatioResponse {
211 #[message(correlation)]
213 pub correlation_id: CorrelationId,
214 pub entity_id: u64,
216 pub ratio: f32,
218 pub scc_count: u32,
220 pub cycle_volume: i64,
222}
223
224#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
228#[message(type_id = 314)]
229#[archive(check_bytes)]
230pub struct CycleDetectedAlert {
231 #[message(id)]
233 pub id: MessageId,
234 pub cycle_id: u64,
236 pub cycle_size: u32,
238 pub total_value: i64,
240 pub timestamp: u64,
242 pub risk_level: u8,
244}
245
246#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
254#[message(type_id = 320)]
255#[archive(check_bytes)]
256pub struct MatchPatternRing {
257 #[message(id)]
259 pub id: MessageId,
260 #[message(correlation)]
262 pub correlation_id: CorrelationId,
263 pub tx_id: u64,
265 pub source_id: u64,
267 pub dest_id: u64,
269 pub amount: i64,
271 pub tx_type: u8,
273 pub timestamp: u64,
275}
276
277#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
281#[message(type_id = 321)]
282#[archive(check_bytes)]
283pub struct MatchPatternResponse {
284 #[message(correlation)]
286 pub correlation_id: CorrelationId,
287 pub tx_id: u64,
289 pub patterns_matched: u64,
291 pub max_score: f32,
293 pub match_count: u32,
295}
296
297#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
301#[message(type_id = 322)]
302#[archive(check_bytes)]
303pub struct AMLPatternAlert {
304 #[message(id)]
306 pub id: MessageId,
307 pub pattern_type: u16,
309 pub entity_id: u64,
311 pub confidence: f32,
313 pub evidence_tx_ids: [u64; 4],
315 pub evidence_count: u8,
317 pub timestamp: u64,
319}
320
321#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
329#[message(type_id = 350)]
330#[archive(check_bytes)]
331pub struct K2KAlertBroadcast {
332 #[message(id)]
334 pub id: MessageId,
335 pub source_kernel: u64,
337 pub alert_type: u8,
339 pub severity: u8,
341 pub entity_id: u64,
343 pub related_entity_id: u64,
345 pub risk_score: u8,
347 pub timestamp: u64,
349 pub context: u64,
351}
352
353#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
357#[message(type_id = 351)]
358#[archive(check_bytes)]
359pub struct K2KAlertAck {
360 pub request_id: u64,
362 pub kernel_id: u64,
364 pub will_action: bool,
366 pub correlation_found: bool,
368}
369
370#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
376#[message(type_id = 352)]
377#[archive(check_bytes)]
378pub struct K2KEntityRiskRequest {
379 #[message(id)]
381 pub id: MessageId,
382 pub source_kernel: u64,
384 pub entity_id: u64,
386 pub time_window_us: u64,
388}
389
390#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
394#[message(type_id = 353)]
395#[archive(check_bytes)]
396pub struct K2KEntityRiskResponse {
397 pub request_id: u64,
399 pub kernel_id: u64,
401 pub entity_id: u64,
403 pub risk_score: u8,
405 pub alert_count: u32,
407 pub max_severity: u8,
409 pub patterns_detected: u64,
411}
412
413#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
419#[message(type_id = 354)]
420#[archive(check_bytes)]
421pub struct K2KAggregatedRisk {
422 #[message(id)]
424 pub id: MessageId,
425 pub entity_id: u64,
427 pub aggregated_score: u8,
429 pub kernel_count: u8,
431 pub total_alerts: u32,
433 pub recommendation: u8,
435 pub timestamp: u64,
437}
438
439#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
445#[message(type_id = 355)]
446#[archive(check_bytes)]
447pub struct K2KCaseCreation {
448 #[message(id)]
450 pub id: MessageId,
451 pub case_id: u64,
453 pub entity_id: u64,
455 pub related_entities: [u64; 4],
457 pub related_count: u8,
459 pub kernel_ids: [u64; 4],
461 pub kernel_count: u8,
463 pub total_risk: u8,
465 pub priority: u8,
467 pub timestamp: u64,
469}
470
471#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
475#[message(type_id = 356)]
476#[archive(check_bytes)]
477pub struct K2KCaseUpdate {
478 #[message(id)]
480 pub id: MessageId,
481 pub case_id: u64,
483 pub kernel_id: u64,
485 pub update_type: u8,
487 pub new_risk: u8,
489 pub evidence: [u64; 4],
491 pub evidence_count: u8,
493 pub timestamp: u64,
495}
496
497#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
503#[message(type_id = 357)]
504#[archive(check_bytes)]
505pub struct K2KSanctionsMatch {
506 #[message(id)]
508 pub id: MessageId,
509 pub entity_id: u64,
511 pub match_type: u8,
513 pub confidence: u8,
515 pub list_code: u32,
517 pub list_entry_id: u64,
519 pub timestamp: u64,
521}
522
523#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
527#[message(type_id = 358)]
528#[archive(check_bytes)]
529pub struct K2KFreezeRequest {
530 #[message(id)]
532 pub id: MessageId,
533 pub source_kernel: u64,
535 pub entity_id: u64,
537 pub reason: u16,
539 pub duration_secs: u32,
541 pub timestamp: u64,
543}
544
545#[derive(Debug, Clone, Archive, RkyvSerialize, RkyvDeserialize, RingMessage)]
549#[message(type_id = 359)]
550#[archive(check_bytes)]
551pub struct K2KFreezeAck {
552 pub request_id: u64,
554 pub kernel_id: u64,
556 pub entity_id: u64,
558 pub frozen: bool,
560 pub blocked_count: u32,
562}
563
564#[derive(Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)]
570#[archive(check_bytes)]
571#[repr(u8)]
572pub enum AlertSeverity {
573 Low = 1,
575 Medium = 2,
577 High = 3,
579 Critical = 4,
581 Emergency = 5,
583}
584
585#[derive(Debug, Clone, Copy, PartialEq, Eq, Archive, RkyvSerialize, RkyvDeserialize)]
587#[archive(check_bytes)]
588#[repr(u16)]
589pub enum AMLPatternType {
590 Structuring = 1,
592 Layering = 2,
594 CircularFlow = 3,
596 RapidMovement = 4,
598 UnusualVolume = 5,
600 GeographicAnomaly = 6,
602 TimingAnomaly = 7,
604 CounterpartyRisk = 8,
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611
612 #[test]
613 fn test_monitor_transaction_ring() {
614 let msg = MonitorTransactionRing::new(1, 100, 200, 1000.0, 1234567890, 1, "USD");
615 assert_eq!(msg.tx_id, 1);
616 assert_eq!(msg.source_id, 100);
617 assert_eq!(msg.dest_id, 200);
618 assert_eq!(msg.amount, 100_000_000_000); }
620
621 #[test]
622 fn test_add_graph_edge_ring() {
623 let msg = AddGraphEdgeRing::new(1, 2, 500.0, 1234567890);
624 assert_eq!(msg.source_id, 1);
625 assert_eq!(msg.dest_id, 2);
626 assert_eq!(msg.amount, 50_000_000_000); }
628
629 #[test]
632 fn test_k2k_alert_broadcast() {
633 let msg = K2KAlertBroadcast {
634 id: MessageId(1),
635 source_kernel: 12345,
636 alert_type: 2, severity: 4,
638 entity_id: 100,
639 related_entity_id: 200,
640 risk_score: 85,
641 timestamp: 1234567890,
642 context: 0,
643 };
644 assert_eq!(msg.alert_type, 2);
645 assert_eq!(msg.severity, 4);
646 assert_eq!(msg.risk_score, 85);
647 }
648
649 #[test]
650 fn test_k2k_entity_risk_request() {
651 let msg = K2KEntityRiskRequest {
652 id: MessageId(2),
653 source_kernel: 111,
654 entity_id: 500,
655 time_window_us: 3_600_000_000, };
657 assert_eq!(msg.entity_id, 500);
658 assert_eq!(msg.time_window_us, 3_600_000_000);
659 }
660
661 #[test]
662 fn test_k2k_aggregated_risk() {
663 let msg = K2KAggregatedRisk {
664 id: MessageId(3),
665 entity_id: 500,
666 aggregated_score: 75,
667 kernel_count: 4,
668 total_alerts: 12,
669 recommendation: 2, timestamp: 1234567890,
671 };
672 assert_eq!(msg.aggregated_score, 75);
673 assert_eq!(msg.kernel_count, 4);
674 assert_eq!(msg.recommendation, 2);
675 }
676
677 #[test]
678 fn test_k2k_case_creation() {
679 let msg = K2KCaseCreation {
680 id: MessageId(4),
681 case_id: 9001,
682 entity_id: 100,
683 related_entities: [200, 300, 0, 0],
684 related_count: 2,
685 kernel_ids: [111, 222, 333, 0],
686 kernel_count: 3,
687 total_risk: 90,
688 priority: 3, timestamp: 1234567890,
690 };
691 assert_eq!(msg.case_id, 9001);
692 assert_eq!(msg.related_count, 2);
693 assert_eq!(msg.kernel_count, 3);
694 assert_eq!(msg.priority, 3);
695 }
696
697 #[test]
698 fn test_k2k_sanctions_match() {
699 let msg = K2KSanctionsMatch {
700 id: MessageId(5),
701 entity_id: 999,
702 match_type: 1, confidence: 98,
704 list_code: 1, list_entry_id: 12345,
706 timestamp: 1234567890,
707 };
708 assert_eq!(msg.entity_id, 999);
709 assert_eq!(msg.match_type, 1);
710 assert_eq!(msg.confidence, 98);
711 }
712
713 #[test]
714 fn test_k2k_freeze_request() {
715 let msg = K2KFreezeRequest {
716 id: MessageId(6),
717 source_kernel: 111,
718 entity_id: 999,
719 reason: 1, duration_secs: 0, timestamp: 1234567890,
722 };
723 assert_eq!(msg.entity_id, 999);
724 assert_eq!(msg.duration_secs, 0);
725 }
726
727 #[test]
728 fn test_k2k_freeze_ack() {
729 let msg = K2KFreezeAck {
730 request_id: 6,
731 kernel_id: 222,
732 entity_id: 999,
733 frozen: true,
734 blocked_count: 3,
735 };
736 assert!(msg.frozen);
737 assert_eq!(msg.blocked_count, 3);
738 }
739}