use blvm_protocol::payment::{
Bip70Error, Payment, PaymentACK, PaymentOutput, PaymentRequest, SignedRefundAddress,
};
use secp256k1::{Message, Secp256k1, SecretKey};
fn generate_test_keypair() -> (SecretKey, secp256k1::PublicKey) {
let secret_key = SecretKey::from_slice(&[1; 32]).unwrap();
let secp = Secp256k1::new();
let public_key = secp256k1::PublicKey::from_secret_key(&secp, &secret_key);
(secret_key, public_key)
}
#[test]
fn test_payment_request_invalid_network() {
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: Some(1000),
}];
let request = PaymentRequest::new("invalid_network".to_string(), outputs, 1234567890);
assert_eq!(request.payment_details.network, "invalid_network");
}
#[test]
fn test_payment_request_expired() {
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: Some(1000),
}];
let past_time = 1000;
let request =
PaymentRequest::new("main".to_string(), outputs, past_time).with_expires(past_time - 1);
assert_eq!(request.payment_details.expires, Some(past_time - 1));
assert!(request.payment_details.expires.unwrap() < request.payment_details.time);
}
#[test]
fn test_payment_request_empty_outputs() {
let outputs = vec![];
let request = PaymentRequest::new("main".to_string(), outputs, 1234567890);
assert_eq!(request.payment_details.outputs.len(), 0);
}
#[test]
fn test_payment_request_invalid_signature() {
let (secret_key, _) = generate_test_keypair();
let secp = Secp256k1::new();
let pubkey_bytes = secp256k1::PublicKey::from_secret_key(&secp, &secret_key).serialize();
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: Some(1000),
}];
let mut request = PaymentRequest::new("main".to_string(), outputs, 1234567890)
.with_merchant_key(pubkey_bytes);
request.signature = Some(vec![0xFF; 64]);
let result = request.verify_signature();
assert!(result.is_err());
if let Err(Bip70Error::SignatureError(_)) = result {
} else {
panic!("Expected SignatureError");
}
}
#[test]
fn test_payment_request_missing_signature() {
let (_, pubkey) = generate_test_keypair();
let pubkey_bytes = pubkey.serialize();
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: Some(1000),
}];
let request = PaymentRequest::new("main".to_string(), outputs, 1234567890)
.with_merchant_key(pubkey_bytes);
let result = request.verify_signature();
assert!(result.is_err());
}
#[test]
fn test_payment_request_malformed_output() {
let outputs = vec![PaymentOutput {
script: vec![], amount: Some(1000),
}];
let request = PaymentRequest::new("main".to_string(), outputs, 1234567890);
assert_eq!(request.payment_details.outputs[0].script.len(), 0);
}
#[test]
fn test_payment_request_negative_amount() {
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: None, }];
let request = PaymentRequest::new("main".to_string(), outputs, 1234567890);
assert_eq!(request.payment_details.outputs[0].amount, None);
}
#[test]
fn test_payment_invalid_transaction() {
let payment = Payment {
transactions: vec![vec![0xFF; 10]], refund_to: None,
merchant_data: None,
memo: None,
};
assert_eq!(payment.transactions.len(), 1);
}
#[test]
fn test_payment_ack_mismatched_payment() {
let payment1 = Payment {
transactions: vec![vec![1, 2, 3]],
refund_to: None,
merchant_data: None,
memo: None,
};
let payment2 = Payment {
transactions: vec![vec![4, 5, 6]],
refund_to: None,
merchant_data: None,
memo: None,
};
let ack = PaymentACK {
payment: payment2.clone(),
memo: None,
signature: None,
};
assert_ne!(ack.payment.transactions, payment1.transactions);
assert_eq!(ack.payment.transactions, payment2.transactions);
}
#[test]
fn test_signed_refund_address_invalid_signature() {
let (_, _pubkey) = generate_test_keypair();
let address = PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: None,
};
let invalid_signature = vec![0xFF; 64];
let signed_refund = SignedRefundAddress {
address,
signature: invalid_signature,
};
use sha2::{Digest, Sha256};
let hash = Sha256::digest(&[0x42; 32]);
let message = Message::from_digest(hash.into());
let sig_result = secp256k1::ecdsa::Signature::from_compact(&signed_refund.signature);
assert!(sig_result.is_err());
}
#[test]
fn test_payment_request_very_large_memo() {
let large_memo = "x".repeat(10000);
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: Some(1000),
}];
let request =
PaymentRequest::new("main".to_string(), outputs, 1234567890).with_memo(large_memo.clone());
assert_eq!(request.payment_details.memo, Some(large_memo));
}
#[test]
fn test_payment_request_multiple_refund_addresses() {
let (secret_key, pubkey) = generate_test_keypair();
let pubkey_bytes = pubkey.serialize();
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: Some(1000),
}];
let refund1 = PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: None,
};
let refund2 = PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_2],
amount: None,
};
use secp256k1::Secp256k1;
use sha2::{Digest, Sha256};
let secp = Secp256k1::new();
let hash1 = Sha256::digest(&refund1.script);
let message1 = Message::from_digest(hash1.into());
let sig1 = secp.sign_ecdsa(&message1, &secret_key);
let signed_refund1 = SignedRefundAddress {
address: refund1,
signature: sig1.serialize_compact().to_vec(),
};
let hash2 = Sha256::digest(&refund2.script);
let message2 = Message::from_digest(hash2.into());
let sig2 = secp.sign_ecdsa(&message2, &secret_key);
let signed_refund2 = SignedRefundAddress {
address: refund2,
signature: sig2.serialize_compact().to_vec(),
};
let request = PaymentRequest::new("main".to_string(), outputs, 1234567890)
.with_merchant_key(pubkey_bytes)
.with_authorized_refund(signed_refund1)
.with_authorized_refund(signed_refund2);
assert_eq!(
request.authorized_refund_addresses.as_ref().unwrap().len(),
2
);
}
#[test]
fn test_payment_request_network_timeout_scenario() {
let outputs = vec![PaymentOutput {
script: vec![blvm_consensus::opcodes::OP_1],
amount: Some(1000),
}];
let current_time = 1234567890;
let short_expiry = current_time + 1;
let request =
PaymentRequest::new("main".to_string(), outputs, current_time).with_expires(short_expiry);
let future_time = current_time + 2;
assert!(future_time > request.payment_details.expires.unwrap());
}