1use crate::Hash;
22use blvm_secp256k1::ecdsa::{
23 ecdsa_sig_parse_compact, ecdsa_sig_verify, ecdsa_sign_compact_rfc6979, ge_from_pubkey_bytes,
24 ge_to_compressed, pubkey_from_secret, verify_ecdsa_direct,
25};
26use blvm_secp256k1::scalar::Scalar;
27use hex;
28use serde::{Deserialize, Serialize};
29use sha2::{Digest, Sha256};
30
31fn sign_compact(hash: &[u8; 32], seckey: &[u8; 32]) -> Result<Vec<u8>, Bip70Error> {
37 ecdsa_sign_compact_rfc6979(hash, seckey)
38 .map(|s| s.to_vec())
39 .ok_or_else(|| Bip70Error::SignatureError("ECDSA signing failed".to_string()))
40}
41
42fn verify_sig(sig_bytes: &[u8], pubkey: &[u8], hash: &[u8; 32]) -> Result<(), Bip70Error> {
44 let valid = if sig_bytes.len() == 64 {
45 let compact: &[u8; 64] = sig_bytes
46 .try_into()
47 .map_err(|_| Bip70Error::SignatureError("Invalid signature length".to_string()))?;
48 let (sigr, sigs) = ecdsa_sig_parse_compact(compact)
49 .ok_or_else(|| Bip70Error::SignatureError("Invalid compact signature".to_string()))?;
50 let pk = ge_from_pubkey_bytes(pubkey)
51 .ok_or_else(|| Bip70Error::SignatureError("Invalid public key".to_string()))?;
52 let mut msg = Scalar::zero();
53 let _ = msg.set_b32(hash);
54 ecdsa_sig_verify(&sigr, &sigs, &pk, &msg)
55 } else {
56 verify_ecdsa_direct(sig_bytes, pubkey, hash, false, false)
57 .ok_or_else(|| Bip70Error::SignatureError("Signature parse error".to_string()))?
58 };
59 if valid {
60 Ok(())
61 } else {
62 Err(Bip70Error::SignatureError(
63 "Signature verification failed".to_string(),
64 ))
65 }
66}
67
68fn pubkey_bytes_from_secret(seckey: &[u8; 32]) -> Result<[u8; 33], Bip70Error> {
70 let mut sec = Scalar::zero();
71 if sec.set_b32(seckey) || sec.is_zero() {
72 return Err(Bip70Error::SignatureError("Invalid secret key".to_string()));
73 }
74 let ge = pubkey_from_secret(&sec);
75 Ok(ge_to_compressed(&ge))
76}
77
78pub const PAYMENT_PROTOCOL_VERSION: u32 = 1;
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct PaymentDetails {
84 pub network: String,
86 pub outputs: Vec<PaymentOutput>,
88 pub time: u64,
90 pub expires: Option<u64>,
92 pub memo: Option<String>,
94 pub payment_url: Option<String>,
96 pub merchant_data: Option<Vec<u8>>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102pub struct PaymentOutput {
103 pub script: Vec<u8>,
105 pub amount: Option<u64>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct SignedRefundAddress {
112 pub address: PaymentOutput,
114 pub signature: Vec<u8>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct PaymentRequest {
121 pub payment_details: PaymentDetails,
123 pub merchant_pubkey: Option<Vec<u8>>,
126 pub signature: Option<Vec<u8>>,
128 pub authorized_refund_addresses: Option<Vec<SignedRefundAddress>>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct Payment {
136 pub transactions: Vec<Vec<u8>>,
138 pub refund_to: Option<Vec<PaymentOutput>>,
140 pub merchant_data: Option<Vec<u8>>,
142 pub memo: Option<String>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct PaymentACK {
149 pub payment: Payment,
151 pub memo: Option<String>,
153 pub signature: Option<Vec<u8>>,
155}
156
157impl PaymentACK {
158 pub fn sign(&mut self, private_key: &[u8; 32]) -> Result<(), Bip70Error> {
160 let mut ack_for_signing = self.clone();
161 ack_for_signing.signature = None;
162 let serialized = bincode::serialize(&ack_for_signing)
163 .map_err(|e| Bip70Error::SerializationError(e.to_string()))?;
164 let hash: [u8; 32] = Sha256::digest(&serialized).into();
165 self.signature = Some(sign_compact(&hash, private_key)?);
166 Ok(())
167 }
168
169 pub fn verify_signature(&self, merchant_pubkey: &[u8]) -> Result<(), Bip70Error> {
171 let sig = self
172 .signature
173 .as_ref()
174 .ok_or_else(|| Bip70Error::SignatureError("No signature".to_string()))?;
175 let mut ack = self.clone();
176 ack.signature = None;
177 let serialized =
178 bincode::serialize(&ack).map_err(|e| Bip70Error::SerializationError(e.to_string()))?;
179 let hash: [u8; 32] = Sha256::digest(&serialized).into();
180 verify_sig(sig, merchant_pubkey, &hash).map_err(|_| {
181 Bip70Error::SignatureError("PaymentACK signature verification failed".to_string())
182 })
183 }
184}
185
186impl PaymentRequest {
187 pub fn new(network: String, outputs: Vec<PaymentOutput>, time: u64) -> Self {
189 Self {
190 payment_details: PaymentDetails {
191 network,
192 outputs,
193 time,
194 expires: None,
195 memo: None,
196 payment_url: None,
197 merchant_data: None,
198 },
199 merchant_pubkey: None,
200 signature: None,
201 authorized_refund_addresses: None,
202 }
203 }
204
205 pub fn with_merchant_key(mut self, pubkey: [u8; 33]) -> Self {
207 self.merchant_pubkey = Some(pubkey.to_vec());
208 self
209 }
210
211 pub fn with_authorized_refund(mut self, signed_refund: SignedRefundAddress) -> Self {
213 if self.authorized_refund_addresses.is_none() {
214 self.authorized_refund_addresses = Some(Vec::new());
215 }
216 self.authorized_refund_addresses
217 .as_mut()
218 .unwrap()
219 .push(signed_refund);
220 self
221 }
222
223 pub fn with_expires(mut self, expires: u64) -> Self {
225 self.payment_details.expires = Some(expires);
226 self
227 }
228
229 pub fn with_memo(mut self, memo: String) -> Self {
231 self.payment_details.memo = Some(memo);
232 self
233 }
234
235 pub fn with_payment_url(mut self, url: String) -> Self {
237 self.payment_details.payment_url = Some(url);
238 self
239 }
240
241 pub fn with_merchant_data(mut self, data: Vec<u8>) -> Self {
243 self.payment_details.merchant_data = Some(data);
244 self
245 }
246
247 pub fn sign(&mut self, private_key: &[u8; 32]) -> Result<(), Bip70Error> {
249 let serialized = bincode::serialize(&self.payment_details)
250 .map_err(|e| Bip70Error::SerializationError(e.to_string()))?;
251 let hash: [u8; 32] = Sha256::digest(&serialized).into();
252 let pubkey = pubkey_bytes_from_secret(private_key)?;
253 self.signature = Some(sign_compact(&hash, private_key)?);
254 self.merchant_pubkey = Some(pubkey.to_vec());
255 Ok(())
256 }
257
258 pub fn verify_signature(&self) -> Result<(), Bip70Error> {
260 let pubkey = self
261 .merchant_pubkey
262 .as_ref()
263 .ok_or_else(|| Bip70Error::SignatureError("No merchant public key".to_string()))?;
264 let sig = self
265 .signature
266 .as_ref()
267 .ok_or_else(|| Bip70Error::SignatureError("No signature".to_string()))?;
268 let serialized = bincode::serialize(&self.payment_details)
269 .map_err(|e| Bip70Error::SerializationError(e.to_string()))?;
270 let hash: [u8; 32] = Sha256::digest(&serialized).into();
271 verify_sig(sig, pubkey, &hash)
272 .map_err(|_| Bip70Error::SignatureError("Signature verification failed".to_string()))
273 }
274
275 pub fn validate(&self) -> Result<(), Bip70Error> {
277 if let Some(expires) = self.payment_details.expires {
279 let now = crate::time::current_timestamp();
280 if now > expires {
281 return Err(Bip70Error::Expired);
282 }
283 }
284
285 if self.payment_details.outputs.is_empty() {
287 return Err(Bip70Error::InvalidRequest("No payment outputs".to_string()));
288 }
289
290 let valid_networks = ["main", "test", "regtest"];
292 if !valid_networks.contains(&self.payment_details.network.as_str()) {
293 return Err(Bip70Error::InvalidRequest(format!(
294 "Invalid network: {}",
295 self.payment_details.network
296 )));
297 }
298
299 Ok(())
300 }
301}
302
303impl Payment {
304 pub fn new(transactions: Vec<Vec<u8>>) -> Self {
306 Self {
307 transactions,
308 refund_to: None,
309 merchant_data: None,
310 memo: None,
311 }
312 }
313
314 pub fn with_refund_to(mut self, outputs: Vec<PaymentOutput>) -> Self {
316 self.refund_to = Some(outputs);
317 self
318 }
319
320 pub fn validate_refund_addresses(
322 &self,
323 authorized_refunds: &[SignedRefundAddress],
324 ) -> Result<(), Bip70Error> {
325 if let Some(ref refund_to) = self.refund_to {
326 for refund_addr in refund_to {
327 let is_authorized = authorized_refunds.iter().any(|auth| {
329 auth.address.script == refund_addr.script
330 && auth.address.amount == refund_addr.amount
331 });
332
333 if !is_authorized {
334 return Err(Bip70Error::InvalidPayment(format!(
335 "Refund address not authorized: {:?}",
336 refund_addr.script
337 )));
338 }
339 }
340 }
341 Ok(())
342 }
343
344 pub fn with_merchant_data(mut self, data: Vec<u8>) -> Self {
346 self.merchant_data = Some(data);
347 self
348 }
349
350 pub fn with_memo(mut self, memo: String) -> Self {
352 self.memo = Some(memo);
353 self
354 }
355
356 pub fn validate(&self) -> Result<(), Bip70Error> {
358 if self.transactions.is_empty() {
359 return Err(Bip70Error::InvalidPayment("No transactions".to_string()));
360 }
361
362 Ok(())
363 }
364}
365
366#[derive(Debug, thiserror::Error)]
368pub enum Bip70Error {
369 #[error("Payment request expired")]
370 Expired,
371
372 #[error("Invalid payment request: {0}")]
373 InvalidRequest(String),
374
375 #[error("Invalid payment: {0}")]
376 InvalidPayment(String),
377
378 #[error("Certificate validation failed: {0}")]
379 CertificateError(String),
380
381 #[error("Signature verification failed: {0}")]
382 SignatureError(String),
383
384 #[error("HTTP error: {0}")]
385 HttpError(String),
386
387 #[error("Serialization error: {0}")]
388 SerializationError(String),
389
390 #[error("Validation error: {0}")]
391 ValidationError(String),
392}
393
394pub struct PaymentProtocolClient;
399
400impl PaymentProtocolClient {
401 pub fn validate_payment_request(
403 payment_request: &PaymentRequest,
404 expected_merchant_pubkey: Option<&[u8]>,
405 ) -> Result<(), Bip70Error> {
406 payment_request.verify_signature()?;
408
409 payment_request.validate()?;
411
412 if let Some(expected_pubkey) = expected_merchant_pubkey {
414 if let Some(ref req_pubkey) = payment_request.merchant_pubkey {
415 if req_pubkey.as_slice() != expected_pubkey {
416 return Err(Bip70Error::SignatureError(
417 "PaymentRequest pubkey mismatch".to_string(),
418 ));
419 }
420 }
421 }
422
423 Ok(())
424 }
425
426 pub fn validate_payment_ack(
428 payment_ack: &PaymentACK,
429 merchant_signature: &[u8],
430 merchant_pubkey: &[u8],
431 ) -> Result<(), Bip70Error> {
432 if let Some(ref sig) = payment_ack.signature {
433 if !sig.is_empty() {
434 return payment_ack.verify_signature(merchant_pubkey);
435 }
436 }
437 if !merchant_signature.is_empty() {
439 let mut ack = payment_ack.clone();
440 ack.signature = None;
441 let serialized = bincode::serialize(&ack)
442 .map_err(|e| Bip70Error::SerializationError(e.to_string()))?;
443 let hash: [u8; 32] = Sha256::digest(&serialized).into();
444 verify_sig(merchant_signature, merchant_pubkey, &hash)?;
445 }
446 Ok(())
447 }
448}
449
450pub struct PaymentProtocolServer;
455
456impl PaymentProtocolServer {
457 pub fn create_signed_payment_request(
459 details: PaymentDetails,
460 merchant_private_key: &[u8; 32],
461 authorized_refunds: Option<Vec<SignedRefundAddress>>,
462 ) -> Result<PaymentRequest, Bip70Error> {
463 let mut payment_request = PaymentRequest {
464 payment_details: details,
465 merchant_pubkey: None,
466 signature: None,
467 authorized_refund_addresses: authorized_refunds,
468 };
469 payment_request.sign(merchant_private_key)?;
470 Ok(payment_request)
471 }
472
473 pub fn process_payment(
475 payment: &Payment,
476 original_request: &PaymentRequest,
477 merchant_private_key: Option<&[u8; 32]>,
478 ) -> Result<PaymentACK, Bip70Error> {
479 payment.validate()?;
480 if let Some(ref authorized_refunds) = original_request.authorized_refund_addresses {
481 payment.validate_refund_addresses(authorized_refunds)?;
482 }
483 if let Some(ref pm_data) = payment.merchant_data {
484 if let Some(ref req_data) = original_request.payment_details.merchant_data {
485 if pm_data != req_data {
486 return Err(Bip70Error::ValidationError(
487 "Merchant data mismatch".to_string(),
488 ));
489 }
490 }
491 } else if original_request.payment_details.merchant_data.is_some() {
492 return Err(Bip70Error::ValidationError(
493 "Payment missing merchant data".to_string(),
494 ));
495 }
496 Self::verify_payment_transactions(payment, original_request)?;
497 let mut payment_ack = PaymentACK {
498 payment: payment.clone(),
499 memo: Some("Payment received".to_string()),
500 signature: None,
501 };
502 if let Some(private_key) = merchant_private_key {
503 payment_ack.sign(private_key)?;
504 }
505 Ok(payment_ack)
506 }
507
508 fn verify_payment_transactions(
510 payment: &Payment,
511 original_request: &PaymentRequest,
512 ) -> Result<(), Bip70Error> {
513 use crate::Transaction;
514
515 let mut all_outputs = Vec::new();
517 for tx_bytes in &payment.transactions {
518 let tx: Transaction = bincode::deserialize(tx_bytes)
519 .map_err(|e| Bip70Error::SerializationError(format!("Invalid transaction: {e}")))?;
520
521 for output in &tx.outputs {
523 all_outputs.push(PaymentOutput {
524 script: output.script_pubkey.clone(),
525 amount: Some(output.value as u64), });
527 }
528 }
529
530 for requested_output in &original_request.payment_details.outputs {
533 let found = all_outputs.iter().any(|output| {
534 output.script == requested_output.script
535 && match (output.amount, requested_output.amount) {
536 (Some(amt), Some(req_amt)) => amt >= req_amt, (Some(_), None) => true, (None, Some(_)) => false, (None, None) => true, }
541 });
542
543 if !found {
544 return Err(Bip70Error::ValidationError(format!(
545 "Payment missing required output: script={}, amount={:?}",
546 hex::encode(&requested_output.script),
547 requested_output.amount
548 )));
549 }
550 }
551
552 Ok(())
553 }
554
555 pub fn sign_refund_address(
557 address: PaymentOutput,
558 merchant_private_key: &[u8; 32],
559 ) -> Result<SignedRefundAddress, Bip70Error> {
560 let serialized = bincode::serialize(&address)
561 .map_err(|e| Bip70Error::SerializationError(e.to_string()))?;
562 let hash: [u8; 32] = Sha256::digest(&serialized).into();
563 Ok(SignedRefundAddress {
564 address,
565 signature: sign_compact(&hash, merchant_private_key)?,
566 })
567 }
568
569 pub fn verify_refund_address(
571 signed_refund: &SignedRefundAddress,
572 merchant_pubkey: &[u8],
573 ) -> Result<(), Bip70Error> {
574 let serialized = bincode::serialize(&signed_refund.address)
575 .map_err(|e| Bip70Error::SerializationError(e.to_string()))?;
576 let hash: [u8; 32] = Sha256::digest(&serialized).into();
577 verify_sig(&signed_refund.signature, merchant_pubkey, &hash).map_err(|_| {
578 Bip70Error::SignatureError("Refund address signature verification failed".to_string())
579 })
580 }
581}
582
583#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
587pub struct CovenantProof {
588 pub template_hash: Hash,
590 pub transaction_template: TransactionTemplate,
592 pub payment_request_id: String,
594 pub created_at: u64,
596 pub signature: Option<Vec<u8>>,
598}
599
600#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
602pub struct TransactionTemplate {
603 pub version: u32,
604 pub inputs: Vec<TemplateInput>,
605 pub outputs: Vec<TemplateOutput>,
606 pub lock_time: u32,
607}
608
609#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
611pub struct TemplateInput {
612 pub prevout_hash: Hash,
613 pub prevout_index: u32,
614 pub sequence: u32,
615}
616
617#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
619pub struct TemplateOutput {
620 pub value: u64,
621 pub script_pubkey: Vec<u8>,
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
626pub enum SettlementStatus {
627 ProofCreated,
629 ProofBroadcast,
631 InMempool { tx_hash: Hash },
633 Settled {
635 tx_hash: Hash,
636 block_hash: Hash,
637 confirmation_count: u32,
638 },
639 Failed { reason: String },
641}
642
643#[cfg(test)]
644mod tests {
645 use super::*;
646
647 #[test]
648 fn test_payment_request_creation() {
649 let output = PaymentOutput {
650 script: vec![blvm_consensus::opcodes::OP_1],
651 amount: Some(100000), };
653
654 let request = PaymentRequest::new("main".to_string(), vec![output], 1234567890);
655
656 assert_eq!(request.payment_details.network, "main");
657 assert_eq!(request.payment_details.outputs.len(), 1);
658 assert_eq!(request.payment_details.time, 1234567890);
659 }
660
661 #[test]
662 fn test_payment_request_validation() {
663 let request = PaymentRequest::new(
664 "main".to_string(),
665 vec![PaymentOutput {
666 script: vec![blvm_consensus::opcodes::OP_1],
667 amount: Some(100000),
668 }],
669 1234567890,
670 );
671
672 assert!(request.validate().is_ok());
673 }
674
675 #[test]
676 fn test_payment_request_expired() {
677 let expired_time = 1000;
678 let request = PaymentRequest::new(
679 "main".to_string(),
680 vec![PaymentOutput {
681 script: vec![blvm_consensus::opcodes::OP_1],
682 amount: Some(100000),
683 }],
684 expired_time,
685 )
686 .with_expires(1001);
687
688 let result = request.validate();
690 assert!(result.is_err());
691 }
692
693 #[test]
694 fn test_payment_creation() {
695 let tx = vec![0x01, 0x00, 0x00, 0x00]; let payment = Payment::new(vec![tx.clone()]);
697
698 assert_eq!(payment.transactions.len(), 1);
699 assert_eq!(payment.transactions[0], tx);
700 }
701
702 #[test]
703 fn test_payment_validation() {
704 let payment = Payment::new(vec![vec![0x01, 0x02, 0x03]]);
705 assert!(payment.validate().is_ok());
706
707 let empty_payment = Payment::new(vec![]);
708 assert!(empty_payment.validate().is_err());
709 }
710}