diff --git a/Cargo.lock b/Cargo.lock index 79199618..fd0270c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2346,7 +2346,7 @@ dependencies = [ [[package]] name = "revault_net" version = "0.2.1" -source = "git+https://github.com/revault/revault_net#4f8a38606d93a2c8277a5ba021ea41882f0b0a16" +source = "git+https://github.com/revault/revault_net#f84f411854525c673b27c9ff881b4f0162488a29" dependencies = [ "bitcoin", "log", @@ -2360,7 +2360,7 @@ dependencies = [ [[package]] name = "revault_tx" version = "0.4.1" -source = "git+https://github.com/revault/revault_tx#8fd1bb37bd06f7d80a4bad3ab0da9620a9e09c0a" +source = "git+https://github.com/revault/revault_tx#23f18b66a0f165a0091e1be8feffe363f37cea97" dependencies = [ "base64", "bitcoinconsensus", @@ -2378,7 +2378,7 @@ dependencies = [ [[package]] name = "revaultd" version = "0.3.1" -source = "git+https://github.com/revault/revaultd?branch=master#697a92501cd0b981cfeadc801ad4d93253a18e42" +source = "git+https://github.com/revault/revaultd#6cb59135052235b3f0ab36b3591c30d58ad24ef4" dependencies = [ "backtrace", "base64", diff --git a/Cargo.toml b/Cargo.toml index 5e0109f9..516104c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ path = "src/main.rs" [dependencies] bitcoin = { version = "0.27", features = ["base64", "use-serde"] } -revaultd = { git = "https://github.com/revault/revaultd", branch = "master", default-features = false} +revaultd = { git = "https://github.com/revault/revaultd", default-features = false} backtrace = "0.3" iced = { version = "0.4", default-features= false, features = ["tokio", "wgpu", "svg", "qr_code"] } diff --git a/contrib/tools/dummysigner/Cargo.lock b/contrib/tools/dummysigner/Cargo.lock index b527d274..c99dfc2f 100644 --- a/contrib/tools/dummysigner/Cargo.lock +++ b/contrib/tools/dummysigner/Cargo.lock @@ -1944,9 +1944,8 @@ dependencies = [ [[package]] name = "revault_tx" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3e08e97cd866f7f1e4a11ca71604f422d7d6d77ec923fd1c140fb19827e381" +version = "0.4.1" +source = "git+https://github.com/revault/revault_tx#23f18b66a0f165a0091e1be8feffe363f37cea97" dependencies = [ "base64", "bitcoinconsensus", diff --git a/contrib/tools/dummysigner/Cargo.toml b/contrib/tools/dummysigner/Cargo.toml index 593a15ba..344305e6 100644 --- a/contrib/tools/dummysigner/Cargo.toml +++ b/contrib/tools/dummysigner/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [workspace] [dependencies] -revault_tx = { version = "0.4.0", features = ["use-serde"] } +revault_tx = { git = "https://github.com/revault/revault_tx", features = ["use-serde"] } base64 = "0.13.0" iced = {version = "0.3", default-features = false, features = ["wgpu", "tokio"]} diff --git a/contrib/tools/dummysigner/README.md b/contrib/tools/dummysigner/README.md index f185f27f..77e69592 100644 --- a/contrib/tools/dummysigner/README.md +++ b/contrib/tools/dummysigner/README.md @@ -84,7 +84,7 @@ If the signature request was refused the response looks like: ```json { - "cancel_tx": "", + "cancel_txs": [""], "emergency_tx": "", "emergency_unvault_tx": "" } @@ -94,7 +94,7 @@ If the signature request was refused the response looks like: ```json { - "cancel_tx": "", + "cancel_tx": [""], "emergency_tx": "", "emergency_unvault_tx": "" } @@ -124,7 +124,7 @@ This method requires the descriptors and the emergency address. ```json [ { - "cancel_tx": "", + "cancel_txs": [""], "emergency_tx": "", "emergency_unvault_tx": "" }, diff --git a/contrib/tools/dummysigner/src/api.rs b/contrib/tools/dummysigner/src/api.rs index 3686183e..264da5cf 100644 --- a/contrib/tools/dummysigner/src/api.rs +++ b/contrib/tools/dummysigner/src/api.rs @@ -27,8 +27,8 @@ pub struct DelegateBatch { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RevocationTransactions { - #[serde(with = "bitcoin_psbt")] - pub cancel_tx: PartiallySignedTransaction, + #[serde(with = "bitcoin_psbt_array")] + pub cancel_txs: [PartiallySignedTransaction; 5], #[serde(with = "bitcoin_psbt")] pub emergency_tx: PartiallySignedTransaction, @@ -121,3 +121,41 @@ mod bitcoin_psbt { serializer.serialize_str(&base64::encode(encode::serialize(&psbt))) } } + +mod bitcoin_psbt_array { + use revault_tx::bitcoin::{consensus::encode, util::psbt::PartiallySignedTransaction as Psbt}; + use serde::{self, ser::SerializeSeq, Deserialize, Deserializer, Serializer}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result<[Psbt; 5], D::Error> + where + D: Deserializer<'de>, + { + let array: [String; 5] = Deserialize::deserialize(deserializer)?; + let to_psbt = |s: &str| -> Result { + let bytes: Vec = base64::decode(s).map_err(serde::de::Error::custom)?; + encode::deserialize(&bytes).map_err(serde::de::Error::custom) + }; + Ok([ + to_psbt(&array[0])?, + to_psbt(&array[1])?, + to_psbt(&array[2])?, + to_psbt(&array[3])?, + to_psbt(&array[4])?, + ]) + } + + pub fn serialize<'se, S>(psbts: &[Psbt; 5], serializer: S) -> Result + where + S: Serializer, + { + let array: Vec = psbts + .iter() + .map(|psbt| base64::encode(encode::serialize(&psbt))) + .collect(); + let mut seq = serializer.serialize_seq(Some(array.len()))?; + for element in array { + seq.serialize_element(&element)?; + } + seq.end() + } +} diff --git a/contrib/tools/dummysigner/src/app.rs b/contrib/tools/dummysigner/src/app.rs index 27276b52..3cbbc7cb 100644 --- a/contrib/tools/dummysigner/src/app.rs +++ b/contrib/tools/dummysigner/src/app.rs @@ -187,9 +187,9 @@ impl Application for App { self.signer .sign_psbt(&selected_keys, &mut target.emergency_unvault_tx) .unwrap(); - self.signer - .sign_psbt(&selected_keys, &mut target.cancel_tx) - .unwrap(); + for cancel_tx in &mut target.cancel_txs { + self.signer.sign_psbt(&selected_keys, cancel_tx).unwrap(); + } *signed = true; return Command::perform( server::respond(writer.clone(), json!(target)), @@ -212,9 +212,9 @@ impl Application for App { .sign_psbt(&selected_keys, &mut revocation_txs.emergency_tx) .unwrap(); - self.signer - .sign_psbt(&selected_keys, &mut revocation_txs.cancel_tx) - .unwrap(); + for cancel_tx in &mut revocation_txs.cancel_txs { + self.signer.sign_psbt(&selected_keys, cancel_tx).unwrap(); + } self.signer .sign_psbt( @@ -443,7 +443,7 @@ impl Method { ) .unwrap(); api::RevocationTransactions { - cancel_tx: txs.cancel_tx, + cancel_txs: txs.cancel_txs, emergency_unvault_tx: txs.emergency_unvault_tx, emergency_tx: txs.emergency_tx, } diff --git a/contrib/tools/dummysigner/src/sign.rs b/contrib/tools/dummysigner/src/sign.rs index 3ca57480..456ab1fb 100644 --- a/contrib/tools/dummysigner/src/sign.rs +++ b/contrib/tools/dummysigner/src/sign.rs @@ -135,7 +135,7 @@ impl Signer { .emergency_address .as_ref() .ok_or(Error("Wallet does not have emergency_address".to_string()))?; - let (_, cancel_tx, emergency_tx, emergency_unvault_tx) = transaction_chain( + let (_, cancel_txs, emergency_tx, emergency_unvault_tx) = transaction_chain( outpoint, amount, &descriptors.deposit_descriptor, @@ -143,13 +143,19 @@ impl Signer { &descriptors.cpfp_descriptor, derivation_index, emer_address.clone(), - 0, &self.curve, ) .map_err(|e| Error(e.to_string()))?; + let cancel_txs = cancel_txs.all_feerates(); Ok(RevocationTransactions { - cancel_tx: cancel_tx.into_psbt(), + cancel_txs: [ + cancel_txs[0].psbt().clone(), + cancel_txs[1].psbt().clone(), + cancel_txs[2].psbt().clone(), + cancel_txs[3].psbt().clone(), + cancel_txs[4].psbt().clone(), + ], emergency_tx: emergency_tx.into_psbt(), emergency_unvault_tx: emergency_unvault_tx.into_psbt(), }) @@ -179,7 +185,7 @@ impl Signer { .derive(derivation_index, &self.curve); let unvault_tx = - UnvaultTransaction::new(deposit_txin, &unvault_descriptor, &cpfp_descriptor, 0) + UnvaultTransaction::new(deposit_txin, &unvault_descriptor, &cpfp_descriptor) .map_err(|e| Error(e.to_string()))?; Ok(unvault_tx.into_psbt()) @@ -187,7 +193,7 @@ impl Signer { } pub struct RevocationTransactions { - pub cancel_tx: PartiallySignedTransaction, + pub cancel_txs: [PartiallySignedTransaction; 5], pub emergency_tx: PartiallySignedTransaction, pub emergency_unvault_tx: PartiallySignedTransaction, } @@ -226,16 +232,16 @@ mod tests { .unwrap(); assert_eq!( - base64::encode(encode::serialize(&revocation_txs.cancel_tx)).to_string(), - "cHNidP8BAF4CAAAAATdzv51EXeeNc1fv6E852OhRxc67KNaWd+BrA3qN1a/1AAAAAAD9////ARRLJgcAAAAAIgAgdfJpF3TIFneDGEawKCIA4oiyxZcQtY90MYPUklUH28UAAAAAAAEBK7iGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwBAwSBAAAAAQVhIQICkzqxA36tCqSnhYxtSdZwXh+zvF9msAkYr3ufAOzVJqxRh2R2qRRyqV8ir5obrrhS+alScvjCHZjyZIisa3apFLbJrbicjJNybIPiobXZR4nXe5VhiKxsk1KHZ1iyaCIGAgKTOrEDfq0KpKeFjG1J1nBeH7O8X2awCRive58A7NUmCCUdYAkAAAAAIgYCWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiBgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAiAgJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQwjWfX/pAAAAACICA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOCHKpXyIAAAAAAA==" + base64::encode(encode::serialize(&revocation_txs.cancel_txs[0])).to_string(), + "cHNidP8BAF4CAAAAATdzv51EXeeNc1fv6E852OhRxc67KNaWd+BrA3qN1a/1AAAAAAD9////ASp5JgcAAAAAIgAgdfJpF3TIFneDGEawKCIA4oiyxZcQtY90MYPUklUH28UAAAAAAAEBK7iGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwBBWEhAgKTOrEDfq0KpKeFjG1J1nBeH7O8X2awCRive58A7NUmrFGHZHapFHKpXyKvmhuuuFL5qVJy+MIdmPJkiKxrdqkUtsmtuJyMk3Jsg+KhtdlHidd7lWGIrGyTUodnWLJoIgYCApM6sQN+rQqkp4WMbUnWcF4fs7xfZrAJGK97nwDs1SYIJR1gCQAAAAAiBgJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQwjWfX/pAAAAACIGA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOCHKpXyIAAAAAACICAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDCNZ9f+kAAAAAIgIDRwTey1W1qoj/0e9dBjZiSMExThllURNv8U6ri7pKSQ4IcqlfIgAAAAAA" ); assert_eq!( base64::encode(encode::serialize(&revocation_txs.emergency_tx)).to_string(), - "cHNidP8BAF4CAAAAAUeuD/NEqc88sk3DoBrKoVKjXbN2xW8Jr/4GO5q87JqJAQAAAAD9////ATheJgcAAAAAIgAgy7Co1PHzwoce0hHQR5RHMS72lSZudTF3bYrNgqLbkDYAAAAAAAEBKwAOJwcAAAAAIgAgdfJpF3TIFneDGEawKCIA4oiyxZcQtY90MYPUklUH28UBAwSBAAAAAQVHUiECWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MhA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOUq4iBgJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQwjWfX/pAAAAACIGA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOCHKpXyIAAAAAAAA=" + "cHNidP8BAF4CAAAAAUeuD/NEqc88sk3DoBrKoVKjXbN2xW8Jr/4GO5q87JqJAQAAAAD9////ARDEJAcAAAAAIgAgy7Co1PHzwoce0hHQR5RHMS72lSZudTF3bYrNgqLbkDYAAAAAAAEBKwAOJwcAAAAAIgAgdfJpF3TIFneDGEawKCIA4oiyxZcQtY90MYPUklUH28UBBUdSIQJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQyEDRwTey1W1qoj/0e9dBjZiSMExThllURNv8U6ri7pKSQ5SriIGAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDCNZ9f+kAAAAAIgYDRwTey1W1qoj/0e9dBjZiSMExThllURNv8U6ri7pKSQ4IcqlfIgAAAAAAAA==" ); assert_eq!( base64::encode(encode::serialize(&revocation_txs.emergency_unvault_tx)).to_string(), - "cHNidP8BAF4CAAAAATdzv51EXeeNc1fv6E852OhRxc67KNaWd+BrA3qN1a/1AAAAAAD9////AWa7JQcAAAAAIgAgy7Co1PHzwoce0hHQR5RHMS72lSZudTF3bYrNgqLbkDYAAAAAAAEBK7iGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwBAwSBAAAAAQVhIQICkzqxA36tCqSnhYxtSdZwXh+zvF9msAkYr3ufAOzVJqxRh2R2qRRyqV8ir5obrrhS+alScvjCHZjyZIisa3apFLbJrbicjJNybIPiobXZR4nXe5VhiKxsk1KHZ1iyaCIGAgKTOrEDfq0KpKeFjG1J1nBeH7O8X2awCRive58A7NUmCCUdYAkAAAAAIgYCWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiBgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAA" + "cHNidP8BAF4CAAAAATdzv51EXeeNc1fv6E852OhRxc67KNaWd+BrA3qN1a/1AAAAAAD9////AfzgIwcAAAAAIgAgy7Co1PHzwoce0hHQR5RHMS72lSZudTF3bYrNgqLbkDYAAAAAAAEBK7iGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwBBWEhAgKTOrEDfq0KpKeFjG1J1nBeH7O8X2awCRive58A7NUmrFGHZHapFHKpXyKvmhuuuFL5qVJy+MIdmPJkiKxrdqkUtsmtuJyMk3Jsg+KhtdlHidd7lWGIrGyTUodnWLJoIgYCApM6sQN+rQqkp4WMbUnWcF4fs7xfZrAJGK97nwDs1SYIJR1gCQAAAAAiBgJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQwjWfX/pAAAAACIGA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOCHKpXyIAAAAAAAA=" ); } @@ -264,7 +270,7 @@ mod tests { assert_eq!( base64::encode(encode::serialize(&unvault_tx)).to_string(), - "cHNidP8BAIkCAAAAAUeuD/NEqc88sk3DoBrKoVKjXbN2xW8Jr/4GO5q87JqJAQAAAAD9////AriGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwwdQAAAAAAACIAIAjkMa8elv7dHUmYpDATWBtmMmpv9yyKFawMunvGQ1AMAAAAAAABASsADicHAAAAACIAIHXyaRd0yBZ3gxhGsCgiAOKIssWXELWPdDGD1JJVB9vFAQMEAQAAAAEFR1IhAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDIQNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDlKuIgYCWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiBgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAiAgICkzqxA36tCqSnhYxtSdZwXh+zvF9msAkYr3ufAOzVJgglHWAJAAAAACICAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDCNZ9f+kAAAAAIgIDRwTey1W1qoj/0e9dBjZiSMExThllURNv8U6ri7pKSQ4IcqlfIgAAAAAAIgICUHL04HZXilyJ1B118e1Smr+S8c1qtja46Le7DzMCaUMI+93szQAAAAAA" + "cHNidP8BAIkCAAAAAUeuD/NEqc88sk3DoBrKoVKjXbN2xW8Jr/4GO5q87JqJAQAAAAD9////AriGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwwdQAAAAAAACIAIAjkMa8elv7dHUmYpDATWBtmMmpv9yyKFawMunvGQ1AMAAAAAAABASsADicHAAAAACIAIHXyaRd0yBZ3gxhGsCgiAOKIssWXELWPdDGD1JJVB9vFAQVHUiECWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MhA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOUq4iBgJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQwjWfX/pAAAAACIGA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOCHKpXyIAAAAAACICAgKTOrEDfq0KpKeFjG1J1nBeH7O8X2awCRive58A7NUmCCUdYAkAAAAAIgICWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiAgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAiAgJQcvTgdleKXInUHXXx7VKav5LxzWq2Nrjot7sPMwJpQwj73ezNAAAAAAA=" ); } } diff --git a/hwi/src/app/revault.rs b/hwi/src/app/revault.rs index 12003cbf..9c9dc0e3 100644 --- a/hwi/src/app/revault.rs +++ b/hwi/src/app/revault.rs @@ -19,8 +19,8 @@ pub trait RevaultHWI: HWI { &mut self, emergency_tx: &Psbt, emergency_unvault_tx: &Psbt, - cancel_tx: &Psbt, - ) -> Result<(Psbt, Psbt, Psbt), HWIError>; + cancel_tx: &[Psbt; 5], + ) -> Result<(Psbt, Psbt, [Psbt; 5]), HWIError>; /// Sign the unvault transaction required for delegation. async fn sign_unvault_tx(&mut self, unvault_tx: &Psbt) -> Result; @@ -30,7 +30,7 @@ pub trait RevaultHWI: HWI { async fn create_vaults( &mut self, deposits: &[(OutPoint, Amount, u32)], - ) -> Result, HWIError>; + ) -> Result, HWIError>; /// Delegate a list of vaults by giving the utxos to an hardware wallet storing the /// descriptors and deriving itself the unvault transactions. @@ -52,12 +52,18 @@ impl RevaultHWI for T { &mut self, emergency_tx: &Psbt, emergency_unvault_tx: &Psbt, - cancel_tx: &Psbt, - ) -> Result<(Psbt, Psbt, Psbt), HWIError> { + cancel_txs: &[Psbt; 5], + ) -> Result<(Psbt, Psbt, [Psbt; 5]), HWIError> { let emergency_tx = self.sign_tx(emergency_tx).await?; let emergency_unvault_tx = self.sign_tx(emergency_unvault_tx).await?; - let cancel_tx = self.sign_tx(cancel_tx).await?; - Ok((emergency_tx, emergency_unvault_tx, cancel_tx)) + let cancel_txs = [ + self.sign_tx(&cancel_txs[0]).await?, + self.sign_tx(&cancel_txs[1]).await?, + self.sign_tx(&cancel_txs[2]).await?, + self.sign_tx(&cancel_txs[3]).await?, + self.sign_tx(&cancel_txs[4]).await?, + ]; + Ok((emergency_tx, emergency_unvault_tx, cancel_txs)) } async fn sign_unvault_tx(&mut self, unvault_tx: &Psbt) -> Result { @@ -67,7 +73,7 @@ impl RevaultHWI for T { async fn create_vaults( &mut self, _deposits: &[(OutPoint, Amount, u32)], - ) -> Result, HWIError> { + ) -> Result, HWIError> { Err(HWIError::UnimplementedMethod) } diff --git a/hwi/src/dummysigner.rs b/hwi/src/dummysigner.rs index b1cbfea0..905ba879 100644 --- a/hwi/src/dummysigner.rs +++ b/hwi/src/dummysigner.rs @@ -81,19 +81,23 @@ impl DummySigner { &mut self, emergency_tx: &Psbt, emergency_unvault_tx: &Psbt, - cancel_tx: &Psbt, - ) -> Result<(Psbt, Psbt, Psbt), DummySignerError> { + cancel_txs: &[Psbt; 5], + ) -> Result<(Psbt, Psbt, [Psbt; 5]), DummySignerError> { + let cancel_txs: Vec = cancel_txs + .iter() + .map(|tx| base64::encode(&encode::serialize(&tx))) + .collect(); let res = self .send(json!({ "emergency_tx": base64::encode(&encode::serialize(&emergency_tx)), "emergency_unvault_tx": base64::encode(&encode::serialize(&emergency_unvault_tx)), - "cancel_tx": base64::encode(&encode::serialize(&cancel_tx)) + "cancel_txs": cancel_txs, })) .await?; let txs: RevocationTransactions = serde_json::from_value(res).map_err(|e| DummySignerError::Device(e.to_string()))?; - Ok((txs.emergency_tx, txs.emergency_unvault_tx, txs.cancel_tx)) + Ok((txs.emergency_tx, txs.emergency_unvault_tx, txs.cancel_txs)) } pub async fn sign_unvault_tx(&mut self, unvault_tx: &Psbt) -> Result { @@ -135,7 +139,7 @@ impl DummySigner { pub async fn create_vaults( &mut self, deposits: &[(OutPoint, Amount, u32)], - ) -> Result, DummySignerError> { + ) -> Result, DummySignerError> { let utxos: Vec = deposits .iter() .map(|(outpoint, amount, derivation_index)| Utxo { @@ -159,7 +163,7 @@ impl DummySigner { .map_err(|e| DummySignerError::Device(e.to_string()))?; Ok(txs .into_iter() - .map(|txs| (txs.emergency_tx, txs.emergency_unvault_tx, txs.cancel_tx)) + .map(|txs| (txs.emergency_tx, txs.emergency_unvault_tx, txs.cancel_txs)) .collect()) } @@ -193,8 +197,8 @@ impl DummySigner { #[derive(Deserialize)] pub struct RevocationTransactions { - #[serde(deserialize_with = "deserialize_psbt")] - pub cancel_tx: Psbt, + #[serde(deserialize_with = "deserialize_psbt_array")] + pub cancel_txs: [Psbt; 5], #[serde(deserialize_with = "deserialize_psbt")] pub emergency_tx: Psbt, @@ -258,6 +262,24 @@ where encode::deserialize(&bytes).map_err(serde::de::Error::custom) } +pub fn deserialize_psbt_array<'de, D>(deserializer: D) -> Result<[Psbt; 5], D::Error> +where + D: Deserializer<'de>, +{ + let array: [String; 5] = Deserialize::deserialize(deserializer)?; + let to_psbt = |s: &str| -> Result { + let bytes: Vec = base64::decode(s).map_err(serde::de::Error::custom)?; + encode::deserialize(&bytes).map_err(serde::de::Error::custom) + }; + Ok([ + to_psbt(&array[0])?, + to_psbt(&array[1])?, + to_psbt(&array[2])?, + to_psbt(&array[3])?, + to_psbt(&array[4])?, + ]) +} + pub type Receiver = SymmetricallyFramed, Value, Json>; @@ -324,9 +346,9 @@ mod revault { &mut self, emergency_tx: &Psbt, emergency_unvault_tx: &Psbt, - cancel_tx: &Psbt, - ) -> Result<(Psbt, Psbt, Psbt), HWIError> { - self.sign_revocation_txs(emergency_tx, emergency_unvault_tx, cancel_tx) + cancel_txs: &[Psbt; 5], + ) -> Result<(Psbt, Psbt, [Psbt; 5]), HWIError> { + self.sign_revocation_txs(emergency_tx, emergency_unvault_tx, cancel_txs) .await .map_err(|e| e.into()) } @@ -338,7 +360,7 @@ mod revault { async fn create_vaults( &mut self, deposits: &[(OutPoint, Amount, u32)], - ) -> Result, HWIError> { + ) -> Result, HWIError> { self.create_vaults(deposits).await.map_err(|e| e.into()) } diff --git a/src/app/message.rs b/src/app/message.rs index daf7dd13..12070b6f 100644 --- a/src/app/message.rs +++ b/src/app/message.rs @@ -123,7 +123,6 @@ pub enum SignMessage { Ping(Result<(), HWIError>), SelectSign, Connected(Result>>, HWIError>), - RevocationTxsSigned(Result>, HWIError>), PsbtSigned(Result, HWIError>), } diff --git a/src/app/state/cmd.rs b/src/app/state/cmd.rs index a4662b70..b322f8f3 100644 --- a/src/app/state/cmd.rs +++ b/src/app/state/cmd.rs @@ -46,16 +46,6 @@ pub async fn get_revocation_txs( revaultd.get_revocation_txs(&outpoint) } -pub async fn set_revocation_txs( - revaultd: Arc, - outpoint: OutPoint, - emergency_tx: Psbt, - emergency_unvault_tx: Psbt, - cancel_tx: Psbt, -) -> Result<(), RevaultDError> { - revaultd.set_revocation_txs(&outpoint, &emergency_tx, &emergency_unvault_tx, &cancel_tx) -} - pub async fn get_unvault_tx( revaultd: Arc, outpoint: OutPoint, diff --git a/src/app/state/sign.rs b/src/app/state/sign.rs index c4cc12e5..bf053bab 100644 --- a/src/app/state/sign.rs +++ b/src/app/state/sign.rs @@ -188,13 +188,13 @@ impl Device { self, emergency_tx: Psbt, emergency_unvault_tx: Psbt, - cancel_tx: Psbt, - ) -> Result<(Psbt, Psbt, Psbt), HWIError> { + cancel_txs: [Psbt; 5], + ) -> Result<(Psbt, Psbt, [Psbt; 5]), HWIError> { if let Some(channel) = self.channel { channel .lock() .await - .sign_revocation_txs(&emergency_tx, &emergency_unvault_tx, &cancel_tx) + .sign_revocation_txs(&emergency_tx, &emergency_unvault_tx, &cancel_txs) .await } else { Err(HWIError::DeviceDisconnected) @@ -221,7 +221,7 @@ impl Device { pub async fn secure_batch( self, deposits: &Vec, - ) -> Result, HWIError> { + ) -> Result, HWIError> { if let Some(channel) = self.channel { let utxos: Vec<(OutPoint, Amount, u32)> = deposits .iter() diff --git a/src/app/state/stakeholder.rs b/src/app/state/stakeholder.rs index 0990c355..ba81363b 100644 --- a/src/app/state/stakeholder.rs +++ b/src/app/state/stakeholder.rs @@ -404,14 +404,14 @@ pub async fn secure_deposits( ) -> Result, Error> { match device.clone().secure_batch(&deposits).await { Ok(revocation_txs) => { - for (i, (emergency_tx, emergency_unvault_tx, cancel_tx)) in + for (i, (emergency_tx, emergency_unvault_tx, cancel_txs)) in revocation_txs.into_iter().enumerate() { revaultd.set_revocation_txs( &outpoint(&deposits[i]), &emergency_tx, &emergency_unvault_tx, - &cancel_tx, + &cancel_txs, )?; } @@ -428,15 +428,26 @@ pub async fn secure_deposits( let outpoint = outpoint(&deposit); let revocation_txs = revaultd.get_revocation_txs(&outpoint)?; - let (emergency_tx, emergency_unvault_tx, cancel_tx) = device + let (emergency_tx, emergency_unvault_tx, cancel_txs) = device .sign_revocation_txs( revocation_txs.emergency_tx.into_psbt(), revocation_txs.emergency_unvault_tx.into_psbt(), - revocation_txs.cancel_tx.into_psbt(), + [ + revocation_txs.cancel_txs[0].psbt().clone(), + revocation_txs.cancel_txs[1].psbt().clone(), + revocation_txs.cancel_txs[2].psbt().clone(), + revocation_txs.cancel_txs[3].psbt().clone(), + revocation_txs.cancel_txs[4].psbt().clone(), + ], ) .await?; - revaultd.set_revocation_txs(&outpoint, &emergency_tx, &emergency_unvault_tx, &cancel_tx)?; + revaultd.set_revocation_txs( + &outpoint, + &emergency_tx, + &emergency_unvault_tx, + &cancel_txs, + )?; Ok(vec![outpoint]) } else { diff --git a/src/daemon/client/mod.rs b/src/daemon/client/mod.rs index a0a875a0..09daf592 100644 --- a/src/daemon/client/mod.rs +++ b/src/daemon/client/mod.rs @@ -116,18 +116,21 @@ impl Daemon for RevaultD { outpoint: &OutPoint, emergency_tx: &Psbt, emergency_unvault_tx: &Psbt, - cancel_tx: &Psbt, + cancel_txs: &[Psbt; 5], ) -> Result<(), RevaultDError> { let emergency = base64::encode(&consensus::serialize(emergency_tx)); let emergency_unvault = base64::encode(&consensus::serialize(emergency_unvault_tx)); - let cancel = base64::encode(&consensus::serialize(cancel_tx)); + let cancel: Vec = cancel_txs + .iter() + .map(|tx| base64::encode(&consensus::serialize(tx))) + .collect(); let _res: serde_json::value::Value = self.call( "revocationtxs", Some(vec![ - outpoint.to_string(), - cancel, - emergency, - emergency_unvault, + json!(outpoint.to_string()), + json!(cancel), + json!(emergency), + json!(emergency_unvault), ]), )?; Ok(()) diff --git a/src/daemon/embedded.rs b/src/daemon/embedded.rs index ef3fd2f8..9370ef89 100644 --- a/src/daemon/embedded.rs +++ b/src/daemon/embedded.rs @@ -151,12 +151,18 @@ impl Daemon for EmbeddedDaemon { outpoint: &OutPoint, emergency_tx: &Psbt, emergency_unvault_tx: &Psbt, - cancel_tx: &Psbt, + cancel_txs: &[Psbt; 5], ) -> Result<(), RevaultDError> { - let cancel = CancelTransaction::from_raw_psbt(&encode::serialize(cancel_tx)).unwrap(); - let emergency = + let cancel_txs: [CancelTransaction; 5] = [ + CancelTransaction::from_raw_psbt(&encode::serialize(&cancel_txs[0])).unwrap(), + CancelTransaction::from_raw_psbt(&encode::serialize(&cancel_txs[1])).unwrap(), + CancelTransaction::from_raw_psbt(&encode::serialize(&cancel_txs[2])).unwrap(), + CancelTransaction::from_raw_psbt(&encode::serialize(&cancel_txs[3])).unwrap(), + CancelTransaction::from_raw_psbt(&encode::serialize(&cancel_txs[4])).unwrap(), + ]; + let emergency_tx = EmergencyTransaction::from_raw_psbt(&encode::serialize(emergency_tx)).unwrap(); - let unvault_emergency = + let emergency_unvault_tx = UnvaultEmergencyTransaction::from_raw_psbt(&encode::serialize(emergency_unvault_tx)) .unwrap(); self.handle @@ -165,7 +171,14 @@ impl Daemon for EmbeddedDaemon { .lock() .unwrap() .control - .set_revocation_txs(*outpoint, cancel, emergency, unvault_emergency) + .set_revocation_txs( + *outpoint, + RevocationTransactions { + cancel_txs, + emergency_tx, + emergency_unvault_tx, + }, + ) .map_err(|e| e.into()) } @@ -265,7 +278,7 @@ impl Daemon for EmbeddedDaemon { .lock() .unwrap() .control - .revault(outpoint) + .revault(*outpoint) .map_err(|e| e.into()) } diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index 2e68df37..8542dbf9 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -76,7 +76,7 @@ pub trait Daemon: Debug { outpoint: &OutPoint, emergency_tx: &Psbt, emergency_unvault_tx: &Psbt, - cancel_tx: &Psbt, + cancel_tx: &[Psbt; 5], ) -> Result<(), RevaultDError>; fn get_unvault_tx(&self, outpoint: &OutPoint) -> Result; diff --git a/tests/app_manager.rs b/tests/app_manager.rs index 65a61f01..0f24ba30 100644 --- a/tests/app_manager.rs +++ b/tests/app_manager.rs @@ -31,6 +31,13 @@ use revault_gui::{ async fn test_manager_create_spend() { let unvault = UnvaultTransaction::from_raw_psbt(&base64::decode("cHNidP8BAIkCAAAAAUeuD/NEqc88sk3DoBrKoVKjXbN2xW8Jr/4GO5q87JqJAQAAAAD9////AriGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwwdQAAAAAAACIAIAjkMa8elv7dHUmYpDATWBtmMmpv9yyKFawMunvGQ1AMAAAAAAABASsADicHAAAAACIAIHXyaRd0yBZ3gxhGsCgiAOKIssWXELWPdDGD1JJVB9vFAQMEAQAAAAEFR1IhAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDIQNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDlKuIgYCWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiBgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAiAgICkzqxA36tCqSnhYxtSdZwXh+zvF9msAkYr3ufAOzVJgglHWAJAAAAACICAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDCNZ9f+kAAAAAIgIDRwTey1W1qoj/0e9dBjZiSMExThllURNv8U6ri7pKSQ4IcqlfIgAAAAAAIgICUHL04HZXilyJ1B118e1Smr+S8c1qtja46Le7DzMCaUMI+93szQAAAAAA").unwrap()).unwrap(); let cancel = CancelTransaction::from_raw_psbt(&base64::decode("cHNidP8BAF4CAAAAATdzv51EXeeNc1fv6E852OhRxc67KNaWd+BrA3qN1a/1AAAAAAD9////ARRLJgcAAAAAIgAgdfJpF3TIFneDGEawKCIA4oiyxZcQtY90MYPUklUH28UAAAAAAAEBK7iGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwBAwSBAAAAAQVhIQICkzqxA36tCqSnhYxtSdZwXh+zvF9msAkYr3ufAOzVJqxRh2R2qRRyqV8ir5obrrhS+alScvjCHZjyZIisa3apFLbJrbicjJNybIPiobXZR4nXe5VhiKxsk1KHZ1iyaCIGAgKTOrEDfq0KpKeFjG1J1nBeH7O8X2awCRive58A7NUmCCUdYAkAAAAAIgYCWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiBgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAiAgJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQwjWfX/pAAAAACICA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOCHKpXyIAAAAAAA==").unwrap()).unwrap(); + let cancels = [ + cancel.clone(), + cancel.clone(), + cancel.clone(), + cancel.clone(), + cancel, + ]; let daemon = Daemon::new(vec![ ( Some(json!({"method": "listvaults", "params": Some(&[[ @@ -94,7 +101,7 @@ async fn test_manager_create_spend() { ) .unwrap(), unvault: unvault.clone(), - cancel: cancel.clone(), + cancel: cancels.clone(), emergency: None, unvault_emergency: None }, @@ -104,7 +111,7 @@ async fn test_manager_create_spend() { ) .unwrap(), unvault, - cancel, + cancel: cancels, emergency: None, unvault_emergency: None } diff --git a/tests/app_stakeholder.rs b/tests/app_stakeholder.rs index 2484b0af..ad3370ca 100644 --- a/tests/app_stakeholder.rs +++ b/tests/app_stakeholder.rs @@ -30,6 +30,13 @@ use revault_gui::{ async fn test_stakeholder_delegate_state() { let unvault = UnvaultTransaction::from_raw_psbt(&base64::decode("cHNidP8BAIkCAAAAAUeuD/NEqc88sk3DoBrKoVKjXbN2xW8Jr/4GO5q87JqJAQAAAAD9////AriGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwwdQAAAAAAACIAIAjkMa8elv7dHUmYpDATWBtmMmpv9yyKFawMunvGQ1AMAAAAAAABASsADicHAAAAACIAIHXyaRd0yBZ3gxhGsCgiAOKIssWXELWPdDGD1JJVB9vFAQMEAQAAAAEFR1IhAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDIQNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDlKuIgYCWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiBgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAiAgICkzqxA36tCqSnhYxtSdZwXh+zvF9msAkYr3ufAOzVJgglHWAJAAAAACICAlgt7b9E9GVk5djNsGdTbWDr40zR0YAc/1G7+desKJtDCNZ9f+kAAAAAIgIDRwTey1W1qoj/0e9dBjZiSMExThllURNv8U6ri7pKSQ4IcqlfIgAAAAAAIgICUHL04HZXilyJ1B118e1Smr+S8c1qtja46Le7DzMCaUMI+93szQAAAAAA").unwrap()).unwrap(); let cancel = CancelTransaction::from_raw_psbt(&base64::decode("cHNidP8BAF4CAAAAATdzv51EXeeNc1fv6E852OhRxc67KNaWd+BrA3qN1a/1AAAAAAD9////ARRLJgcAAAAAIgAgdfJpF3TIFneDGEawKCIA4oiyxZcQtY90MYPUklUH28UAAAAAAAEBK7iGJgcAAAAAIgAgSOjPZes2prPdrcgiv+IG1sjXyTCc4KDr9+C9F+xk6LwBAwSBAAAAAQVhIQICkzqxA36tCqSnhYxtSdZwXh+zvF9msAkYr3ufAOzVJqxRh2R2qRRyqV8ir5obrrhS+alScvjCHZjyZIisa3apFLbJrbicjJNybIPiobXZR4nXe5VhiKxsk1KHZ1iyaCIGAgKTOrEDfq0KpKeFjG1J1nBeH7O8X2awCRive58A7NUmCCUdYAkAAAAAIgYCWC3tv0T0ZWTl2M2wZ1NtYOvjTNHRgBz/Ubv516wom0MI1n1/6QAAAAAiBgNHBN7LVbWqiP/R710GNmJIwTFOGWVRE2/xTquLukpJDghyqV8iAAAAAAAiAgJYLe2/RPRlZOXYzbBnU21g6+NM0dGAHP9Ru/nXrCibQwjWfX/pAAAAACICA0cE3stVtaqI/9HvXQY2YkjBMU4ZZVETb/FOq4u6SkkOCHKpXyIAAAAAAA==").unwrap()).unwrap(); + let cancels = [ + cancel.clone(), + cancel.clone(), + cancel.clone(), + cancel.clone(), + cancel.clone(), + ]; let daemon = Daemon::new(vec![ ( Some(json!({"method": "listvaults", "params": Some(&[[ @@ -135,7 +142,7 @@ async fn test_stakeholder_delegate_state() { ) .unwrap(), unvault: unvault.clone(), - cancel: cancel.clone(), + cancel: cancels.clone(), emergency: None, unvault_emergency: None, }, @@ -145,7 +152,7 @@ async fn test_stakeholder_delegate_state() { ) .unwrap(), unvault: unvault.clone(), - cancel: cancel.clone(), + cancel: cancels.clone(), emergency: None, unvault_emergency: None, }, @@ -155,7 +162,7 @@ async fn test_stakeholder_delegate_state() { ) .unwrap(), unvault: unvault.clone(), - cancel: cancel.clone(), + cancel: cancels.clone(), emergency: None, unvault_emergency: None, }, @@ -165,7 +172,7 @@ async fn test_stakeholder_delegate_state() { ) .unwrap(), unvault: unvault.clone(), - cancel: cancel.clone(), + cancel: cancels.clone(), emergency: None, unvault_emergency: None, }