#![cfg(all(feature = "ctv", feature = "bip70-http"))]
use blvm_node::config::PaymentConfig;
use blvm_node::payment::processor::PaymentProcessor;
use blvm_node::payment::state_machine::PaymentStateMachine;
use blvm_node::storage::Storage;
use serde_json::json;
use std::sync::Arc;
use tempfile::TempDir;
fn create_test_state_machine_with_storage() -> (Arc<PaymentStateMachine>, TempDir) {
let temp_dir = TempDir::new().unwrap();
let storage_path = temp_dir.path();
let storage = Storage::new(storage_path).expect("Failed to create storage");
let storage_arc = Arc::new(storage);
let config = PaymentConfig::default();
let processor =
Arc::new(PaymentProcessor::new(config).expect("Failed to create payment processor"));
let state_machine = Arc::new(
PaymentStateMachine::with_storage(processor, Some(storage_arc.clone()))
.with_congestion_manager(
None, Some(storage_arc),
blvm_node::payment::congestion::BatchConfig::default(),
),
);
(state_machine, temp_dir)
}
#[tokio::test]
async fn test_vault_complete_lifecycle() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let vault_engine = state_machine
.vault_engine()
.expect("Vault engine should be available");
let vault_id = "e2e_vault_1";
let deposit_amount = 100000;
let withdrawal_script = vec![0x51, 0x87];
let vault_state = vault_engine
.create_vault(
vault_id,
deposit_amount,
withdrawal_script.clone(),
blvm_node::payment::vault::VaultConfig {
withdrawal_delay_blocks: 144,
require_unvault: true,
..Default::default()
},
)
.expect("Vault creation should succeed");
assert_eq!(vault_state.vault_id, vault_id);
assert_eq!(vault_state.deposit_amount, deposit_amount);
assert_eq!(
vault_state.state,
blvm_node::payment::vault::VaultLifecycle::Deposited
);
let unvault_script = vec![0x52, 0x87]; let unvaulted_state = vault_engine
.unvault(&vault_state, unvault_script)
.expect("Unvaulting should succeed");
assert_eq!(
unvaulted_state.state,
blvm_node::payment::vault::VaultLifecycle::Unvaulting
);
let unvault_tx_hash = [0x01; 32];
let unvault_block_height = 1000;
let unvaulted_state = vault_engine
.mark_unvaulted(&unvaulted_state, unvault_tx_hash, unvault_block_height)
.expect("Mark unvaulted should succeed");
assert!(matches!(
unvaulted_state.state,
blvm_node::payment::vault::VaultLifecycle::Unvaulted { .. }
));
let current_block_height = unvault_block_height + 145; let final_withdrawal_script = vec![0x53, 0x87]; let withdrawn_state = vault_engine
.withdraw(
&unvaulted_state,
final_withdrawal_script,
current_block_height,
)
.expect("Withdrawal should succeed after delay");
assert_eq!(
withdrawn_state.state,
blvm_node::payment::vault::VaultLifecycle::Withdrawing
);
assert!(withdrawn_state.withdrawal_covenant.is_some());
}
#[tokio::test]
async fn test_vault_recovery_path() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let vault_engine = state_machine
.vault_engine()
.expect("Vault engine should be available");
let vault_id = "e2e_vault_2";
let deposit_amount = 100000;
let withdrawal_script = vec![0x51, 0x87];
let vault_state = vault_engine
.create_vault(
vault_id,
deposit_amount,
withdrawal_script,
blvm_node::payment::vault::VaultConfig {
recovery_script: Some(vec![0x51, 0x87]), ..Default::default()
},
)
.expect("Vault creation should succeed");
let recovery_script = vec![0x54, 0x87]; let recovered_state = vault_engine
.recover(&vault_state, recovery_script)
.expect("Recovery should succeed");
assert_eq!(
recovered_state.state,
blvm_node::payment::vault::VaultLifecycle::Recovered
);
}
#[tokio::test]
async fn test_pool_complete_workflow() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let pool_engine = state_machine
.pool_engine()
.expect("Pool engine should be available");
let pool_id = "e2e_pool_1";
let initial_participants = vec![
("participant_1".to_string(), 10000, vec![0x51, 0x87]),
("participant_2".to_string(), 20000, vec![0x52, 0x87]),
];
let pool_state = pool_engine
.create_pool(
pool_id,
initial_participants,
blvm_node::payment::pool::PoolConfig::default(),
)
.expect("Pool creation should succeed");
assert_eq!(pool_state.pool_id, pool_id);
assert_eq!(pool_state.total_balance, 30000);
assert_eq!(pool_state.participants.len(), 2);
let new_participant_id = "participant_3";
let contribution = 15000;
let script_pubkey = vec![0x53, 0x87];
let pool_state = pool_engine
.join_pool(&pool_state, new_participant_id, contribution, script_pubkey)
.expect("Join pool should succeed");
assert_eq!(pool_state.participants.len(), 3);
assert_eq!(pool_state.total_balance, 45000);
let distribution = vec![
("participant_1".to_string(), 5000),
("participant_2".to_string(), 10000),
];
let (pool_state, covenant_proof) = pool_engine
.distribute(&pool_state, distribution)
.expect("Distribution should succeed");
assert_eq!(pool_state.total_balance, 30000); assert!(covenant_proof.template_hash.len() == 32);
}
#[tokio::test]
async fn test_pool_exit_workflow() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let pool_engine = state_machine
.pool_engine()
.expect("Pool engine should be available");
let pool_id = "e2e_pool_2";
let initial_participants = vec![
("participant_1".to_string(), 10000, vec![0x51, 0x87]),
("participant_2".to_string(), 20000, vec![0x52, 0x87]),
];
let pool_state = pool_engine
.create_pool(
pool_id,
initial_participants,
blvm_node::payment::pool::PoolConfig::default(),
)
.expect("Pool creation should succeed");
let (pool_state, exit_covenant) = pool_engine
.exit_pool(&pool_state, "participant_1", Some(5000))
.expect("Exit pool should succeed");
assert_eq!(pool_state.total_balance, 25000); assert!(exit_covenant.template_hash.len() == 32);
}
#[tokio::test]
async fn test_congestion_complete_workflow() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let congestion_manager = state_machine
.congestion_manager()
.expect("Congestion manager should be available");
let batch_id = "e2e_batch_1";
let target_fee_rate = 10;
let mut manager = congestion_manager.lock().await;
let created_id = manager.create_batch(batch_id, Some(target_fee_rate));
assert_eq!(created_id, batch_id);
let batch = manager.get_batch(batch_id).expect("Batch should exist");
assert_eq!(batch.target_fee_rate, target_fee_rate);
assert_eq!(batch.transactions.len(), 0);
let tx1 = blvm_node::payment::congestion::PendingTransaction {
tx_id: "tx_1".to_string(),
outputs: vec![blvm_protocol::payment::PaymentOutput {
script: vec![0x51, 0x87],
amount: Some(10000),
}],
priority: blvm_node::payment::congestion::TransactionPriority::Normal,
created_at: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
deadline: None,
};
manager
.add_to_batch(batch_id, tx1)
.expect("Add transaction should succeed");
let batch = manager.get_batch(batch_id).expect("Batch should exist");
assert_eq!(batch.transactions.len(), 1);
let result = manager.broadcast_batch(batch_id);
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn test_vault_and_pool_integration() {
let (state_machine, _temp_dir) = create_test_state_machine_with_storage();
let vault_engine = state_machine
.vault_engine()
.expect("Vault engine should be available");
let vault_id = "e2e_vault_pool_1";
let vault_state = vault_engine
.create_vault(
vault_id,
100000,
vec![0x51, 0x87],
blvm_node::payment::vault::VaultConfig::default(),
)
.expect("Vault creation should succeed");
let pool_engine = state_machine
.pool_engine()
.expect("Pool engine should be available");
let pool_id = "e2e_pool_vault_1";
let pool_state = pool_engine
.create_pool(
pool_id,
vec![("participant_1".to_string(), 10000, vec![0x51, 0x87])],
blvm_node::payment::pool::PoolConfig::default(),
)
.expect("Pool creation should succeed");
assert_eq!(vault_state.vault_id, vault_id);
assert_eq!(pool_state.pool_id, pool_id);
let retrieved_vault = vault_engine
.get_vault(vault_id)
.expect("Should retrieve vault");
assert!(retrieved_vault.is_some());
let retrieved_pool = pool_engine.get_pool(pool_id).expect("Should retrieve pool");
assert!(retrieved_pool.is_some());
}
#[tokio::test]
async fn test_state_persistence() {
let (state_machine, temp_dir) = create_test_state_machine_with_storage();
let vault_engine = state_machine
.vault_engine()
.expect("Vault engine should be available");
let vault_id = "e2e_persist_1";
let vault_state = vault_engine
.create_vault(
vault_id,
100000,
vec![0x51, 0x87],
blvm_node::payment::vault::VaultConfig::default(),
)
.expect("Vault creation should succeed");
let retrieved = vault_engine
.get_vault(vault_id)
.expect("Should retrieve vault");
assert!(retrieved.is_some());
let retrieved_state = retrieved.unwrap();
assert_eq!(retrieved_state.vault_id, vault_state.vault_id);
assert_eq!(retrieved_state.deposit_amount, vault_state.deposit_amount);
drop(state_machine);
drop(vault_engine);
let storage_path = temp_dir.path();
let storage = Storage::new(storage_path).expect("Failed to recreate storage");
let storage_arc = Arc::new(storage);
let config = PaymentConfig::default();
let processor =
Arc::new(PaymentProcessor::new(config).expect("Failed to create payment processor"));
let new_state_machine = Arc::new(
PaymentStateMachine::with_storage(processor, Some(storage_arc.clone()))
.with_congestion_manager(
None,
Some(storage_arc),
blvm_node::payment::congestion::BatchConfig::default(),
),
);
let new_vault_engine = new_state_machine
.vault_engine()
.expect("Vault engine should be available");
let persisted_vault = new_vault_engine
.get_vault(vault_id)
.expect("Should retrieve persisted vault");
assert!(persisted_vault.is_some());
let persisted_state = persisted_vault.unwrap();
assert_eq!(persisted_state.vault_id, vault_id);
assert_eq!(persisted_state.deposit_amount, 100000);
}