فهرست منبع

feat: htlc from hash (#753)

* feat: htlc from hash

* chore: typos
thesimplekid 1 ماه پیش
والد
کامیت
67342ec793
2فایلهای تغییر یافته به همراه55 افزوده شده و 4 حذف شده
  1. 10 0
      crates/cashu/src/nuts/nut11/mod.rs
  2. 45 4
      crates/cdk-cli/src/sub_commands/send.rs

+ 10 - 0
crates/cashu/src/nuts/nut11/mod.rs

@@ -312,6 +312,16 @@ impl SpendingConditions {
         })
     }
 
+    /// New HTLC [SpendingConditions] from a hash directly instead of preimage
+    pub fn new_htlc_hash(hash: &str, conditions: Option<Conditions>) -> Result<Self, Error> {
+        let hash = Sha256Hash::from_str(hash).map_err(|_| Error::InvalidHash)?;
+
+        Ok(Self::HTLCConditions {
+            data: hash,
+            conditions,
+        })
+    }
+
     /// New P2PK [SpendingConditions]
     pub fn new_p2pk(pubkey: PublicKey, conditions: Option<Conditions>) -> Self {
         Self::P2PKConditions {

+ 45 - 4
crates/cdk-cli/src/sub_commands/send.rs

@@ -16,8 +16,11 @@ pub struct SendSubCommand {
     #[arg(short, long)]
     memo: Option<String>,
     /// Preimage
-    #[arg(long)]
+    #[arg(long, conflicts_with = "hash")]
     preimage: Option<String>,
+    /// Hash for HTLC (alternative to preimage)
+    #[arg(long, conflicts_with = "preimage")]
+    hash: Option<String>,
     /// Required number of signatures
     #[arg(long)]
     required_sigs: Option<u64>,
@@ -62,8 +65,12 @@ pub async fn send(
 
     check_sufficient_funds(mints_amounts[mint_number].1, token_amount)?;
 
-    let conditions = match &sub_command_args.preimage {
-        Some(preimage) => {
+    let conditions = match (&sub_command_args.preimage, &sub_command_args.hash) {
+        (Some(_), Some(_)) => {
+            // This case shouldn't be reached due to Clap's conflicts_with attribute
+            unreachable!("Both preimage and hash were provided despite conflicts_with attribute")
+        }
+        (Some(preimage), None) => {
             let pubkeys = match sub_command_args.pubkey.is_empty() {
                 true => None,
                 false => Some(
@@ -100,7 +107,41 @@ pub async fn send(
                 Some(conditions),
             )?)
         }
-        None => match sub_command_args.pubkey.is_empty() {
+        (None, Some(hash)) => {
+            let pubkeys = match sub_command_args.pubkey.is_empty() {
+                true => None,
+                false => Some(
+                    sub_command_args
+                        .pubkey
+                        .iter()
+                        .map(|p| PublicKey::from_str(p).unwrap())
+                        .collect(),
+                ),
+            };
+
+            let refund_keys = match sub_command_args.refund_keys.is_empty() {
+                true => None,
+                false => Some(
+                    sub_command_args
+                        .refund_keys
+                        .iter()
+                        .map(|p| PublicKey::from_str(p).unwrap())
+                        .collect(),
+                ),
+            };
+
+            let conditions = Conditions::new(
+                sub_command_args.locktime,
+                pubkeys,
+                refund_keys,
+                sub_command_args.required_sigs,
+                None,
+            )
+            .unwrap();
+
+            Some(SpendingConditions::new_htlc_hash(hash, Some(conditions))?)
+        }
+        (None, None) => match sub_command_args.pubkey.is_empty() {
             true => None,
             false => {
                 let pubkeys: Vec<PublicKey> = sub_command_args