287 lines
7.8 KiB
Rust
287 lines
7.8 KiB
Rust
//! 安全机制模块
|
||
|
||
use crate::error::{BridgeError, BridgeResult};
|
||
use chrono::Datelike;
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::HashMap;
|
||
|
||
/// 安全配置
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct SecurityConfig {
|
||
/// 单笔交易上限
|
||
pub max_transaction_amount: u128,
|
||
/// 每日交易上限
|
||
pub daily_limit: u128,
|
||
/// 是否暂停
|
||
pub paused: bool,
|
||
/// 紧急提款启用
|
||
pub emergency_withdrawal_enabled: bool,
|
||
/// 审计日志启用
|
||
pub audit_log_enabled: bool,
|
||
}
|
||
|
||
impl Default for SecurityConfig {
|
||
fn default() -> Self {
|
||
Self {
|
||
max_transaction_amount: 1_000_000_000_000_000_000, // 1 ETH
|
||
daily_limit: 10_000_000_000_000_000_000, // 10 ETH
|
||
paused: false,
|
||
emergency_withdrawal_enabled: true,
|
||
audit_log_enabled: true,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 审计日志条目
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct AuditLogEntry {
|
||
/// 日志ID
|
||
pub log_id: String,
|
||
/// 操作类型
|
||
pub operation: String,
|
||
/// 操作者
|
||
pub operator: String,
|
||
/// 时间戳
|
||
pub timestamp: u64,
|
||
/// 详细信息
|
||
pub details: HashMap<String, String>,
|
||
/// 结果
|
||
pub result: OperationResult,
|
||
}
|
||
|
||
/// 操作结果
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub enum OperationResult {
|
||
/// 成功
|
||
Success,
|
||
/// 失败
|
||
Failure(String),
|
||
}
|
||
|
||
/// 每日交易统计
|
||
#[derive(Debug, Clone)]
|
||
struct DailyStats {
|
||
/// 日期(YYYYMMDD)
|
||
date: u32,
|
||
/// 总金额
|
||
total_amount: u128,
|
||
}
|
||
|
||
/// 安全管理器
|
||
pub struct SecurityManager {
|
||
/// 安全配置
|
||
config: SecurityConfig,
|
||
/// 审计日志
|
||
audit_logs: Vec<AuditLogEntry>,
|
||
/// 每日统计
|
||
daily_stats: HashMap<String, DailyStats>,
|
||
}
|
||
|
||
impl SecurityManager {
|
||
/// 创建新的安全管理器
|
||
pub fn new(config: SecurityConfig) -> Self {
|
||
Self {
|
||
config,
|
||
audit_logs: Vec::new(),
|
||
daily_stats: HashMap::new(),
|
||
}
|
||
}
|
||
|
||
/// 检查是否暂停
|
||
pub fn is_paused(&self) -> bool {
|
||
self.config.paused
|
||
}
|
||
|
||
/// 暂停桥接
|
||
pub fn pause(&mut self, operator: &str) -> BridgeResult<()> {
|
||
if self.config.paused {
|
||
return Err(BridgeError::SecurityError(
|
||
"Bridge already paused".to_string()
|
||
));
|
||
}
|
||
|
||
self.config.paused = true;
|
||
|
||
self.log_operation(AuditLogEntry {
|
||
log_id: format!("pause_{}", chrono::Utc::now().timestamp()),
|
||
operation: "pause".to_string(),
|
||
operator: operator.to_string(),
|
||
timestamp: chrono::Utc::now().timestamp() as u64,
|
||
details: HashMap::new(),
|
||
result: OperationResult::Success,
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 恢复桥接
|
||
pub fn unpause(&mut self, operator: &str) -> BridgeResult<()> {
|
||
if !self.config.paused {
|
||
return Err(BridgeError::SecurityError(
|
||
"Bridge not paused".to_string()
|
||
));
|
||
}
|
||
|
||
self.config.paused = false;
|
||
|
||
self.log_operation(AuditLogEntry {
|
||
log_id: format!("unpause_{}", chrono::Utc::now().timestamp()),
|
||
operation: "unpause".to_string(),
|
||
operator: operator.to_string(),
|
||
timestamp: chrono::Utc::now().timestamp() as u64,
|
||
details: HashMap::new(),
|
||
result: OperationResult::Success,
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 验证交易金额
|
||
pub fn validate_amount(&mut self, amount: u128, token: &str) -> BridgeResult<()> {
|
||
// 检查单笔上限
|
||
if amount > self.config.max_transaction_amount {
|
||
return Err(BridgeError::SecurityError(
|
||
format!("Amount exceeds max transaction limit: {} > {}",
|
||
amount, self.config.max_transaction_amount)
|
||
));
|
||
}
|
||
|
||
// 检查每日限额
|
||
let today = self.get_today();
|
||
let stats = self.daily_stats.entry(token.to_string())
|
||
.or_insert(DailyStats {
|
||
date: today,
|
||
total_amount: 0,
|
||
});
|
||
|
||
// 如果是新的一天,重置统计
|
||
if stats.date != today {
|
||
stats.date = today;
|
||
stats.total_amount = 0;
|
||
}
|
||
|
||
if stats.total_amount + amount > self.config.daily_limit {
|
||
return Err(BridgeError::SecurityError(
|
||
format!("Amount exceeds daily limit: {} + {} > {}",
|
||
stats.total_amount, amount, self.config.daily_limit)
|
||
));
|
||
}
|
||
|
||
// 更新统计
|
||
stats.total_amount += amount;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 紧急提款
|
||
pub fn emergency_withdraw(
|
||
&mut self,
|
||
operator: &str,
|
||
amount: u128,
|
||
destination: &str,
|
||
) -> BridgeResult<()> {
|
||
if !self.config.emergency_withdrawal_enabled {
|
||
return Err(BridgeError::SecurityError(
|
||
"Emergency withdrawal not enabled".to_string()
|
||
));
|
||
}
|
||
|
||
let mut details = HashMap::new();
|
||
details.insert("amount".to_string(), amount.to_string());
|
||
details.insert("destination".to_string(), destination.to_string());
|
||
|
||
self.log_operation(AuditLogEntry {
|
||
log_id: format!("emergency_withdraw_{}", chrono::Utc::now().timestamp()),
|
||
operation: "emergency_withdraw".to_string(),
|
||
operator: operator.to_string(),
|
||
timestamp: chrono::Utc::now().timestamp() as u64,
|
||
details,
|
||
result: OperationResult::Success,
|
||
});
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 记录审计日志
|
||
pub fn log_operation(&mut self, entry: AuditLogEntry) {
|
||
if self.config.audit_log_enabled {
|
||
self.audit_logs.push(entry);
|
||
}
|
||
}
|
||
|
||
/// 查询审计日志
|
||
pub fn query_audit_logs(&self, operation: Option<&str>, operator: Option<&str>) -> Vec<&AuditLogEntry> {
|
||
self.audit_logs.iter()
|
||
.filter(|entry| {
|
||
if let Some(op) = operation {
|
||
if entry.operation != op {
|
||
return false;
|
||
}
|
||
}
|
||
if let Some(opr) = operator {
|
||
if entry.operator != opr {
|
||
return false;
|
||
}
|
||
}
|
||
true
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
/// 获取今天的日期(YYYYMMDD)
|
||
fn get_today(&self) -> u32 {
|
||
let now = chrono::Utc::now();
|
||
(now.year() as u32) * 10000 + (now.month() * 100) + now.day()
|
||
}
|
||
|
||
/// 获取配置
|
||
pub fn config(&self) -> &SecurityConfig {
|
||
&self.config
|
||
}
|
||
|
||
/// 更新配置
|
||
pub fn update_config(&mut self, config: SecurityConfig) {
|
||
self.config = config;
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_pause_unpause() {
|
||
let mut manager = SecurityManager::new(SecurityConfig::default());
|
||
|
||
assert!(!manager.is_paused());
|
||
assert!(manager.pause("admin").is_ok());
|
||
assert!(manager.is_paused());
|
||
assert!(manager.unpause("admin").is_ok());
|
||
assert!(!manager.is_paused());
|
||
}
|
||
|
||
#[test]
|
||
fn test_validate_amount() {
|
||
let mut manager = SecurityManager::new(SecurityConfig::default());
|
||
|
||
// 测试单笔上限
|
||
let result = manager.validate_amount(2_000_000_000_000_000_000, "ETH");
|
||
assert!(result.is_err());
|
||
|
||
// 测试正常金额
|
||
let result = manager.validate_amount(500_000_000_000_000_000, "ETH");
|
||
assert!(result.is_ok());
|
||
}
|
||
|
||
#[test]
|
||
fn test_audit_log() {
|
||
let mut manager = SecurityManager::new(SecurityConfig::default());
|
||
|
||
manager.pause("admin").unwrap();
|
||
|
||
let logs = manager.query_audit_logs(Some("pause"), None);
|
||
assert_eq!(logs.len(), 1);
|
||
assert_eq!(logs[0].operator, "admin");
|
||
}
|
||
}
|