From 98767bb95668727d66f7c9e34430468b276b2095 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Wed, 10 Jun 2020 13:42:03 +0100 Subject: [PATCH] add wallet unpack command --- api/src/owner.rs | 12 +++-- api/src/owner_rpc.rs | 17 +++++-- controller/src/command.rs | 84 +++++++++++++++++++++++++++++-- controller/tests/slatepack.rs | 4 +- impls/src/adapters/slatepack.rs | 6 +-- libwallet/src/api_impl/owner.rs | 8 +-- libwallet/src/slatepack/packer.rs | 6 ++- src/bin/grin-wallet.yml | 26 ++++++---- src/cmd/wallet_args.rs | 30 +++++++++++ 9 files changed, 163 insertions(+), 30 deletions(-) diff --git a/api/src/owner.rs b/api/src/owner.rs index 3a3922242..92119b5e9 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -2123,6 +2123,7 @@ where /// /// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using /// * `slatepack` - A string representing an armored slatepack + /// * `decrypt` - If true and the slatepack message content is encrypted, attempt to decrypt /// /// # Returns /// * Ok with a [Slatepack](../grin_wallet_libwallet/slatepack/types/struct.Slatepack.html) if successful @@ -2142,13 +2143,18 @@ where /// # let slatepack_string = String::from(""); /// // .. receive a slatepack from somewhere /// let res = api_owner.decode_slatepack_message( - /// slatepack_string + /// slatepack_string, + /// false, /// ); /// /// ``` - pub fn decode_slatepack_message(&self, slatepack: String) -> Result { - owner::decode_slatepack_message(slatepack) + pub fn decode_slatepack_message( + &self, + slatepack: String, + decrypt: bool, + ) -> Result { + owner::decode_slatepack_message(slatepack, decrypt) } // PAYMENT PROOFS diff --git a/api/src/owner_rpc.rs b/api/src/owner_rpc.rs index d159351b0..7caf46c61 100644 --- a/api/src/owner_rpc.rs +++ b/api/src/owner_rpc.rs @@ -1577,7 +1577,8 @@ pub trait OwnerRpc { "jsonrpc": "2.0", "method": "decode_slatepack_message", "params": { - "message": "BEGINSLATEPACK. 8GQrdcwdLKJD28F 3a9siP7ZhZgAh7w\nBR2EiZHza5WMWmZ Cc8zBUemrrYRjhq j3VBwA8vYnvXXKU\nBDmQBN2yKgmR8mX UzvXHezfznA61d7 qFZYChhz94vd8Ew\nNEPLz7jmcVN2C3w wrfHbeiLubYozP2 uhLouFiYRrbe3fQ\n4uhWGfT3sQYXScT dAeo29EaZJpfauh j8VL5jsxST2SPHq\nnzXFC2w9yYVjt7D ju7GSgHEp5aHz9R xstGbHjbsb4JQod\nkYLuELta1ohUwDD pvjhyJmsbLcsPei k5AQhZsJ8RJGBtY\nbou6cU7tZeFJvor 4LB9CBfFB3pmVWD vSLd5RPS75dcnHP\nnbXD8mSDZ8hJS2Q A9wgvppWzuWztJ2 dLUU8f9tLJgsRBw\nYZAs71HiVeg7. ENDSLATEPACK.\n" + "message": "BEGINSLATEPACK. 8GQrdcwdLKJD28F 3a9siP7ZhZgAh7w\nBR2EiZHza5WMWmZ Cc8zBUemrrYRjhq j3VBwA8vYnvXXKU\nBDmQBN2yKgmR8mX UzvXHezfznA61d7 qFZYChhz94vd8Ew\nNEPLz7jmcVN2C3w wrfHbeiLubYozP2 uhLouFiYRrbe3fQ\n4uhWGfT3sQYXScT dAeo29EaZJpfauh j8VL5jsxST2SPHq\nnzXFC2w9yYVjt7D ju7GSgHEp5aHz9R xstGbHjbsb4JQod\nkYLuELta1ohUwDD pvjhyJmsbLcsPei k5AQhZsJ8RJGBtY\nbou6cU7tZeFJvor 4LB9CBfFB3pmVWD vSLd5RPS75dcnHP\nnbXD8mSDZ8hJS2Q A9wgvppWzuWztJ2 dLUU8f9tLJgsRBw\nYZAs71HiVeg7. ENDSLATEPACK.\n", + "decrypt" : false }, "id": 1 } @@ -1601,7 +1602,11 @@ pub trait OwnerRpc { ``` */ - fn decode_slatepack_message(&self, message: String) -> Result; + fn decode_slatepack_message( + &self, + message: String, + decrypt: bool, + ) -> Result; /** Networked version of [Owner::retrieve_payment_proof](struct.Owner.html#method.retrieve_payment_proof). @@ -2069,8 +2074,12 @@ where Ok(VersionedSlate::into_version(slate, version).map_err(|e| e.kind())?) } - fn decode_slatepack_message(&self, message: String) -> Result { - Owner::decode_slatepack_message(self, message).map_err(|e| e.kind()) + fn decode_slatepack_message( + &self, + message: String, + decrypt: bool, + ) -> Result { + Owner::decode_slatepack_message(self, message, decrypt).map_err(|e| e.kind()) } fn retrieve_payment_proof( diff --git a/controller/src/command.rs b/controller/src/command.rs index 07d610b30..ba6579a41 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -23,7 +23,7 @@ use crate::impls::SlateGetter as _; use crate::impls::{HttpSlateSender, PathToSlate, PathToSlatepack, SlatePutter}; use crate::keychain; use crate::libwallet::{ - self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateVersion, + self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateVersion, Slatepack, SlatepackAddress, Slatepacker, SlatepackerArgs, WalletLCProvider, }; use crate::util::secp::key::SecretKey; @@ -603,7 +603,7 @@ where }); let pts = PathToSlatepack::new(f.into(), &packer, true); sl = Some(pts.get_tx()?.0); - ret_address = pts.get_slatepack()?.sender; + ret_address = pts.get_slatepack(true)?.sender; Ok(()) })?; } @@ -626,7 +626,7 @@ where |api, m| { slate = api.slate_from_slatepack_message(m, message.clone(), vec![0])?; - let slatepack = api.decode_slatepack_message(message)?; + let slatepack = api.decode_slatepack_message(message, true)?; ret_address = slatepack.sender; Ok(()) }, @@ -713,6 +713,84 @@ where } } +pub fn unpack( + owner_api: &mut Owner, + keychain_mask: Option<&SecretKey>, + args: ReceiveArgs, +) -> Result<(), Error> +where + L: WalletLCProvider<'static, C, K> + 'static, + C: NodeClient + 'static, + K: keychain::Keychain + 'static, +{ + let mut slatepack = match args.input_file { + Some(f) => { + let packer = Slatepacker::new(SlatepackerArgs { + sender: None, + recipients: vec![], + dec_key: None, + }); + PathToSlatepack::new(f.into(), &packer, true).get_slatepack(false)? + } + None => match args.input_slatepack_message { + Some(mes) => { + let mut sp = Slatepack::default(); + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, _| { + sp = api.decode_slatepack_message(mes, false)?; + Ok(()) + })?; + sp + } + None => { + return Err(ErrorKind::ArgumentError("Invalid Slatepack Input".into()).into()); + } + }, + }; + println!(); + println!("SLATEPACK CONTENTS"); + println!("------------------"); + println!("{}", slatepack); + println!("------------------"); + + let packer = Slatepacker::new(SlatepackerArgs { + sender: None, + recipients: vec![], + dec_key: None, + }); + + if slatepack.mode == 1 { + controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { + let dec_key = api.get_slatepack_secret_key(m, 0)?; + match slatepack.try_decrypt_payload(Some(&dec_key)) { + Ok(_) => { + println!("Slatepack is encrypted for this wallet"); + println!(); + println!("DECRYPTED SLATEPACK"); + println!("-------------------"); + println!("{}", slatepack); + let slate = packer.get_slate(&slatepack)?; + println!(); + println!("DECRYPTED SLATE"); + println!("---------------"); + println!("{}", slate); + } + Err(_) => { + println!("Slatepack payload cannot be decrypted by this wallet"); + } + } + Ok(()) + })?; + } else { + let slate = packer.get_slate(&slatepack)?; + println!("Slatepack is not encrypted"); + println!(); + println!("SLATE"); + println!("-----"); + println!("{}", slate); + } + Ok(()) +} + /// Finalize command args #[derive(Clone)] pub struct FinalizeArgs { diff --git a/controller/tests/slatepack.rs b/controller/tests/slatepack.rs index d5129b7c3..bc7c8cc5b 100644 --- a/controller/tests/slatepack.rs +++ b/controller/tests/slatepack.rs @@ -72,7 +72,7 @@ fn slate_from_packed( if armored { file = format!("{}.armored", file); } - let slatepack = PathToSlatepack::new(file.into(), &packer, armored).get_slatepack()?; + let slatepack = PathToSlatepack::new(file.into(), &packer, armored).get_slatepack(true)?; Ok((slatepack.clone(), packer.get_slate(&slatepack)?)) } @@ -496,7 +496,7 @@ fn slatepack_api_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { let enc_addr = api.get_slatepack_address(m, 0)?; let slatepack = api.create_slatepack_message(m, &slate, Some(0), vec![enc_addr])?; println!("{}", slatepack); - let slatepack_raw = api.decode_slatepack_message(slatepack.clone())?; + let slatepack_raw = api.decode_slatepack_message(slatepack.clone(), true)?; println!("{}", slatepack_raw); let decoded_slate = api.slate_from_slatepack_message(m, slatepack, vec![0])?; println!("{}", decoded_slate); diff --git a/impls/src/adapters/slatepack.rs b/impls/src/adapters/slatepack.rs index acd5bfcb1..987e5a5f3 100644 --- a/impls/src/adapters/slatepack.rs +++ b/impls/src/adapters/slatepack.rs @@ -45,9 +45,9 @@ impl<'a> PathToSlatepack<'a> { Ok(data) } - pub fn get_slatepack(&self) -> Result { + pub fn get_slatepack(&self, decrypt: bool) -> Result { let data = self.get_slatepack_file_contents()?; - self.packer.deser_slatepack(data) + self.packer.deser_slatepack(data, decrypt) } } @@ -80,7 +80,7 @@ impl<'a> SlatePutter for PathToSlatepack<'a> { impl<'a> SlateGetter for PathToSlatepack<'a> { fn get_tx(&self) -> Result<(Slate, bool), Error> { let data = self.get_slatepack_file_contents()?; - let slatepack = self.packer.deser_slatepack(data)?; + let slatepack = self.packer.deser_slatepack(data, true)?; Ok((self.packer.get_slate(&slatepack)?, true)) } } diff --git a/libwallet/src/api_impl/owner.rs b/libwallet/src/api_impl/owner.rs index da903de94..aaa41f7a3 100644 --- a/libwallet/src/api_impl/owner.rs +++ b/libwallet/src/api_impl/owner.rs @@ -169,7 +169,7 @@ where recipients: vec![], dec_key: None, }); - let slatepack = packer.deser_slatepack(slatepack.as_bytes().to_vec())?; + let slatepack = packer.deser_slatepack(slatepack.as_bytes().to_vec(), true)?; return packer.get_slate(&slatepack); } else { for index in secret_indices { @@ -183,7 +183,7 @@ where recipients: vec![], dec_key: (&dec_key).as_ref(), }); - let res = packer.deser_slatepack(slatepack.as_bytes().to_vec()); + let res = packer.deser_slatepack(slatepack.as_bytes().to_vec(), true); let slatepack = match res { Ok(sp) => sp, Err(_) => { @@ -201,13 +201,13 @@ where } /// Decode a slatepack message, to allow viewing -pub fn decode_slatepack_message(slatepack: String) -> Result { +pub fn decode_slatepack_message(slatepack: String, decrypt: bool) -> Result { let packer = Slatepacker::new(SlatepackerArgs { sender: None, recipients: vec![], dec_key: None, }); - packer.deser_slatepack(slatepack.as_bytes().to_vec()) + packer.deser_slatepack(slatepack.as_bytes().to_vec(), decrypt) } /// retrieve outputs diff --git a/libwallet/src/slatepack/packer.rs b/libwallet/src/slatepack/packer.rs index a96515cc1..388056c2c 100644 --- a/libwallet/src/slatepack/packer.rs +++ b/libwallet/src/slatepack/packer.rs @@ -47,7 +47,7 @@ impl<'a> Slatepacker<'a> { } /// return slatepack - pub fn deser_slatepack(&self, data: Vec) -> Result { + pub fn deser_slatepack(&self, data: Vec, decrypt: bool) -> Result { // check if data is armored, if so, remove and continue if data.len() < super::armor::HEADER.len() { let msg = format!("Data too short"); @@ -90,7 +90,9 @@ impl<'a> Slatepacker<'a> { }; slatepack.ver_check_warn(); - slatepack.try_decrypt_payload(self.0.dec_key)?; + if decrypt { + slatepack.try_decrypt_payload(self.0.dec_key)?; + } Ok(slatepack) } diff --git a/src/bin/grin-wallet.yml b/src/bin/grin-wallet.yml index cf07022cb..c90589774 100644 --- a/src/bin/grin-wallet.yml +++ b/src/bin/grin-wallet.yml @@ -88,7 +88,7 @@ subcommands: long: run_foreign takes_value: false - send: - about: Builds a transaction to send coins and sends to the specified listener directly + about: Builds a transaction to send coins and sends to the recipient via the Slatepack workflow args: - amount: help: Number of coins to send with optional fraction, e.g. 12.423 @@ -119,7 +119,7 @@ subcommands: default_value: "1" takes_value: true - dest: - help: Send the transaction to the provided server (start with http://) or save as file. + help: Intended recipient's Slatepack Address (or http listener address (DEPRECATED)) short: d long: dest takes_value: true @@ -146,16 +146,24 @@ subcommands: help: Output a V4 slate prior to HF3 block long: v4 takes_value: false + - unpack: + about: Unpack and display an armored Slatepack Message, decrypting if possible + args: + - input: + help: File containing a Slatepack Message + short: i + long: input + takes_value: true - receive: - about: Processes a transaction file to accept a transfer from a sender + about: Processes a Slatepack Message to accept a transfer from a sender args: - input: - help: Partial transaction to process, expects the sender's transaction file. + help: File containing a Slatepack Message short: i long: input takes_value: true - finalize: - about: Processes a receiver's transaction file to finalize a transfer. + about: Processes a Slatepack Message to finalize a transfer. args: - input: help: Partial transaction to process, expects the receiver's transaction file. @@ -171,13 +179,13 @@ subcommands: short: n long: nopost - invoice: - about: Initialize an invoice transaction. + about: Initialize an invoice transaction, outputting a Slatepack Message with the result args: - amount: help: Number of coins to invoice with optional fraction, e.g. 12.423 index: 1 - dest: - help: Name of destination slate output file + help: Intended recipient's Slatepack Address short: d long: dest takes_value: true @@ -209,12 +217,12 @@ subcommands: short: e long: estimate-selection - dest: - help: Send the transaction to the provided server (start with http://) or save as file. + help: The Slatepack address of the invoicing party's wallet (will override the address contained in the Slatepack) short: d long: dest takes_value: true - input: - help: Partial transaction to process, expects the invoicer's transaction file. + help: Incoming Slatepack Message to process short: i long: input takes_value: true diff --git a/src/cmd/wallet_args.rs b/src/cmd/wallet_args.rs index 572694451..7c182429d 100644 --- a/src/cmd/wallet_args.rs +++ b/src/cmd/wallet_args.rs @@ -569,6 +569,32 @@ pub fn parse_receive_args(args: &ArgMatches) -> Result Result { + // input file + let input_file = match args.is_present("input") { + true => { + let file = args.value_of("input").unwrap().to_owned(); + // validate input + if !Path::new(&file).is_file() { + let msg = format!("File {} not found.", &file); + return Err(ParseError::ArgumentError(msg)); + } + Some(file) + } + false => None, + }; + + let mut input_slatepack_message = None; + if input_file.is_none() { + input_slatepack_message = Some(prompt_slatepack()?); + } + + Ok(command::ReceiveArgs { + input_file, + input_slatepack_message, + }) +} + pub fn parse_finalize_args(args: &ArgMatches) -> Result { let fluff = args.is_present("fluff"); let nopost = args.is_present("nopost"); @@ -1134,6 +1160,10 @@ where test_mode, ) } + ("unpack", Some(args)) => { + let a = arg_parse!(parse_unpack_args(&args)); + command::unpack(owner_api, km, a) + } ("finalize", Some(args)) => { let a = arg_parse!(parse_finalize_args(&args)); command::finalize(owner_api, km, a)