1use std::str::FromStr;
6
7use serde::{Deserialize, Serialize};
8
9use super::nut01::PublicKey;
10use crate::{nut11, nut14};
11
12pub mod spending_conditions;
13pub use spending_conditions::{Conditions, SpendingConditions};
14
15pub mod secret;
16pub use secret::Secret;
17
18pub mod error;
19pub use error::Error;
20
21pub mod tag;
22pub use tag::{Tag, TagKind};
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub(crate) struct RefundPath {
27 pub pubkeys: Vec<PublicKey>,
29 pub required_sigs: u64,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
38pub(crate) struct SpendingRequirements {
39 pub preimage_needed: bool,
41 pub pubkeys: Vec<PublicKey>,
43 pub required_sigs: u64,
45 pub refund_path: Option<RefundPath>,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
52pub enum Kind {
53 P2PK,
55 HTLC,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
61pub struct SecretData {
62 nonce: String,
64 data: String,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 tags: Option<Vec<Vec<String>>>,
69}
70
71impl SecretData {
72 pub fn new<S, V>(data: S, tags: Option<V>) -> Self
74 where
75 S: Into<String>,
76 V: Into<Vec<Vec<String>>>,
77 {
78 let nonce = crate::secret::Secret::generate().to_string();
79
80 Self {
81 nonce,
82 data: data.into(),
83 tags: tags.map(Into::into),
84 }
85 }
86
87 pub fn nonce(&self) -> &str {
89 &self.nonce
90 }
91
92 pub fn data(&self) -> &str {
94 &self.data
95 }
96
97 pub fn tags(&self) -> Option<&Vec<Vec<String>>> {
99 self.tags.as_ref()
100 }
101}
102
103fn check_duplicate_pubkeys(pubkeys: &[PublicKey]) -> Result<(), Error> {
104 let mut x_coords = std::collections::HashSet::with_capacity(pubkeys.len());
105 for pk in pubkeys {
106 if !x_coords.insert(pk.x_only_public_key().serialize()) {
107 return Err(Error::NUT11(crate::nuts::nut11::Error::DuplicatePubkey));
108 }
109 }
110 Ok(())
111}
112
113pub(crate) fn get_pubkeys_and_required_sigs(
134 secret: &Secret,
135 current_time: u64,
136) -> Result<SpendingRequirements, Error> {
137 debug_assert!(
138 secret.kind() == Kind::P2PK || secret.kind() == Kind::HTLC,
139 "get_pubkeys_and_required_sigs called with invalid kind - this is a bug"
140 );
141
142 let conditions: Conditions = secret
143 .secret_data()
144 .tags()
145 .cloned()
146 .unwrap_or_default()
147 .try_into()?;
148
149 let locktime_passed = conditions
151 .locktime
152 .map(|locktime| locktime < current_time)
153 .unwrap_or(false);
154
155 match secret.kind() {
156 Kind::P2PK => {
157 let mut primary_keys = vec![];
164
165 let data_pubkey = PublicKey::from_str(secret.secret_data().data())?;
167 primary_keys.push(data_pubkey);
168
169 if let Some(additional_keys) = &conditions.pubkeys {
171 primary_keys.extend(additional_keys.clone());
172 }
173
174 check_duplicate_pubkeys(&primary_keys)?;
175
176 let primary_num_sigs_required = conditions.num_sigs.unwrap_or(1);
177
178 let refund_path = if locktime_passed {
180 if let Some(refund_keys) = &conditions.refund_keys {
181 check_duplicate_pubkeys(refund_keys)?;
182 Some(RefundPath {
183 pubkeys: refund_keys.clone(),
184 required_sigs: conditions.num_sigs_refund.unwrap_or(1),
185 })
186 } else {
187 Some(RefundPath {
189 pubkeys: vec![],
190 required_sigs: 0,
191 })
192 }
193 } else {
194 None
195 };
196
197 Ok(SpendingRequirements {
198 preimage_needed: false,
199 pubkeys: primary_keys,
200 required_sigs: primary_num_sigs_required,
201 refund_path,
202 })
203 }
204 Kind::HTLC => {
205 let pubkeys = conditions.pubkeys.clone().unwrap_or_default();
208
209 if !pubkeys.is_empty() {
210 check_duplicate_pubkeys(&pubkeys)?;
211 }
212
213 let required_sigs = if pubkeys.is_empty() {
214 0
215 } else {
216 conditions.num_sigs.unwrap_or(1)
217 };
218
219 let refund_path = if locktime_passed {
221 if let Some(refund_keys) = &conditions.refund_keys {
222 check_duplicate_pubkeys(refund_keys)?;
223 Some(RefundPath {
224 pubkeys: refund_keys.clone(),
225 required_sigs: conditions.num_sigs_refund.unwrap_or(1),
226 })
227 } else {
228 Some(RefundPath {
230 pubkeys: vec![],
231 required_sigs: 0,
232 })
233 }
234 } else {
235 None
236 };
237
238 Ok(SpendingRequirements {
239 preimage_needed: true,
240 pubkeys,
241 required_sigs,
242 refund_path,
243 })
244 }
245 }
246}
247
248use super::Proofs;
249
250pub trait SpendingConditionVerification {
252 fn inputs(&self) -> &Proofs;
254
255 fn sig_all_msg_to_sign(&self) -> String;
261
262 fn has_at_least_one_sig_all(&self) -> Result<bool, Error> {
267 for proof in self.inputs() {
268 if let Ok(spending_conditions) = super::SpendingConditions::try_from(&proof.secret) {
270 let has_sig_all = match spending_conditions {
272 super::SpendingConditions::P2PKConditions { conditions, .. } => conditions
273 .map(|c| c.sig_flag == super::SigFlag::SigAll)
274 .unwrap_or(false),
275 super::SpendingConditions::HTLCConditions { conditions, .. } => conditions
276 .map(|c| c.sig_flag == super::SigFlag::SigAll)
277 .unwrap_or(false),
278 };
279
280 if has_sig_all {
281 return Ok(true);
282 }
283 } else if proof.witness.is_some() {
284 return Err(Error::NUT11(nut11::Error::IncorrectWitnessKind));
285 }
286 }
287
288 Ok(false)
289 }
290
291 fn verify_all_inputs_match_for_sig_all(&self) -> Result<(), Error> {
299 let inputs = self.inputs();
300
301 let first_input = inputs.first().ok_or(Error::SpendConditionsNotMet)?;
303 let first_secret = Secret::try_from(&first_input.secret)?;
304 let first_kind = first_secret.kind();
305 let first_data = first_secret.secret_data().data();
306 let first_tags = first_secret.secret_data().tags();
307
308 let first_conditions =
310 super::Conditions::try_from(first_tags.cloned().unwrap_or_default())?;
311
312 if first_conditions.sig_flag != super::SigFlag::SigAll {
314 return Err(Error::SpendConditionsNotMet);
315 }
316
317 for proof in inputs.iter().skip(1) {
319 let secret = Secret::try_from(&proof.secret)?;
320
321 if secret.kind() != first_kind {
323 return Err(Error::SpendConditionsNotMet);
324 }
325
326 if secret.secret_data().data() != first_data {
328 return Err(Error::SpendConditionsNotMet);
329 }
330
331 if secret.secret_data().tags() != first_tags {
333 return Err(Error::SpendConditionsNotMet);
334 }
335 }
336
337 Ok(())
338 }
339
340 fn verify_spending_conditions(&self) -> Result<(), Error> {
345 if self.has_at_least_one_sig_all()? {
347 self.verify_full_sig_all_check()
349 } else {
350 self.verify_inputs_individually()
354 }
355 }
356
357 fn verify_full_sig_all_check(&self) -> Result<(), Error> {
361 debug_assert!(
362 self.has_at_least_one_sig_all()?,
363 "verify_full_sig_all_check() called on proofs without SIG_ALL. This shouldn't happen"
364 );
365 self.verify_all_inputs_match_for_sig_all()?;
368
369 let first_input = self.inputs().first().ok_or(Error::SpendConditionsNotMet)?;
371 let first_secret =
372 Secret::try_from(&first_input.secret).map_err(|_| Error::IncorrectSecretKind)?;
373
374 match first_secret.kind() {
376 Kind::P2PK => {
377 nut11::verify_sig_all_p2pk(first_input, self.sig_all_msg_to_sign())?;
378 }
379 Kind::HTLC => {
380 nut14::verify_sig_all_htlc(first_input, self.sig_all_msg_to_sign())?;
381 }
382 }
383
384 Ok(())
385 }
386
387 fn verify_inputs_individually(&self) -> Result<(), Error> {
393 debug_assert!(
394 !(self.has_at_least_one_sig_all()?),
395 "verify_inputs_individually() called on SIG_ALL. This shouldn't happen"
396 );
397 for proof in self.inputs() {
398 if let Ok(secret) = Secret::try_from(&proof.secret) {
400 if let Ok(conditions) = super::Conditions::try_from(
402 secret.secret_data().tags().cloned().unwrap_or_default(),
403 ) {
404 debug_assert!(
405 conditions.sig_flag != super::SigFlag::SigAll,
406 "verify_inputs_individually called with SIG_ALL proof - this is a bug"
407 );
408 }
409
410 match secret.kind() {
411 Kind::P2PK => {
412 proof.verify_p2pk()?;
413 }
414 Kind::HTLC => {
415 proof.verify_htlc()?;
416 }
417 }
418 }
419 }
421 Ok(())
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use std::assert_eq;
428 use std::str::FromStr;
429
430 use super::*;
431
432 #[test]
433 fn test_secret_serialize() {
434 let secret_data = SecretData::new(
435 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198".to_string(),
436 Some(vec![vec![
437 "key".to_string(),
438 "value1".to_string(),
439 "value2".to_string(),
440 ]]),
441 );
442
443 let secret = Secret::new(Kind::P2PK, secret_data.clone());
444
445 let secret_str = format!(
446 r#"["P2PK",{{"nonce":"{}","data":"026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198","tags":[["key","value1","value2"]]}}]"#,
447 secret_data.nonce(),
448 );
449
450 assert_eq!(serde_json::to_string(&secret).unwrap(), secret_str);
451 }
452
453 #[test]
454 fn test_secret_round_trip_serialization() {
455 let original_secret = Secret::new(
457 Kind::P2PK,
458 SecretData::new(
459 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198".to_string(),
460 None::<Vec<Vec<String>>>,
461 ),
462 );
463
464 let serialized = serde_json::to_string(&original_secret).unwrap();
466
467 let deserialized_secret: Secret = serde_json::from_str(&serialized).unwrap();
469
470 assert_eq!(original_secret, deserialized_secret);
472
473 let cashu_secret = crate::secret::Secret::from_str(&serialized).unwrap();
475 let deserialized_from_cashu: Secret = TryFrom::try_from(&cashu_secret).unwrap();
476 assert_eq!(original_secret, deserialized_from_cashu);
477 }
478
479 #[test]
480 fn test_htlc_secret_round_trip() {
481 let payment_hash = "5c23fc3aec9d985bd5fc88ca8bceaccc52cf892715dd94b42b84f1b43350751e";
486
487 let secret_data = SecretData::new(payment_hash.to_string(), None::<Vec<Vec<String>>>);
489
490 let original_secret = Secret::new(Kind::HTLC, secret_data.clone());
491
492 let serialized = serde_json::to_string(&original_secret).unwrap();
494
495 let expected_json = format!(
497 r#"["HTLC",{{"nonce":"{}","data":"{}"}}]"#,
498 secret_data.nonce(),
499 payment_hash
500 );
501 assert_eq!(serialized, expected_json);
502
503 let deserialized_secret: Secret = serde_json::from_str(&serialized).unwrap();
505
506 assert_eq!(original_secret, deserialized_secret);
508 assert_eq!(deserialized_secret.kind(), Kind::HTLC);
509 assert_eq!(deserialized_secret.secret_data().data, payment_hash);
510 }
511}