mirror of
https://github.com/jwinterm/grin-wallet.git
synced 2026-10-17 15:21:39 +02:00
Payment Proof Implementation (#259)
* refactor address generation code into libwallet, bool to flag whether to include proof, add sender address in init_send_tx * rustfmt * require payment proof addr as part of init_tx * rustfmt * store payment proof on sender transaction side * rustfmt * change sig to ed25519 sig * rustfmt * add message creation and signature * rustfmt * add payment proof verification function * rustfmt * validate proof on sender side, store proof * rustfmt * fix json tests * fixes and updates to tests * added API functions for converting and retrieving proof addresses * rustfmt * add payment proof to init_send_tx example * rustfmt * incorrect comment
This commit is contained in:
@@ -22,6 +22,7 @@ easy-jsonrpc-mw = "0.5.3"
|
||||
chrono = { version = "0.4.4", features = ["serde"] }
|
||||
ring = "0.13"
|
||||
base64 = "0.9"
|
||||
ed25519-dalek = "1.0.0-pre.1"
|
||||
|
||||
grin_wallet_libwallet = { path = "../libwallet", version = "3.0.0-alpha.1" }
|
||||
grin_wallet_config = { path = "../config", version = "3.0.0-alpha.1" }
|
||||
|
||||
+3
-1
@@ -56,4 +56,6 @@ pub use crate::foreign_rpc::foreign_rpc as foreign_rpc_client;
|
||||
pub use crate::foreign_rpc::run_doctest_foreign;
|
||||
pub use crate::owner_rpc::run_doctest_owner;
|
||||
|
||||
pub use types::{ECDHPubkey, EncryptedRequest, EncryptedResponse, EncryptionErrorResponse, Token};
|
||||
pub use types::{
|
||||
ECDHPubkey, EncryptedRequest, EncryptedResponse, EncryptionErrorResponse, PubAddress, Token,
|
||||
};
|
||||
|
||||
+105
-1
@@ -15,6 +15,7 @@
|
||||
//! Owner API External Definition
|
||||
|
||||
use chrono::prelude::*;
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::config::{TorConfig, WalletConfig};
|
||||
@@ -25,7 +26,7 @@ use crate::keychain::{Identifier, Keychain};
|
||||
use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage};
|
||||
use crate::libwallet::api_impl::{owner, owner_updater};
|
||||
use crate::libwallet::{
|
||||
AcctPathMapping, Error, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
|
||||
address, AcctPathMapping, Error, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
|
||||
NodeHeightResult, OutputCommitMapping, Slate, TxLogEntry, WalletInfo, WalletInst,
|
||||
WalletLCProvider,
|
||||
};
|
||||
@@ -1886,6 +1887,109 @@ where
|
||||
let index = q.len().saturating_sub(count);
|
||||
Ok(q.split_off(index))
|
||||
}
|
||||
|
||||
/// Retrieve the public proof "addresses" associated with the active account at the
|
||||
/// given derivation path.
|
||||
///
|
||||
/// In this case, an "address" means a Dalek ed25519 public key corresponding to
|
||||
/// a private key derived as follows:
|
||||
///
|
||||
/// e.g. The default parent account is at
|
||||
///
|
||||
/// `m/0/0`
|
||||
///
|
||||
/// With output blinding factors created as
|
||||
///
|
||||
/// `m/0/0/0`
|
||||
/// `m/0/0/1` etc...
|
||||
///
|
||||
/// The corresponding public address derivation path would be at:
|
||||
///
|
||||
/// `m/0/1`
|
||||
///
|
||||
/// With addresses created as:
|
||||
///
|
||||
/// `m/0/1/0`
|
||||
/// `m/0/1/1` etc...
|
||||
///
|
||||
/// Note that these addresses correspond to the public keys used in the addresses
|
||||
/// of TOR hidden services configured by the wallet listener.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
|
||||
/// * `derivation_index` - The index along the derivation path to retrieve an address for
|
||||
///
|
||||
/// # Returns
|
||||
/// * Ok with a DalekPublicKey representing the address
|
||||
/// * 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());
|
||||
///
|
||||
/// let res = api_owner.get_public_proof_address(None, 0);
|
||||
///
|
||||
/// if let Ok(_) = res {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
|
||||
pub fn get_public_proof_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)
|
||||
}
|
||||
|
||||
/// Helper function to convert an Onion v3 address to a payment proof address (essentially
|
||||
/// exctacting and verifying the public key)
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `address_v3` - An V3 Onion address
|
||||
///
|
||||
/// # 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
|
||||
///
|
||||
/// # 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());
|
||||
///
|
||||
/// let res = api_owner.proof_address_from_onion_v3(
|
||||
/// "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid"
|
||||
/// );
|
||||
///
|
||||
/// if let Ok(_) = res {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// let res = api_owner.stop_updater();
|
||||
/// ```
|
||||
|
||||
pub fn proof_address_from_onion_v3(&self, address_v3: &str) -> Result<DalekPublicKey, Error> {
|
||||
address::pubkey_from_onion_v3(address_v3)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -240,6 +240,7 @@ pub trait OwnerRpc: Sync + Send {
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"tx_slate_id": null,
|
||||
"payment_proof": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
},
|
||||
{
|
||||
@@ -258,6 +259,7 @@ pub trait OwnerRpc: Sync + Send {
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"tx_slate_id": null,
|
||||
"payment_proof": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
}
|
||||
]
|
||||
@@ -340,6 +342,7 @@ pub trait OwnerRpc: Sync + Send {
|
||||
"selection_strategy_is_use_all": true,
|
||||
"message": "my message",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
@@ -561,6 +564,7 @@ pub trait OwnerRpc: Sync + Send {
|
||||
"selection_strategy_is_use_all": true,
|
||||
"message": "Ok, here are your grins",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"send_args": null
|
||||
}
|
||||
],
|
||||
@@ -1485,6 +1489,8 @@ 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();
|
||||
|
||||
+95
-2
@@ -27,7 +27,7 @@ use crate::libwallet::{
|
||||
};
|
||||
use crate::util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::util::{static_secp_instance, LoggingConfig, ZeroingString};
|
||||
use crate::{ECDHPubkey, Owner, Token};
|
||||
use crate::{ECDHPubkey, Owner, PubAddress, Token};
|
||||
use easy_jsonrpc_mw;
|
||||
use rand::thread_rng;
|
||||
use std::time::Duration;
|
||||
@@ -264,6 +264,7 @@ pub trait OwnerRpcS {
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"tx_slate_id": null,
|
||||
"payment_proof": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
},
|
||||
{
|
||||
@@ -281,6 +282,7 @@ pub trait OwnerRpcS {
|
||||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": null,
|
||||
"payment_proof": null,
|
||||
"tx_slate_id": null,
|
||||
"tx_type": "ConfirmedCoinbase"
|
||||
}
|
||||
@@ -371,6 +373,7 @@ pub trait OwnerRpcS {
|
||||
"selection_strategy_is_use_all": true,
|
||||
"message": "my message",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb",
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
@@ -391,7 +394,11 @@ pub trait OwnerRpcS {
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": "0",
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"payment_proof": {
|
||||
"receiver_address": "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb",
|
||||
"receiver_signature": null,
|
||||
"sender_address": "32cdd63928854f8b2628b1dce4626ddcdf35d56cb7cfdf7d64cca5822b78d4d3"
|
||||
},
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
@@ -598,6 +605,7 @@ pub trait OwnerRpcS {
|
||||
"selection_strategy_is_use_all": true,
|
||||
"message": "Ok, here are your grins",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"send_args": null
|
||||
}
|
||||
},
|
||||
@@ -1776,6 +1784,71 @@ pub trait OwnerRpcS {
|
||||
*/
|
||||
|
||||
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).
|
||||
```
|
||||
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
|
||||
# r#"
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_public_proof_address",
|
||||
"params": {
|
||||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"derivation_index": 0
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
# "#
|
||||
# ,
|
||||
# r#"
|
||||
{
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": "32cdd63928854f8b2628b1dce4626ddcdf35d56cb7cfdf7d64cca5822b78d4d3"
|
||||
}
|
||||
}
|
||||
# "#
|
||||
# , true, 0, false, false, false);
|
||||
```
|
||||
*/
|
||||
|
||||
fn get_public_proof_address(
|
||||
&self,
|
||||
token: Token,
|
||||
derivation_index: u32,
|
||||
) -> Result<PubAddress, ErrorKind>;
|
||||
|
||||
/**
|
||||
Networked version of [Owner::proof_address_from_onion_v3](struct.Owner.html#method.proof_address_from_onion_v3).
|
||||
```
|
||||
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
|
||||
# r#"
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "proof_address_from_onion_v3",
|
||||
"params": {
|
||||
"address_v3": "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid"
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
# "#
|
||||
# ,
|
||||
# r#"
|
||||
{
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb"
|
||||
}
|
||||
}
|
||||
# "#
|
||||
# , true, 0, false, false, false);
|
||||
```
|
||||
*/
|
||||
|
||||
fn proof_address_from_onion_v3(&self, address_v3: String) -> Result<PubAddress, ErrorKind>;
|
||||
}
|
||||
|
||||
impl<L, C, K> OwnerRpcS for Owner<L, C, K>
|
||||
@@ -2080,4 +2153,24 @@ where
|
||||
fn get_updater_messages(&self, count: u32) -> Result<Vec<StatusMessage>, ErrorKind> {
|
||||
Owner::get_updater_messages(self, count as usize).map_err(|e| e.kind())
|
||||
}
|
||||
|
||||
fn get_public_proof_address(
|
||||
&self,
|
||||
token: Token,
|
||||
derivation_index: u32,
|
||||
) -> Result<PubAddress, ErrorKind> {
|
||||
let address = Owner::get_public_proof_address(
|
||||
self,
|
||||
(&token.keychain_mask).as_ref(),
|
||||
derivation_index,
|
||||
)
|
||||
.map_err(|e| e.kind())?;
|
||||
Ok(PubAddress { address })
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::core::libtx::secp_ser;
|
||||
use crate::libwallet::dalek_ser;
|
||||
use crate::libwallet::{Error, ErrorKind};
|
||||
use crate::util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::util::{from_hex, to_hex};
|
||||
use failure::ResultExt;
|
||||
|
||||
use base64;
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
use rand::{thread_rng, Rng};
|
||||
use ring::aead;
|
||||
use serde_json::{self, Value};
|
||||
@@ -32,6 +34,15 @@ 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)]
|
||||
|
||||
Reference in New Issue
Block a user