Skip to main content

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