Jelajahi Sumber

darkotc: Implement signing of partially signed transaction.

parazyd 4 tahun lalu
induk
melakukan
eb1a9f1349
3 mengubah file dengan 121 tambahan dan 28 penghapusan
  1. 119 26
      bin/darkotc/src/main.rs
  2. 1 1
      bin/darkotc/src/rpc.rs
  3. 1 1
      src/tx/mod.rs

+ 119 - 26
bin/darkotc/src/main.rs

@@ -72,10 +72,13 @@ enum Subcmd {
     },
     },
 
 
     /// Inspect partial swap data from stdin.
     /// Inspect partial swap data from stdin.
-    Inspect,
+    InspectPartial,
 
 
     /// Join two partial swap data files and build a tx
     /// Join two partial swap data files and build a tx
     Join { data0: String, data1: String },
     Join { data0: String, data1: String },
+
+    /// Sign a transaction given from stdin.
+    SignTx,
 }
 }
 
 
 #[derive(SerialEncodable, SerialDecodable)]
 #[derive(SerialEncodable, SerialDecodable)]
@@ -258,7 +261,7 @@ async fn init_swap(
     Ok(partial_swap_data)
     Ok(partial_swap_data)
 }
 }
 
 
-fn inspect(data: &str) -> Result<()> {
+fn inspect_partial(data: &str) -> Result<()> {
     let mut mint_valid = false;
     let mut mint_valid = false;
     let mut burn_valid = false;
     let mut burn_valid = false;
     let mut mint_value_valid = false;
     let mut mint_value_valid = false;
@@ -403,9 +406,6 @@ fn inspect(data: &str) -> Result<()> {
 }
 }
 
 
 async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result<Transaction> {
 async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result<Transaction> {
-    let rpc_client = RpcClient::new(endpoint).await?;
-    let rpc = Rpc { rpc_client };
-
     eprintln!("Joining data into a transaction");
     eprintln!("Joining data into a transaction");
 
 
     let input0 = PartialTransactionInput { burn_proof: d0.burn_proof, revealed: d0.burn_revealed };
     let input0 = PartialTransactionInput { burn_proof: d0.burn_proof, revealed: d0.burn_revealed };
@@ -432,7 +432,14 @@ async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result
     let mut signed: bool;
     let mut signed: bool;
 
 
     eprint!("Trying to decrypt the note of the first half... ");
     eprint!("Trying to decrypt the note of the first half... ");
-    if let Some(note) = rpc.decrypt_note(&d0.encrypted_note).await? {
+    let rpc_client = RpcClient::new(endpoint.clone()).await?;
+    let rpc = Rpc { rpc_client };
+    let note = match rpc.decrypt_note(&d0.encrypted_note).await {
+        Ok(v) => v,
+        Err(_) => None,
+    };
+
+    if let Some(note) = note {
         eprintln!("{}", fg_green("Success"));
         eprintln!("{}", fg_green("Success"));
         let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
         let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
         let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
         let input = TransactionInput::from_partial(partial_tx.inputs[0].clone(), signature);
@@ -446,21 +453,30 @@ async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result
         signed = false;
         signed = false;
     }
     }
 
 
-    // If we have signed, we shouldn't have to look in the other one.
-    if !signed {
-        eprint!("Trying to decrypt the note of the second half... ");
-        if let Some(note) = rpc.decrypt_note(&d1.encrypted_note).await? {
-            eprintln!("{}", fg_green("Success"));
-            let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
-            let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
-            inputs.push(input);
-            signed = true;
-        } else {
-            eprintln!("{}", fg_red("Failure"));
-            let signature = schnorr::Signature::dummy();
-            let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
-            inputs.push(input);
-            signed = false;
+    // If we have signed, we shouldn't have to look in the other one, but we might
+    // be sending to ourself for some reason.
+    eprint!("Trying to decrypt the note of the second half... ");
+    let rpc_client = RpcClient::new(endpoint).await?;
+    let rpc = Rpc { rpc_client };
+    let note = match rpc.decrypt_note(&d1.encrypted_note).await {
+        Ok(v) => v,
+        Err(_) => None,
+    };
+
+    if let Some(note) = note {
+        eprintln!("{}", fg_green("Success"));
+        let signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
+        let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
+        inputs.push(input);
+        signed = true;
+    } else {
+        eprintln!("{}", fg_red("Failure"));
+        let signature = schnorr::Signature::dummy();
+        let input = TransactionInput::from_partial(partial_tx.inputs[1].clone(), signature);
+        inputs.push(input);
+        if !signed {
+            eprintln!("Error: Failed to sign transaction!");
+            exit(1);
         }
         }
     }
     }
 
 
@@ -473,6 +489,67 @@ async fn join(endpoint: Url, d0: PartialSwapData, d1: PartialSwapData) -> Result
     Ok(tx)
     Ok(tx)
 }
 }
 
 
+async fn sign_tx(endpoint: Url, data: &str) -> Result<Transaction> {
+    eprintln!("Trying to sign transaction");
+    let mut tx: Transaction = deserialize(&bs58::decode(data).into_vec()?)?;
+
+    let mut input_idxs = vec![];
+    let mut signature = schnorr::Signature::dummy();
+
+    // Find dummy signatures to fill. We assume we're using the same
+    // signature everywhere.
+    eprintln!("Looking for dummy signatures...");
+    for (i, input) in tx.inputs.iter().enumerate() {
+        if input.signature == schnorr::Signature::dummy() {
+            eprintln!("Found dummy signature in input {}", i);
+            input_idxs.push(i);
+        }
+    }
+
+    if input_idxs.is_empty() {
+        eprintln!("Error: Did not find any dummy signatures in the transaction.");
+        exit(1);
+    }
+
+    // Find a note to decrypt that holds our secret key.
+    let mut found_secret = false;
+    for (i, output) in tx.outputs.iter().enumerate() {
+        // TODO: FIXME: Consider not closing the RPC on failure.
+        let rpc_client = RpcClient::new(endpoint.clone()).await?;
+        let rpc = Rpc { rpc_client };
+
+        let note = match rpc.decrypt_note(&output.enc_note).await {
+            Ok(v) => v,
+            Err(_) => continue,
+        };
+
+        if let Some(note) = note {
+            eprintln!("Successfully decrypted note in output {}", i);
+            eprintln!("Creating signature...");
+            let mut unsigned_tx_data = vec![];
+            let _ = tx.encode_without_signature(&mut unsigned_tx_data)?;
+
+            signature = try_sign_tx(&note, &unsigned_tx_data[..])?;
+            found_secret = true;
+            break
+        }
+
+        eprintln!("Failed to find a note to decrypt. Signing failed.");
+        exit(1);
+    }
+
+    if !found_secret {
+        eprintln!("Error: Did not manage to sign transaction. Couldn't find any secret keys.");
+        exit(1);
+    }
+
+    for i in input_idxs {
+        tx.inputs[i].signature = signature.clone();
+    }
+
+    Ok(tx)
+}
+
 fn try_sign_tx(note: &Note, tx_data: &[u8]) -> Result<schnorr::Signature> {
 fn try_sign_tx(note: &Note, tx_data: &[u8]) -> Result<schnorr::Signature> {
     if note.memo.len() != 32 {
     if note.memo.len() != 32 {
         eprintln!("Error: The note memo is not 32 bytes");
         eprintln!("Error: The note memo is not 32 bytes");
@@ -482,7 +559,7 @@ fn try_sign_tx(note: &Note, tx_data: &[u8]) -> Result<schnorr::Signature> {
     let secret = match SecretKey::from_bytes(note.memo.clone().try_into().unwrap()) {
     let secret = match SecretKey::from_bytes(note.memo.clone().try_into().unwrap()) {
         Ok(v) => v,
         Ok(v) => v,
         Err(e) => {
         Err(e) => {
-            eprintln!("Did not manage to coerce the bytes into SecretKey: {}", e);
+            eprintln!("Did not manage to cast bytes into SecretKey: {}", e);
             exit(1);
             exit(1);
         }
         }
     };
     };
@@ -501,21 +578,37 @@ async fn main() -> Result<()> {
             let token_pair = parse_token_pair(&token_pair)?;
             let token_pair = parse_token_pair(&token_pair)?;
             let value_pair = parse_value_pair(&value_pair)?;
             let value_pair = parse_value_pair(&value_pair)?;
             let swap_data = init_swap(args.endpoint, token_pair, value_pair).await?;
             let swap_data = init_swap(args.endpoint, token_pair, value_pair).await?;
+
             println!("{}", bs58::encode(serialize(&swap_data)).into_string());
             println!("{}", bs58::encode(serialize(&swap_data)).into_string());
             Ok(())
             Ok(())
         }
         }
-        Subcmd::Inspect => {
+        Subcmd::InspectPartial => {
             let mut buf = String::new();
             let mut buf = String::new();
             stdin().read_to_string(&mut buf)?;
             stdin().read_to_string(&mut buf)?;
-            inspect(&buf.trim())
+
+            inspect_partial(&buf.trim())
         }
         }
         Subcmd::Join { data0, data1 } => {
         Subcmd::Join { data0, data1 } => {
             let d0 = std::fs::read_to_string(data0)?;
             let d0 = std::fs::read_to_string(data0)?;
             let d1 = std::fs::read_to_string(data1)?;
             let d1 = std::fs::read_to_string(data1)?;
-            let d0 = deserialize(&bs58::decode(&d0).into_vec()?)?;
-            let d1 = deserialize(&bs58::decode(&d1).into_vec()?)?;
+
+            let d0 = deserialize(&bs58::decode(&d0.trim()).into_vec()?)?;
+            let d1 = deserialize(&bs58::decode(&d1.trim()).into_vec()?)?;
+
             let tx = join(args.endpoint, d0, d1).await?;
             let tx = join(args.endpoint, d0, d1).await?;
+
+            println!("{}", bs58::encode(&serialize(&tx)).into_string());
+            eprintln!("Successfully signed transaction");
+            Ok(())
+        }
+        Subcmd::SignTx => {
+            let mut buf = String::new();
+            stdin().read_to_string(&mut buf)?;
+
+            let tx = sign_tx(args.endpoint, &buf.trim()).await?;
+
             println!("{}", bs58::encode(&serialize(&tx)).into_string());
             println!("{}", bs58::encode(&serialize(&tx)).into_string());
+            eprintln!("Successfully signed transaction");
             Ok(())
             Ok(())
         }
         }
     }
     }

+ 1 - 1
bin/darkotc/src/rpc.rs

@@ -157,7 +157,7 @@ impl Rpc {
     pub async fn decrypt_note(&self, enc_note: &EncryptedNote) -> Result<Option<Note>> {
     pub async fn decrypt_note(&self, enc_note: &EncryptedNote) -> Result<Option<Note>> {
         let encoded = bs58::encode(&serialize(enc_note)).into_string();
         let encoded = bs58::encode(&serialize(enc_note)).into_string();
         let req = JsonRequest::new("wallet.decrypt_note", json!([encoded]));
         let req = JsonRequest::new("wallet.decrypt_note", json!([encoded]));
-        let rep = self.rpc_client.request(req).await?;
+        let rep = self.rpc_client.oneshot_request(req).await?;
 
 
         if !rep.is_string() {
         if !rep.is_string() {
             eprintln!("Error: decrypt_note() RPC call returned invalid data");
             eprintln!("Error: decrypt_note() RPC call returned invalid data");

+ 1 - 1
src/tx/mod.rs

@@ -153,7 +153,7 @@ impl Transaction {
         Ok(())
         Ok(())
     }
     }
 
 
-    fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
+    pub fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
         let mut len = 0;
         let mut len = 0;
         len += self.clear_inputs.encode_without_signature(&mut s)?;
         len += self.clear_inputs.encode_without_signature(&mut s)?;
         len += self.inputs.encode_without_signature(&mut s)?;
         len += self.inputs.encode_without_signature(&mut s)?;