use chrono::{DateTime, Duration, Utc};
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use uuid::Uuid;
use crate::error::{CoreError, Result};
use crate::trading::order_book::{LimitOrder, OrderSide};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrivatePoolConfig {
pub min_delay: Duration,
pub max_delay: Duration,
pub max_pending_orders: usize,
pub allow_immediate_execution: bool,
}
impl Default for PrivatePoolConfig {
fn default() -> Self {
Self {
min_delay: Duration::seconds(30),
max_delay: Duration::minutes(10),
max_pending_orders: 1000,
allow_immediate_execution: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PrivateOrderStatus {
Pending,
Ready,
Executing,
Executed,
Cancelled,
Expired,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EncryptionScheme {
SimpleXor,
Advanced,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrivateOrder {
pub id: Uuid,
pub user_id: Uuid,
pub token_id: Uuid,
pub encrypted_data: String,
pub encryption_scheme: EncryptionScheme,
pub key_hash: String,
pub submitted_at: DateTime<Utc>,
pub executable_at: DateTime<Utc>,
pub expires_at: DateTime<Utc>,
pub status: PrivateOrderStatus,
pub decrypted_order: Option<DecryptedOrderData>,
}
impl PrivateOrder {
pub fn new(
user_id: Uuid,
token_id: Uuid,
encrypted_data: String,
encryption_scheme: EncryptionScheme,
key_hash: String,
config: &PrivatePoolConfig,
) -> Self {
let now = Utc::now();
let executable_at = now + config.min_delay;
let expires_at = now + config.max_delay;
Self {
id: Uuid::new_v4(),
user_id,
token_id,
encrypted_data,
encryption_scheme,
key_hash,
submitted_at: now,
executable_at,
expires_at,
status: PrivateOrderStatus::Pending,
decrypted_order: None,
}
}
pub fn is_ready(&self) -> bool {
Utc::now() >= self.executable_at && self.status == PrivateOrderStatus::Pending
}
pub fn is_expired(&self) -> bool {
Utc::now() > self.expires_at
}
pub fn update_status(&mut self) {
if self.is_expired() && self.status == PrivateOrderStatus::Pending {
self.status = PrivateOrderStatus::Expired;
} else if self.is_ready() {
self.status = PrivateOrderStatus::Ready;
}
}
pub fn decrypt(&mut self, key: &str) -> Result<&DecryptedOrderData> {
let computed_hash = Self::hash_key(key);
if computed_hash != self.key_hash {
return Err(CoreError::Validation("Invalid decryption key".to_string()));
}
if let Some(ref data) = self.decrypted_order {
return Ok(data);
}
let decrypted_json = match self.encryption_scheme {
EncryptionScheme::SimpleXor => Self::decrypt_xor(&self.encrypted_data, key)?,
EncryptionScheme::Advanced => {
return Err(CoreError::FeatureNotEnabled(
"Advanced encryption not yet implemented".to_string(),
));
}
};
let data: DecryptedOrderData = serde_json::from_str(&decrypted_json)
.map_err(|e| CoreError::Validation(format!("Failed to parse order data: {}", e)))?;
self.decrypted_order = Some(data);
Ok(self.decrypted_order.as_ref().unwrap())
}
fn encrypt_xor(data: &str, key: &str) -> String {
let key_bytes = key.as_bytes();
let encrypted: Vec<u8> = data
.as_bytes()
.iter()
.enumerate()
.map(|(i, &b)| b ^ key_bytes[i % key_bytes.len()])
.collect();
hex::encode(encrypted)
}
fn decrypt_xor(encrypted_hex: &str, key: &str) -> Result<String> {
let encrypted = hex::decode(encrypted_hex)
.map_err(|e| CoreError::Validation(format!("Invalid hex data: {}", e)))?;
let key_bytes = key.as_bytes();
let decrypted: Vec<u8> = encrypted
.iter()
.enumerate()
.map(|(i, &b)| b ^ key_bytes[i % key_bytes.len()])
.collect();
String::from_utf8(decrypted)
.map_err(|e| CoreError::Validation(format!("Invalid UTF-8 data: {}", e)))
}
fn hash_key(key: &str) -> String {
use sha2::{Digest, Sha256};
let hash = Sha256::digest(key.as_bytes());
hex::encode(hash)
}
pub fn encrypt_and_create(
user_id: Uuid,
token_id: Uuid,
order_data: &DecryptedOrderData,
key: &str,
config: &PrivatePoolConfig,
) -> Result<Self> {
let json_data = serde_json::to_string(order_data)
.map_err(|e| CoreError::Validation(format!("Failed to serialize order: {}", e)))?;
let encrypted_data = Self::encrypt_xor(&json_data, key);
let key_hash = Self::hash_key(key);
Ok(Self::new(
user_id,
token_id,
encrypted_data,
EncryptionScheme::SimpleXor,
key_hash,
config,
))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecryptedOrderData {
pub side: OrderSide,
pub price: Decimal,
pub amount: Decimal,
}
impl DecryptedOrderData {
pub fn to_limit_order(&self, user_id: Uuid, token_id: Uuid) -> LimitOrder {
LimitOrder {
order_id: Uuid::new_v4(),
user_id,
token_id,
side: self.side,
price: self.price,
amount: self.amount,
filled_amount: dec!(0),
timestamp: Utc::now().timestamp_millis(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrivatePoolStats {
pub pending_count: usize,
pub ready_count: usize,
pub executed_count: usize,
pub expired_count: usize,
pub failed_count: usize,
pub avg_delay_seconds: i64,
pub oldest_pending_age_seconds: Option<i64>,
}
pub struct PrivateTransactionPool {
config: PrivatePoolConfig,
pending_orders: VecDeque<PrivateOrder>,
ready_orders: VecDeque<PrivateOrder>,
executed_orders: Vec<PrivateOrder>,
user_orders: HashMap<Uuid, Vec<Uuid>>,
token_orders: HashMap<Uuid, Vec<Uuid>>,
}
impl PrivateTransactionPool {
pub fn new(config: PrivatePoolConfig) -> Self {
Self {
config,
pending_orders: VecDeque::new(),
ready_orders: VecDeque::new(),
executed_orders: Vec::new(),
user_orders: HashMap::new(),
token_orders: HashMap::new(),
}
}
pub fn submit_order(&mut self, order: PrivateOrder) -> Result<Uuid> {
if self.pending_orders.len() + self.ready_orders.len() >= self.config.max_pending_orders {
return Err(CoreError::InvalidState(
"Private pool is at capacity".to_string(),
));
}
let order_id = order.id;
let user_id = order.user_id;
let token_id = order.token_id;
self.user_orders.entry(user_id).or_default().push(order_id);
self.token_orders
.entry(token_id)
.or_default()
.push(order_id);
self.pending_orders.push_back(order);
Ok(order_id)
}
pub fn update_statuses(&mut self) {
let mut still_pending = VecDeque::new();
while let Some(mut order) = self.pending_orders.pop_front() {
order.update_status();
match order.status {
PrivateOrderStatus::Ready => {
self.ready_orders.push_back(order);
}
PrivateOrderStatus::Expired => {
self.executed_orders.push(order);
}
_ => {
still_pending.push_back(order);
}
}
}
self.pending_orders = still_pending;
let mut still_ready = VecDeque::new();
while let Some(mut order) = self.ready_orders.pop_front() {
order.update_status();
if order.status == PrivateOrderStatus::Expired {
self.executed_orders.push(order);
} else {
still_ready.push_back(order);
}
}
self.ready_orders = still_ready;
}
pub fn get_next_ready(&mut self) -> Option<PrivateOrder> {
self.update_statuses();
self.ready_orders.pop_front()
}
pub fn decrypt_and_execute_next(&mut self, key: &str) -> Result<Option<LimitOrder>> {
if let Some(mut order) = self.get_next_ready() {
order.status = PrivateOrderStatus::Executing;
let user_id = order.user_id;
let token_id = order.token_id;
let decrypted = order.decrypt(key)?;
let limit_order = decrypted.to_limit_order(user_id, token_id);
order.status = PrivateOrderStatus::Executed;
self.executed_orders.push(order);
Ok(Some(limit_order))
} else {
Ok(None)
}
}
pub fn cancel_order(&mut self, order_id: Uuid, user_id: Uuid) -> Result<()> {
if let Some(pos) = self.pending_orders.iter().position(|o| o.id == order_id) {
let order = &self.pending_orders[pos];
if order.user_id != user_id {
return Err(CoreError::Unauthorized);
}
let mut order = self.pending_orders.remove(pos).unwrap();
order.status = PrivateOrderStatus::Cancelled;
self.executed_orders.push(order);
return Ok(());
}
if let Some(pos) = self.ready_orders.iter().position(|o| o.id == order_id) {
let order = &self.ready_orders[pos];
if order.user_id != user_id {
return Err(CoreError::Unauthorized);
}
let mut order = self.ready_orders.remove(pos).unwrap();
order.status = PrivateOrderStatus::Cancelled;
self.executed_orders.push(order);
return Ok(());
}
Err(CoreError::NotFound(format!("Order {} not found", order_id)))
}
pub fn get_user_orders(&self, user_id: &Uuid) -> Vec<Uuid> {
self.user_orders.get(user_id).cloned().unwrap_or_default()
}
pub fn get_token_orders(&self, token_id: &Uuid) -> Vec<Uuid> {
self.token_orders.get(token_id).cloned().unwrap_or_default()
}
pub fn stats(&self) -> PrivatePoolStats {
let pending_count = self.pending_orders.len();
let ready_count = self.ready_orders.len();
let executed_count = self
.executed_orders
.iter()
.filter(|o| o.status == PrivateOrderStatus::Executed)
.count();
let expired_count = self
.executed_orders
.iter()
.filter(|o| o.status == PrivateOrderStatus::Expired)
.count();
let failed_count = self
.executed_orders
.iter()
.filter(|o| o.status == PrivateOrderStatus::Failed)
.count();
let total_executed: Vec<&PrivateOrder> = self
.executed_orders
.iter()
.filter(|o| o.status == PrivateOrderStatus::Executed)
.collect();
let avg_delay_seconds = if !total_executed.is_empty() {
let total_delay: i64 = total_executed
.iter()
.map(|o| (o.executable_at - o.submitted_at).num_seconds())
.sum();
total_delay / total_executed.len() as i64
} else {
0
};
let oldest_pending_age_seconds = self.pending_orders.front().map(|o| {
let age = Utc::now() - o.submitted_at;
age.num_seconds()
});
PrivatePoolStats {
pending_count,
ready_count,
executed_count,
expired_count,
failed_count,
avg_delay_seconds,
oldest_pending_age_seconds,
}
}
pub fn cleanup_executed(&mut self, keep_last: usize) {
if self.executed_orders.len() > keep_last {
self.executed_orders
.drain(0..self.executed_orders.len() - keep_last);
}
}
pub fn pending_count(&self) -> usize {
self.pending_orders.len() + self.ready_orders.len()
}
pub fn ready_count(&self) -> usize {
self.ready_orders.len()
}
}
impl Default for PrivateTransactionPool {
fn default() -> Self {
Self::new(PrivatePoolConfig::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encryption_decryption() {
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "my_secret_key_123";
let config = PrivatePoolConfig::default();
let mut order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
let decrypted = order.decrypt(key).unwrap();
assert_eq!(decrypted.side, OrderSide::Buy);
assert_eq!(decrypted.price, dec!(100));
assert_eq!(decrypted.amount, dec!(10));
}
#[test]
fn test_invalid_decryption_key() {
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "my_secret_key";
let config = PrivatePoolConfig::default();
let mut order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
let result = order.decrypt("wrong_key");
assert!(result.is_err());
}
#[test]
fn test_private_pool_submission() {
let mut pool = PrivateTransactionPool::default();
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "test_key";
let config = PrivatePoolConfig::default();
let order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
let order_id = pool.submit_order(order).unwrap();
assert!(order_id != Uuid::nil());
assert_eq!(pool.pending_count(), 1);
}
#[test]
fn test_order_ready_status() {
let config = PrivatePoolConfig {
min_delay: Duration::seconds(0), ..Default::default()
};
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "test_key";
let order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
let mut pool = PrivateTransactionPool::new(config);
pool.submit_order(order).unwrap();
pool.update_statuses();
assert_eq!(pool.ready_count(), 1);
}
#[test]
fn test_order_cancellation() {
let mut pool = PrivateTransactionPool::default();
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "test_key";
let config = PrivatePoolConfig::default();
let order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
let order_id = order.id;
pool.submit_order(order).unwrap();
pool.cancel_order(order_id, user_id).unwrap();
assert_eq!(pool.pending_count(), 0);
}
#[test]
fn test_unauthorized_cancellation() {
let mut pool = PrivateTransactionPool::default();
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let other_user = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "test_key";
let config = PrivatePoolConfig::default();
let order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
let order_id = order.id;
pool.submit_order(order).unwrap();
let result = pool.cancel_order(order_id, other_user);
assert!(result.is_err());
}
#[test]
fn test_pool_stats() {
let mut pool = PrivateTransactionPool::default();
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "test_key";
let config = PrivatePoolConfig::default();
for _ in 0..5 {
let order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
pool.submit_order(order).unwrap();
}
let stats = pool.stats();
assert_eq!(stats.pending_count, 5);
assert_eq!(stats.ready_count, 0);
}
#[test]
fn test_max_pending_orders() {
let config = PrivatePoolConfig {
max_pending_orders: 3,
..Default::default()
};
let mut pool = PrivateTransactionPool::new(config.clone());
let data = DecryptedOrderData {
side: OrderSide::Buy,
price: dec!(100),
amount: dec!(10),
};
let user_id = Uuid::new_v4();
let token_id = Uuid::new_v4();
let key = "test_key";
for _ in 0..3 {
let order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
pool.submit_order(order).unwrap();
}
let order =
PrivateOrder::encrypt_and_create(user_id, token_id, &data, key, &config).unwrap();
let result = pool.submit_order(order);
assert!(result.is_err());
}
}