Skip to main content

aura_core/effects/
ledger.rs

1//! Effect API interface
2//!
3//! Pure trait definitions for event sourcing and audit operations used by protocols.
4//!
5//! Note: This trait works with serialized event data to avoid circular dependencies
6//! with aura-journal. Implementations can internally use richer types like AccountState.
7//!
8//! # Effect Classification
9//!
10//! - **Category**: Application Effect
11//! - **Implementation**: `aura-journal` or `aura-protocol` (Layer 2 or Layer 4)
12//! - **Usage**: Event sourcing, audit trails, journal graph operations
13//!
14//! This is an application effect for event sourcing and journal graph operations.
15//! Combines traditional event sourcing (events, devices, authorization) with journal
16//! graph operations (nodes, edges, CRDT merging) for Aura's graph-based threshold
17//! identity structure. Handlers in `aura-journal` or `aura-protocol`.
18
19use crate::types::identifiers::DeviceId;
20use async_trait::async_trait;
21
22/// Effect API effects for event sourcing and audit trails
23///
24/// This trait combines traditional event sourcing operations (events, devices, authorization)
25/// with journal graph operations (nodes, edges, CRDT merging) needed for the journal's
26/// graph-based threshold identity structure.
27#[async_trait]
28pub trait EffectApiEffects: Send + Sync {
29    // Traditional Effect API Operations
30
31    /// Append an event to the effect_api
32    async fn append_event(&self, event: Vec<u8>) -> Result<(), EffectApiError>;
33
34    /// Get the current epoch/sequence number
35    async fn current_epoch(&self) -> Result<u64, EffectApiError>;
36
37    /// Get events since a specific epoch
38    async fn events_since(&self, epoch: u64) -> Result<Vec<Vec<u8>>, EffectApiError>;
39
40    /// Check if a device is authorized for an operation
41    async fn is_device_authorized(
42        &self,
43        device_id: DeviceId,
44        operation: &str,
45    ) -> Result<bool, EffectApiError>;
46
47    /// Update device last seen timestamp
48    async fn update_device_activity(&self, device_id: DeviceId) -> Result<(), EffectApiError>;
49
50    /// Subscribe to effect_api events
51    async fn subscribe_to_events(&self) -> Result<EffectApiEventStream, EffectApiError>;
52
53    // Journal Graph Operations
54    // Operations for managing the journal's graph-based threshold identity structure
55
56    /// Check if adding an edge would create a cycle in the journal graph
57    async fn would_create_cycle(
58        &self,
59        edges: &[(Vec<u8>, Vec<u8>)],
60        new_edge: (Vec<u8>, Vec<u8>),
61    ) -> Result<bool, EffectApiError>;
62
63    /// Find strongly connected components in the journal graph
64    async fn find_connected_components(
65        &self,
66        edges: &[(Vec<u8>, Vec<u8>)],
67    ) -> Result<Vec<Vec<Vec<u8>>>, EffectApiError>;
68
69    /// Find topological ordering of nodes in the journal graph
70    async fn topological_sort(
71        &self,
72        edges: &[(Vec<u8>, Vec<u8>)],
73    ) -> Result<Vec<Vec<u8>>, EffectApiError>;
74
75    /// Calculate shortest path between two nodes in the journal graph
76    async fn shortest_path(
77        &self,
78        edges: &[(Vec<u8>, Vec<u8>)],
79        start: Vec<u8>,
80        end: Vec<u8>,
81    ) -> Result<Option<Vec<Vec<u8>>>, EffectApiError>;
82
83    /// Generate a random secret for cryptographic operations
84    async fn generate_secret(&self, length: usize) -> Result<Vec<u8>, EffectApiError>;
85
86    /// Hash data with cryptographic hash function
87    async fn hash_data(&self, data: &[u8]) -> Result<[u8; 32], EffectApiError>;
88
89    /// Get current timestamp (seconds since Unix epoch)
90    async fn current_timestamp(&self) -> Result<u64, EffectApiError>;
91
92    /// Get device ID for this effect_api instance
93    async fn effect_api_device_id(&self) -> Result<DeviceId, EffectApiError>;
94
95    /// Generate a new UUID
96    async fn new_uuid(&self) -> Result<uuid::Uuid, EffectApiError>;
97}
98
99/// Effect API-related errors
100#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)]
101pub enum EffectApiError {
102    /// Effect API is not available
103    #[error("Effect API not available")]
104    NotAvailable,
105
106    /// Access denied for the requested operation
107    #[error("Access denied for operation: {operation}")]
108    AccessDenied {
109        /// The operation that was denied
110        operation: String,
111    },
112
113    /// Device not found in effect_api
114    #[error("Device not found: {device_id}")]
115    DeviceNotFound {
116        /// The device ID that was not found
117        device_id: DeviceId,
118    },
119
120    /// Event has invalid format
121    #[error("Invalid event format")]
122    InvalidEvent,
123
124    /// Requested epoch is out of range
125    #[error("Epoch out of range: {epoch}")]
126    EpochOutOfRange {
127        /// The invalid epoch number
128        epoch: u64,
129    },
130
131    /// Effect API data is corrupted
132    #[error("Effect API corrupted: {reason}")]
133    Corrupted {
134        /// Reason for corruption
135        reason: String,
136    },
137
138    /// Concurrent access conflict detected
139    #[error("Concurrent access conflict")]
140    ConcurrentAccess,
141
142    /// Backend storage error
143    #[error("Backend error: {error}")]
144    Backend {
145        /// Backend error message
146        error: String,
147    },
148
149    /// Journal graph operation failed
150    #[error("Graph operation failed: {message}")]
151    GraphOperationFailed {
152        /// Description of the failure
153        message: String,
154    },
155
156    /// Cryptographic operation failed
157    #[error("Cryptographic operation failed: {message}")]
158    CryptoOperationFailed {
159        /// Description of the failure
160        message: String,
161    },
162
163    /// Invalid node or edge data
164    #[error("Invalid graph data: {message}")]
165    InvalidGraphData {
166        /// Description of invalid data
167        message: String,
168    },
169}
170
171/// Effect API events
172#[derive(Debug, Clone)]
173pub enum EffectApiEvent {
174    /// New event appended to effect_api
175    EventAppended {
176        /// Epoch number of the new event
177        epoch: u64,
178        /// The event data
179        event: Vec<u8>,
180    },
181    /// Device activity timestamp updated
182    DeviceActivity {
183        /// The device that was active
184        device_id: DeviceId,
185        /// Updated last seen timestamp
186        last_seen: u64,
187    },
188    /// Account state has changed
189    StateChanged,
190}
191
192/// Stream of effect_api events
193pub type EffectApiEventStream = Box<dyn futures::Stream<Item = EffectApiEvent> + Send + Unpin>;