NAC_Blockchain/nac-nvm/src/sandbox.rs

918 lines
27 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 沙箱安全系统 - 生产级别完整实现
//!
//! 提供完整的沙箱隔离、权限控制、资源限制、安全审计等功能
use crate::bytecode::Opcode;
use crate::executor::Executor;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use serde::{Serialize, Deserialize};
/// 沙箱执行环境
#[derive(Debug)]
pub struct Sandbox {
/// 权限策略
policy: SecurityPolicy,
/// 资源限制
resource_limits: ResourceLimits,
/// 当前资源使用
resource_usage: Arc<Mutex<ResourceUsage>>,
/// 安全审计日志
audit_log: Arc<Mutex<Vec<AuditEntry>>>,
/// 执行器
executor: Option<Executor>,
/// 沙箱状态
state: SandboxState,
/// 监控器
monitor: SecurityMonitor,
}
/// 安全策略
#[derive(Debug, Clone)]
pub struct SecurityPolicy {
/// 允许的操作码
pub allowed_opcodes: HashSet<Opcode>,
/// 禁止的操作码
pub forbidden_opcodes: HashSet<Opcode>,
/// 权限级别
pub permission_level: PermissionLevel,
/// 是否允许外部调用
pub allow_external_calls: bool,
/// 是否允许文件访问
pub allow_file_access: bool,
/// 是否允许网络访问
pub allow_network_access: bool,
/// 是否允许系统调用
pub allow_system_calls: bool,
/// 允许的地址白名单
pub address_whitelist: HashSet<String>,
/// 地址黑名单
pub address_blacklist: HashSet<String>,
/// 自定义规则
pub custom_rules: Vec<CustomRule>,
}
impl Default for SecurityPolicy {
fn default() -> Self {
SecurityPolicy {
allowed_opcodes: HashSet::new(),
forbidden_opcodes: HashSet::new(),
permission_level: PermissionLevel::Restricted,
allow_external_calls: false,
allow_file_access: false,
allow_network_access: false,
allow_system_calls: false,
address_whitelist: HashSet::new(),
address_blacklist: HashSet::new(),
custom_rules: Vec::new(),
}
}
}
/// 权限级别
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PermissionLevel {
/// 无限制(仅用于测试)
Unrestricted,
/// 标准权限
Standard,
/// 受限权限
Restricted,
/// 最小权限
Minimal,
}
/// 自定义规则
#[derive(Debug, Clone)]
pub struct CustomRule {
pub name: String,
pub condition: RuleCondition,
pub action: RuleAction,
}
/// 规则条件
#[derive(Debug, Clone)]
pub enum RuleCondition {
/// 操作码匹配
OpcodeMatch(Opcode),
/// Gas超过阈值
GasExceeds(u64),
/// 内存超过阈值
MemoryExceeds(usize),
/// 调用深度超过阈值
CallDepthExceeds(usize),
/// 自定义条件
Custom(String),
}
/// 规则动作
#[derive(Debug, Clone)]
pub enum RuleAction {
/// 允许
Allow,
/// 拒绝
Deny,
/// 警告
Warn,
/// 记录日志
Log,
/// 限流
Throttle,
}
/// 资源限制
#[derive(Debug, Clone)]
pub struct ResourceLimits {
/// 最大Gas限制
pub max_gas: u64,
/// 最大内存(字节)
pub max_memory: usize,
/// 最大栈深度
pub max_stack_depth: usize,
/// 最大调用深度
pub max_call_depth: usize,
/// 最大执行时间(秒)
pub max_execution_time: Duration,
/// 最大日志大小
pub max_log_size: usize,
/// 最大存储大小
pub max_storage_size: usize,
/// 最大事件数量
pub max_events: usize,
}
impl Default for ResourceLimits {
fn default() -> Self {
ResourceLimits {
max_gas: 1_000_000,
max_memory: 64 * 1024 * 1024, // 64 MB
max_stack_depth: 1024,
max_call_depth: 128,
max_execution_time: Duration::from_secs(30),
max_log_size: 10 * 1024 * 1024, // 10 MB
max_storage_size: 100 * 1024 * 1024, // 100 MB
max_events: 1000,
}
}
}
/// 资源使用情况
#[derive(Debug, Clone, Default)]
pub struct ResourceUsage {
/// 已使用Gas
pub gas_used: u64,
/// 已使用内存
pub memory_used: usize,
/// 当前栈深度
pub stack_depth: usize,
/// 当前调用深度
pub call_depth: usize,
/// 执行时间
pub execution_time: Duration,
/// 日志大小
pub log_size: usize,
/// 存储大小
pub storage_size: usize,
/// 事件数量
pub event_count: usize,
}
/// 审计日志条目
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEntry {
/// 时间戳
pub timestamp: u64,
/// 事件类型
pub event_type: AuditEventType,
/// 严重程度
pub severity: Severity,
/// 消息
pub message: String,
/// 上下文数据
pub context: HashMap<String, String>,
}
/// 审计事件类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuditEventType {
/// 执行开始
ExecutionStart,
/// 执行完成
ExecutionComplete,
/// 权限检查
PermissionCheck,
/// 权限拒绝
PermissionDenied,
/// 资源超限
ResourceLimitExceeded,
/// 安全违规
SecurityViolation,
/// 异常错误
Error,
/// 警告
Warning,
}
/// 严重程度
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Severity {
Info,
Warning,
Error,
Critical,
}
/// 沙箱状态
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SandboxState {
/// 未初始化
Uninitialized,
/// 就绪
Ready,
/// 运行中
Running,
/// 已暂停
Paused,
/// 已停止
Stopped,
/// 错误
Error,
}
/// 安全监控器
#[derive(Debug)]
pub struct SecurityMonitor {
/// 异常检测器
anomaly_detector: AnomalyDetector,
/// 入侵检测器
intrusion_detector: IntrusionDetector,
/// 行为分析器
behavior_analyzer: BehaviorAnalyzer,
}
impl SecurityMonitor {
fn new() -> Self {
SecurityMonitor {
anomaly_detector: AnomalyDetector::new(),
intrusion_detector: IntrusionDetector::new(),
behavior_analyzer: BehaviorAnalyzer::new(),
}
}
fn check_security(&mut self, opcode: Opcode, context: &ExecutionContext) -> Result<(), SecurityError> {
// 异常检测
self.anomaly_detector.check(opcode, context)?;
// 入侵检测
self.intrusion_detector.check(opcode, context)?;
// 行为分析
self.behavior_analyzer.analyze(opcode, context)?;
Ok(())
}
}
/// 异常检测器
#[derive(Debug)]
struct AnomalyDetector {
baseline: HashMap<Opcode, u64>,
threshold: f64,
}
impl AnomalyDetector {
fn new() -> Self {
AnomalyDetector {
baseline: HashMap::new(),
threshold: 2.0,
}
}
fn check(&mut self, opcode: Opcode, context: &ExecutionContext) -> Result<(), SecurityError> {
// 实现异常检测逻辑
// 更新基线统计
*self.baseline.entry(opcode).or_insert(0) += 1;
// 计算平均值和标准差
if self.baseline.len() > 10 {
let current_count = *self.baseline.get(&opcode).unwrap_or(&0);
let total: u64 = self.baseline.values().sum();
let avg = total as f64 / self.baseline.len() as f64;
// 计算方差
let variance: f64 = self.baseline.values()
.map(|&v| {
let diff = v as f64 - avg;
diff * diff
})
.sum::<f64>() / self.baseline.len() as f64;
let std_dev = variance.sqrt();
// 检测异常:如果某个操作码频率超过阈值
if current_count as f64 > avg + self.threshold * std_dev {
return Err(SecurityError::AnomalyDetected(
format!("Opcode {:?} frequency anomaly: {} > {} + {}*{}",
opcode, current_count, avg, self.threshold, std_dev)
));
}
}
// 检测资源异常
if context.gas_used > 900_000 {
return Err(SecurityError::AnomalyDetected(
format!("Gas usage approaching limit: {}", context.gas_used)
));
}
if context.memory_used > 60 * 1024 * 1024 {
return Err(SecurityError::AnomalyDetected(
format!("Memory usage high: {} bytes", context.memory_used)
));
}
if context.call_depth > 100 {
return Err(SecurityError::AnomalyDetected(
format!("Call depth suspicious: {}", context.call_depth)
));
}
Ok(())
}
}
/// 入侵检测器
#[derive(Debug)]
struct IntrusionDetector {
patterns: Vec<AttackPattern>,
}
impl IntrusionDetector {
fn new() -> Self {
IntrusionDetector {
patterns: vec![
AttackPattern::ReentrancyAttack,
AttackPattern::IntegerOverflow,
AttackPattern::UnderflowAttack,
AttackPattern::DenialOfService,
],
}
}
fn check(&self, opcode: Opcode, context: &ExecutionContext) -> Result<(), SecurityError> {
// 实现入侵检测逻辑
// 检测重入攻击模式
if self.patterns.contains(&AttackPattern::ReentrancyAttack) {
// 检测嵌套调用深度异常
if context.call_depth > 50 && matches!(opcode, Opcode::Call) {
return Err(SecurityError::IntrusionDetected(
"Possible reentrancy attack detected".to_string()
));
}
}
// 检测整数溢出攻击
if self.patterns.contains(&AttackPattern::IntegerOverflow) {
if matches!(opcode, Opcode::Add | Opcode::Mul) {
// 在实际执行中需要检查操作数
// 这里只是模式检测
}
}
// 检测下溢攻击
if self.patterns.contains(&AttackPattern::UnderflowAttack) {
if matches!(opcode, Opcode::Sub) {
// 在实际执行中需要检查操作数
}
}
// 检测DoS攻击
if self.patterns.contains(&AttackPattern::DenialOfService) {
// 检测过多Gas消耗
if context.gas_used > 950_000 {
return Err(SecurityError::IntrusionDetected(
"Possible DoS attack: excessive gas usage".to_string()
));
}
// 检测过多内存使用
if context.memory_used > 62 * 1024 * 1024 {
return Err(SecurityError::IntrusionDetected(
"Possible DoS attack: excessive memory usage".to_string()
));
}
// 检测无限循环
if context.stack_depth > 900 {
return Err(SecurityError::IntrusionDetected(
"Possible DoS attack: infinite loop detected".to_string()
));
}
}
Ok(())
}
}
/// 攻击模式
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AttackPattern {
ReentrancyAttack,
IntegerOverflow,
UnderflowAttack,
DenialOfService,
}
/// 行为分析器
#[derive(Debug)]
struct BehaviorAnalyzer {
history: Vec<Opcode>,
max_history: usize,
}
impl BehaviorAnalyzer {
fn new() -> Self {
BehaviorAnalyzer {
history: Vec::new(),
max_history: 1000,
}
}
fn analyze(&mut self, opcode: Opcode, context: &ExecutionContext) -> Result<(), SecurityError> {
self.history.push(opcode);
if self.history.len() > self.max_history {
self.history.remove(0);
}
// 实现行为分析逻辑
// 分析执行模式
if self.history.len() >= 10 {
let recent = &self.history[self.history.len() - 10..];
// 检测重复模式
if recent.windows(5).all(|w| w[0] == w[1] && w[1] == w[2] && w[2] == w[3] && w[3] == w[4]) {
return Err(SecurityError::SuspiciousBehavior(
format!("Repetitive pattern detected: {:?}", recent[0])
));
}
// 检测循环模式
if recent.len() >= 6 {
let pattern = &recent[0..3];
let repeat = &recent[3..6];
if pattern == repeat {
return Err(SecurityError::SuspiciousBehavior(
"Loop pattern detected".to_string()
));
}
}
}
// 分析资源使用趋势
if context.gas_used > 800_000 {
// Gas使用接近限制
if matches!(opcode, Opcode::Call) {
return Err(SecurityError::SuspiciousBehavior(
"High gas usage with external call".to_string()
));
}
}
// 分析危险操作序列
if self.history.len() >= 3 {
let last_three = &self.history[self.history.len() - 3..];
// 检测危险的调用序列SSTORE -> CALL -> SLOAD
if matches!(last_three[0], Opcode::Store) &&
matches!(last_three[1], Opcode::Call) &&
matches!(last_three[2], Opcode::Load) {
return Err(SecurityError::SuspiciousBehavior(
"Suspicious STORE-CALL-LOAD pattern".to_string()
));
}
}
// 分析调用深度异常
if context.call_depth > 80 {
return Err(SecurityError::SuspiciousBehavior(
format!("Unusual call depth: {}", context.call_depth)
));
}
Ok(())
}
}
/// 执行上下文
#[derive(Debug, Clone)]
pub struct ExecutionContext {
pub gas_used: u64,
pub memory_used: usize,
pub stack_depth: usize,
pub call_depth: usize,
}
impl Sandbox {
/// 创建新的沙箱
pub fn new() -> Self {
Self::with_policy(SecurityPolicy::default())
}
/// 使用指定策略创建沙箱
pub fn with_policy(policy: SecurityPolicy) -> Self {
Sandbox {
policy,
resource_limits: ResourceLimits::default(),
resource_usage: Arc::new(Mutex::new(ResourceUsage::default())),
audit_log: Arc::new(Mutex::new(Vec::new())),
executor: None,
state: SandboxState::Uninitialized,
monitor: SecurityMonitor::new(),
}
}
/// 使用自定义限制创建沙箱
pub fn with_limits(policy: SecurityPolicy, limits: ResourceLimits) -> Self {
Sandbox {
policy,
resource_limits: limits,
resource_usage: Arc::new(Mutex::new(ResourceUsage::default())),
audit_log: Arc::new(Mutex::new(Vec::new())),
executor: None,
state: SandboxState::Uninitialized,
monitor: SecurityMonitor::new(),
}
}
/// 初始化沙箱
pub fn initialize(&mut self) -> Result<(), SandboxError> {
if self.state != SandboxState::Uninitialized {
return Err(SandboxError::InvalidState("沙箱已初始化".to_string()));
}
self.executor = Some(Executor::new());
self.state = SandboxState::Ready;
self.log_audit(AuditEventType::ExecutionStart, Severity::Info, "沙箱已初始化".to_string());
Ok(())
}
/// 检查操作码权限
pub fn check_opcode_permission(&self, opcode: Opcode) -> Result<(), SecurityError> {
// 检查禁止列表
if self.policy.forbidden_opcodes.contains(&opcode) {
return Err(SecurityError::ForbiddenOpcode(opcode));
}
// 检查允许列表(如果非空)
if !self.policy.allowed_opcodes.is_empty() && !self.policy.allowed_opcodes.contains(&opcode) {
return Err(SecurityError::UnauthorizedOpcode(opcode));
}
// 检查自定义规则
for rule in &self.policy.custom_rules {
if let RuleCondition::OpcodeMatch(rule_opcode) = rule.condition {
if rule_opcode == opcode {
match rule.action {
RuleAction::Deny => return Err(SecurityError::CustomRuleDenied(rule.name.clone())),
RuleAction::Warn => {
self.log_audit(AuditEventType::Warning, Severity::Warning,
format!("自定义规则警告: {}", rule.name));
}
_ => {}
}
}
}
}
Ok(())
}
/// 检查资源限制
pub fn check_resource_limits(&self) -> Result<(), SecurityError> {
let usage = self.resource_usage.lock().expect("lock not poisoned");
if usage.gas_used > self.resource_limits.max_gas {
return Err(SecurityError::GasLimitExceeded);
}
if usage.memory_used > self.resource_limits.max_memory {
return Err(SecurityError::MemoryLimitExceeded);
}
if usage.stack_depth > self.resource_limits.max_stack_depth {
return Err(SecurityError::StackOverflow);
}
if usage.call_depth > self.resource_limits.max_call_depth {
return Err(SecurityError::CallDepthExceeded);
}
if usage.execution_time > self.resource_limits.max_execution_time {
return Err(SecurityError::ExecutionTimeout);
}
Ok(())
}
/// 更新资源使用
pub fn update_resource_usage(&self, gas: u64, memory: usize) {
let mut usage = self.resource_usage.lock().expect("lock not poisoned");
usage.gas_used += gas;
usage.memory_used = memory;
}
/// 检查地址权限
pub fn check_address_permission(&self, address: &str) -> Result<(), SecurityError> {
// 检查黑名单
if self.policy.address_blacklist.contains(address) {
return Err(SecurityError::BlacklistedAddress(address.to_string()));
}
// 检查白名单(如果非空)
if !self.policy.address_whitelist.is_empty() && !self.policy.address_whitelist.contains(address) {
return Err(SecurityError::UnauthorizedAddress(address.to_string()));
}
Ok(())
}
/// 执行前检查
pub fn pre_execution_check(&mut self, opcode: Opcode) -> Result<(), SecurityError> {
// 检查操作码权限
self.check_opcode_permission(opcode)?;
// 检查资源限制
self.check_resource_limits()?;
// 安全监控
let usage = self.resource_usage.lock().expect("lock not poisoned");
let context = ExecutionContext {
gas_used: usage.gas_used,
memory_used: usage.memory_used,
stack_depth: usage.stack_depth,
call_depth: usage.call_depth,
};
drop(usage);
self.monitor.check_security(opcode, &context)?;
Ok(())
}
/// 记录审计日志
fn log_audit(&self, event_type: AuditEventType, severity: Severity, message: String) {
let entry = AuditEntry {
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("FIX-006: unexpected None/Err")
.as_secs(),
event_type,
severity,
message,
context: HashMap::new(),
};
let mut log = self.audit_log.lock().expect("lock not poisoned");
log.push(entry);
}
/// 获取审计日志
pub fn get_audit_log(&self) -> Vec<AuditEntry> {
self.audit_log.lock().expect("lock not poisoned").clone()
}
/// 清空审计日志
pub fn clear_audit_log(&self) {
self.audit_log.lock().expect("lock not poisoned").clear();
}
/// 获取资源使用情况
pub fn get_resource_usage(&self) -> ResourceUsage {
self.resource_usage.lock().expect("lock not poisoned").clone()
}
/// 重置资源使用
pub fn reset_resource_usage(&self) {
let mut usage = self.resource_usage.lock().expect("lock not poisoned");
*usage = ResourceUsage::default();
}
/// 暂停执行
pub fn pause(&mut self) -> Result<(), SandboxError> {
if self.state != SandboxState::Running {
return Err(SandboxError::InvalidState("沙箱未在运行".to_string()));
}
self.state = SandboxState::Paused;
self.log_audit(AuditEventType::Warning, Severity::Info, "执行已暂停".to_string());
Ok(())
}
/// 恢复执行
pub fn resume(&mut self) -> Result<(), SandboxError> {
if self.state != SandboxState::Paused {
return Err(SandboxError::InvalidState("沙箱未暂停".to_string()));
}
self.state = SandboxState::Running;
self.log_audit(AuditEventType::ExecutionStart, Severity::Info, "执行已恢复".to_string());
Ok(())
}
/// 停止执行
pub fn stop(&mut self) -> Result<(), SandboxError> {
if self.state == SandboxState::Stopped {
return Err(SandboxError::InvalidState("沙箱已停止".to_string()));
}
self.state = SandboxState::Stopped;
self.log_audit(AuditEventType::ExecutionComplete, Severity::Info, "执行已停止".to_string());
Ok(())
}
/// 获取沙箱状态
pub fn state(&self) -> SandboxState {
self.state
}
/// 生成安全报告
pub fn generate_security_report(&self) -> SecurityReport {
let usage = self.resource_usage.lock().expect("lock not poisoned").clone();
let audit_log = self.audit_log.lock().expect("lock not poisoned").clone();
SecurityReport {
resource_usage: usage,
resource_limits: self.resource_limits.clone(),
audit_entries: audit_log,
violations: self.count_violations(),
warnings: self.count_warnings(),
}
}
fn count_violations(&self) -> usize {
self.audit_log.lock().expect("lock not poisoned")
.iter()
.filter(|e| e.event_type == AuditEventType::SecurityViolation)
.count()
}
fn count_warnings(&self) -> usize {
self.audit_log.lock().expect("lock not poisoned")
.iter()
.filter(|e| e.severity == Severity::Warning)
.count()
}
}
impl Default for Sandbox {
fn default() -> Self {
Self::new()
}
}
/// 安全报告
#[derive(Debug, Clone)]
pub struct SecurityReport {
pub resource_usage: ResourceUsage,
pub resource_limits: ResourceLimits,
pub audit_entries: Vec<AuditEntry>,
pub violations: usize,
pub warnings: usize,
}
/// 沙箱错误
#[derive(Debug, thiserror::Error)]
pub enum SandboxError {
#[error("无效状态: {0}")]
InvalidState(String),
#[error("初始化失败: {0}")]
InitializationFailed(String),
#[error("安全错误: {0}")]
SecurityError(#[from] SecurityError),
}
/// 安全错误
#[derive(Debug, thiserror::Error)]
pub enum SecurityError {
#[error("禁止的操作码: {0:?}")]
ForbiddenOpcode(Opcode),
#[error("未授权的操作码: {0:?}")]
UnauthorizedOpcode(Opcode),
#[error("Gas限制超出")]
GasLimitExceeded,
#[error("内存限制超出")]
MemoryLimitExceeded,
#[error("栈溢出")]
StackOverflow,
#[error("调用深度超出")]
CallDepthExceeded,
#[error("执行超时")]
ExecutionTimeout,
#[error("黑名单地址: {0}")]
BlacklistedAddress(String),
#[error("未授权地址: {0}")]
UnauthorizedAddress(String),
#[error("自定义规则拒绝: {0}")]
CustomRuleDenied(String),
#[error("安全违规: {0}")]
SecurityViolation(String),
#[error("异常检测: {0}")]
AnomalyDetected(String),
#[error("入侵检测: {0}")]
IntrusionDetected(String),
#[error("可疑行为: {0}")]
SuspiciousBehavior(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sandbox_creation() {
let sandbox = Sandbox::new();
assert_eq!(sandbox.state(), SandboxState::Uninitialized);
}
#[test]
fn test_sandbox_initialization() {
let mut sandbox = Sandbox::new();
sandbox.initialize().expect("FIX-006: unexpected None/Err");
assert_eq!(sandbox.state(), SandboxState::Ready);
}
#[test]
fn test_opcode_permission() {
let mut policy = SecurityPolicy::default();
policy.forbidden_opcodes.insert(Opcode::Push);
let sandbox = Sandbox::with_policy(policy);
assert!(sandbox.check_opcode_permission(Opcode::Push).is_err());
assert!(sandbox.check_opcode_permission(Opcode::Pop).is_ok());
}
#[test]
fn test_resource_limits() {
let mut limits = ResourceLimits::default();
limits.max_gas = 100;
let sandbox = Sandbox::with_limits(SecurityPolicy::default(), limits);
sandbox.update_resource_usage(50, 0);
assert!(sandbox.check_resource_limits().is_ok());
sandbox.update_resource_usage(60, 0);
assert!(sandbox.check_resource_limits().is_err());
}
#[test]
fn test_address_permission() {
let mut policy = SecurityPolicy::default();
policy.address_blacklist.insert("0x123".to_string());
let sandbox = Sandbox::with_policy(policy);
assert!(sandbox.check_address_permission("0x123").is_err());
assert!(sandbox.check_address_permission("0x456").is_ok());
}
#[test]
fn test_audit_log() {
let sandbox = Sandbox::new();
sandbox.log_audit(AuditEventType::ExecutionStart, Severity::Info, "测试".to_string());
let log = sandbox.get_audit_log();
assert_eq!(log.len(), 1);
assert_eq!(log[0].event_type, AuditEventType::ExecutionStart);
}
#[test]
fn test_security_report() {
let sandbox = Sandbox::new();
let report = sandbox.generate_security_report();
assert_eq!(report.violations, 0);
assert_eq!(report.warnings, 0);
}
#[test]
fn test_sandbox_pause_resume() {
let mut sandbox = Sandbox::new();
sandbox.initialize().expect("FIX-006: unexpected None/Err");
sandbox.state = SandboxState::Running;
sandbox.pause().expect("FIX-006: unexpected None/Err");
assert_eq!(sandbox.state(), SandboxState::Paused);
sandbox.resume().expect("FIX-006: unexpected None/Err");
assert_eq!(sandbox.state(), SandboxState::Running);
}
}