diff --git a/api/src/lib.rs b/api/src/lib.rs index dca28b758..a3ea1fd92 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -55,6 +55,6 @@ pub use crate::foreign_rpc::run_doctest_foreign; pub use crate::owner_rpc::run_doctest_owner; pub use types::{ - ECDHPubkey, EncryptedRequest, EncryptedResponse, EncryptionErrorResponse, JsonId, PubAddress, - Token, + ECDHPubkey, Ed25519SecretKey, EncryptedRequest, EncryptedResponse, EncryptionErrorResponse, + JsonId, Token, }; diff --git a/api/src/owner.rs b/api/src/owner.rs index c73602807..72c19ab8f 100644 --- a/api/src/owner.rs +++ b/api/src/owner.rs @@ -15,7 +15,6 @@ //! Owner API External Definition use chrono::prelude::*; -use ed25519_dalek::PublicKey as DalekPublicKey; use ed25519_dalek::SecretKey as DalekSecretKey; use uuid::Uuid; @@ -28,14 +27,12 @@ use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, Status use crate::libwallet::api_impl::{owner, owner_updater}; use crate::libwallet::{ AcctPathMapping, Error, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient, - NodeHeightResult, OutputCommitMapping, PaymentProof, Slate, TxLogEntry, WalletInfo, WalletInst, - WalletLCProvider, + NodeHeightResult, OutputCommitMapping, PaymentProof, Slate, Slatepack, SlatepackAddress, + TxLogEntry, WalletInfo, WalletInst, WalletLCProvider, }; use crate::util::logger::LoggingConfig; use crate::util::secp::key::SecretKey; use crate::util::{from_hex, static_secp_instance, Mutex, ZeroingString}; -use grin_wallet_util::OnionV3Address; -use std::convert::TryFrom; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Sender}; use std::sync::Arc; @@ -1854,10 +1851,12 @@ where Ok(q.split_off(index)) } - /// Retrieve the public proof "addresses" associated with the active account at the + // SLATEPACK + + /// Retrieve the public slatepack address associated with the active account at the /// given derivation path. /// - /// In this case, an "address" means a Dalek ed25519 public key corresponding to + /// In this case, an "address" means a Slatepack Address corresponding to /// a private key derived as follows: /// /// e.g. The default parent account is at @@ -1887,7 +1886,7 @@ where /// * `derivation_index` - The index along the derivation path to retrieve an address for /// /// # Returns - /// * Ok with a DalekPublicKey representing the address + /// * Ok with a SlatepackAddress representing the address /// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered. /// /// # Example @@ -1902,7 +1901,7 @@ where /// // Set up as above /// # let api_owner = Owner::new(wallet.clone(), None); /// - /// let res = api_owner.get_public_proof_address(None, 0); + /// let res = api_owner.get_slatepack_address(None, 0); /// /// if let Ok(_) = res { /// // ... @@ -1910,35 +1909,66 @@ where /// /// ``` - pub fn get_public_proof_address( + pub fn get_slatepack_address( &self, keychain_mask: Option<&SecretKey>, derivation_index: u32, - ) -> Result { - owner::get_public_proof_address(self.wallet_inst.clone(), keychain_mask, derivation_index) + ) -> Result { + owner::get_slatepack_address(self.wallet_inst.clone(), keychain_mask, derivation_index) } - // TODO: Doc - /// get public proof a - pub fn get_secret_key( + /// Retrieve the private ed25519 slatepack key at the given derivation index. Currently + /// used to decrypt encrypted slatepack messages. + /// + /// # Arguments + /// + /// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if + /// * `derivation_index` - The index along the derivation path to for which to retrieve the secret key + /// + /// # Returns + /// * Ok with an ed25519_dalek::SecretKey if successful + /// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered. + /// + /// # Example + /// Set up as in [`new`](struct.Owner.html#method.new) method above. + /// ``` + /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); + /// + /// use grin_core::global::ChainTypes; + /// + /// use std::time::Duration; + /// + /// // Set up as above + /// # let api_owner = Owner::new(wallet.clone(), None); + /// + /// let res = api_owner.get_slatepack_secret_key(None, 0); + /// + /// if let Ok(_) = res { + /// // ... + /// } + /// + /// ``` + pub fn get_slatepack_secret_key( &self, keychain_mask: Option<&SecretKey>, derivation_index: u32, ) -> Result { - owner::get_secret_key(self.wallet_inst.clone(), keychain_mask, derivation_index) + owner::get_slatepack_secret_key(self.wallet_inst.clone(), keychain_mask, derivation_index) } - /// Helper function to convert an Onion v3 address to a payment proof address (essentially - /// exctacting and verifying the public key) + /// Create a slatepack from a given slate, optionally encoding the slate with the provided + /// recipient public keys /// /// # Arguments /// - /// * `address_v3` - An V3 Onion address + /// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if + /// * `sender_index` - If Some(n), the index along the derivation path to include as the sender + /// * `recipients` - Optional recipients for which to encrypt the slatepack's payload (i.e. the + /// slate). If an empty vec, the payload will remain unencrypted /// /// # Returns - /// * Ok(DalekPublicKey) representing the public key associated with the address, if successful - /// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered - /// or the address provided is invalid + /// * Ok with a String representing an armored slatepack if successful + /// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered. /// /// # Example /// Set up as in [`new`](struct.Owner.html#method.new) method above. @@ -1952,22 +1982,135 @@ where /// // Set up as above /// # let api_owner = Owner::new(wallet.clone(), None); /// - /// let res = api_owner.proof_address_from_onion_v3( - /// "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid" + /// let mut api_owner = Owner::new(wallet.clone(), None); + /// let args = InitTxArgs { + /// src_acct_name: None, + /// amount: 2_000_000_000, + /// minimum_confirmations: 10, + /// max_outputs: 500, + /// num_change_outputs: 1, + /// selection_strategy_is_use_all: false, + /// ..Default::default() + /// }; + /// let result = api_owner.init_send_tx( + /// None, + /// args, /// ); /// - /// if let Ok(_) = res { - /// // ... + /// if let Ok(slate) = result { + /// // Create a slatepack from our slate + /// let slatepack = api_owner.create_slatepack_message( + /// None, + /// &slate, + /// Some(0), + /// vec![], + /// ); /// } /// - /// let res = api_owner.stop_updater(); /// ``` - pub fn proof_address_from_onion_v3(&self, address_v3: &str) -> Result { - let addr = OnionV3Address::try_from(address_v3)?; - Ok(addr.to_ed25519()?) + pub fn create_slatepack_message( + &self, + keychain_mask: Option<&SecretKey>, + slate: &Slate, + sender_index: Option, + recipients: Vec, + ) -> Result { + owner::create_slatepack_message( + self.wallet_inst.clone(), + keychain_mask, + slate, + sender_index, + recipients, + ) } + /// Extract the slate from the given slatepack. If the slatepack payload is encrypted, attempting to + /// decrypt with keys at the given address derivation path indices. + /// + /// # Arguments + /// + /// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if + /// * `slatepack` - A string representing an armored slatepack + /// * `secret_indices` - Indices along this wallet's deriviation path with which to attempt + /// decryption. This function will attempt to use secret keys at each index along this path + /// to attempt to decrypt the payload, returning an error if none of the keys match. + /// + /// # Returns + /// * Ok with a [Slate](../grin_wallet_libwallet/slate/struct.Slate.html) if successful + /// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered. + /// + /// # Example + /// Set up as in [`new`](struct.Owner.html#method.new) method above. + /// ``` + /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); + /// + /// use grin_core::global::ChainTypes; + /// + /// use std::time::Duration; + /// + /// // Set up as above + /// # let api_owner = Owner::new(wallet.clone(), None); + /// // ... receive a slatepack from somewhere + /// # let slatepack_string = String::from(""); + /// let res = api_owner.slate_from_slatepack_message( + /// None, + /// slatepack_string, + /// vec![0, 1, 2], + /// ); + /// ``` + + pub fn slate_from_slatepack_message( + &self, + keychain_mask: Option<&SecretKey>, + slatepack: String, + secret_indices: Vec, + ) -> Result { + owner::slate_from_slatepack_message( + self.wallet_inst.clone(), + keychain_mask, + slatepack, + secret_indices, + ) + } + + /// Decode an armored slatepack, returning a Slatepack object that can be + /// viewed, manipulated, output as json, etc + /// + /// # Arguments + /// + /// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using + /// * `slatepack` - A string representing an armored slatepack + /// + /// # Returns + /// * Ok with a [Slatepack](../grin_wallet_libwallet/slatepack/types/struct.Slatepack.html) if successful + /// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered. + /// + /// # Example + /// Set up as in [`new`](struct.Owner.html#method.new) method above. + /// ``` + /// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config); + /// + /// use grin_core::global::ChainTypes; + /// + /// use std::time::Duration; + /// + /// // Set up as above + /// # let api_owner = Owner::new(wallet.clone(), None); + /// # let slatepack_string = String::from(""); + /// // .. receive a slatepack from somewhere + /// let res = api_owner.decode_slatepack_message( + /// slatepack_string + /// ); + /// + /// ``` + + pub fn decode_slatepack_message(&self, slatepack: String) -> Result { + owner::decode_slatepack_message(slatepack) + } + + // PAYMENT PROOFS + /// Returns a single, exportable [PaymentProof](../grin_wallet_libwallet/api_impl/types/struct.PaymentProof.html) /// from a completed transaction within the wallet. /// @@ -2094,8 +2237,9 @@ where owner::verify_payment_proof(self.wallet_inst.clone(), keychain_mask, proof) } - /// Return my participant data - // TODO: This will be removed once state is added to slate + /// Return whether this transaction is marked as invoice in the context + // TODO: Remove post HF3 + // This will be removed once state is added to slate pub fn context_is_invoice( &self, keychain_mask: Option<&SecretKey>, diff --git a/api/src/owner_rpc.rs b/api/src/owner_rpc.rs index edef6784a..d159351b0 100644 --- a/api/src/owner_rpc.rs +++ b/api/src/owner_rpc.rs @@ -21,13 +21,13 @@ use crate::core::global; use crate::keychain::{Identifier, Keychain}; use crate::libwallet::{ AcctPathMapping, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient, NodeHeightResult, - OutputCommitMapping, PaymentProof, Slate, SlateVersion, StatusMessage, TxLogEntry, - VersionedSlate, WalletInfo, WalletLCProvider, + OutputCommitMapping, PaymentProof, Slate, SlateVersion, Slatepack, SlatepackAddress, + StatusMessage, TxLogEntry, VersionedSlate, WalletInfo, WalletLCProvider, }; use crate::util::logger::LoggingConfig; use crate::util::secp::key::{PublicKey, SecretKey}; use crate::util::{static_secp_instance, Mutex, ZeroingString}; -use crate::{ECDHPubkey, Owner, PubAddress, Token}; +use crate::{ECDHPubkey, Ed25519SecretKey, Owner, Token}; use easy_jsonrpc_mw; use grin_wallet_util::OnionV3Address; use rand::thread_rng; @@ -378,7 +378,7 @@ pub trait OwnerRpc { "num_change_outputs": 1, "selection_strategy_is_use_all": true, "target_slate_version": null, - "payment_proof_recipient_address": "pa7wkkdgs5bkteha7lykl7ff2wztgdrxxo442xdcq2lnaphe5aidd4id", + "payment_proof_recipient_address": "slatepack10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqnlumr7", "ttl_blocks": null, "send_args": null } @@ -1389,13 +1389,13 @@ pub trait OwnerRpc { fn get_updater_messages(&self, count: u32) -> Result, ErrorKind>; /** - Networked version of [Owner::get_public_proof_address](struct.Owner.html#method.get_public_proof_address). + Networked version of [Owner::get_slatepack_address](struct.Owner.html#method.get_slatepack_address). ``` # grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!( # r#" { "jsonrpc": "2.0", - "method": "get_public_proof_address", + "method": "get_slatepack_address", "params": { "token": "d202964900000000d302964900000000d402964900000000d502964900000000", "derivation_index": 0 @@ -1409,7 +1409,7 @@ pub trait OwnerRpc { "id": 1, "jsonrpc": "2.0", "result": { - "Ok": "32cdd63928854f8b2628b1dce4626ddcdf35d56cb7cfdf7d64cca5822b78d4d3" + "Ok": "slatepack1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfskdvkdu" } } # "# @@ -1417,22 +1417,167 @@ pub trait OwnerRpc { ``` */ - fn get_public_proof_address( + fn get_slatepack_address( &self, token: Token, derivation_index: u32, - ) -> Result; + ) -> Result; /** - Networked version of [Owner::proof_address_from_onion_v3](struct.Owner.html#method.proof_address_from_onion_v3). + Networked version of [Owner::get_slatepack_secret_key](struct.Owner.html#method.get_slatepack_secret_key). ``` # grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!( # r#" { "jsonrpc": "2.0", - "method": "proof_address_from_onion_v3", + "method": "get_slatepack_secret_key", "params": { - "address_v3": "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid" + "token": "d202964900000000d302964900000000d402964900000000d502964900000000", + "derivation_index": 0 + }, + "id": 1 + } + # "# + # , + # r#" + { + "id": 1, + "jsonrpc": "2.0", + "result": { + "Ok": "86cca2aedea7989dfcca62e54477301d098bac260656d11373e314c099f0b26f" + } + } + # "# + # , 0, false, false, false, false); + ``` + */ + + fn get_slatepack_secret_key( + &self, + token: Token, + derivation_index: u32, + ) -> Result; + + /** + Networked version of [Owner::create_slatepack_message](struct.Owner.html#method.create_slatepack_message). + ``` + # grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!( + # r#" + { + "jsonrpc": "2.0", + "method": "create_slatepack_message", + "params": { + "token": "d202964900000000d302964900000000d402964900000000d502964900000000", + "sender_index": 0, + "recipients": [], + "slate": { + "amt": "6000000000", + "fee": "8000000", + "id": "0436430c-2b02-624c-2032-570501212b00", + "off": "0gKWSQAAAADTApZJAAAAANQClkkAAAAA1QKWSQAAAAA=", + "proof": { + "raddr": "eD9lKGaXQqmQ4Prwpfyl1bMzDje7uc1cYoaW0Dzk6BA=", + "saddr": "Ms3WOSiFT4smKLHc5GJt3N811Wy3z999ZMylgit41NM=" + }, + "sigs": [ + { + "nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP", + "xs": "Ajh4zoRXJ/Ok7HbKPz20s4otBdY2uMNjIQi4V/7WPJbe" + } + ], + "sta": "S1", + "ver": "4:2" + } + }, + "id": 1 + } + # "# + # , + # r#" + { + "id": 1, + "jsonrpc": "2.0", + "result": { + "Ok": "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.\n" + } + } + # "# + # , 0, false, false, false, false); + ``` + */ + + fn create_slatepack_message( + &self, + token: Token, + slate: VersionedSlate, + sender_index: Option, + recipients: Vec, + ) -> Result; + + /** + Networked version of [Owner::slate_from_slatepack_message](struct.Owner.html#method.slate_from_slatepack_message). + ``` + # grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!( + # r#" + { + "jsonrpc": "2.0", + "method": "slate_from_slatepack_message", + "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.\n" + }, + "id": 1 + } + # "# + # , + # r#" + { + "id": 1, + "jsonrpc": "2.0", + "result": { + "Ok": { + "amt": "6000000000", + "fee": "8000000", + "id": "0436430c-2b02-624c-2032-570501212b00", + "off": "0gKWSQAAAADTApZJAAAAANQClkkAAAAA1QKWSQAAAAA=", + "proof": { + "raddr": "eD9lKGaXQqmQ4Prwpfyl1bMzDje7uc1cYoaW0Dzk6BA=", + "saddr": "Ms3WOSiFT4smKLHc5GJt3N811Wy3z999ZMylgit41NM=" + }, + "sigs": [ + { + "nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP", + "xs": "Ajh4zoRXJ/Ok7HbKPz20s4otBdY2uMNjIQi4V/7WPJbe" + } + ], + "sta": "S1", + "ver": "4:2" + } + } + } + # "# + # , 0, false, false, false, false); + ``` + */ + + fn slate_from_slatepack_message( + &self, + token: Token, + message: String, + secret_indices: Vec, + ) -> Result; + + /** + Networked version of [Owner::decode_slatepack_message](struct.Owner.html#method.decode_slatepack_message). + ``` + # grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!( + # r#" + { + "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" }, "id": 1 } @@ -1443,7 +1588,12 @@ pub trait OwnerRpc { "id": 1, "jsonrpc": "2.0", "result": { - "Ok": "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb" + "Ok": { + "mode": 0, + "payload": "AAQAAgQ2QwwrAmJMIDJXBQEhKwAB0gKWSQAAAADTApZJAAAAANQClkkAAAAA1QKWSQAAAAAGAAAAAWWgvAAAAAAAAHoSAAEAAjh4zoRXJ/Ok7HbKPz20s4otBdY2uMNjIQi4V/7WPJbeAxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QePAjLN1jkohU+LJiix3ORibdzfNdVst8/ffWTMpYIreNTTeD9lKGaXQqmQ4Prwpfyl1bMzDje7uc1cYoaW0Dzk6BAA", + "sender": "slatepack1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfskdvkdu", + "slatepack": "1.0" + } } } # "# @@ -1451,7 +1601,7 @@ pub trait OwnerRpc { ``` */ - fn proof_address_from_onion_v3(&self, address_v3: String) -> Result; + fn decode_slatepack_message(&self, message: String) -> Result; /** Networked version of [Owner::retrieve_payment_proof](struct.Owner.html#method.retrieve_payment_proof). @@ -1479,9 +1629,9 @@ pub trait OwnerRpc { "Ok": { "amount": "60000000000", "excess": "091f151170bfac881479bfb56c7012c52cd4ce4198ad661586374dd499925922fb", - "recipient_address": "pa7wkkdgs5bkteha7lykl7ff2wztgdrxxo442xdcq2lnaphe5aidd4id", + "recipient_address": "slatepack10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqnlumr7", "recipient_sig": "b9b1885a3f33297df32e1aa4db23220bd305da8ed92ff6873faf3ab2c116fea25e9d0e34bd4f567f022b88a37400821ffbcaec71c9a8c3a327c4626611886d0d", - "sender_address": "glg5mojiqvhywjriwhooiytn3tptlvlmw7h567lezssyek3y2tjzznad", + "sender_address": "slatepack1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfskdvkdu", "sender_sig": "611b92331e395c3d29871ac35b1fce78ec595e28ccbe8cc55452da40775e8e46d35a2e84eaffd986935da3275e34d46a8d777d02dabcf4339704c2a621da9700" } } @@ -1512,9 +1662,9 @@ pub trait OwnerRpc { "proof": { "amount": "60000000000", "excess": "091f151170bfac881479bfb56c7012c52cd4ce4198ad661586374dd499925922fb", - "recipient_address": "pa7wkkdgs5bkteha7lykl7ff2wztgdrxxo442xdcq2lnaphe5aidd4id", + "recipient_address": "slatepack10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqnlumr7", "recipient_sig": "b9b1885a3f33297df32e1aa4db23220bd305da8ed92ff6873faf3ab2c116fea25e9d0e34bd4f567f022b88a37400821ffbcaec71c9a8c3a327c4626611886d0d", - "sender_address": "glg5mojiqvhywjriwhooiytn3tptlvlmw7h567lezssyek3y2tjzznad", + "sender_address": "slatepack1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfskdvkdu", "sender_sig": "611b92331e395c3d29871ac35b1fce78ec595e28ccbe8cc55452da40775e8e46d35a2e84eaffd986935da3275e34d46a8d777d02dabcf4339704c2a621da9700" } }, @@ -1862,18 +2012,65 @@ where Owner::get_updater_messages(self, count as usize).map_err(|e| e.kind()) } - fn get_public_proof_address( + fn get_slatepack_address( &self, token: Token, derivation_index: u32, - ) -> Result { - let address = Owner::get_public_proof_address( + ) -> Result { + Owner::get_slatepack_address(self, (&token.keychain_mask).as_ref(), derivation_index) + .map_err(|e| e.kind()) + } + + fn get_slatepack_secret_key( + &self, + token: Token, + derivation_index: u32, + ) -> Result { + let key = Owner::get_slatepack_secret_key( self, (&token.keychain_mask).as_ref(), derivation_index, ) .map_err(|e| e.kind())?; - Ok(PubAddress { address }) + Ok(Ed25519SecretKey { key }) + } + + fn create_slatepack_message( + &self, + token: Token, + slate: VersionedSlate, + sender_index: Option, + recipients: Vec, + ) -> Result { + Owner::create_slatepack_message( + self, + (&token.keychain_mask).as_ref(), + &Slate::from(slate), + sender_index, + recipients, + ) + .map_err(|e| e.kind()) + } + + fn slate_from_slatepack_message( + &self, + token: Token, + message: String, + secret_indices: Vec, + ) -> Result { + let slate = Owner::slate_from_slatepack_message( + self, + (&token.keychain_mask).as_ref(), + message, + secret_indices, + ) + .map_err(|e| e.kind())?; + let version = SlateVersion::V4; + 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 retrieve_payment_proof( @@ -1902,12 +2099,6 @@ where .map_err(|e| e.kind()) } - fn proof_address_from_onion_v3(&self, address_v3: String) -> Result { - let address = - Owner::proof_address_from_onion_v3(self, &address_v3).map_err(|e| e.kind())?; - Ok(PubAddress { address }) - } - fn set_tor_config(&self, tor_config: Option) -> Result<(), ErrorKind> { Owner::set_tor_config(self, tor_config); Ok(()) @@ -2046,8 +2237,6 @@ pub fn run_doctest_owner( assert!(wallet_refreshed); } - //let proof_address = api_impl::owner::get_public_proof_address(wallet2.clone(), (&mask2).as_ref(), 0).unwrap(); - if perform_tx { let amount = 60_000_000_000; let mut w_lock = wallet1.lock(); @@ -2055,7 +2244,8 @@ pub fn run_doctest_owner( let proof_address = match payment_proof { true => { let address = "783f6528669742a990e0faf0a5fca5d5b3330e37bbb9cd5c628696d03ce4e810"; - Some(OnionV3Address::try_from(address).unwrap()) + let address = OnionV3Address::try_from(address).unwrap(); + Some(SlatepackAddress::try_from(address).unwrap()) } false => None, }; diff --git a/api/src/types.rs b/api/src/types.rs index 05f306162..e071436f5 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -12,14 +12,14 @@ // limitations under the License. use crate::core::libtx::secp_ser; -use crate::libwallet::dalek_ser; +use crate::libwallet::slate_versions::ser as dalek_ser; use crate::libwallet::{Error, ErrorKind}; use crate::util::secp::key::{PublicKey, SecretKey}; use crate::util::{from_hex, ToHex}; +use ed25519_dalek::SecretKey as DalekSecretKey; use failure::ResultExt; use base64; -use ed25519_dalek::PublicKey as DalekPublicKey; use rand::{thread_rng, Rng}; use ring::aead; use serde_json::{self, Value}; @@ -45,15 +45,6 @@ pub struct Token { pub keychain_mask: Option, } -/// Wrapper for dalek public keys, used as addresses -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(transparent)] -pub struct PubAddress { - #[serde(with = "dalek_ser::dalek_pubkey_serde")] - /// Public address - pub address: DalekPublicKey, -} - /// Wrapper for ECDH Public keys #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(transparent)] @@ -63,6 +54,15 @@ pub struct ECDHPubkey { pub ecdh_pubkey: PublicKey, } +/// Wrapper for Secret Keys +#[derive(Serialize, Deserialize, Debug)] +#[serde(transparent)] +pub struct Ed25519SecretKey { + #[serde(with = "dalek_ser::dalek_seckey_serde")] + /// Token to XOR mask against the stored wallet seed + pub key: DalekSecretKey, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct EncryptedBody { /// nonce used for encryption diff --git a/controller/src/command.rs b/controller/src/command.rs index f04d66bf6..0b5a3ff2e 100644 --- a/controller/src/command.rs +++ b/controller/src/command.rs @@ -24,12 +24,11 @@ use crate::impls::{HttpSlateSender, PathToSlate, SlatePutter}; use crate::keychain; use crate::libwallet::{ self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateVersion, - WalletLCProvider, + SlatepackAddress, WalletLCProvider, }; use crate::util::secp::key::SecretKey; use crate::util::{Mutex, ZeroingString}; use crate::{controller, display}; -use grin_wallet_util::OnionV3Address; use serde_json as json; use std::fs::File; use std::io::{Read, Write}; @@ -272,7 +271,7 @@ pub struct SendArgs { 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, //TODO: Remove HF3 pub output_v4_slate: bool, @@ -997,12 +996,11 @@ where { controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| { // Just address at derivation index 0 for now - let pub_key = api.get_public_proof_address(m, 0)?; - let addr = OnionV3Address::from_bytes(pub_key.to_bytes()); + let address = api.get_slatepack_address(m, 0)?; println!(); println!("Address for account - {}", g_args.account); println!("-------------------------------------"); - println!("{}", addr); + println!("{}", address); println!(); Ok(()) })?; diff --git a/controller/tests/file.rs b/controller/tests/file.rs index eea81cc02..36cb4ecbd 100644 --- a/controller/tests/file.rs +++ b/controller/tests/file.rs @@ -19,7 +19,6 @@ extern crate grin_wallet_impls as impls; use grin_wallet_libwallet as libwallet; use grin_wallet_util::grin_core as core; -use grin_wallet_util::OnionV3Address; use impls::test_framework::{self, LocalWalletClient}; use impls::{PathToSlate, SlateGetter as _, SlatePutter as _}; @@ -253,12 +252,10 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(), let mut slate = Slate::blank(2, true); let mut address = None; wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { - address = Some(api.get_public_proof_address(m, 0)?); + address = Some(api.get_slatepack_address(m, 0)?); Ok(()) })?; - let address = OnionV3Address::from_bytes(address.as_ref().unwrap().to_bytes()); - wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // send to send let args = InitTxArgs { @@ -268,7 +265,7 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(), max_outputs: 500, num_change_outputs: 1, selection_strategy_is_use_all: true, - payment_proof_recipient_address: Some(address.clone()), + payment_proof_recipient_address: address.clone(), ..Default::default() }; let slate = api.init_send_tx(m, args)?; diff --git a/controller/tests/payment_proofs.rs b/controller/tests/payment_proofs.rs index 819b39bcb..8d022d836 100644 --- a/controller/tests/payment_proofs.rs +++ b/controller/tests/payment_proofs.rs @@ -25,8 +25,6 @@ use std::sync::atomic::Ordering; use std::thread; use std::time::Duration; -use grin_wallet_util::OnionV3Address; - #[macro_use] mod common; use common::{clean_output_dir, create_wallet_proxy, setup}; @@ -80,11 +78,10 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err let mut address = None; wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { - address = Some(api.get_public_proof_address(m, 0)?); + address = Some(api.get_slatepack_address(m, 0)?); Ok(()) })?; - let address = OnionV3Address::from_bytes(address.as_ref().unwrap().to_bytes()); println!("Public address is: {:?}", address); let amount = 60_000_000_000; let mut slate = Slate::blank(1, false); @@ -97,14 +94,14 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err max_outputs: 500, num_change_outputs: 1, selection_strategy_is_use_all: true, - payment_proof_recipient_address: Some(address.clone()), + payment_proof_recipient_address: address.clone(), ..Default::default() }; let slate_i = sender_api.init_send_tx(m, args)?; assert_eq!( slate_i.payment_proof.as_ref().unwrap().receiver_address, - address.to_ed25519()?, + address.as_ref().unwrap().pub_key, ); println!( "Sender addr: {:?}", diff --git a/controller/tests/slatepack.rs b/controller/tests/slatepack.rs index ccd9d0012..c53f45116 100644 --- a/controller/tests/slatepack.rs +++ b/controller/tests/slatepack.rs @@ -19,7 +19,6 @@ extern crate grin_wallet_impls as impls; use grin_wallet_libwallet as libwallet; use grin_wallet_util::grin_core as core; -use grin_wallet_util::OnionV3Address; use impls::test_framework::{self, LocalWalletClient}; use impls::{PathToSlatepack, SlatePutter as _}; @@ -152,7 +151,7 @@ fn slatepack_exchange_test_impl( let mut rec_address = SlatepackAddress::random(); let mut sec_key = edDalekSecretKey::from_bytes(&[0u8; 32]).unwrap(); wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { - sec_key = api.get_secret_key(m, 0)?; + sec_key = api.get_slatepack_secret_key(m, 0)?; let pub_key = edDalekPublicKey::from(&sec_key); rec_address = SlatepackAddress::new(&pub_key); Ok(()) @@ -171,7 +170,7 @@ fn slatepack_exchange_test_impl( let mut rec_address = SlatepackAddress::random(); let mut sec_key = edDalekSecretKey::from_bytes(&[0u8; 32]).unwrap(); wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { - sec_key = api.get_secret_key(m, 0)?; + sec_key = api.get_slatepack_secret_key(m, 0)?; let pub_key = edDalekPublicKey::from(&sec_key); rec_address = SlatepackAddress::new(&pub_key); Ok(()) @@ -379,12 +378,10 @@ fn slatepack_exchange_test_impl( let mut slate = Slate::blank(2, true); let mut address = None; wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { - address = Some(api.get_public_proof_address(m, 0)?); + address = Some(api.get_slatepack_address(m, 0)?); Ok(()) })?; - let address = OnionV3Address::from_bytes(address.as_ref().unwrap().to_bytes()); - wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { // send to send let args = InitTxArgs { @@ -394,7 +391,7 @@ fn slatepack_exchange_test_impl( max_outputs: 500, num_change_outputs: 1, selection_strategy_is_use_all: true, - payment_proof_recipient_address: Some(address.clone()), + payment_proof_recipient_address: address.clone(), ..Default::default() }; let slate = api.init_send_tx(m, args)?; @@ -447,6 +444,71 @@ fn slatepack_exchange_test_impl( Ok(()) } +/// Exercise slate encryption/decryption via the API, +/// Since doctests don't cover encryption +fn slatepack_api_impl(test_dir: &'static str) -> Result<(), libwallet::Error> { + // Create a new proxy to simulate server and wallet responses + let mut wallet_proxy = create_wallet_proxy(test_dir); + let chain = wallet_proxy.chain.clone(); + let stopper = wallet_proxy.running.clone(); + + // Create a new wallet test client, and set its queues to communicate with the + // proxy + create_wallet_and_add!( + client1, + wallet1, + mask1_i, + test_dir, + "wallet1", + None, + &mut wallet_proxy, + false + ); + let mask1 = (&mask1_i).as_ref(); + + // Set the wallet proxy listener running + thread::spawn(move || { + if let Err(e) = wallet_proxy.run() { + error!("Wallet Proxy error: {}", e); + } + }); + + // few values to keep things shorter + let reward = core::consensus::REWARD; + + // Get some mining done + let bh = 6u64; + let _ = + test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false); + + wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { + let args = InitTxArgs { + src_acct_name: Some("mining".to_owned()), + amount: reward * 2, + minimum_confirmations: 2, + max_outputs: 500, + num_change_outputs: 1, + selection_strategy_is_use_all: true, + ..Default::default() + }; + let slate = api.init_send_tx(m, args)?; + // create an encrypted slatepack (just encrypted for self) + 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())?; + println!("{}", slatepack_raw); + let decoded_slate = api.slate_from_slatepack_message(m, slatepack, vec![0])?; + println!("{}", decoded_slate); + Ok(()) + })?; + + // let logging finish + stopper.store(false, Ordering::Relaxed); + thread::sleep(Duration::from_millis(200)); + Ok(()) +} + #[test] fn slatepack_exchange_json() { let test_dir = "test_output/slatepack_exchange_json"; @@ -512,3 +574,14 @@ fn slatepack_exchange_armored_enc() { } clean_output_dir(test_dir); } + +#[test] +fn slatepack_api() { + let test_dir = "test_output/slatepack_api"; + setup(test_dir); + // Json output + if let Err(e) = slatepack_api_impl(test_dir) { + panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap()); + } + clean_output_dir(test_dir); +} diff --git a/libwallet/src/api_impl/owner.rs b/libwallet/src/api_impl/owner.rs index 0a1942181..da903de94 100644 --- a/libwallet/src/api_impl/owner.rs +++ b/libwallet/src/api_impl/owner.rs @@ -29,12 +29,14 @@ use crate::slate::{PaymentInfo, Slate, SlateState}; use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletBackend, WalletInfo}; use crate::{ address, wallet_lock, InitTxArgs, IssueInvoiceTxArgs, NodeHeightResult, OutputCommitMapping, - PaymentProof, ScannedBlockInfo, TxLogEntryType, WalletInitStatus, WalletInst, WalletLCProvider, + PaymentProof, ScannedBlockInfo, Slatepack, SlatepackAddress, Slatepacker, SlatepackerArgs, + TxLogEntryType, WalletInitStatus, WalletInst, WalletLCProvider, }; use crate::{Error, ErrorKind}; use ed25519_dalek::PublicKey as DalekPublicKey; use ed25519_dalek::SecretKey as DalekSecretKey; +use std::convert::TryFrom; use std::sync::mpsc::Sender; use std::sync::Arc; @@ -72,14 +74,14 @@ where w.set_parent_key_id_by_name(label) } -/// Retrieve the payment proof address for the current parent key at +/// Retrieve the slatepack address for the current parent key at /// the given index /// set active account -pub fn get_public_proof_address<'a, L, C, K>( +pub fn get_slatepack_address<'a, L, C, K>( wallet_inst: Arc>>>, keychain_mask: Option<&SecretKey>, index: u32, -) -> Result +) -> Result where L: WalletLCProvider<'a, C, K>, C: NodeClient + 'a, @@ -89,14 +91,13 @@ where let parent_key_id = w.parent_key_id(); let k = w.keychain(keychain_mask)?; let sec_addr_key = address::address_from_derivation_path(&k, &parent_key_id, index)?; - let addr = OnionV3Address::from_private(&sec_addr_key.0)?; - Ok(addr.to_ed25519()?) + SlatepackAddress::try_from(&sec_addr_key) } /// Retrieve the decryption key for the current parent key /// the given index /// set active account -pub fn get_secret_key<'a, L, C, K>( +pub fn get_slatepack_secret_key<'a, L, C, K>( wallet_inst: Arc>>>, keychain_mask: Option<&SecretKey>, index: u32, @@ -123,6 +124,92 @@ where Ok(d_skey) } +/// Create a slatepack message from the given slate +pub fn create_slatepack_message<'a, L, C, K>( + wallet_inst: Arc>>>, + keychain_mask: Option<&SecretKey>, + slate: &Slate, + sender_index: Option, + recipients: Vec, +) -> Result +where + L: WalletLCProvider<'a, C, K>, + C: NodeClient + 'a, + K: Keychain + 'a, +{ + let sender = match sender_index { + Some(i) => Some(get_slatepack_address(wallet_inst, keychain_mask, i)?), + None => None, + }; + let packer = Slatepacker::new(SlatepackerArgs { + sender, + recipients, + dec_key: None, + }); + let slatepack = packer.create_slatepack(slate)?; + packer.armor_slatepack(&slatepack) +} + +/// Unpack a slate from the given slatepack message, +/// optionally decrypting +pub fn slate_from_slatepack_message<'a, L, C, K>( + wallet_inst: Arc>>>, + keychain_mask: Option<&SecretKey>, + slatepack: String, + secret_indices: Vec, +) -> Result +where + L: WalletLCProvider<'a, C, K>, + C: NodeClient + 'a, + K: Keychain + 'a, +{ + if secret_indices.is_empty() { + let packer = Slatepacker::new(SlatepackerArgs { + sender: None, + recipients: vec![], + dec_key: None, + }); + let slatepack = packer.deser_slatepack(slatepack.as_bytes().to_vec())?; + return packer.get_slate(&slatepack); + } else { + for index in secret_indices { + let dec_key = Some(get_slatepack_secret_key( + wallet_inst.clone(), + keychain_mask, + index, + )?); + let packer = Slatepacker::new(SlatepackerArgs { + sender: None, + recipients: vec![], + dec_key: (&dec_key).as_ref(), + }); + let res = packer.deser_slatepack(slatepack.as_bytes().to_vec()); + let slatepack = match res { + Ok(sp) => sp, + Err(_) => { + continue; + } + }; + return packer.get_slate(&slatepack); + } + return Err(ErrorKind::SlatepackDecryption( + "Could not decrypt slatepack with any provided index on the address derivation path" + .into(), + ) + .into()); + } +} + +/// Decode a slatepack message, to allow viewing +pub fn decode_slatepack_message(slatepack: String) -> Result { + let packer = Slatepacker::new(SlatepackerArgs { + sender: None, + recipients: vec![], + dec_key: None, + }); + packer.deser_slatepack(slatepack.as_bytes().to_vec()) +} + /// retrieve outputs pub fn retrieve_outputs<'a, L, C, K>( wallet_inst: Arc>>>, @@ -316,9 +403,9 @@ where Ok(PaymentProof { amount: amount, excess: excess, - recipient_address: OnionV3Address::from_bytes(proof.receiver_address.to_bytes()), + recipient_address: SlatepackAddress::new(&proof.receiver_address), recipient_sig: r_sig, - sender_address: OnionV3Address::from_bytes(proof.sender_address.to_bytes()), + sender_address: SlatepackAddress::new(&proof.sender_address), sender_sig: s_sig, }) } @@ -405,7 +492,7 @@ where slate.payment_proof = Some(PaymentInfo { sender_address: sender_address.to_ed25519()?, - receiver_address: a.to_ed25519()?, + receiver_address: a.pub_key, receiver_signature: None, }); @@ -993,7 +1080,7 @@ where C: NodeClient + 'a, K: Keychain + 'a, { - let sender_pubkey = proof.sender_address.to_ed25519()?; + let sender_pubkey = proof.sender_address.pub_key; let msg = tx::payment_proof_message(proof.amount, &proof.excess, sender_pubkey)?; let (mut client, parent_key_id, keychain) = { @@ -1025,12 +1112,12 @@ where }; // Check Sigs - let recipient_pubkey = proof.recipient_address.to_ed25519()?; + let recipient_pubkey = proof.recipient_address.pub_key; if recipient_pubkey.verify(&msg, &proof.recipient_sig).is_err() { return Err(ErrorKind::PaymentProof("Invalid recipient signature".to_owned()).into()); }; - let sender_pubkey = proof.sender_address.to_ed25519()?; + let sender_pubkey = proof.sender_address.pub_key; if sender_pubkey.verify(&msg, &proof.sender_sig).is_err() { return Err(ErrorKind::PaymentProof("Invalid sender signature".to_owned()).into()); }; diff --git a/libwallet/src/api_impl/types.rs b/libwallet/src/api_impl/types.rs index 1aa6e7ae9..3e5980b8f 100644 --- a/libwallet/src/api_impl/types.rs +++ b/libwallet/src/api_impl/types.rs @@ -20,7 +20,7 @@ use crate::grin_util::secp::pedersen; use crate::slate_versions::ser as dalek_ser; use crate::slate_versions::SlateVersion; use crate::types::OutputData; -use grin_wallet_util::OnionV3Address; +use crate::SlatepackAddress; use ed25519_dalek::Signature as DalekSignature; @@ -64,9 +64,8 @@ pub struct InitTxArgs { #[serde(default)] pub ttl_blocks: Option, /// If set, require a payment proof for the particular recipient - #[serde(with = "dalek_ser::option_ov3_serde")] #[serde(default)] - pub payment_proof_recipient_address: Option, + pub payment_proof_recipient_address: Option, /// If true, just return an estimate of the resulting slate, containing fees and amounts /// locked without actually locking outputs or creating the transaction. Note if this is set to /// 'true', the amount field in the slate will contain the total amount locked, not the provided @@ -203,15 +202,13 @@ pub struct PaymentProof { deserialize_with = "secp_ser::commitment_from_hex" )] pub excess: pedersen::Commitment, - /// Recipient Wallet Address (Onion V3) - #[serde(with = "dalek_ser::ov3_serde")] - pub recipient_address: OnionV3Address, + /// Recipient Wallet Address + pub recipient_address: SlatepackAddress, /// Recipient Signature #[serde(with = "dalek_ser::dalek_sig_serde")] pub recipient_sig: DalekSignature, - /// Sender Wallet Address (Onion V3) - #[serde(with = "dalek_ser::ov3_serde")] - pub sender_address: OnionV3Address, + /// Sender Wallet Address + pub sender_address: SlatepackAddress, /// Sender Signature #[serde(with = "dalek_ser::dalek_sig_serde")] pub sender_sig: DalekSignature, diff --git a/libwallet/src/error.rs b/libwallet/src/error.rs index eb08dfe76..0b07fdf92 100644 --- a/libwallet/src/error.rs +++ b/libwallet/src/error.rs @@ -286,6 +286,10 @@ pub enum ErrorKind { #[fail(display = "Couldn't encrypt Slatepack: {}", _0)] SlatepackEncryption(String), + /// Slatepack Decryption + #[fail(display = "Couldn't decrypt Slatepack: {}", _0)] + SlatepackDecryption(String), + /// age error #[fail(display = "Age error: {}", _0)] Age(String), diff --git a/libwallet/src/slate_versions/ser.rs b/libwallet/src/slate_versions/ser.rs index 163d6f3c3..de34ff1c2 100644 --- a/libwallet/src/slate_versions/ser.rs +++ b/libwallet/src/slate_versions/ser.rs @@ -261,6 +261,34 @@ pub mod ov3_serde { } } +/// Serializes an ed25519 PublicKey to and from hex +pub mod dalek_seckey_serde { + use crate::grin_util::{from_hex, ToHex}; + use ed25519_dalek::SecretKey as DalekSecretKey; + use serde::{Deserialize, Deserializer, Serializer}; + + /// + pub fn serialize(key: &DalekSecretKey, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&key.to_bytes().to_hex()) + } + + /// + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + String::deserialize(deserializer) + .and_then(|string| from_hex(&string).map_err(|err| Error::custom(err.to_string()))) + .and_then(|bytes: Vec| { + DalekSecretKey::from_bytes(&bytes).map_err(|err| Error::custom(err.to_string())) + }) + } +} + /// Serializes an ed25519 PublicKey to and from hex pub mod dalek_pubkey_serde { use crate::grin_util::{from_hex, ToHex}; diff --git a/libwallet/src/slatepack/address.rs b/libwallet/src/slatepack/address.rs index fc381335a..acc46860f 100644 --- a/libwallet/src/slatepack/address.rs +++ b/libwallet/src/slatepack/address.rs @@ -21,6 +21,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use x25519_dalek::PublicKey as xDalekPublicKey; use crate::grin_core::ser::{self, Readable, Reader, Writeable, Writer}; +use crate::grin_util::secp::key::SecretKey; use crate::util::OnionV3Address; use crate::{Error, ErrorKind}; @@ -98,6 +99,13 @@ impl From<&SlatepackAddress> for OnionV3Address { } } +impl TryFrom for SlatepackAddress { + type Error = Error; + fn try_from(addr: OnionV3Address) -> Result { + Ok(SlatepackAddress::new(&addr.to_ed25519()?)) + } +} + impl TryFrom<&SlatepackAddress> for xDalekPublicKey { type Error = Error; fn try_from(addr: &SlatepackAddress) -> Result { @@ -116,6 +124,24 @@ impl TryFrom<&SlatepackAddress> for xDalekPublicKey { } } +impl TryFrom<&SecretKey> for SlatepackAddress { + type Error = Error; + fn try_from(key: &SecretKey) -> Result { + let d_skey = match edDalekSecretKey::from_bytes(&key.0) { + Ok(k) => k, + Err(e) => { + return Err(ErrorKind::ED25519Key(format!( + "Can't create slatepack address from SecretKey: {}", + e + )) + .into()); + } + }; + let d_pub_key: edDalekPublicKey = (&d_skey).into(); + Ok(Self::new(&d_pub_key)) + } +} + /// Serializes a SlatepackAddress to a bech32 string impl Serialize for SlatepackAddress { /// diff --git a/libwallet/src/slatepack/packer.rs b/libwallet/src/slatepack/packer.rs index ceff6fcf0..a96515cc1 100644 --- a/libwallet/src/slatepack/packer.rs +++ b/libwallet/src/slatepack/packer.rs @@ -49,6 +49,10 @@ impl<'a> Slatepacker<'a> { /// return slatepack pub fn deser_slatepack(&self, data: Vec) -> Result { // check if data is armored, if so, remove and continue + if data.len() < super::armor::HEADER.len() { + let msg = format!("Data too short"); + return Err(ErrorKind::SlatepackDeser(msg).into()); + } let test_header = Vec::from_iter(data[0..super::armor::HEADER.len()].iter().cloned()); let data = match String::from_utf8(test_header) { Ok(s) => { diff --git a/libwallet/src/slatepack/types.rs b/libwallet/src/slatepack/types.rs index 8e05d99b5..363cb5f40 100644 --- a/libwallet/src/slatepack/types.rs +++ b/libwallet/src/slatepack/types.rs @@ -24,6 +24,7 @@ use crate::Error; use super::SlatepackAddress; use std::convert::TryInto; +use std::fmt; use std::io::{Read, Write}; pub const SLATEPACK_MAJOR_VERSION: u8 = 1; @@ -58,6 +59,12 @@ fn default_sender_none() -> Option { None } +impl fmt::Display for Slatepack { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", serde_json::to_string_pretty(&self).unwrap()) + } +} + impl Default for Slatepack { fn default() -> Self { Self { diff --git a/src/cmd/wallet_args.rs b/src/cmd/wallet_args.rs index 4f7b76564..f480f5fe8 100644 --- a/src/cmd/wallet_args.rs +++ b/src/cmd/wallet_args.rs @@ -28,8 +28,8 @@ use grin_wallet_controller::{Error, ErrorKind}; use grin_wallet_impls::tor::config::is_tor_address; use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl}; use grin_wallet_impls::{PathToSlate, SlateGetter as _}; -use grin_wallet_libwallet::Slate; use grin_wallet_libwallet::{IssueInvoiceTxArgs, NodeClient, WalletInst, WalletLCProvider}; +use grin_wallet_libwallet::{Slate, SlatepackAddress}; use grin_wallet_util::grin_core as core; use grin_wallet_util::grin_core::core::amount_to_hr_string; use grin_wallet_util::grin_keychain as keychain; @@ -469,11 +469,11 @@ pub fn parse_send_args(args: &ArgMatches) -> Result Some(a), + Ok(a) => Some(SlatepackAddress::try_from(a).unwrap()), Err(_) => { let addr = parse_required(args, "proof_address")?; match OnionV3Address::try_from(addr) { - Ok(a) => Some(a), + Ok(a) => Some(SlatepackAddress::try_from(a).unwrap()), Err(e) => { let msg = format!("Invalid proof address: {:?}", e); return Err(ParseError::ArgumentError(msg));