From 1161b3dac1cdc7461eb954a348fc1ca6640d5241 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sun, 3 Mar 2024 12:49:35 +0000 Subject: [PATCH] chore: nut11 tests --- crates/cashu/src/nuts/nut11.rs | 126 +++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 36 deletions(-) diff --git a/crates/cashu/src/nuts/nut11.rs b/crates/cashu/src/nuts/nut11.rs index f6630aa6..f91f49b1 100644 --- a/crates/cashu/src/nuts/nut11.rs +++ b/crates/cashu/src/nuts/nut11.rs @@ -247,7 +247,7 @@ impl TryFrom for P2PKConditions { .secret_data .tags .into_iter() - .flat_map(Tag::try_from) + .map(|t| Tag::try_from(t).unwrap()) .map(|t| (t.kind(), t)) .collect(); @@ -344,12 +344,15 @@ impl Proof { return Ok(()); } + println!("{:?}", spending_conditions.refund_keys); + if let Some(locktime) = spending_conditions.locktime { // If lock time has passed check if refund witness signature is valid if locktime.lt(&unix_time()) && !spending_conditions.refund_keys.is_empty() { for s in &self.witness.signatures { for v in &spending_conditions.refund_keys { - let sig = Signature::try_from(s.as_bytes()) + println!("{}", v); + let sig = Signature::try_from(hex::decode(s)?.as_slice()) .map_err(|_| Error::InvalidSignature)?; // As long as there is one valid refund signature it can be spent @@ -488,43 +491,34 @@ where type Error = Error; fn try_from(tag: Vec) -> Result { - let tag_len = tag.len(); let tag_kind: TagKind = match tag.first() { Some(kind) => TagKind::from(kind), None => return Err(Error::KindNotFound), }; - if tag_len.eq(&2) { - match tag_kind { - TagKind::SigFlag => Ok(Tag::SigFlag(SigFlag::from(tag[1].as_ref()))), - TagKind::NSigs => Ok(Tag::NSigs(tag[1].as_ref().parse()?)), - TagKind::Locktime => Ok(Tag::LockTime(tag[1].as_ref().parse()?)), - _ => Err(Error::UnknownTag), - } - } else if tag_len.gt(&1) { - match tag_kind { - TagKind::Refund => { - let pubkeys = tag - .iter() - .skip(1) - .flat_map(|p| VerifyingKey::from_str(p.as_ref())) - .collect(); + match tag_kind { + TagKind::SigFlag => Ok(Tag::SigFlag(SigFlag::from(tag[1].as_ref()))), + TagKind::NSigs => Ok(Tag::NSigs(tag[1].as_ref().parse()?)), + TagKind::Locktime => Ok(Tag::LockTime(tag[1].as_ref().parse()?)), + TagKind::Refund => { + let pubkeys = tag + .iter() + .skip(1) + .flat_map(|p| VerifyingKey::from_str(p.as_ref())) + .collect(); - Ok(Self::Refund(pubkeys)) - } - TagKind::Pubkeys => { - let pubkeys = tag - .iter() - .skip(1) - .flat_map(|p| VerifyingKey::from_str(p.as_ref())) - .collect(); - - Ok(Self::PubKeys(pubkeys)) - } - _ => Err(Error::UnknownTag), + Ok(Self::Refund(pubkeys)) } - } else { - Err(Error::UnknownTag) + TagKind::Pubkeys => { + let pubkeys = tag + .iter() + .skip(1) + .flat_map(|p| VerifyingKey::from_str(p.as_ref())) + .collect(); + + Ok(Self::PubKeys(pubkeys)) + } + _ => Err(Error::UnknownTag), } } } @@ -775,13 +769,24 @@ mod tests { ) .unwrap(); + let signing_key_two = SigningKey::from_str( + "0000000000000000000000000000000000000000000000000000000000000001", + ) + .unwrap(); + + let signing_key_three = SigningKey::from_str( + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + ) + .unwrap(); let v_key: VerifyingKey = secret_key.verifying_key(); + let v_key_two: VerifyingKey = signing_key_two.verifying_key(); + let v_key_three: VerifyingKey = signing_key_three.verifying_key(); let conditions = P2PKConditions { - locktime: None, - pubkeys: vec![v_key], - refund_keys: vec![], - num_sigs: None, + locktime: Some(21), + pubkeys: vec![v_key.clone(), v_key_two, v_key_three], + refund_keys: vec![v_key], + num_sigs: Some(2), sig_flag: SigFlag::SigInputs, }; @@ -802,4 +807,53 @@ mod tests { assert!(proof.verify_p2pk().is_ok()); } + + #[test] + fn test_verify() { + // Proof with a valid signature + let valid_proof = r#"{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\"]}"}"#; + + let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap(); + + assert!(valid_proof.verify_p2pk().is_ok()); + + // Proof with a signature that is in a different secret + let invalid_proof = r#"{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"3426df9730d365a9d18d79bed2f3e78e9172d7107c55306ac5ddd1b2d065893366cfa24ff3c874ebf1fc22360ba5888ddf6ff5dbcb9e5f2f5a1368f7afc64f15\"]}"}"#; + + let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap(); + + assert!(invalid_proof.verify_p2pk().is_err()); + } + + #[test] + fn verify_multi_sig() { + // Proof with 2 valid signatures to satifiy the condition + 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\"]}"}"#; + + let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap(); + + assert!(valid_proof.verify_p2pk().is_ok()); + + // Proof with onlt one of the required signatures + 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\"]}"}"#; + + let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap(); + + // Verification should fail without the requires signatures + assert!(invalid_proof.verify_p2pk().is_err()); + } + + #[test] + fn verify_refund() { + let valid_proof = r#"{"amount":0,"secret":"[\"P2PK\",{\"nonce\":\"3eff971bb1ca70b16be3446a4d3feedf2f37f054c5c8621d832744df71b028f0\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"49098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"94c6355461ca88e5d22c4e65e920b2e8253ccb4dd084675453a7bba7044e580246bd05e2520691afeccb2a88784cc56064353aec8b6a61e172727ba9cb3054a1\"]}"}"#; + + let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap(); + assert!(valid_proof.verify_p2pk().is_ok()); + + let invalid_proof = r#"{"amount":0,"secret":"[\"P2PK\",{\"nonce\":\"d14cf9be9d9438d548b6b9d29bf800611136d053421b0f48c38d1447a7a92fc8\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"2100000000000\"],[\"n_sigs\",\"2\"],[\"refund\",\"49098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"c3079dccf828e9d38bbbb17edf19c7915ee11920cf271c36b8780fdeb88b16fbfbe0328c7dcbe80e56cdc8f85c5831c79df77b27e81e5630a4dd392601fab9eb\"]}"}"#; + + let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap(); + + assert!(invalid_proof.verify_p2pk().is_err()); + } }