1use std::collections::{HashMap, HashSet};
6use std::str::FromStr;
7use std::{fmt, vec};
8
9use bitcoin::hashes::sha256::Hash as Sha256Hash;
10use bitcoin::hashes::Hash;
11use bitcoin::secp256k1::schnorr::Signature;
12use serde::de::Error as DeserializerError;
13use serde::ser::SerializeSeq;
14use serde::{Deserialize, Deserializer, Serialize, Serializer};
15use thiserror::Error;
16
17use super::nut00::Witness;
18use super::nut01::PublicKey;
19use super::{Kind, Nut10Secret, Proof, Proofs, SecretKey};
20use crate::ensure_cdk;
21use crate::nuts::nut00::BlindedMessage;
22use crate::secret::Secret;
23use crate::util::{hex, unix_time};
24
25pub mod serde_p2pk_witness;
26
27#[derive(Debug, Error)]
29pub enum Error {
30 #[error("Secret is not a p2pk secret")]
32 IncorrectSecretKind,
33 #[error("Witness is not a p2pk witness")]
35 IncorrectWitnessKind,
36 #[error("Locktime in past")]
38 LocktimeInPast,
39 #[error("Invalid signature")]
41 InvalidSignature,
42 #[error("Unknown tag P2PK secret")]
44 UnknownTag,
45 #[error("Unknown sigflag")]
47 UnknownSigFlag,
48 #[error("P2PK spend conditions are not met")]
50 SpendConditionsNotMet,
51 #[error("P2PK required in secret data")]
53 P2PKPubkeyRequired,
54 #[error("Kind not found")]
56 KindNotFound,
57 #[error("Invalid hash")]
59 InvalidHash,
60 #[error("Witness signatures not provided")]
62 SignaturesNotProvided,
63 #[error("Duplicate signature from the same pubkey detected")]
65 DuplicateSignature,
66 #[error(transparent)]
68 UrlParseError(#[from] url::ParseError),
69 #[error(transparent)]
71 ParseInt(#[from] std::num::ParseIntError),
72 #[error(transparent)]
74 HexError(#[from] hex::Error),
75 #[error(transparent)]
77 SerdeJsonError(#[from] serde_json::Error),
78 #[error(transparent)]
80 Secp256k1(#[from] bitcoin::secp256k1::Error),
81 #[error(transparent)]
83 NUT01(#[from] crate::nuts::nut01::Error),
84 #[error(transparent)]
86 Secret(#[from] crate::secret::Error),
87}
88
89#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
91#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
92pub struct P2PKWitness {
93 pub signatures: Vec<String>,
95}
96
97impl P2PKWitness {
98 #[inline]
99 pub fn is_empty(&self) -> bool {
101 self.signatures.is_empty()
102 }
103}
104
105impl Proof {
106 pub fn sign_p2pk(&mut self, secret_key: SecretKey) -> Result<(), Error> {
108 let msg: Vec<u8> = self.secret.to_bytes();
109 let signature: Signature = secret_key.sign(&msg)?;
110
111 let signatures = vec![signature.to_string()];
112
113 match self.witness.as_mut() {
114 Some(witness) => {
115 witness.add_signatures(signatures);
116 }
117 None => {
118 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
119 p2pk_witness.add_signatures(signatures);
120 self.witness = Some(p2pk_witness);
121 }
122 };
123
124 Ok(())
125 }
126
127 pub fn verify_p2pk(&self) -> Result<(), Error> {
129 let secret: Nut10Secret = self.secret.clone().try_into()?;
130 let spending_conditions: Conditions = secret
131 .secret_data()
132 .tags()
133 .cloned()
134 .unwrap_or_default()
135 .try_into()?;
136 let msg: &[u8] = self.secret.as_bytes();
137
138 let mut verified_pubkeys = HashSet::new();
139
140 let witness_signatures = match &self.witness {
141 Some(witness) => witness.signatures(),
142 None => None,
143 };
144
145 let witness_signatures = witness_signatures.ok_or(Error::SignaturesNotProvided)?;
146
147 let mut pubkeys = spending_conditions.pubkeys.clone().unwrap_or_default();
148
149 if secret.kind().eq(&Kind::P2PK) {
150 pubkeys.push(PublicKey::from_str(secret.secret_data().data())?);
151 }
152
153 for signature in witness_signatures.iter() {
154 for v in &pubkeys {
155 let sig = Signature::from_str(signature)?;
156
157 if v.verify(msg, &sig).is_ok() {
158 if !verified_pubkeys.insert(*v) {
160 return Err(Error::DuplicateSignature);
161 }
162 } else {
163 tracing::debug!(
164 "Could not verify signature: {sig} on message: {}",
165 self.secret.to_string()
166 )
167 }
168 }
169 }
170
171 let valid_sigs = verified_pubkeys.len() as u64;
172
173 if valid_sigs >= spending_conditions.num_sigs.unwrap_or(1) {
174 return Ok(());
175 }
176
177 if let (Some(locktime), Some(refund_keys)) = (
178 spending_conditions.locktime,
179 spending_conditions.refund_keys,
180 ) {
181 let needed_refund_sigs = spending_conditions.num_sigs_refund.unwrap_or(1) as usize;
182
183 let mut valid_pubkeys = HashSet::new();
184
185 if locktime.lt(&unix_time()) {
187 for s in witness_signatures.iter() {
188 for v in &refund_keys {
189 let sig = Signature::from_str(s).map_err(|_| Error::InvalidSignature)?;
190
191 if v.verify(msg, &sig).is_ok() {
192 if !valid_pubkeys.insert(v) {
193 return Err(Error::DuplicateSignature);
194 }
195
196 if valid_pubkeys.len() >= needed_refund_sigs {
197 return Ok(());
198 }
199 }
200 }
201 }
202 }
203 }
204
205 Err(Error::SpendConditionsNotMet)
206 }
207}
208
209pub fn valid_signatures(
212 msg: &[u8],
213 pubkeys: &[PublicKey],
214 signatures: &[Signature],
215) -> Result<u64, Error> {
216 let mut verified_pubkeys = HashSet::new();
217
218 for pubkey in pubkeys {
219 for signature in signatures {
220 if pubkey.verify(msg, signature).is_ok() {
221 if !verified_pubkeys.insert(*pubkey) {
223 return Err(Error::DuplicateSignature);
224 }
225 }
226 }
227 }
228
229 Ok(verified_pubkeys.len() as u64)
230}
231
232impl BlindedMessage {
233 pub fn sign_p2pk(&mut self, secret_key: SecretKey) -> Result<(), Error> {
235 let msg: [u8; 33] = self.blinded_secret.to_bytes();
236 let signature: Signature = secret_key.sign(&msg)?;
237
238 let signatures = vec![signature.to_string()];
239
240 match self.witness.as_mut() {
241 Some(witness) => {
242 witness.add_signatures(signatures);
243 }
244 None => {
245 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
246 p2pk_witness.add_signatures(signatures);
247 self.witness = Some(p2pk_witness);
248 }
249 };
250
251 Ok(())
252 }
253
254 pub fn verify_p2pk(&self, pubkeys: &Vec<PublicKey>, required_sigs: u64) -> Result<(), Error> {
256 let mut verified_pubkeys = HashSet::new();
257 if let Some(witness) = &self.witness {
258 for signature in witness
259 .signatures()
260 .ok_or(Error::SignaturesNotProvided)?
261 .iter()
262 {
263 for v in pubkeys {
264 let msg = &self.blinded_secret.to_bytes();
265 let sig = Signature::from_str(signature)?;
266
267 if v.verify(msg, &sig).is_ok() {
268 if !verified_pubkeys.insert(*v) {
270 return Err(Error::DuplicateSignature);
271 }
272 } else {
273 tracing::debug!(
274 "Could not verify signature: {sig} on message: {}",
275 self.blinded_secret
276 )
277 }
278 }
279 }
280 }
281
282 let valid_sigs = verified_pubkeys.len() as u64;
283
284 if valid_sigs.ge(&required_sigs) {
285 Ok(())
286 } else {
287 Err(Error::SpendConditionsNotMet)
288 }
289 }
290}
291
292#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
296pub enum SpendingConditions {
297 P2PKConditions {
301 data: PublicKey,
303 conditions: Option<Conditions>,
305 },
306 HTLCConditions {
310 data: Sha256Hash,
312 conditions: Option<Conditions>,
314 },
315}
316
317impl SpendingConditions {
318 pub fn new_htlc(preimage: String, conditions: Option<Conditions>) -> Result<Self, Error> {
320 let htlc = Sha256Hash::hash(&hex::decode(preimage)?);
321
322 Ok(Self::HTLCConditions {
323 data: htlc,
324 conditions,
325 })
326 }
327
328 pub fn new_htlc_hash(hash: &str, conditions: Option<Conditions>) -> Result<Self, Error> {
330 let hash = Sha256Hash::from_str(hash).map_err(|_| Error::InvalidHash)?;
331
332 Ok(Self::HTLCConditions {
333 data: hash,
334 conditions,
335 })
336 }
337
338 pub fn new_p2pk(pubkey: PublicKey, conditions: Option<Conditions>) -> Self {
340 Self::P2PKConditions {
341 data: pubkey,
342 conditions,
343 }
344 }
345
346 pub fn kind(&self) -> Kind {
348 match self {
349 Self::P2PKConditions { .. } => Kind::P2PK,
350 Self::HTLCConditions { .. } => Kind::HTLC,
351 }
352 }
353
354 pub fn num_sigs(&self) -> Option<u64> {
356 match self {
357 Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.num_sigs),
358 Self::HTLCConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.num_sigs),
359 }
360 }
361
362 pub fn pubkeys(&self) -> Option<Vec<PublicKey>> {
364 match self {
365 Self::P2PKConditions { data, conditions } => {
366 let mut pubkeys = vec![*data];
367 if let Some(conditions) = conditions {
368 pubkeys.extend(conditions.pubkeys.clone().unwrap_or_default());
369 }
370
371 Some(pubkeys)
372 }
373 Self::HTLCConditions { conditions, .. } => conditions.clone().and_then(|c| c.pubkeys),
374 }
375 }
376
377 pub fn locktime(&self) -> Option<u64> {
379 match self {
380 Self::P2PKConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.locktime),
381 Self::HTLCConditions { conditions, .. } => conditions.as_ref().and_then(|c| c.locktime),
382 }
383 }
384
385 pub fn refund_keys(&self) -> Option<Vec<PublicKey>> {
387 match self {
388 Self::P2PKConditions { conditions, .. } => {
389 conditions.clone().and_then(|c| c.refund_keys)
390 }
391 Self::HTLCConditions { conditions, .. } => {
392 conditions.clone().and_then(|c| c.refund_keys)
393 }
394 }
395 }
396}
397
398impl TryFrom<&Secret> for SpendingConditions {
399 type Error = Error;
400 fn try_from(secret: &Secret) -> Result<SpendingConditions, Error> {
401 let nut10_secret: Nut10Secret = secret.try_into()?;
402
403 nut10_secret.try_into()
404 }
405}
406
407impl TryFrom<Nut10Secret> for SpendingConditions {
408 type Error = Error;
409 fn try_from(secret: Nut10Secret) -> Result<SpendingConditions, Error> {
410 match secret.kind() {
411 Kind::P2PK => Ok(SpendingConditions::P2PKConditions {
412 data: PublicKey::from_str(secret.secret_data().data())?,
413 conditions: secret
414 .secret_data()
415 .tags()
416 .and_then(|t| t.clone().try_into().ok()),
417 }),
418 Kind::HTLC => Ok(Self::HTLCConditions {
419 data: Sha256Hash::from_str(secret.secret_data().data())
420 .map_err(|_| Error::InvalidHash)?,
421 conditions: secret
422 .secret_data()
423 .tags()
424 .and_then(|t| t.clone().try_into().ok()),
425 }),
426 }
427 }
428}
429
430impl From<SpendingConditions> for super::nut10::Secret {
431 fn from(conditions: SpendingConditions) -> super::nut10::Secret {
432 match conditions {
433 SpendingConditions::P2PKConditions { data, conditions } => {
434 super::nut10::Secret::new(Kind::P2PK, data.to_hex(), conditions)
435 }
436 SpendingConditions::HTLCConditions { data, conditions } => {
437 super::nut10::Secret::new(Kind::HTLC, data.to_string(), conditions)
438 }
439 }
440 }
441}
442
443#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
445pub struct Conditions {
446 #[serde(skip_serializing_if = "Option::is_none")]
448 pub locktime: Option<u64>,
449 #[serde(skip_serializing_if = "Option::is_none")]
451 pub pubkeys: Option<Vec<PublicKey>>,
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub refund_keys: Option<Vec<PublicKey>>,
455 #[serde(skip_serializing_if = "Option::is_none")]
459 pub num_sigs: Option<u64>,
460 pub sig_flag: SigFlag,
464 #[serde(skip_serializing_if = "Option::is_none")]
468 pub num_sigs_refund: Option<u64>,
469}
470
471impl Conditions {
472 pub fn new(
474 locktime: Option<u64>,
475 pubkeys: Option<Vec<PublicKey>>,
476 refund_keys: Option<Vec<PublicKey>>,
477 num_sigs: Option<u64>,
478 sig_flag: Option<SigFlag>,
479 num_sigs_refund: Option<u64>,
480 ) -> Result<Self, Error> {
481 if let Some(locktime) = locktime {
482 ensure_cdk!(locktime.ge(&unix_time()), Error::LocktimeInPast);
483 }
484
485 Ok(Self {
486 locktime,
487 pubkeys,
488 refund_keys,
489 num_sigs,
490 sig_flag: sig_flag.unwrap_or_default(),
491 num_sigs_refund,
492 })
493 }
494}
495impl From<Conditions> for Vec<Vec<String>> {
496 fn from(conditions: Conditions) -> Vec<Vec<String>> {
497 let Conditions {
498 locktime,
499 pubkeys,
500 refund_keys,
501 num_sigs,
502 sig_flag,
503 num_sigs_refund: _,
504 } = conditions;
505
506 let mut tags = Vec::new();
507
508 if let Some(pubkeys) = pubkeys {
509 tags.push(Tag::PubKeys(pubkeys.into_iter().collect()).as_vec());
510 }
511
512 if let Some(locktime) = locktime {
513 tags.push(Tag::LockTime(locktime).as_vec());
514 }
515
516 if let Some(num_sigs) = num_sigs {
517 tags.push(Tag::NSigs(num_sigs).as_vec());
518 }
519
520 if let Some(refund_keys) = refund_keys {
521 tags.push(Tag::Refund(refund_keys).as_vec())
522 }
523 tags.push(Tag::SigFlag(sig_flag).as_vec());
524 tags
525 }
526}
527
528impl TryFrom<Vec<Vec<String>>> for Conditions {
529 type Error = Error;
530 fn try_from(tags: Vec<Vec<String>>) -> Result<Conditions, Self::Error> {
531 let tags: HashMap<TagKind, Tag> = tags
532 .into_iter()
533 .map(|t| Tag::try_from(t).unwrap())
534 .map(|t| (t.kind(), t))
535 .collect();
536
537 let pubkeys = match tags.get(&TagKind::Pubkeys) {
538 Some(Tag::PubKeys(pubkeys)) => Some(pubkeys.clone()),
539 _ => None,
540 };
541
542 let locktime = if let Some(tag) = tags.get(&TagKind::Locktime) {
543 match tag {
544 Tag::LockTime(locktime) => Some(*locktime),
545 _ => None,
546 }
547 } else {
548 None
549 };
550
551 let refund_keys = if let Some(tag) = tags.get(&TagKind::Refund) {
552 match tag {
553 Tag::Refund(keys) => Some(keys.clone()),
554 _ => None,
555 }
556 } else {
557 None
558 };
559
560 let sig_flag = if let Some(tag) = tags.get(&TagKind::SigFlag) {
561 match tag {
562 Tag::SigFlag(sigflag) => *sigflag,
563 _ => SigFlag::SigInputs,
564 }
565 } else {
566 SigFlag::SigInputs
567 };
568
569 let num_sigs = if let Some(tag) = tags.get(&TagKind::NSigs) {
570 match tag {
571 Tag::NSigs(num_sigs) => Some(*num_sigs),
572 _ => None,
573 }
574 } else {
575 None
576 };
577
578 Ok(Conditions {
579 locktime,
580 pubkeys,
581 refund_keys,
582 num_sigs,
583 sig_flag,
584 num_sigs_refund: None,
585 })
586 }
587}
588
589#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
591#[serde(rename_all = "lowercase")]
592pub enum TagKind {
593 SigFlag,
595 #[serde(rename = "n_sigs")]
597 NSigs,
598 Locktime,
600 Refund,
602 Pubkeys,
604 #[serde(rename = "n_sigs_refund")]
606 NSigsRefund,
607 Custom(String),
609}
610
611impl fmt::Display for TagKind {
612 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
613 match self {
614 Self::SigFlag => write!(f, "sigflag"),
615 Self::NSigs => write!(f, "n_sigs"),
616 Self::Locktime => write!(f, "locktime"),
617 Self::Refund => write!(f, "refund"),
618 Self::Pubkeys => write!(f, "pubkeys"),
619 Self::NSigsRefund => write!(f, "n_sigs_refund"),
620 Self::Custom(kind) => write!(f, "{kind}"),
621 }
622 }
623}
624
625impl<S> From<S> for TagKind
626where
627 S: AsRef<str>,
628{
629 fn from(tag: S) -> Self {
630 match tag.as_ref() {
631 "sigflag" => Self::SigFlag,
632 "n_sigs" => Self::NSigs,
633 "locktime" => Self::Locktime,
634 "refund" => Self::Refund,
635 "pubkeys" => Self::Pubkeys,
636 t => Self::Custom(t.to_owned()),
637 }
638 }
639}
640
641#[derive(
645 Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash,
646)]
647pub enum SigFlag {
648 #[default]
649 SigInputs,
653 SigAll,
655}
656
657impl fmt::Display for SigFlag {
658 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
659 match self {
660 Self::SigAll => write!(f, "SIG_ALL"),
661 Self::SigInputs => write!(f, "SIG_INPUTS"),
662 }
663 }
664}
665
666impl FromStr for SigFlag {
667 type Err = Error;
668 fn from_str(tag: &str) -> Result<Self, Self::Err> {
669 match tag {
670 "SIG_ALL" => Ok(Self::SigAll),
671 "SIG_INPUTS" => Ok(Self::SigInputs),
672 _ => Err(Error::UnknownSigFlag),
673 }
674 }
675}
676
677pub fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag {
680 let mut sig_flag = SigFlag::SigInputs;
681 let mut pubkeys = HashSet::new();
682 let mut sigs_required = 1;
683 for proof in proofs {
684 if let Ok(secret) = Nut10Secret::try_from(proof.secret) {
685 if secret.kind().eq(&Kind::P2PK) {
686 if let Ok(verifying_key) = PublicKey::from_str(secret.secret_data().data()) {
687 pubkeys.insert(verifying_key);
688 }
689 }
690
691 if let Some(tags) = secret.secret_data().tags() {
692 if let Ok(conditions) = Conditions::try_from(tags.clone()) {
693 if conditions.sig_flag.eq(&SigFlag::SigAll) {
694 sig_flag = SigFlag::SigAll;
695 }
696
697 if let Some(sigs) = conditions.num_sigs {
698 if sigs > sigs_required {
699 sigs_required = sigs;
700 }
701 }
702
703 if let Some(pubs) = conditions.pubkeys {
704 pubkeys.extend(pubs);
705 }
706 }
707 }
708 }
709 }
710
711 EnforceSigFlag {
712 sig_flag,
713 pubkeys,
714 sigs_required,
715 }
716}
717
718#[derive(Debug, Clone, PartialEq, Eq)]
720pub struct EnforceSigFlag {
721 pub sig_flag: SigFlag,
723 pub pubkeys: HashSet<PublicKey>,
725 pub sigs_required: u64,
727}
728
729#[derive(Debug, Clone, Hash, PartialEq, Eq)]
731pub enum Tag {
732 SigFlag(SigFlag),
734 NSigs(u64),
736 LockTime(u64),
738 Refund(Vec<PublicKey>),
740 PubKeys(Vec<PublicKey>),
742}
743
744impl Tag {
745 pub fn kind(&self) -> TagKind {
747 match self {
748 Self::SigFlag(_) => TagKind::SigFlag,
749 Self::NSigs(_) => TagKind::NSigs,
750 Self::LockTime(_) => TagKind::Locktime,
751 Self::Refund(_) => TagKind::Refund,
752 Self::PubKeys(_) => TagKind::Pubkeys,
753 }
754 }
755
756 pub fn as_vec(&self) -> Vec<String> {
758 self.clone().into()
759 }
760}
761
762impl<S> TryFrom<Vec<S>> for Tag
763where
764 S: AsRef<str>,
765{
766 type Error = Error;
767
768 fn try_from(tag: Vec<S>) -> Result<Self, Self::Error> {
769 let tag_kind = tag.first().map(TagKind::from).ok_or(Error::KindNotFound)?;
770
771 match tag_kind {
772 TagKind::SigFlag => Ok(Tag::SigFlag(SigFlag::from_str(tag[1].as_ref())?)),
773 TagKind::NSigs => Ok(Tag::NSigs(tag[1].as_ref().parse()?)),
774 TagKind::Locktime => Ok(Tag::LockTime(tag[1].as_ref().parse()?)),
775 TagKind::Refund => {
776 let pubkeys = tag
777 .iter()
778 .skip(1)
779 .map(|p| PublicKey::from_str(p.as_ref()))
780 .collect::<Result<Vec<PublicKey>, _>>()?;
781
782 Ok(Self::Refund(pubkeys))
783 }
784 TagKind::Pubkeys => {
785 let pubkeys = tag
786 .iter()
787 .skip(1)
788 .map(|p| PublicKey::from_str(p.as_ref()))
789 .collect::<Result<Vec<PublicKey>, _>>()?;
790
791 Ok(Self::PubKeys(pubkeys))
792 }
793 _ => Err(Error::UnknownTag),
794 }
795 }
796}
797
798impl From<Tag> for Vec<String> {
799 fn from(data: Tag) -> Self {
800 match data {
801 Tag::SigFlag(sigflag) => vec![TagKind::SigFlag.to_string(), sigflag.to_string()],
802 Tag::NSigs(num_sig) => vec![TagKind::NSigs.to_string(), num_sig.to_string()],
803 Tag::LockTime(locktime) => vec![TagKind::Locktime.to_string(), locktime.to_string()],
804 Tag::PubKeys(pubkeys) => {
805 let mut tag = vec![TagKind::Pubkeys.to_string()];
806 for pubkey in pubkeys.into_iter() {
807 tag.push(pubkey.to_string())
808 }
809 tag
810 }
811 Tag::Refund(pubkeys) => {
812 let mut tag = vec![TagKind::Refund.to_string()];
813
814 for pubkey in pubkeys {
815 tag.push(pubkey.to_string())
816 }
817 tag
818 }
819 }
820 }
821}
822
823impl Serialize for Tag {
824 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
825 where
826 S: Serializer,
827 {
828 let data: Vec<String> = self.as_vec();
829 let mut seq = serializer.serialize_seq(Some(data.len()))?;
830 for element in data.into_iter() {
831 seq.serialize_element(&element)?;
832 }
833 seq.end()
834 }
835}
836
837impl<'de> Deserialize<'de> for Tag {
838 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
839 where
840 D: Deserializer<'de>,
841 {
842 type Data = Vec<String>;
843 let vec: Vec<String> = Data::deserialize(deserializer)?;
844 Self::try_from(vec).map_err(DeserializerError::custom)
845 }
846}
847
848#[cfg(test)]
849mod tests {
850 use std::str::FromStr;
851
852 use super::*;
853 use crate::nuts::Id;
854 use crate::secret::Secret;
855 use crate::Amount;
856
857 #[test]
858 fn test_secret_ser() {
859 let data = PublicKey::from_str(
860 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
861 )
862 .unwrap();
863
864 let conditions = Conditions {
865 locktime: Some(99999),
866 pubkeys: Some(vec![
867 PublicKey::from_str(
868 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
869 )
870 .unwrap(),
871 PublicKey::from_str(
872 "023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c54",
873 )
874 .unwrap(),
875 ]),
876 refund_keys: Some(vec![PublicKey::from_str(
877 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
878 )
879 .unwrap()]),
880 num_sigs: Some(2),
881 sig_flag: SigFlag::SigAll,
882 num_sigs_refund: None,
883 };
884
885 let secret: Nut10Secret = Nut10Secret::new(Kind::P2PK, data.to_string(), Some(conditions));
886
887 let secret_str = serde_json::to_string(&secret).unwrap();
888
889 let secret_der: Nut10Secret = serde_json::from_str(&secret_str).unwrap();
890
891 assert_eq!(secret_der, secret);
892 }
893
894 #[test]
895 fn sign_proof() {
896 let secret_key =
897 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
898 .unwrap();
899
900 let signing_key_two =
901 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
902 .unwrap();
903
904 let signing_key_three =
905 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
906 .unwrap();
907 let v_key: PublicKey = secret_key.public_key();
908 let v_key_two: PublicKey = signing_key_two.public_key();
909 let v_key_three: PublicKey = signing_key_three.public_key();
910
911 let conditions = Conditions {
912 locktime: Some(21000000000),
913 pubkeys: Some(vec![v_key_two, v_key_three]),
914 refund_keys: Some(vec![v_key]),
915 num_sigs: Some(2),
916 sig_flag: SigFlag::SigInputs,
917 num_sigs_refund: None,
918 };
919
920 let secret: Secret = Nut10Secret::new(Kind::P2PK, v_key.to_string(), Some(conditions))
921 .try_into()
922 .unwrap();
923
924 let mut proof = Proof {
925 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
926 amount: Amount::ZERO,
927 secret,
928 c: PublicKey::from_str(
929 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
930 )
931 .unwrap(),
932 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
933 dleq: None,
934 };
935
936 proof.sign_p2pk(secret_key).unwrap();
937 proof.sign_p2pk(signing_key_two).unwrap();
938
939 assert!(proof.verify_p2pk().is_ok());
940 }
941
942 #[test]
943 fn test_verify() {
944 let json: &str = r#"{
946 "amount":1,
947 "secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]",
948 "C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
949 "id":"009a1f293253e41e",
950 "witness":"{\"signatures\":[\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\"]}"
951 }"#;
952 let valid_proof: Proof = serde_json::from_str(json).unwrap();
953
954 valid_proof.verify_p2pk().unwrap();
955 assert!(valid_proof.verify_p2pk().is_ok());
956
957 let invalid_proof = r#"{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"3426df9730d365a9d18d79bed2f3e78e9172d7107c55306ac5ddd1b2d065893366cfa24ff3c874ebf1fc22360ba5888ddf6ff5dbcb9e5f2f5a1368f7afc64f15\"]}"}"#;
959
960 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
961
962 assert!(invalid_proof.verify_p2pk().is_err());
963 }
964
965 #[test]
966 fn verify_multi_sig() {
967 let valid_proof = r#"{"amount":0,"secret":"[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\",\"9a72ca2d4d5075be5b511ee48dbc5e45f259bcf4a4e8bf18587f433098a9cd61ff9737dc6e8022de57c76560214c4568377792d4c2c6432886cc7050487a1f22\"]}"}"#;
969
970 let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap();
971
972 assert!(valid_proof.verify_p2pk().is_ok());
973
974 let invalid_proof = r#"{"amount":0,"secret":"[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}"}"#;
976
977 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
978
979 assert!(invalid_proof.verify_p2pk().is_err());
981 }
982
983 #[test]
984 fn verify_refund() {
985 let valid_proof = r#"{"amount":1,"id":"009a1f293253e41e","secret":"[\"P2PK\",{\"nonce\":\"902685f492ef3bb2ca35a47ddbba484a3365d143b9776d453947dcbf1ddf9689\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","witness":"{\"signatures\":[\"710507b4bc202355c91ea3c147c0d0189c75e179d995e566336afd759cb342bcad9a593345f559d9b9e108ac2c9b5bd9f0b4b6a295028a98606a0a2e95eb54f7\"]}"}"#;
986
987 let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap();
988 assert!(valid_proof.verify_p2pk().is_ok());
989
990 let invalid_proof = r#"{"amount":1,"id":"009a1f293253e41e","secret":"[\"P2PK\",{\"nonce\":\"64c46e5d30df27286166814b71b5d69801704f23a7ad626b05688fbdb48dcc98\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","witness":"{\"signatures\":[\"f661d3dc046d636d47cb3d06586da42c498f0300373d1c2a4f417a44252cdf3809bce207c8888f934dba0d2b1671f1b8622d526840f2d5883e571b462630c1ff\"]}"}"#;
991
992 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
993
994 assert!(invalid_proof.verify_p2pk().is_err());
995 }
996
997 #[test]
998 fn test_duplicate_signatures_counting() {
999 let proof: Proof = serde_json::from_str(
1000 r#"{"amount":1,"id":"009a1f293253e41e","secret":"[\"P2PK\",{\"nonce\":\"e434a9efbc5f65d144a620e368c9a6dc12c719d0ebc57e0c74f7341864dc449a\",\"data\":\"02a60c27104cf6023581e790970fc33994a320abe36e7ceed16771b0f8d76f0666\",\"tags\":[[\"pubkeys\",\"039c6a20a6ba354b7bb92eb9750716c1098063006362a1fa2afca7421f262d45c5\",\"0203eb2f7cd72a4f725d3327216365d2df18bb4bbc810522fd973c9af987e9b05b\"],[\"locktime\",\"1744876528\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","witness":"{\"signatures\":[\"3e9ff9e55c9eccb9e5aa0b6c62d54500b40d0eebadb06efcc8e76f3ce38e0923f956ec1bccb9080db96a17c1e98a1b857abfd1a56bb25670037cea3db1f73d81\",\"c5e29c38e60c4db720cf3f78e590358cf1291a06b9eadf77c1108ae84d533520c2707ffda224eb6a63fddaee9abd5ecf8f2cd263d2556950550e3061a5511f65\"]}"}"#,
1001 ).unwrap();
1002
1003 assert!(proof.verify_p2pk().is_err());
1004 }
1005}