breez_sdk_spark/signer/
rtsync.rs

1use anyhow::anyhow;
2use bitcoin::bip32::DerivationPath;
3use breez_sdk_common::sync::SyncSigner;
4use std::sync::Arc;
5
6use crate::{Network, signer::BreezSigner};
7
8const SIGNING_DERIVATION_PATH: &str = "m/1220588449'/0'/0'/0/0";
9const SIGNING_DERIVATION_PATH_TEST: &str = "m/1220588449'/1'/0'/0/0";
10const ENCRYPTION_DERIVATION_PATH: &str = "m/1782705014'/0'/0'/0/0";
11const ENCRYPTION_DERIVATION_PATH_TEST: &str = "m/1782705014'/1'/0'/0/0";
12
13pub struct RTSyncSigner {
14    signer: Arc<dyn BreezSigner>,
15    signing_path: DerivationPath,
16    encryption_path: DerivationPath,
17}
18
19impl RTSyncSigner {
20    pub fn new(
21        signer: Arc<dyn BreezSigner>,
22        network: Network,
23    ) -> Result<Self, bitcoin::bip32::Error> {
24        let signing_path: DerivationPath = match network {
25            Network::Mainnet => SIGNING_DERIVATION_PATH,
26            Network::Regtest => SIGNING_DERIVATION_PATH_TEST,
27        }
28        .parse()?;
29        let encryption_path: DerivationPath = match network {
30            Network::Mainnet => ENCRYPTION_DERIVATION_PATH,
31            Network::Regtest => ENCRYPTION_DERIVATION_PATH_TEST,
32        }
33        .parse()?;
34
35        Ok(Self {
36            signer,
37            signing_path,
38            encryption_path,
39        })
40    }
41}
42
43#[macros::async_trait]
44impl SyncSigner for RTSyncSigner {
45    async fn sign_ecdsa_recoverable(&self, data: &[u8]) -> anyhow::Result<Vec<u8>> {
46        use bitcoin::hashes::{Hash, sha256};
47        use bitcoin::secp256k1::Message;
48
49        // Real-time sync requires double SHA256 hash
50        let hash = sha256::Hash::hash(sha256::Hash::hash(data).as_ref());
51        let message = Message::from_digest(hash.to_byte_array());
52        let sig = self
53            .signer
54            .sign_ecdsa_recoverable(message, &self.signing_path)
55            .await
56            .map_err(|e| anyhow!(e.to_string()))?;
57
58        // Serialize the recoverable signature: recovery_id + 64 bytes
59        let (recovery_id, sig_bytes) = sig.serialize_compact();
60        let mut complete_signature = vec![31u8.saturating_add(
61            u8::try_from(recovery_id.to_i32()).map_err(|e| anyhow!(e.to_string()))?,
62        )];
63        complete_signature.extend_from_slice(&sig_bytes);
64        Ok(complete_signature)
65    }
66
67    async fn encrypt_ecies(&self, msg: Vec<u8>) -> anyhow::Result<Vec<u8>> {
68        self.signer
69            .encrypt_ecies(&msg, &self.encryption_path)
70            .await
71            .map_err(|e| anyhow!(e.to_string()))
72    }
73
74    async fn decrypt_ecies(&self, msg: Vec<u8>) -> anyhow::Result<Vec<u8>> {
75        self.signer
76            .decrypt_ecies(&msg, &self.encryption_path)
77            .await
78            .map_err(|e| anyhow!(e.to_string()))
79    }
80}