Slatepack OwnerAPI Changes (#421)

* change all user-facing instances of addresses to a SlatepackAddress

* finish renaming get_slatepack_address + documentation

* get_slatepack_secret_key OwnerRPC implementation and test

* add owner api functions

* OwnerRPC functions + doctests

* add explicit slatepack API tests to exercise encryption

* update api function names to better reflect RFC
This commit is contained in:
Yeastplume 2020-05-28 08:17:51 +01:00 committed by GitHub
parent 788d050bbe
commit db12712928
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 682 additions and 130 deletions

View file

@ -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,
};

View file

@ -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,25 @@ where
///
/// ```
pub fn get_public_proof_address(
pub fn get_slatepack_address(
&self,
keychain_mask: Option<&SecretKey>,
derivation_index: u32,
) -> Result<DalekPublicKey, Error> {
owner::get_public_proof_address(self.wallet_inst.clone(), keychain_mask, derivation_index)
) -> Result<SlatepackAddress, Error> {
owner::get_slatepack_address(self.wallet_inst.clone(), keychain_mask, derivation_index)
}
// TODO: Doc
/// get public proof a
pub fn get_secret_key(
&self,
keychain_mask: Option<&SecretKey>,
derivation_index: u32,
) -> Result<DalekSecretKey, Error> {
owner::get_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)
/// Retrieve the private ed25519 slatepack key at the given derivation index. Currently
/// used to decrypt encrypted slatepack messages.
///
/// # Arguments
///
/// * `address_v3` - An V3 Onion address
/// * `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(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 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.
@ -1952,22 +1941,176 @@ where
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
///
/// let res = api_owner.proof_address_from_onion_v3(
/// "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid"
/// );
/// let res = api_owner.get_slatepack_secret_key(None, 0);
///
/// if let Ok(_) = res {
/// // ...
/// }
///
/// let res = api_owner.stop_updater();
/// ```
pub fn get_slatepack_secret_key(
&self,
keychain_mask: Option<&SecretKey>,
derivation_index: u32,
) -> Result<DalekSecretKey, Error> {
owner::get_slatepack_secret_key(self.wallet_inst.clone(), keychain_mask, derivation_index)
}
/// Create a slatepack from a given slate, optionally encoding the slate with the provided
/// recipient public keys
///
/// # Arguments
///
/// * `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 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.
/// ```
/// # 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 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(slate) = result {
/// // Create a slatepack from our slate
/// let slatepack = api_owner.create_slatepack_message(
/// None,
/// &slate,
/// Some(0),
/// vec![],
/// );
/// }
///
/// ```
pub fn proof_address_from_onion_v3(&self, address_v3: &str) -> Result<DalekPublicKey, Error> {
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<u32>,
recipients: Vec<SlatepackAddress>,
) -> Result<String, Error> {
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<u32>,
) -> Result<Slate, Error> {
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<Slatepack, Error> {
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>,

View file

@ -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<Vec<StatusMessage>, 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,23 @@ pub trait OwnerRpc {
```
*/
fn get_public_proof_address(
fn get_slatepack_address(
&self,
token: Token,
derivation_index: u32,
) -> Result<PubAddress, ErrorKind>;
) -> Result<SlatepackAddress, ErrorKind>;
/**
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
}
@ -1443,7 +1444,7 @@ pub trait OwnerRpc {
"id": 1,
"jsonrpc": "2.0",
"result": {
"Ok": "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb"
"Ok": "86cca2aedea7989dfcca62e54477301d098bac260656d11373e314c099f0b26f"
}
}
# "#
@ -1451,7 +1452,156 @@ pub trait OwnerRpc {
```
*/
fn proof_address_from_onion_v3(&self, address_v3: String) -> Result<PubAddress, ErrorKind>;
fn get_slatepack_secret_key(
&self,
token: Token,
derivation_index: u32,
) -> Result<Ed25519SecretKey, ErrorKind>;
/**
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<u32>,
recipients: Vec<SlatepackAddress>,
) -> Result<String, ErrorKind>;
/**
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<u32>,
) -> Result<VersionedSlate, ErrorKind>;
/**
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
}
# "#
# ,
# r#"
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"Ok": {
"mode": 0,
"payload": "AAQAAgQ2QwwrAmJMIDJXBQEhKwAB0gKWSQAAAADTApZJAAAAANQClkkAAAAA1QKWSQAAAAAGAAAAAWWgvAAAAAAAAHoSAAEAAjh4zoRXJ/Ok7HbKPz20s4otBdY2uMNjIQi4V/7WPJbeAxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QePAjLN1jkohU+LJiix3ORibdzfNdVst8/ffWTMpYIreNTTeD9lKGaXQqmQ4Prwpfyl1bMzDje7uc1cYoaW0Dzk6BAA",
"sender": "slatepack1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfskdvkdu",
"slatepack": "1.0"
}
}
}
# "#
# , 0, false, false, false, false);
```
*/
fn decode_slatepack_message(&self, message: String) -> Result<Slatepack, ErrorKind>;
/**
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<PubAddress, ErrorKind> {
let address = Owner::get_public_proof_address(
) -> Result<SlatepackAddress, ErrorKind> {
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<Ed25519SecretKey, ErrorKind> {
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<u32>,
recipients: Vec<SlatepackAddress>,
) -> Result<String, ErrorKind> {
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<u32>,
) -> Result<VersionedSlate, ErrorKind> {
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<Slatepack, ErrorKind> {
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<PubAddress, ErrorKind> {
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<TorConfig>) -> 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,
};

View file

@ -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<SecretKey>,
}
/// 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

View file

@ -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<u16>,
pub payment_proof_address: Option<OnionV3Address>,
pub payment_proof_address: Option<SlatepackAddress>,
pub ttl_blocks: Option<u64>,
//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(())
})?;

View file

@ -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)?;

View file

@ -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: {:?}",

View file

@ -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);
}

View file

@ -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<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
index: u32,
) -> Result<DalekPublicKey, Error>
) -> Result<SlatepackAddress, Error>
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<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
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<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
sender_index: Option<u32>,
recipients: Vec<SlatepackAddress>,
) -> Result<String, Error>
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<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
slatepack: String,
secret_indices: Vec<u32>,
) -> Result<Slate, Error>
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<Slatepack, Error> {
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<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
@ -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());
};

View file

@ -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<u64>,
/// 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<OnionV3Address>,
pub payment_proof_recipient_address: Option<SlatepackAddress>,
/// 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,

View file

@ -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),

View file

@ -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<S>(key: &DalekSecretKey, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&key.to_bytes().to_hex())
}
///
pub fn deserialize<'de, D>(deserializer: D) -> Result<DalekSecretKey, D::Error>
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<u8>| {
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};

View file

@ -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<OnionV3Address> for SlatepackAddress {
type Error = Error;
fn try_from(addr: OnionV3Address) -> Result<SlatepackAddress, Error> {
Ok(SlatepackAddress::new(&addr.to_ed25519()?))
}
}
impl TryFrom<&SlatepackAddress> for xDalekPublicKey {
type Error = Error;
fn try_from(addr: &SlatepackAddress) -> Result<Self, Self::Error> {
@ -116,6 +124,24 @@ impl TryFrom<&SlatepackAddress> for xDalekPublicKey {
}
}
impl TryFrom<&SecretKey> for SlatepackAddress {
type Error = Error;
fn try_from(key: &SecretKey) -> Result<Self, Self::Error> {
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 {
///

View file

@ -49,6 +49,10 @@ impl<'a> Slatepacker<'a> {
/// return slatepack
pub fn deser_slatepack(&self, data: Vec<u8>) -> Result<Slatepack, Error> {
// 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) => {

View file

@ -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<SlatepackAddress> {
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 {

View file

@ -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<command::SendArgs, ParseErro
// if the destination address is a TOR address, we don't need the address
// separately
match OnionV3Address::try_from(dest) {
Ok(a) => 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));