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:
Yeastplume
2019-11-28 14:34:27 +00:00
committed by GitHub
parent 9c2177e3d9
commit 7293ca99c3
28 changed files with 988 additions and 125 deletions
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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)]
+6
View File
@@ -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
View File
@@ -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 })
}
}
+11
View File
@@ -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)]