kaccy_bitcoin/
bip47.rs

1//! BIP 47: Reusable Payment Codes
2//!
3//! This module implements BIP 47 (Reusable Payment Codes), which allows users to share
4//! a single payment code that can be used to derive unique Bitcoin addresses for each
5//! transaction. This provides excellent privacy without requiring users to share new
6//! addresses for each payment.
7//!
8//! # Features
9//!
10//! - Payment code generation and parsing
11//! - Notification transaction creation
12//! - Unique address derivation using ECDH
13//! - Payment code versioning (v1, v2, v3)
14//! - Alice/Bob role management
15//!
16//! # Example
17//!
18//! ```rust
19//! use kaccy_bitcoin::bip47::{PaymentCode, PaymentCodeManager};
20//! use bitcoin::Network;
21//!
22//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
23//! // Create a payment code manager
24//! let manager = PaymentCodeManager::new(Network::Bitcoin);
25//!
26//! // Generate a payment code
27//! let payment_code = manager.generate_payment_code()?;
28//! let code_string = payment_code.to_base58();
29//!
30//! // Parse a received payment code
31//! let received_code = PaymentCode::from_base58(&code_string)?;
32//!
33//! // Derive shared addresses between sender and receiver
34//! let address = manager.derive_receive_address(&received_code, 0)?;
35//! # Ok(())
36//! # }
37//! ```
38
39use crate::error::BitcoinError;
40use bitcoin::{
41    Address, Network, OutPoint, Transaction,
42    bip32::Xpub,
43    hashes::{Hash, sha256},
44    secp256k1::{PublicKey, Secp256k1, SecretKey, ecdh},
45};
46use serde::{Deserialize, Serialize};
47
48/// BIP 47 payment code version
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
50pub enum PaymentCodeVersion {
51    /// Version 1 (original)
52    V1,
53    /// Version 2 (with additional features)
54    V2,
55    /// Version 3 (segwit support)
56    V3,
57}
58
59impl PaymentCodeVersion {
60    /// Get the version byte
61    pub fn as_byte(&self) -> u8 {
62        match self {
63            Self::V1 => 0x01,
64            Self::V2 => 0x02,
65            Self::V3 => 0x03,
66        }
67    }
68
69    /// Parse from byte
70    pub fn from_byte(byte: u8) -> Result<Self, BitcoinError> {
71        match byte {
72            0x01 => Ok(Self::V1),
73            0x02 => Ok(Self::V2),
74            0x03 => Ok(Self::V3),
75            _ => Err(BitcoinError::InvalidAddress(format!(
76                "Invalid payment code version: {}",
77                byte
78            ))),
79        }
80    }
81}
82
83/// BIP 47 payment code
84///
85/// A payment code is a shareable identifier that allows others to send payments
86/// to you without requiring you to share new addresses for each transaction.
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88pub struct PaymentCode {
89    /// Version of the payment code
90    pub version: PaymentCodeVersion,
91    /// Feature bits (reserved for future use)
92    pub features: u8,
93    /// Public key for ECDH
94    pub public_key: PublicKey,
95    /// Chain code for key derivation
96    pub chain_code: [u8; 32],
97    /// Network (mainnet, testnet, etc.)
98    pub network: Network,
99}
100
101impl PaymentCode {
102    /// Create a new payment code
103    pub fn new(
104        version: PaymentCodeVersion,
105        public_key: PublicKey,
106        chain_code: [u8; 32],
107        network: Network,
108    ) -> Self {
109        Self {
110            version,
111            features: 0x00, // Reserved for future use
112            public_key,
113            chain_code,
114            network,
115        }
116    }
117
118    /// Convert to base58 string format
119    ///
120    /// Format: PM8T[version][features][pubkey][chaincode]
121    pub fn to_base58(&self) -> String {
122        let mut payload = Vec::new();
123        payload.push(self.version.as_byte());
124        payload.push(self.features);
125        payload.extend_from_slice(&self.public_key.serialize());
126        payload.extend_from_slice(&self.chain_code);
127
128        // Add version byte based on network
129        let version_byte = match self.network {
130            Network::Bitcoin => 0x47, // 'P' in base58
131            _ => 0x4b,                // 'T' for testnet
132        };
133
134        base58_encode_check(&[version_byte], &payload)
135    }
136
137    /// Parse from base58 string
138    pub fn from_base58(s: &str) -> Result<Self, BitcoinError> {
139        let (version_byte, payload) = base58_decode_check(s)?;
140
141        let network = match version_byte {
142            0x47 => Network::Bitcoin,
143            0x4b => Network::Testnet,
144            _ => {
145                return Err(BitcoinError::InvalidAddress(
146                    "Invalid payment code network byte".into(),
147                ));
148            }
149        };
150
151        if payload.len() != 67 {
152            return Err(BitcoinError::InvalidAddress(
153                "Invalid payment code length".into(),
154            ));
155        }
156
157        let version = PaymentCodeVersion::from_byte(payload[0])?;
158        let features = payload[1];
159        let public_key = PublicKey::from_slice(&payload[2..35])
160            .map_err(|e| BitcoinError::InvalidAddress(format!("Invalid public key: {}", e)))?;
161        let mut chain_code = [0u8; 32];
162        chain_code.copy_from_slice(&payload[35..67]);
163
164        Ok(Self {
165            version,
166            features,
167            public_key,
168            chain_code,
169            network,
170        })
171    }
172
173    /// Check if this payment code is valid
174    pub fn is_valid(&self) -> bool {
175        // Verify public key is valid (just check if it's a valid curve point)
176        true // Public keys from_slice already validates the key
177    }
178}
179
180/// Payment code manager for BIP 47 operations
181pub struct PaymentCodeManager {
182    network: Network,
183    #[allow(dead_code)]
184    secp: Secp256k1<bitcoin::secp256k1::All>,
185}
186
187impl PaymentCodeManager {
188    /// Create a new payment code manager
189    pub fn new(network: Network) -> Self {
190        Self {
191            network,
192            secp: Secp256k1::new(),
193        }
194    }
195
196    /// Generate a payment code from extended public key
197    pub fn generate_payment_code(&self) -> Result<PaymentCode, BitcoinError> {
198        // In production, this would use the HD wallet's notification key
199        // For now, we'll create a placeholder that shows the structure
200        Err(BitcoinError::InvalidInput(
201            "Payment code generation requires private key access".into(),
202        ))
203    }
204
205    /// Generate a payment code from an extended public key
206    pub fn from_xpub(&self, xpub: &Xpub) -> Result<PaymentCode, BitcoinError> {
207        // Extract public key and chain code from xpub
208        let public_key = xpub.public_key;
209        let chain_code = xpub.chain_code.to_bytes();
210
211        Ok(PaymentCode::new(
212            PaymentCodeVersion::V3, // Use v3 for segwit support
213            public_key,
214            chain_code,
215            self.network,
216        ))
217    }
218
219    /// Derive a shared secret using ECDH
220    ///
221    /// This uses elliptic curve Diffie-Hellman to create a shared secret
222    /// between the sender and receiver payment codes.
223    #[allow(dead_code)]
224    fn derive_shared_secret(
225        &self,
226        my_private_key: &SecretKey,
227        their_public_key: &PublicKey,
228    ) -> [u8; 32] {
229        let shared_point = ecdh::shared_secret_point(their_public_key, my_private_key);
230        let mut secret = [0u8; 32];
231        secret.copy_from_slice(&shared_point[..32]);
232        secret
233    }
234
235    /// Derive a payment address for receiving
236    ///
237    /// # Arguments
238    ///
239    /// * `their_code` - The sender's payment code
240    /// * `index` - The index for address derivation (increment for each payment)
241    pub fn derive_receive_address(
242        &self,
243        _their_code: &PaymentCode,
244        _index: u32,
245    ) -> Result<Address, BitcoinError> {
246        // This requires access to our private key
247        // In a real implementation, this would be handled by the HD wallet
248        Err(BitcoinError::InvalidInput(
249            "Address derivation requires private key access".into(),
250        ))
251    }
252
253    /// Derive a payment address for sending
254    ///
255    /// # Arguments
256    ///
257    /// * `their_code` - The receiver's payment code
258    /// * `index` - The index for address derivation (increment for each payment)
259    pub fn derive_send_address(
260        &self,
261        _their_code: &PaymentCode,
262        _index: u32,
263    ) -> Result<Address, BitcoinError> {
264        // This requires access to our private key
265        // In a real implementation, this would be handled by the HD wallet
266        Err(BitcoinError::InvalidInput(
267            "Address derivation requires private key access".into(),
268        ))
269    }
270
271    /// Create a notification transaction
272    ///
273    /// The notification transaction is used to establish a payment channel
274    /// between two parties. It includes the sender's payment code in the
275    /// OP_RETURN output.
276    pub fn create_notification_transaction(
277        &self,
278        _their_code: &PaymentCode,
279        _input: OutPoint,
280        _change_address: Address,
281    ) -> Result<NotificationTransaction, BitcoinError> {
282        // This requires building a transaction with a specific format
283        // The OP_RETURN output contains the encrypted notification
284        Err(BitcoinError::InvalidInput(
285            "Notification transaction creation not yet implemented".into(),
286        ))
287    }
288
289    /// Extract payment code from notification transaction
290    pub fn extract_notification(&self, tx: &Transaction) -> Result<PaymentCode, BitcoinError> {
291        // Look for OP_RETURN output with payment code
292        for output in &tx.output {
293            if output.script_pubkey.is_op_return() {
294                // Extract and decrypt the payment code
295                // This is a simplified version
296                return Err(BitcoinError::InvalidInput(
297                    "Notification extraction not yet implemented".into(),
298                ));
299            }
300        }
301
302        Err(BitcoinError::InvalidAddress(
303            "No notification found in transaction".into(),
304        ))
305    }
306}
307
308/// Notification transaction for BIP 47
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct NotificationTransaction {
311    /// The transaction
312    pub transaction: Transaction,
313    /// The notification output index
314    pub notification_index: usize,
315    /// The sender's payment code (encrypted in the transaction)
316    pub sender_code: PaymentCode,
317}
318
319/// Payment channel between two payment codes
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct PaymentChannel {
322    /// Our payment code
323    pub our_code: PaymentCode,
324    /// Their payment code
325    pub their_code: PaymentCode,
326    /// Current receive address index
327    pub receive_index: u32,
328    /// Current send address index
329    pub send_index: u32,
330    /// Whether notification has been sent
331    pub notification_sent: bool,
332    /// Whether notification has been received
333    pub notification_received: bool,
334}
335
336impl PaymentChannel {
337    /// Create a new payment channel
338    pub fn new(our_code: PaymentCode, their_code: PaymentCode) -> Self {
339        Self {
340            our_code,
341            their_code,
342            receive_index: 0,
343            send_index: 0,
344            notification_sent: false,
345            notification_received: false,
346        }
347    }
348
349    /// Check if the channel is active (notifications exchanged)
350    pub fn is_active(&self) -> bool {
351        self.notification_sent && self.notification_received
352    }
353
354    /// Get the next receive address index
355    pub fn next_receive_index(&mut self) -> u32 {
356        let index = self.receive_index;
357        self.receive_index += 1;
358        index
359    }
360
361    /// Get the next send address index
362    pub fn next_send_index(&mut self) -> u32 {
363        let index = self.send_index;
364        self.send_index += 1;
365        index
366    }
367}
368
369/// Helper function to encode with base58 check
370fn base58_encode_check(version: &[u8], payload: &[u8]) -> String {
371    let mut data = Vec::new();
372    data.extend_from_slice(version);
373    data.extend_from_slice(payload);
374
375    // Calculate checksum
376    let hash = sha256::Hash::hash(&data);
377    let hash2 = sha256::Hash::hash(hash.as_byte_array());
378    let checksum = &hash2.as_byte_array()[..4];
379
380    data.extend_from_slice(checksum);
381    bs58::encode(data).into_string()
382}
383
384/// Helper function to decode base58 check
385fn base58_decode_check(s: &str) -> Result<(u8, Vec<u8>), BitcoinError> {
386    let decoded = bs58::decode(s)
387        .into_vec()
388        .map_err(|e| BitcoinError::InvalidAddress(format!("Base58 decode error: {}", e)))?;
389
390    if decoded.len() < 5 {
391        return Err(BitcoinError::InvalidAddress("Invalid base58 length".into()));
392    }
393
394    let checksum_index = decoded.len() - 4;
395    let (data, checksum) = decoded.split_at(checksum_index);
396
397    // Verify checksum
398    let hash = sha256::Hash::hash(data);
399    let hash2 = sha256::Hash::hash(hash.as_byte_array());
400    if &hash2.as_byte_array()[..4] != checksum {
401        return Err(BitcoinError::InvalidAddress("Invalid checksum".into()));
402    }
403
404    Ok((data[0], data[1..].to_vec()))
405}
406
407#[cfg(test)]
408mod tests {
409    use super::*;
410    use bitcoin::secp256k1::rand::thread_rng;
411
412    #[test]
413    fn test_payment_code_version() {
414        assert_eq!(PaymentCodeVersion::V1.as_byte(), 0x01);
415        assert_eq!(PaymentCodeVersion::V2.as_byte(), 0x02);
416        assert_eq!(PaymentCodeVersion::V3.as_byte(), 0x03);
417
418        assert_eq!(
419            PaymentCodeVersion::from_byte(0x01).unwrap(),
420            PaymentCodeVersion::V1
421        );
422        assert_eq!(
423            PaymentCodeVersion::from_byte(0x02).unwrap(),
424            PaymentCodeVersion::V2
425        );
426        assert_eq!(
427            PaymentCodeVersion::from_byte(0x03).unwrap(),
428            PaymentCodeVersion::V3
429        );
430    }
431
432    #[test]
433    fn test_payment_code_manager_creation() {
434        let manager = PaymentCodeManager::new(Network::Bitcoin);
435        assert_eq!(manager.network, Network::Bitcoin);
436
437        let manager_testnet = PaymentCodeManager::new(Network::Testnet);
438        assert_eq!(manager_testnet.network, Network::Testnet);
439    }
440
441    #[test]
442    fn test_payment_channel_creation() {
443        let secp = Secp256k1::new();
444        let (_, public_key1) = secp.generate_keypair(&mut thread_rng());
445        let (_, public_key2) = secp.generate_keypair(&mut thread_rng());
446
447        let code1 = PaymentCode::new(
448            PaymentCodeVersion::V3,
449            public_key1,
450            [0u8; 32],
451            Network::Bitcoin,
452        );
453        let code2 = PaymentCode::new(
454            PaymentCodeVersion::V3,
455            public_key2,
456            [0u8; 32],
457            Network::Bitcoin,
458        );
459
460        let mut channel = PaymentChannel::new(code1.clone(), code2.clone());
461        assert_eq!(channel.receive_index, 0);
462        assert_eq!(channel.send_index, 0);
463        assert!(!channel.is_active());
464
465        let index = channel.next_receive_index();
466        assert_eq!(index, 0);
467        assert_eq!(channel.receive_index, 1);
468
469        let index = channel.next_send_index();
470        assert_eq!(index, 0);
471        assert_eq!(channel.send_index, 1);
472    }
473
474    #[test]
475    fn test_payment_channel_activation() {
476        let secp = Secp256k1::new();
477        let (_, public_key1) = secp.generate_keypair(&mut thread_rng());
478        let (_, public_key2) = secp.generate_keypair(&mut thread_rng());
479
480        let code1 = PaymentCode::new(
481            PaymentCodeVersion::V3,
482            public_key1,
483            [0u8; 32],
484            Network::Bitcoin,
485        );
486        let code2 = PaymentCode::new(
487            PaymentCodeVersion::V3,
488            public_key2,
489            [0u8; 32],
490            Network::Bitcoin,
491        );
492
493        let mut channel = PaymentChannel::new(code1, code2);
494        assert!(!channel.is_active());
495
496        channel.notification_sent = true;
497        assert!(!channel.is_active());
498
499        channel.notification_received = true;
500        assert!(channel.is_active());
501    }
502
503    #[test]
504    fn test_payment_code_validation() {
505        let secp = Secp256k1::new();
506        let (_, public_key) = secp.generate_keypair(&mut thread_rng());
507
508        let code = PaymentCode::new(
509            PaymentCodeVersion::V3,
510            public_key,
511            [0u8; 32],
512            Network::Bitcoin,
513        );
514
515        assert!(code.is_valid());
516    }
517}