mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-02-01 08:51:09 +03:00
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:
parent
788d050bbe
commit
db12712928
16 changed files with 682 additions and 130 deletions
|
@ -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,
|
||||
};
|
||||
|
|
218
api/src/owner.rs
218
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,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>,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
})?;
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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: {:?}",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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 {
|
||||
///
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue