diff --git a/Cargo.lock b/Cargo.lock index 44b607c..1f4930f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -415,29 +415,6 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" -[[package]] -name = "aws-lc-rs" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", - "pkg-config", -] - [[package]] name = "backtrace" version = "0.3.76" @@ -750,12 +727,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chacha20" version = "0.8.2" @@ -862,15 +833,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - [[package]] name = "coarsetime" version = "0.1.37" @@ -882,16 +844,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes 1.12.0", - "memchr", -] - [[package]] name = "compression-codecs" version = "0.4.38" @@ -1626,12 +1578,6 @@ dependencies = [ "phf 0.11.3", ] -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "dyn-clone" version = "1.0.20" @@ -2073,12 +2019,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "fslock-guard" version = "0.8.0" @@ -3039,6 +2979,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes 1.12.0", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -3403,55 +3359,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jni" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" -dependencies = [ - "cfg-if 1.0.4", - "combine", - "jni-macros", - "jni-sys", - "log", - "simd_cesu8", - "thiserror 2.0.18", - "walkdir", - "windows-link", -] - -[[package]] -name = "jni-macros" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" -dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "rustc_version", - "simd_cesu8", - "syn 2.0.118", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote 1.0.46", - "syn 2.0.118", -] - [[package]] name = "jobserver" version = "0.1.34" @@ -3729,12 +3636,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - [[package]] name = "matchers" version = "0.2.0" @@ -4672,63 +4573,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quinn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" -dependencies = [ - "bytes 1.12.0", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.1.3", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" -dependencies = [ - "aws-lc-rs", - "bytes 1.12.0", - "getrandom 0.4.3", - "lru-slab", - "rand 0.10.2", - "rand_pcg 0.10.2", - "ring", - "rustc-hash 2.1.3", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.61.2", -] - [[package]] name = "quote" version = "0.6.13" @@ -4802,7 +4646,7 @@ dependencies = [ "rand_isaac", "rand_jitter 0.1.4", "rand_os", - "rand_pcg 0.1.2", + "rand_pcg", "rand_xorshift", "winapi", ] @@ -5023,15 +4867,6 @@ dependencies = [ "rand_core 0.4.2", ] -[[package]] -name = "rand_pcg" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" -dependencies = [ - "rand_core 0.10.1", -] - [[package]] name = "rand_xorshift" version = "0.1.1" @@ -5198,19 +5033,18 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", - "quinn", - "rustls", "rustls-pki-types", - "rustls-platform-verifier", "sync_wrapper", "tokio", - "tokio-rustls", + "tokio-native-tls", "tower", "tower-http", "tower-service", @@ -5456,7 +5290,6 @@ version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ - "aws-lc-rs", "log", "once_cell", "ring", @@ -5493,44 +5326,15 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ - "web-time", "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - [[package]] name = "rustls-webpki" version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -6022,22 +5826,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" -[[package]] -name = "simd_cesu8" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" -dependencies = [ - "rustc_version", - "simdutf8", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - [[package]] name = "siphasher" version = "0.3.11" @@ -6597,6 +6385,16 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -8366,15 +8164,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "webpki-root-certs" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/api/src/foreign.rs b/api/src/foreign.rs index cd2d142..32a0e8f 100644 --- a/api/src/foreign.rs +++ b/api/src/foreign.rs @@ -27,6 +27,7 @@ use crate::try_slatepack_sync_workflow; use crate::util::secp::key::SecretKey; use crate::util::Mutex; use ed25519_dalek::VerifyingKey as DalekPublicKey; +use libwallet::SlatepackAddress; use std::sync::Arc; /// ForeignAPI Middleware Check callback @@ -345,7 +346,7 @@ where &self, slate: &Slate, dest_acct_name: Option<&str>, - r_addr: Option, + r_addr: Option, ) -> Result { let mut w_lock = self.wallet_inst.lock(); let tor_config = r_addr diff --git a/api/src/foreign_rpc.rs b/api/src/foreign_rpc.rs index 6b67b6e..78a44ea 100644 --- a/api/src/foreign_rpc.rs +++ b/api/src/foreign_rpc.rs @@ -22,6 +22,7 @@ use crate::libwallet::{ use crate::{Foreign, ForeignCheckMiddlewareFn}; use easy_jsonrpc_mw; use grin_wallet_config::initial_setup_wallet; +use libwallet::SlatepackAddress; use std::path::PathBuf; /// Public definition used to generate Foreign jsonrpc api. @@ -312,6 +313,16 @@ where ) -> Result { let version = in_slate.version(); let slate_from = Slate::from(in_slate); + let dest = match dest { + None => None, + Some(a) => match SlatepackAddress::try_from(a.as_str()) { + Ok(d) => Some(d), + Err(_) => { + error!("Error parsing Slatepack address: {}", a); + None + } + }, + }; let out_slate = Foreign::receive_tx( self, &slate_from, diff --git a/api/src/owner.rs b/api/src/owner.rs index cf333f5..6eee54b 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -707,6 +707,12 @@ where args: InitTxArgs, ) -> Result { let send_args = args.send_args.clone(); + let dest = match send_args.as_ref() { + Some(args) if !args.dest.is_empty() => { + Some(SlatepackAddress::try_from(args.dest.as_str())?) + } + _ => None, + }; let (slate, tor_config) = { let mut w_lock = self.wallet_inst.lock(); let tor_config = send_args @@ -725,10 +731,11 @@ where Error::TorConfig("Tor config was not loaded with send arguments".into()) })?; let can_send = tc.send_tor(sa.skip_tor); - if self.doctest_mode || !can_send { + if self.doctest_mode || !can_send || dest.is_none() { return Ok(slate); } - let res = try_slatepack_sync_workflow(&slate, &sa.dest, Some(tc), None, false); + let res = + try_slatepack_sync_workflow(&slate, &dest.unwrap(), Some(tc), None, false); match res { Ok(s) => { self.tx_lock_outputs(keychain_mask, &s)?; @@ -928,6 +935,12 @@ where args: InitTxArgs, ) -> Result { let send_args = args.send_args.clone(); + let dest = match send_args.as_ref() { + Some(args) if !args.dest.is_empty() => { + Some(SlatepackAddress::try_from(args.dest.as_str())?) + } + _ => None, + }; let (slate, tor_config) = { let mut w_lock = self.wallet_inst.lock(); let tor_config = send_args @@ -946,10 +959,11 @@ where Error::TorConfig("Tor config was not loaded with send arguments".into()) })?; let can_send = tc.send_tor(sa.skip_tor); - if self.doctest_mode || !can_send { + if self.doctest_mode || !can_send || dest.is_none() { return Ok(slate); } - let res = try_slatepack_sync_workflow(&slate, &sa.dest, Some(tc), None, true); + let dest = dest.unwrap(); + let res = try_slatepack_sync_workflow(&slate, &dest, Some(tc), None, true); match res { Ok(s) => { // Update slate state. @@ -963,7 +977,7 @@ where } } // Output slatepack message to file. - match output_slatepack_file(&self, keychain_mask, &s, &sa.dest) { + match output_slatepack_file(&self, keychain_mask, &s, Some(dest)) { Ok(_) => {} Err(e) => error!("Error on saving output slatepack message: {}", e), } @@ -2650,7 +2664,7 @@ where /// attempt to send slate synchronously with TOR pub fn try_slatepack_sync_workflow( slate: &Slate, - dest: &str, + dest: &SlatepackAddress, tor_config: Option, tor_sender: Option, send_to_finalize: bool, @@ -2672,42 +2686,32 @@ pub fn try_slatepack_sync_workflow( }; }; - // Try parsing Slatepack address. - match SlatepackAddress::try_from(dest) { - Ok(address) => { - let tor_addr = OnionV3Address::try_from(&address).map_err(|_| { - Error::SlatepackAddress(format!( - "Destination {} is not a valid Onion address.", - dest - )) - })?; - // Try sending to the destination via Tor. - let sender = match tor_sender { - None => { - if let Some(tc) = tor_config { - match TorSlateSender::new(&tor_addr.to_http_str(), tc) { - Ok(s) => s, - Err(e) => { - debug!("Send (Tor): Cannot create TOR Slate sender {:?}", e); - return Err(e); - } - } - } else { - return Err(Error::TorConfig("Tor config is not set".to_string())); + let tor_addr = OnionV3Address::try_from(dest).map_err(|_| { + Error::SlatepackAddress(format!( + "Destination {} is not a valid Onion address.", + dest + )) + })?; + let sender = match tor_sender { + None => { + if let Some(tc) = tor_config { + match TorSlateSender::new(&tor_addr.to_http_str(), tc) { + Ok(s) => s, + Err(e) => { + debug!("Send (Tor): Cannot create TOR Slate sender {:?}", e); + return Err(e); } } - Some(s) => s, - }; - warn!("Attempting to send transaction via Tor"); - match send_sync(sender, "Tor") { - Ok(_) => Ok(ret_slate), - Err(e) => Err(e), + } else { + return Err(Error::TorConfig("Tor config is not set".to_string())); } } - Err(e) => { - error!("Destination {} is not a valid Slatepack address.", dest); - Err(e) - } + Some(s) => s, + }; + warn!("Attempting to send transaction via Tor"); + match send_sync(sender, "Tor") { + Ok(_) => Ok(ret_slate), + Err(e) => Err(e), } } @@ -2786,19 +2790,15 @@ fn output_slatepack_file( api: &Owner, keychain_mask: Option<&SecretKey>, slate: &Slate, - dest: &str, + dest: Option, ) -> Result<(), Error> where L: WalletLCProvider<'static, C, K> + 'static, C: NodeClient + 'static, K: Keychain + 'static, { - let address = match SlatepackAddress::try_from(dest) { - Ok(a) => Some(a), - Err(_) => None, - }; // encrypt for recipient by default - let recipients = match address.clone() { + let recipients = match dest { Some(a) => vec![a], None => vec![], }; diff --git a/api/src/owner_rpc.rs b/api/src/owner_rpc.rs index 461a016..6bf9d8d 100644 --- a/api/src/owner_rpc.rs +++ b/api/src/owner_rpc.rs @@ -1761,7 +1761,7 @@ pub trait OwnerRpc { "params": { "token": "d202964900000000d302964900000000d402964900000000d502964900000000", "secret_indices": [0], - "message": "BEGINSLATEPACK. 8GQrdcwdLKJD28F 3a9siP7ZhZgAh7w BR2EiZHza5WMWmZ Cc8zBUemrrYRjhq j3VBwA8vYnvXXKU BDmQBN2yKgmR8mX UzvXHezfznA61d7 qFZYChhz94vd8Ew NEPLz7jmcVN2C3w wrfHbeiLubYozP2 uhLouFiYRrbe3fQ 4uhWGfT3sQYXScT dAeo29EaZJpfauh j8VL5jsxST2SPHq nzXFC2w9yYVjt7D ju7GSgHEp5aHz9R xstGbHjbsb4JQod kYLuELta1ohUwDD pvjhyJmsbLcsPei k5AQhZsJ8RJGBtY bou6cU7tZeFJvor 4LB9CBfFB3pmVWD vSLd5RPS75dcnHP nbXD8mSDZ8hJS2Q A9wgvppWzuWztJ2 dLUU8f9tLJgsRBw YZAs71HiVeg7. ENDSLATEPACK." + "message": "BEGINSLATEPACK. t9EcGgrKr1GFCQB SK2jPCxME6Hgpqx bntpQm3zKFycoPY nW4UeoL4KQ7ExNK At6EQsvpz6MjUs8 6WG8KHEbMfqufJQ ZJTw2gkcdJmJjiJ f29oGgYqqXDZox4 ujPSjrtoxCN4h3e i1sZ8dYsm3dPeXL 7VQLsYNjAefciqj ZJXPm4Pqd7VDdd4 okGBGBu3YJvYzT6 arAxeCEx66us31h AJLcDweFwyWBkW5 J1DLiYAjt5ftFTo CjpfW9KjiLq2LM5 jepXWEHJPSDAYVK 4macDZUhRbJiG6E hrQcPrJBVC716mb Hw5E1PFrE6on5wq oEmrS4j9vaB5nw8 Z9ZyXvPc2LN7tER yt6pSHZeY9EpYdY zv4bthzfRfF8ePT TMeMpV2gpgyRXQa CPD2TR. ENDSLATEPACK." }, "id": 1 } @@ -1905,9 +1905,9 @@ pub trait OwnerRpc { "proof": { "amount": "60000000000", "excess": "09eac5f5872fa5e08e0c29fd900f1b8f77ff3ad1d0d1c46aeb202cbf92363fe0af", - "recipient_address": "slatepack10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqnlumr7", + "recipient_address": "tgrin10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqq6t83r", "recipient_sig": "02868f2d2b983981f8f98043701687a8531ed2de564ea3df48e9e7e0229ccbe8359efe506896df2efbe3528e977252c50e4a41ca3cc9896e7c5a30bbb1d33604", - "sender_address": "slatepack1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfskdvkdu", + "sender_address": "tgrin1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfs9gm2lp", "sender_sig": "c511764f3f61ed3d1cbca9514df8bc6811fad5662b1cb0e0587b9c9e49db9f33183cce71af6cb24b507fabf525a2bc405c6e84e63a60334edff0b451ae5e6102" } }, @@ -2720,7 +2720,8 @@ pub fn run_doctest_owner( true => { let address = "783f6528669742a990e0faf0a5fca5d5b3330e37bbb9cd5c628696d03ce4e810"; let address = OnionV3Address::try_from(address).unwrap(); - Some(SlatepackAddress::try_from(address).unwrap()) + let sp = SlatepackAddress::try_from(address).unwrap(); + Some(sp) } false => None, }; diff --git a/controller/src/command.rs b/controller/src/command.rs index ca98c1e..d99c092 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -322,12 +322,12 @@ pub struct SendArgs { pub selection_strategy: String, pub estimate_selection_strategies: bool, pub late_lock: bool, - pub dest: String, + pub dest: Option, pub change_outputs: usize, pub fluff: bool, pub max_outputs: usize, pub target_slate_version: Option, - pub payment_proof_address: Option, + pub payment_proof_address: Option, pub ttl_blocks: Option, pub skip_tor: Option, pub outfile: Option, @@ -348,6 +348,15 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { + let dest = if args.estimate_selection_strategies { + None + } else { + args.dest + .as_deref() + .map(SlatepackAddress::try_from) + .transpose()? + }; + let mut slate = Slate::blank(2, false); let mut amount = args.amount; if args.use_max_amount { @@ -376,6 +385,11 @@ where .collect::, grin_wallet_libwallet::Error>>()?; display::estimate(amount, strategies, dark_scheme); } else { + let payment_proof_recipient_address = args + .payment_proof_address + .as_deref() + .map(SlatepackAddress::try_from) + .transpose()?; let init_args = InitTxArgs { src_acct_name: None, amount, @@ -385,7 +399,7 @@ where num_change_outputs: args.change_outputs as u32, selection_strategy_is_use_all: args.selection_strategy == "all", target_slate_version: args.target_slate_version, - payment_proof_recipient_address: args.payment_proof_address.clone(), + payment_proof_recipient_address, ttl_blocks: args.ttl_blocks, send_args: None, late_lock: Some(args.late_lock), @@ -397,7 +411,9 @@ where info!( "Tx created: {} grin to {} (strategy '{}')", core::amount_to_hr_string(amount, false), - args.dest, + dest.as_ref() + .map(ToString::to_string) + .unwrap_or_else(|| "no destination".to_string()), args.selection_strategy, ); s @@ -422,7 +438,7 @@ where owner_api, keychain_mask, &slate, - args.dest.as_str(), + dest.clone(), args.outfile, true, false, @@ -431,11 +447,12 @@ where }; let can_send = tor_config.send_tor(args.skip_tor); - if test_mode || !can_send { + if test_mode || !can_send || dest.is_none() { return output_sp(); } - let res = try_slatepack_sync_workflow(&slate, &args.dest, Some(tor_config), None, false); + let dest = dest.as_ref().unwrap(); + let res = try_slatepack_sync_workflow(&slate, dest, Some(tor_config), None, false); match res { Ok(s) => { @@ -464,7 +481,7 @@ pub fn output_slatepack( owner_api: &mut Owner, keychain_mask: Option<&SecretKey>, slate: &Slate, - dest: &str, + dest: Option, out_file_override: Option, lock: bool, finalizing: bool, @@ -476,12 +493,8 @@ where K: keychain::Keychain + 'static, { // Output the slatepack file to stdout and to a file - let address = match SlatepackAddress::try_from(dest) { - Ok(a) => Some(a), - Err(_) => None, - }; // encrypt for recipient by default - let recipients = match address.clone() { + let recipients = match dest.clone() { Some(a) => vec![a], None => vec![], }; @@ -527,7 +540,7 @@ where println!(); } } - if address.is_some() { + if dest.is_some() { println!("The slatepack data is encrypted for the recipient only"); } else { println!("The slatepack data is NOT encrypted"); @@ -783,17 +796,12 @@ where }, )?; - let dest = match ret_address { - Some(a) => String::try_from(&a)?, - None => String::from(""), - }; - let output_sp = || -> Result<(), Error> { Ok(output_slatepack( owner_api, keychain_mask, &slate, - &dest, + ret_address.clone(), args.outfile, false, false, @@ -802,11 +810,12 @@ where }; let can_send = tor_config.send_tor(args.skip_tor); - if test_mode || !can_send { + if test_mode || !can_send || ret_address.is_none() { return output_sp(); } - let res = try_slatepack_sync_workflow(&slate, &dest, Some(tor_config), None, true); + let dest = ret_address.as_ref().unwrap(); + let res = try_slatepack_sync_workflow(&slate, dest, Some(tor_config), None, true); match res { Ok(s) => { @@ -974,7 +983,7 @@ where owner_api, keychain_mask, &slate, - "", + None, args.outfile, false, true, @@ -987,7 +996,7 @@ where /// Issue Invoice Args pub struct IssueInvoiceArgs { /// Slatepack address - pub dest: String, + pub dest: Option, /// issue invoice tx args pub issue_args: IssueInvoiceTxArgs, /// output file override @@ -1006,6 +1015,11 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { + let dest = args + .dest + .as_deref() + .map(SlatepackAddress::try_from) + .transpose()?; let issue_args = args.issue_args.clone(); let slate = owner_api.issue_invoice_tx(keychain_mask, issue_args)?; @@ -1014,7 +1028,7 @@ where owner_api, keychain_mask, &slate, - args.dest.as_str(), + dest, args.outfile, false, false, @@ -1027,7 +1041,7 @@ where pub struct ProcessInvoiceArgs { pub minimum_confirmations: u64, pub selection_strategy: String, - pub ret_address: Option, + pub ret_address: Option, pub max_outputs: usize, pub slate: Slate, pub estimate_selection_strategies: bool, @@ -1052,11 +1066,12 @@ where C: NodeClient + 'static, K: keychain::Keychain + 'static, { + let ret_address = args + .ret_address + .as_deref() + .map(SlatepackAddress::try_from) + .transpose()?; let mut slate = args.slate.clone(); - let dest = match args.ret_address.clone() { - Some(a) => String::try_from(&a)?, - None => String::from(""), - }; if args.estimate_selection_strategies { let strategies = vec!["smallest", "all"] @@ -1115,7 +1130,7 @@ where owner_api, keychain_mask, &slate, - &dest, + ret_address.clone(), args.outfile, true, false, @@ -1124,11 +1139,12 @@ where }; let can_send = tor_config.send_tor(args.skip_tor); - if test_mode || !can_send { + if test_mode || !can_send || ret_address.is_none() { return output_sp(); } - let res = try_slatepack_sync_workflow(&slate, &dest, Some(tor_config), None, true); + let dest = ret_address.as_ref().unwrap(); + let res = try_slatepack_sync_workflow(&slate, dest, Some(tor_config), None, true); match res { Ok(s) => { diff --git a/controller/tests/config_api.rs b/controller/tests/config_api.rs index fd37e10..5936a7f 100644 --- a/controller/tests/config_api.rs +++ b/controller/tests/config_api.rs @@ -129,7 +129,7 @@ fn send_preflight() { let args = InitTxArgs { amount: 1, send_args: Some(InitTxSendArgs { - dest: "unused".into(), + dest: "tgrin1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfs9gm2lp".into(), post_tx: false, fluff: false, skip_tor: Some(true), diff --git a/controller/tests/slatepack.rs b/controller/tests/slatepack.rs index cf2a7e1..9bdd740 100644 --- a/controller/tests/slatepack.rs +++ b/controller/tests/slatepack.rs @@ -18,6 +18,7 @@ extern crate grin_wallet_controller as wallet; extern crate grin_wallet_impls as impls; use grin_core as core; +use grin_wallet_config::GlobalWalletConfig; use grin_wallet_libwallet as libwallet; use std::path::PathBuf; @@ -28,8 +29,8 @@ use std::thread; use std::time::Duration; use grin_wallet_libwallet::{ - InitTxArgs, IssueInvoiceTxArgs, Slate, Slatepack, SlatepackAddress, Slatepacker, - SlatepackerArgs, + InitTxArgs, InitTxSendArgs, IssueInvoiceTxArgs, Slate, Slatepack, SlatepackAddress, + Slatepacker, SlatepackerArgs, }; use ed25519_dalek::SigningKey as edDalekSecretKey; @@ -601,6 +602,110 @@ fn slatepack_api_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { Ok(()) } +/// Do not create transaction for invalid or wrong network Slatepack address. +fn slatepack_address_validation(test_dir: &'static str) -> Result<(), libwallet::Error> { + let mut wallet_proxy = create_wallet_proxy(test_dir); + let chain = wallet_proxy.chain.clone(); + let stopper = wallet_proxy.running.clone(); + let config_path = PathBuf::from(test_dir).join("grin-wallet.toml"); + GlobalWalletConfig::for_chain(&core::global::ChainTypes::AutomatedTesting, &config_path) + .write_to_file(config_path.to_str().unwrap(), false, None, None) + .unwrap(); + + create_wallet_and_add!( + client1, + wallet1, + mask1_i, + test_dir, + "wallet1", + None, + &mut wallet_proxy, + false + ); + let mask1 = (&mask1_i).as_ref(); + + create_wallet_and_add!( + client2, + wallet2, + mask2_i, + test_dir, + "wallet2", + None, + &mut wallet_proxy, + false + ); + let mask2 = (&mask2_i).as_ref(); + + let proxy_thread = thread::spawn(move || { + if let Err(e) = wallet_proxy.run() { + error!("Wallet Proxy error: {}", e); + } + }); + + let reward = core::consensus::REWARD; + let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 6, false); + let mut slate = Slate::blank(2, true); + + wallet::controller::owner_single_use(wallet2.clone(), mask2, config_path.clone(), |api, m| { + slate = api.issue_invoice_tx( + m, + IssueInvoiceTxArgs { + amount: reward, + ..Default::default() + }, + )?; + Ok(()) + })?; + + wallet::controller::owner_single_use(wallet1.clone(), mask1, config_path, |api, m| { + let args = InitTxArgs { + src_acct_name: Some("mining".to_owned()), + amount: reward, + minimum_confirmations: 2, + max_outputs: 500, + num_change_outputs: 1, + selection_strategy_is_use_all: true, + ..Default::default() + }; + + let mut wrong_net_args = args.clone(); + wrong_net_args.send_args = Some(InitTxSendArgs { + dest: "grin1dvge9z4uqgqlpspmljrd7smh3grrw9xu2r9lkz3u67s3emj3ud2sd5gk9p".to_string(), + post_tx: false, + fluff: false, + skip_tor: Some(true), + }); + assert!(api.init_send_tx(m, wrong_net_args.clone()).is_err()); + assert!(api.process_invoice_tx(m, &slate, wrong_net_args).is_err()); + + let mut invalid_args = args.clone(); + invalid_args.send_args = Some(InitTxSendArgs { + dest: "tgrinaddr10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqnlumr7" + .to_string(), + post_tx: false, + fluff: false, + skip_tor: Some(true), + }); + assert!(api.init_send_tx(m, invalid_args.clone()).is_err()); + assert!(api.process_invoice_tx(m, &slate, invalid_args).is_err()); + + let mut valid_args = args; + valid_args.send_args = Some(InitTxSendArgs { + dest: "tgrin1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfs9gm2lp".to_string(), + post_tx: false, + fluff: false, + skip_tor: Some(true), + }); + api.process_invoice_tx(m, &slate, valid_args.clone())?; + api.init_send_tx(m, valid_args)?; + Ok(()) + })?; + + stopper.store(false, Ordering::Relaxed); + proxy_thread.join().expect("wallet proxy thread panicked"); + Ok(()) +} + #[test] fn slatepack_exchange_json() { let test_dir = "test_output/slatepack_exchange_json"; @@ -677,3 +782,13 @@ fn slatepack_api() { } clean_output_dir(test_dir); } + +#[test] +fn slatepack_address() { + let test_dir = "test_output/slatepack_address"; + setup(test_dir); + if let Err(e) = slatepack_address_validation(test_dir) { + panic!("Libwallet Error: {}", e); + } + clean_output_dir(test_dir); +} diff --git a/impls/Cargo.toml b/impls/Cargo.toml index 893f61f..da64641 100644 --- a/impls/Cargo.toml +++ b/impls/Cargo.toml @@ -22,7 +22,7 @@ ring = "0.17.14" chrono = { version = "0.4.11", features = ["serde"] } lazy_static = "1" tokio = { version = "1.52.3", features = ["full"] } -reqwest = { version = "0.13.3", features = ["socks"] } +reqwest = { version = "0.13.4", default-features = false, features = ["socks", "native-tls", "system-proxy", "charset", "http2"] } #Socks/Tor/Bridge/Proxy ed25519-dalek = "2" diff --git a/impls/src/client_utils/client.rs b/impls/src/client_utils/client.rs index 4bb8f97..94b7b37 100644 --- a/impls/src/client_utils/client.rs +++ b/impls/src/client_utils/client.rs @@ -72,7 +72,6 @@ impl Client { let mut builder = ClientBuilder::new() .timeout(request_timeout) - .use_rustls_tls() .default_headers(headers); if let Some(p) = proxy { diff --git a/libwallet/Cargo.toml b/libwallet/Cargo.toml index b2770aa..fd8842b 100644 --- a/libwallet/Cargo.toml +++ b/libwallet/Cargo.toml @@ -9,6 +9,9 @@ keywords = [ "crypto", "grin", "mimblewimble" ] exclude = ["**/*.grin", "**/*.grin2"] edition = "2021" +[features] +mwixnet-test = [] + [dependencies] blake2-rfc = "0.2" rand = "0.6" diff --git a/libwallet/src/api_impl/owner.rs b/libwallet/src/api_impl/owner.rs index 0241de2..a888467 100644 --- a/libwallet/src/api_impl/owner.rs +++ b/libwallet/src/api_impl/owner.rs @@ -614,6 +614,19 @@ where C: NodeClient, K: Keychain, { + let payment_proof_address = if let Some(a) = &args.payment_proof_recipient_address { + if a.valid_network() { + Some(a) + } else { + return Err(Error::PaymentProofRetrieval(format!( + "Wrong network for address {}", + a + ))); + } + } else { + None + }; + let parent_key_id = match &args.src_acct_name { Some(d) => { let pm = w.get_acct_path(d.clone())?; @@ -683,7 +696,7 @@ where // probably want to allow sender to specify which one let deriv_path = 0u32; - if let Some(a) = args.payment_proof_recipient_address { + if let Some(a) = payment_proof_address { let k = w.keychain(keychain_mask)?; let sec_addr_key = address::address_from_derivation_path(&k, &parent_key_id, deriv_path)?; diff --git a/libwallet/src/mwixnet/mod.rs b/libwallet/src/mwixnet/mod.rs index 0605265..ebc5a9c 100644 --- a/libwallet/src/mwixnet/mod.rs +++ b/libwallet/src/mwixnet/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. //! Onion modules for mxmixnet -mod onion; +pub mod onion; mod types; pub use onion::{ diff --git a/libwallet/src/mwixnet/onion/crypto/dalek.rs b/libwallet/src/mwixnet/onion/crypto/dalek.rs index 62084dd..adbebd7 100644 --- a/libwallet/src/mwixnet/onion/crypto/dalek.rs +++ b/libwallet/src/mwixnet/onion/crypto/dalek.rs @@ -69,7 +69,6 @@ impl AsRef for DalekPublicKey { } } -#[cfg(test)] /// Serializes an Option to and from hex pub mod option_dalek_pubkey_serde { use super::DalekPublicKey; @@ -152,7 +151,6 @@ impl AsRef for DalekSignature { } /// Serializes a DalekSignature to and from hex -#[cfg(test)] pub mod dalek_sig_serde { use super::DalekSignature; use grin_util::ToHex; @@ -179,8 +177,6 @@ pub mod dalek_sig_serde { } /// Dalek signature sign wrapper -// TODO: This is likely duplicated throughout crate, check -#[cfg(test)] pub fn sign(sk: &SecretKey, message: &[u8]) -> Result { use ed25519_dalek::{Signer, SigningKey}; let secret = SigningKey::from_bytes(&sk.0); diff --git a/libwallet/src/mwixnet/onion/crypto/secp.rs b/libwallet/src/mwixnet/onion/crypto/secp.rs index eda6abe..200c24f 100644 --- a/libwallet/src/mwixnet/onion/crypto/secp.rs +++ b/libwallet/src/mwixnet/onion/crypto/secp.rs @@ -18,7 +18,7 @@ pub use grin_util::secp::{ self as secp256k1zkp, constants::SECRET_KEY_SIZE, key::{SecretKey, ZERO_KEY}, - pedersen::Commitment, + pedersen::{Commitment, RangeProof}, rand::thread_rng, ContextFlag, Secp256k1, }; @@ -47,7 +47,6 @@ pub fn read_secret_key(reader: &mut R) -> Result Result { let secp = Secp256k1::with_caps(ContextFlag::Commit); let commit = secp.commit(value, blind.clone())?; @@ -76,7 +75,6 @@ pub fn sub_value(commitment: &Commitment, value: u64) -> Result &'static str { + match global::get_chain_type() { + global::ChainTypes::Mainnet => "grin", + _ => "tgrin", + } +} + impl SlatepackAddress { /// new with default hrp pub fn new(pub_key: &edDalekPublicKey) -> Self { - let hrp = match global::get_chain_type() { - global::ChainTypes::Mainnet => "grin", - _ => "tgrin", - }; Self { - hrp: String::from(hrp), + hrp: String::from(slatepack_hrp()), pub_key: pub_key.clone(), } } @@ -75,11 +78,19 @@ impl SlatepackAddress { let x_key = xDalekPublicKey::try_from(self)?; Ok(bech32::encode("age", x_key.as_bytes().to_base32())?.to_string()) } + + /// Check if address network is valid. + pub fn valid_network(&self) -> bool { + valid_network(&self.hrp) + } } impl Display for SlatepackAddress { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(&String::try_from(self).unwrap()) + match String::try_from(self) { + Ok(encoded) => formatter.write_str(&encoded), + Err(_) => formatter.write_str(""), + } } } @@ -87,6 +98,12 @@ impl TryFrom<&str> for SlatepackAddress { type Error = Error; fn try_from(encoded: &str) -> Result { let (hrp, data) = bech32::decode(&encoded)?; + if !valid_network(&hrp) { + return Err(Error::SlatepackAddress(format!( + "wrong address prefix for chain {:?}", + global::get_chain_type() + ))); + } let bytes = Vec::::from_base32(&data)?; let b = <&[u8; 32]>::try_from(bytes.as_slice()) .map_err(|_| Error::SlatepackAddress("Wrong encoded data".to_string()))?; @@ -242,10 +259,52 @@ impl Readable for SlatepackAddress { } } +/// Check if encoded HRP is for valid network. +fn valid_network(hrp: &str) -> bool { + slatepack_hrp() == hrp +} + #[test] fn slatepack_address() -> Result<(), Error> { use rand::{thread_rng, Rng}; global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + + // Check validation. + let bytes: [u8; 32] = thread_rng().gen(); + let pub_key = edDalekPublicKey::from(&edDalekSecretKey::from_bytes(&bytes)); + let wrong_net_addr = SlatepackAddress { + hrp: "grin".to_string(), + pub_key, + }; + assert!(!wrong_net_addr.valid_network()); + let right_addr = SlatepackAddress { + hrp: "tgrin".to_string(), + pub_key, + }; + assert!(right_addr.valid_network()); + let invalid_addr = SlatepackAddress { + hrp: "invalid hrp".to_string(), + pub_key, + }; + assert!(!invalid_addr.valid_network()); + assert_eq!(invalid_addr.to_string(), ""); + + let valid_addr = "tgrin1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfs9gm2lp"; + let parsed_valid_addr = SlatepackAddress::try_from(valid_addr); + assert!(parsed_valid_addr.is_ok()); + + let valid_addr2 = "TGRIN1XTXAVWFGS48CKF3GK8WWGCNDMN0NT4TVKL8A7LTYEJJCY2MC6NFS9GM2LP"; + let parsed_valid_addr2 = SlatepackAddress::try_from(valid_addr2); + assert!(parsed_valid_addr2.is_ok()); + + let invalid_addr = "slatepack10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqnlumr7"; + let parsed_invalid_addr = SlatepackAddress::try_from(invalid_addr); + assert!(parsed_invalid_addr.is_err()); + + let wrong_net_addr = "grin1dvge9z4uqgqlpspmljrd7smh3grrw9xu2r9lkz3u67s3emj3ud2sd5gk9p"; + let parsed_wrong_net_addr = SlatepackAddress::try_from(wrong_net_addr); + assert!(parsed_wrong_net_addr.is_err()); + let sec_key_bytes: [u8; 32] = thread_rng().gen(); let ed_sec_key = edDalekSecretKey::from_bytes(&sec_key_bytes); diff --git a/src/bin/grin-wallet.yml b/src/bin/grin-wallet.yml index 98c24c9..fc0fe26 100644 --- a/src/bin/grin-wallet.yml +++ b/src/bin/grin-wallet.yml @@ -132,7 +132,7 @@ subcommands: default_value: "1" takes_value: true - dest: - help: Intended recipient's Slatepack Address (or http listener address (DEPRECATED)) + help: Intended recipient's Slatepack Address short: d long: dest takes_value: true diff --git a/src/cmd/wallet_args.rs b/src/cmd/wallet_args.rs index 7be382d..18f0af6 100644 --- a/src/cmd/wallet_args.rs +++ b/src/cmd/wallet_args.rs @@ -476,7 +476,11 @@ pub fn parse_send_args(args: &ArgMatches) -> Result Result match SlatepackAddress::try_from(dest) { - Ok(a) => Some(a), - Err(_) => { - if !estimate_selection_strategies { - println!("No recipient Slatepack address or provided address invalid. No payment proof will be requested."); - } - None - } - }, - true => None, + if let Some(a) = dest.clone() { + match args.is_present("no_payment_proof") { + false => Some(a), + true => None, + } + } else { + if !estimate_selection_strategies { + println!("No recipient Slatepack address or provided address invalid. No payment proof will be requested."); + } + None } }; @@ -540,7 +543,7 @@ pub fn parse_send_args(args: &ArgMatches) -> Result