lightspark_remote_signing/
handler.rs

1use lightspark::{
2    objects::{
3        id_and_signature::IdAndSignature, remote_signing_sub_event_type::RemoteSigningSubEventType,
4        webhook_event_type::WebhookEventType,
5    },
6    webhooks::WebhookEvent,
7};
8use log::info;
9use serde::Deserialize;
10use serde_json::from_value;
11
12use crate::{response::Response, signer::LightsparkSigner, validation::Validation, Error};
13
14/// A handler for lightspark remote signing webhook events.
15pub struct Handler {
16    signer: LightsparkSigner,
17    validator: Box<dyn Validation>,
18}
19
20impl Handler {
21    /// Create a new handler.
22    /// # Arguments
23    ///
24    /// * `signer` - A LightsparkSigner instance, which will be used to sign messages.
25    /// * `validator` - A Validation instance, which will be used to determine whether to sign messages.
26    pub fn new(signer: LightsparkSigner, validator: Box<dyn Validation>) -> Self {
27        Self { signer, validator }
28    }
29
30    pub fn handle_remote_signing_webhook_msg(
31        &self,
32        event: &WebhookEvent,
33    ) -> Result<Option<Response>, Error> {
34        if !matches!(event.event_type, WebhookEventType::RemoteSigning) {
35            return Err(Error::WebhookEventNotRemoteSigning);
36        }
37
38        let data = &event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
39        let sub_type: RemoteSigningSubEventType = from_value(data["sub_event_type"].clone())
40            .map_err(|_| Error::WebhookEventDataMissing)?;
41        println!("handler for sub_type: {:?}", sub_type.to_string());
42        let event_json =
43            serde_json::to_string(&event).expect("Serialize event to json should not fail");
44        if !self.validator.should_sign(event_json) {
45            self.handle_decline_to_sign_messages(event).map(Some)
46        } else {
47            match sub_type {
48                RemoteSigningSubEventType::Ecdh => self.handle_ecdh(event).map(Some),
49                RemoteSigningSubEventType::SignInvoice => self.handle_sign_invoice(event).map(Some),
50                RemoteSigningSubEventType::ReleasePaymentPreimage => {
51                    self.handle_release_payment_preimage(event).map(Some)
52                }
53                RemoteSigningSubEventType::GetPerCommitmentPoint => {
54                    self.handle_get_per_commitment_point(event).map(Some)
55                }
56                RemoteSigningSubEventType::ReleasePerCommitmentSecret => {
57                    self.handle_release_per_commitment_secret(event).map(Some)
58                }
59                RemoteSigningSubEventType::DeriveKeyAndSign => {
60                    self.handle_derive_key_and_sign(event).map(Some)
61                }
62                RemoteSigningSubEventType::RequestInvoicePaymentHash => {
63                    self.handle_request_invoice_payment_hash(event).map(Some)
64                }
65                RemoteSigningSubEventType::RevealCounterpartyPerCommitmentSecret => Ok(None),
66            }
67        }
68    }
69
70    pub fn handle_request_invoice_payment_hash(
71        &self,
72        event: &WebhookEvent,
73    ) -> Result<Response, Error> {
74        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
75        let invoice_id = data["invoice_id"]
76            .as_str()
77            .ok_or(Error::WebhookEventDataMissing)?;
78        let nonce = self.signer.generate_preimage_nonce();
79        let nonce_str = hex::encode(&nonce);
80
81        let payment_hash = self
82            .signer
83            .generate_preimage_hash(nonce)
84            .map_err(Error::SignerError)?;
85        let payment_hash_str = hex::encode(payment_hash);
86        Ok(Response::set_invoice_payment_hash_response(
87            invoice_id,
88            &payment_hash_str,
89            &nonce_str,
90        ))
91    }
92
93    pub fn handle_decline_to_sign_messages(&self, event: &WebhookEvent) -> Result<Response, Error> {
94        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
95
96        let signing_jobs: Vec<SigningJob> = serde_json::from_value(data["signing_jobs"].clone())
97            .map_err(|_| Error::WebhookEventDataMissing)?;
98
99        let payload_ids: Vec<String> = signing_jobs.iter().map(|job| job.id.clone()).collect();
100        Ok(Response::decline_to_sign_message_response(&payload_ids))
101    }
102
103    pub fn handle_ecdh(&self, event: &WebhookEvent) -> Result<Response, Error> {
104        info!("Handling ECDH webhook event");
105        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
106        let node_id = &event.entity_id;
107        let public_key = data["peer_public_key"]
108            .as_str()
109            .ok_or(Error::WebhookEventDataMissing)?;
110        let public_key_bytes = hex::decode(public_key).map_err(Error::PublicKeyDecodeError)?;
111        let ss = self
112            .signer
113            .ecdh(public_key_bytes.to_vec())
114            .map_err(Error::SignerError)?;
115        let ss_str = hex::encode(ss);
116        Ok(Response::ecdh_response(node_id, &ss_str))
117    }
118
119    pub fn handle_sign_invoice(&self, event: &WebhookEvent) -> Result<Response, Error> {
120        info!("Handling sign invoice webhook event");
121        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
122        let invoice_id = data["invoice_id"]
123            .as_str()
124            .ok_or(Error::WebhookEventDataMissing)?;
125        let invoice_hash = data["payreq_hash"]
126            .as_str()
127            .ok_or(Error::WebhookEventDataMissing)?;
128        let invoice_hash_bytes = hex::decode(invoice_hash).map_err(|_| Error::HexEncodingError)?;
129        let signature = self
130            .signer
131            .sign_invoice_hash(invoice_hash_bytes)
132            .map_err(Error::SignerError)?;
133        Ok(Response::sign_invoice_response(
134            invoice_id,
135            hex::encode(signature.get_signature()).as_str(),
136            signature.get_recovery_id(),
137        ))
138    }
139
140    pub fn handle_release_payment_preimage(&self, event: &WebhookEvent) -> Result<Response, Error> {
141        info!("Handling release payment preimage webhook event");
142        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
143        let nonce = data["preimage_nonce"]
144            .as_str()
145            .ok_or(Error::WebhookEventDataMissing)?;
146        let invoice_id = data["invoice_id"]
147            .as_str()
148            .ok_or(Error::WebhookEventDataMissing)?;
149
150        let nonce_bytes = hex::decode(nonce).map_err(|_| Error::HexEncodingError)?;
151        let preimage = self
152            .signer
153            .generate_preimage(nonce_bytes)
154            .map_err(Error::SignerError)?;
155
156        Ok(Response::release_payment_preimage_response(
157            invoice_id,
158            hex::encode(preimage).as_str(),
159        ))
160    }
161
162    pub fn handle_get_per_commitment_point(&self, event: &WebhookEvent) -> Result<Response, Error> {
163        info!("Handling get per commitment point webhook event");
164        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
165        let per_commitment_point_idx = data["per_commitment_point_idx"]
166            .as_u64()
167            .ok_or(Error::WebhookEventDataMissing)?;
168
169        let derivation_path = data["derivation_path"]
170            .as_str()
171            .ok_or(Error::WebhookEventDataMissing)?;
172
173        let channel_id = &event.entity_id;
174
175        let per_commitment_point = self
176            .signer
177            .get_per_commitment_point(derivation_path.to_string(), per_commitment_point_idx)
178            .map_err(Error::SignerError)?;
179
180        let commitment_point_str = hex::encode(per_commitment_point);
181        Ok(Response::get_channel_per_commitment_response(
182            channel_id,
183            commitment_point_str.as_str(),
184            per_commitment_point_idx,
185        ))
186    }
187
188    pub fn handle_release_per_commitment_secret(
189        &self,
190        event: &WebhookEvent,
191    ) -> Result<Response, Error> {
192        info!("Handling release per commitment secret webhook event");
193        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
194        let per_commitment_point_idx = data["per_commitment_point_idx"]
195            .as_u64()
196            .ok_or(Error::WebhookEventDataMissing)?;
197
198        let derivation_path = data["derivation_path"]
199            .as_str()
200            .ok_or(Error::WebhookEventDataMissing)?;
201
202        let channel_id = &event.entity_id;
203        let commitment_secret = self
204            .signer
205            .release_per_commitment_secret(derivation_path.to_string(), per_commitment_point_idx)
206            .map_err(Error::SignerError)?;
207
208        let commitment_secret_str = hex::encode(commitment_secret);
209
210        Ok(Response::release_channel_per_commitment_secret_response(
211            channel_id,
212            &commitment_secret_str,
213            per_commitment_point_idx as i64,
214        ))
215    }
216
217    pub fn handle_derive_key_and_sign(&self, event: &WebhookEvent) -> Result<Response, Error> {
218        info!("Handling derive key and sign webhook event");
219        let data = event.data.as_ref().ok_or(Error::WebhookEventDataMissing)?;
220
221        let signing_jobs: Vec<SigningJob> = serde_json::from_value(data["signing_jobs"].clone())
222            .map_err(|_| Error::WebhookEventDataMissing)?;
223
224        let mut signatures: Vec<IdAndSignature> = vec![];
225        for signing_job in signing_jobs {
226            let signature = self
227                .signer
228                .derive_key_and_sign(
229                    hex::decode(signing_job.message).map_err(|_| Error::HexEncodingError)?,
230                    signing_job.derivation_path,
231                    signing_job
232                        .add_tweak
233                        .map(|tweak| hex::decode(tweak).map_err(|_| Error::HexEncodingError))
234                        .transpose()?,
235                    signing_job
236                        .mul_tweak
237                        .map(|tweak| hex::decode(tweak).map_err(|_| Error::HexEncodingError))
238                        .transpose()?,
239                )
240                .map_err(Error::SignerError)?;
241
242            signatures.push(IdAndSignature {
243                id: signing_job.id,
244                signature: hex::encode(signature),
245            });
246        }
247        Ok(Response::sign_messages_response(signatures))
248    }
249}
250
251#[derive(Clone, Deserialize, Debug)]
252struct SigningJob {
253    id: String,
254    derivation_path: String,
255    message: String,
256    add_tweak: Option<String>,
257    mul_tweak: Option<String>,
258}