mod common;
use blvm_node::config::PaymentConfig;
use blvm_node::module::encryption::{load_encrypted_module, ModuleEncryption};
use blvm_node::module::registry::client::{ModuleEntry, ModuleRegistry};
use blvm_node::module::registry::manifest::{ModuleManifest, PaymentSection};
use blvm_node::payment::processor::{PaymentError, PaymentProcessor};
use blvm_protocol::address::{BitcoinAddress, Network};
use blvm_protocol::payment::{Payment, PaymentRequest};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::sync::Arc;
use tempfile::TempDir;
fn test_payment_tb1_addresses() -> (String, String) {
let author = BitcoinAddress::new(Network::Testnet, 0, vec![0x01; 20]).unwrap();
let marketplace = BitcoinAddress::new(Network::Testnet, 0, vec![0x02; 20]).unwrap();
(author.encode().unwrap(), marketplace.encode().unwrap())
}
fn create_test_manifest_with_payment(name: &str) -> ModuleManifest {
use blvm_node::module::registry::manifest::{MaintainerSignature, SignatureSection};
let test_key = common::test_secp256k1_scalar_one();
let (author_addr, commons_addr) = test_payment_tb1_addresses();
let price_sats = 100000u64;
let message_data = format!("{}||{}||{}", author_addr, commons_addr, price_sats);
let message_hash: [u8; 32] = Sha256::digest(message_data.as_bytes()).into();
let (signature_hex, pubkey_hex) =
common::ecdsa_compact_sig_hex_and_pubkey_hex(&test_key, &message_hash);
let signature_section = SignatureSection {
maintainers: vec![MaintainerSignature {
name: "test-maintainer".to_string(),
public_key: pubkey_hex,
signature: "dummy".to_string(),
}],
threshold: Some("1-of-1".to_string()),
};
let payment_section = PaymentSection {
required: true,
price_sats: Some(price_sats),
author_payment_code: None,
author_address: Some(author_addr),
commons_payment_code: None,
commons_address: Some(commons_addr),
payment_signature: Some(signature_hex),
};
ModuleManifest {
name: name.to_string(),
version: "1.0.0".to_string(),
description: Some(format!("Test module: {}", name)),
author: Some("Test Author".to_string()),
capabilities: Vec::new(),
rpc_overrides: Vec::new(),
dependencies: HashMap::new(),
optional_dependencies: HashMap::new(),
entry_point: format!("{}.so", name),
config_schema: HashMap::new(),
binary: None,
signatures: Some(signature_section),
payment: Some(payment_section),
}
}
async fn create_test_registry_with_module(
temp_dir: &TempDir,
module_name: &str,
module_binary: &[u8],
) -> Arc<ModuleRegistry> {
use blvm_node::module::registry::cache::LocalCache;
use blvm_node::module::registry::cas::ContentAddressableStorage;
let cache_dir = temp_dir.path().join("cache");
let cas_dir = temp_dir.path().join("cas");
std::fs::create_dir_all(&cache_dir).unwrap();
std::fs::create_dir_all(&cas_dir).unwrap();
let cas = Arc::new(tokio::sync::RwLock::new(
ContentAddressableStorage::new(&cas_dir).unwrap(),
));
let cache = Arc::new(tokio::sync::RwLock::new(LocalCache::new()));
let manifest = create_test_manifest_with_payment(module_name);
let manifest_toml = toml::to_string(&manifest).unwrap();
let manifest_hash = cas.write().await.store(manifest_toml.as_bytes()).unwrap();
let binary_hash = cas.write().await.store(module_binary).unwrap();
let combined = format!("{}{}", hex::encode(manifest_hash), hex::encode(binary_hash));
let module_hash = Sha256::digest(combined.as_bytes());
let mut hash = [0u8; 32];
hash.copy_from_slice(&module_hash);
use blvm_node::module::registry::cache::CachedModule;
let cached = CachedModule {
name: module_name.to_string(),
version: "1.0.0".to_string(),
hash,
manifest_hash,
binary_hash,
verified_at: 0,
verified_by: Vec::new(),
local_path: temp_dir.path().join(format!("{}.so", module_name)),
expires_at: None,
};
cache.write().await.cache(cached);
Arc::new(ModuleRegistry::new(&cache_dir, &cas_dir, Vec::new()).unwrap())
}
#[tokio::test]
async fn test_process_payment_encrypts_module() {
let temp_dir = TempDir::new().unwrap();
let modules_dir = temp_dir.path().join("modules");
std::fs::create_dir_all(&modules_dir).unwrap();
let config = PaymentConfig::default();
let mut processor = PaymentProcessor::new(config).unwrap();
let encryption = Arc::new(ModuleEncryption::new());
processor = processor.with_module_encryption(Arc::clone(&encryption));
processor = processor.with_modules_dir(modules_dir.clone());
let module_name = "test-module";
let module_binary = b"test_module_binary_data";
let registry = create_test_registry_with_module(&temp_dir, module_name, module_binary).await;
processor = processor.with_module_registry(registry);
let module_hash = {
let hash = Sha256::digest(module_binary);
let mut h = [0u8; 32];
h.copy_from_slice(&hash);
h
};
let manifest = create_test_manifest_with_payment(module_name);
let node_script = vec![0x51, 0x00];
let payment_request = processor
.create_module_payment_request(&manifest, &module_hash, node_script, None)
.await
.unwrap();
let serialized = bincode::serialize(&payment_request).unwrap_or_default();
let hash = Sha256::digest(&serialized);
let payment_id = hex::encode(&hash[..16]);
let mut payment = Payment::new(vec![vec![0x01, 0x00, 0x00, 0x00]]); payment.merchant_data = payment_request.payment_details.merchant_data.clone();
let _ack = processor
.process_payment(payment, payment_id.clone(), None)
.await;
let encrypted_path = modules_dir.join("encrypted").join(module_name);
if encrypted_path.exists() {
let (encrypted_binary, metadata) = load_encrypted_module(&modules_dir, module_name)
.await
.unwrap();
assert_eq!(metadata.payment_id, payment_id);
assert!(!encrypted_binary.is_empty());
assert_ne!(encrypted_binary, module_binary);
} else {
}
}
#[tokio::test]
async fn test_process_payment_non_module_payment() {
let config = PaymentConfig::default();
let processor = PaymentProcessor::new(config).unwrap();
let outputs = vec![blvm_protocol::payment::PaymentOutput {
script: vec![0x51, 0x00],
amount: Some(100000),
}];
let payment_request = processor
.create_payment_request(outputs, None, None)
.await
.unwrap();
let serialized = bincode::serialize(&payment_request).unwrap_or_default();
let hash = Sha256::digest(&serialized);
let payment_id = hex::encode(&hash[..16]);
let mut payment = Payment::new(vec![vec![0x01, 0x00, 0x00, 0x00]]);
payment.merchant_data = payment_request.payment_details.merchant_data.clone();
let result = processor.process_payment(payment, payment_id, None).await;
assert!(result.is_err() || result.is_ok()); }
#[tokio::test]
async fn test_encrypt_module_missing_registry() {
let temp_dir = TempDir::new().unwrap();
let modules_dir = temp_dir.path().join("modules");
std::fs::create_dir_all(&modules_dir).unwrap();
let config = PaymentConfig::default();
let mut processor = PaymentProcessor::new(config).unwrap();
let encryption = Arc::new(ModuleEncryption::new());
processor = processor.with_module_encryption(Arc::clone(&encryption));
processor = processor.with_modules_dir(modules_dir);
let module_hash = {
let hash = Sha256::digest(b"test");
let mut h = [0u8; 32];
h.copy_from_slice(&hash);
h
};
let manifest = create_test_manifest_with_payment("test-module");
let node_script = vec![0x51, 0x00];
let payment_request = processor
.create_module_payment_request(&manifest, &module_hash, node_script, None)
.await
.unwrap();
let serialized = bincode::serialize(&payment_request).unwrap_or_default();
let hash = Sha256::digest(&serialized);
let payment_id = hex::encode(&hash[..16]);
let mut payment = Payment::new(vec![vec![0x01, 0x00, 0x00, 0x00]]);
payment.merchant_data = payment_request.payment_details.merchant_data.clone();
let result = processor.process_payment(payment, payment_id, None).await;
assert!(result.is_err() || result.is_ok()); }
#[tokio::test]
async fn test_encrypt_module_missing_encryption() {
let temp_dir = TempDir::new().unwrap();
let modules_dir = temp_dir.path().join("modules");
std::fs::create_dir_all(&modules_dir).unwrap();
let config = PaymentConfig::default();
let mut processor = PaymentProcessor::new(config).unwrap();
let module_name = "test-module";
let module_binary = b"test_module_binary_data";
let registry = create_test_registry_with_module(&temp_dir, module_name, module_binary).await;
processor = processor.with_module_registry(registry);
processor = processor.with_modules_dir(modules_dir);
let module_hash = {
let hash = Sha256::digest(module_binary);
let mut h = [0u8; 32];
h.copy_from_slice(&hash);
h
};
let manifest = create_test_manifest_with_payment(module_name);
let node_script = vec![0x51, 0x00];
let payment_request = processor
.create_module_payment_request(&manifest, &module_hash, node_script, None)
.await
.unwrap();
let serialized = bincode::serialize(&payment_request).unwrap_or_default();
let hash = Sha256::digest(&serialized);
let payment_id = hex::encode(&hash[..16]);
let mut payment = Payment::new(vec![vec![0x01, 0x00, 0x00, 0x00]]);
payment.merchant_data = payment_request.payment_details.merchant_data.clone();
let result = processor.process_payment(payment, payment_id, None).await;
assert!(result.is_err() || result.is_ok()); }