Skip to content

Commit

Permalink
Ownership proof
Browse files Browse the repository at this point in the history
  • Loading branch information
bayk committed Sep 30, 2024
1 parent 7be7ac8 commit 240afb3
Show file tree
Hide file tree
Showing 11 changed files with 782 additions and 9 deletions.
50 changes: 49 additions & 1 deletion api/src/owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use crate::util::secp::key::SecretKey;
use crate::util::{from_hex, Mutex, ZeroingString};
use grin_wallet_util::grin_util::secp::key::PublicKey;
use grin_wallet_util::grin_util::static_secp_instance;
use libwallet::RetrieveTxQueryArgs;
use libwallet::{OwnershipProof, OwnershipProofValidation, RetrieveTxQueryArgs};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Sender};
use std::sync::Arc;
Expand Down Expand Up @@ -2435,6 +2435,31 @@ where
owner::verify_payment_proof(self.wallet_inst.clone(), keychain_mask, proof)
}

pub fn retrieve_ownership_proof(
&self,
keychain_mask: Option<&SecretKey>,
message: String,
include_public_root_key: bool,
include_tor_address: bool,
include_mqs_address: bool,
) -> Result<OwnershipProof, Error> {
owner::generate_ownership_proof(
self.wallet_inst.clone(),
keychain_mask,
message,
include_public_root_key,
include_tor_address,
include_mqs_address,
)
}

pub fn validate_ownership_proof(
&self,
proof: OwnershipProof,
) -> Result<OwnershipProofValidation, Error> {
owner::validate_ownership_proof(proof)
}

/// Start swap trade process. Return SwapID that can be used to check the status or perform further action.
pub fn swap_start(
&self,
Expand Down Expand Up @@ -2796,3 +2821,26 @@ macro_rules! doctest_helper_setup_doc_env {
let mut $wallet = Arc::new(Mutex::new(wallet));
};
}

/*#[test]
#[allow(unused_mut)]
fn test_api() {
use crate as grin_wallet_api;
global::set_local_chain_type(global::ChainTypes::AutomatedTesting);
grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
let mut api_owner = Owner::new(wallet.clone(), None, None);
let proof = api_owner.retrieve_ownership_proof(None,
"my message to sign".to_string(),
true,
true,
true).unwrap();
let valiation = api_owner.validate_ownership_proof(proof).unwrap();
assert_eq!(valiation.network, "floonet");
assert_eq!(valiation.message, "my message to sign".to_string());
assert!(valiation.viewing_key.is_some());
assert!(valiation.tor_address.is_some());
assert!(valiation.mqs_address.is_some());
}*/
78 changes: 77 additions & 1 deletion controller/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use grin_wallet_libwallet::swap::fsm::state::StateId;
use grin_wallet_libwallet::swap::trades;
use grin_wallet_libwallet::swap::types::Action;
use grin_wallet_libwallet::swap::{message, Swap};
use grin_wallet_libwallet::{Slate, StatusMessage, TxLogEntry, WalletInst};
use grin_wallet_libwallet::{OwnershipProof, Slate, StatusMessage, TxLogEntry, WalletInst};
use grin_wallet_util::grin_core::consensus::MWC_BASE;
use grin_wallet_util::grin_core::core::{amount_to_hr_string, Transaction};
use grin_wallet_util::grin_core::global::{FLOONET_DNS_SEEDS, MAINNET_DNS_SEEDS};
Expand Down Expand Up @@ -178,6 +178,82 @@ where
Ok(())
}

/// Arguments for generate_ownership_proof command
pub struct GenerateOwnershipProofArgs {
/// Message to sign
pub message: String,
/// does need to include public root key
pub include_public_root_key: bool,
/// does need to include tor address
pub include_tor_address: bool,
/// does need to include MQS address
pub include_mqs_address: bool,
}

pub fn generate_ownership_proof<'a, L, C, K>(
owner_api: &mut Owner<L, C, K>,
keychain_mask: Option<&SecretKey>,
args: GenerateOwnershipProofArgs,
) -> Result<(), Error>
where
L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
let proof = api.retrieve_ownership_proof(
m,
args.message,
args.include_public_root_key,
args.include_tor_address,
args.include_mqs_address,
)?;

let proof_json = serde_json::to_string(&proof).map_err(|e| {
Error::GenericError(format!("Failed convert proof result into json, {}", e))
})?;

println!("Ownership Proof: {}", proof_json);
Ok(())
})?;
Ok(())
}

pub fn validate_ownership_proof<'a, L, C, K>(
owner_api: &mut Owner<L, C, K>,
keychain_mask: Option<&SecretKey>,
proof: &str,
) -> Result<(), Error>
where
L: WalletLCProvider<'static, C, K>,
C: NodeClient + 'static,
K: keychain::Keychain + 'static,
{
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, _m| {
let proof = serde_json::from_str::<OwnershipProof>(proof).map_err(|e| {
Error::ArgumentError(format!("Unable to decode proof from json, {}", e))
})?;

let validation = api.validate_ownership_proof(proof)?;
println!("Network: {}", validation.network);
println!("Message: {}", validation.message);
println!(
"Viewing Key: {}",
validation.viewing_key.unwrap_or("Not provided".to_string())
);
println!(
"Tor Address: {}",
validation.tor_address.unwrap_or("Not provided".to_string())
);
println!(
"MWCMQS Address: {}",
validation.mqs_address.unwrap_or("Not provided".to_string())
);
Ok(())
})?;
Ok(())
}

/// Arguments for rewind hash view wallet scan command
pub struct ViewWalletScanArgs {
pub rewind_hash: String,
Expand Down
237 changes: 237 additions & 0 deletions controller/tests/ownership_proofs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// Copyright 2024 The Grin Developers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Test a wallet sending to self
#[macro_use]
extern crate log;
extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;

use grin_wallet_util::grin_core::global;

use impls::test_framework::{self, LocalWalletClient};
use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;

#[macro_use]
mod common;
use common::{clean_output_dir, create_wallet_proxy, setup};
use grin_wallet_libwallet::PubKeySignature;
use grin_wallet_util::grin_util::ZeroingString;

/// self send impl
fn ownership_proof_impl(test_dir: &'static str) -> Result<(), wallet::Error> {
// Create a new proxy to simulate server and wallet responses
global::set_local_chain_type(global::ChainTypes::AutomatedTesting);
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",
Some(ZeroingString::from(
"room plastic there over junior comfort drip envelope hope divide cake trophy"
)),
&mut wallet_proxy,
true
);
let mask1 = (&mask1_i).as_ref();

// Set the wallet proxy listener running
thread::spawn(move || {
global::set_local_chain_type(global::ChainTypes::AutomatedTesting);
if let Err(e) = wallet_proxy.run() {
error!("Wallet Proxy error: {}", e);
}
});

let _ =
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 10 as usize, false);

wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert!(wallet1_info.last_confirmed_height > 0);
assert!(wallet1_info.total > 0);

let proof =
api.retrieve_ownership_proof(m, "my message to sign".to_string(), true, true, true)?;

assert_eq!(proof.message, "my message to sign");
assert_eq!(proof.network, "floonet");
assert!(proof.wallet_root.is_some());
assert!(proof.tor_address.is_some());
assert!(proof.mqs_address.is_some());

assert_eq!(format!("{:?}", proof), "OwnershipProof { network: \"floonet\", message: \"my message to sign\", wallet_root: Some(PubKeySignature { public_key: \"022e4a08245fc03ca5da9c717c1b29c589413fac96150a400500eff613d05dd34d\", signature: \"3045022100e12d078b67446cc83ab46918b5a641a61b4a02bcdfac03b8978865ca6740def302202f463ec8dea18921942e20452d15ddd2fbcf590aa36b0c414d45078a83d38b92\" }), tor_address: Some(PubKeySignature { public_key: \"fa30d0726b505d6304d93a4ed6c4a428d467b1da13a7826cfdd4131046ca27a4\", signature: \"1cb237ca781f0868752dcc0e503eb70904fd208c7b64c3d23ed6830c808663cae723ac909709f1c16495b2162ec9028a225f7074863172246930eaaee3853706\" }), mqs_address: Some(PubKeySignature { public_key: \"0214f727d0503231f7dd4797509b3e7cde3f7cfdf8c32a3b562c8bd3f650bb2509\", signature: \"30440220035e56caa572ade99ebbffdc2d166c943fe433418269cb156d21f4a0e02557520220628e9e530e662ca1632774106f37a1404873f6bc482bc603a8cfee6c3e92f2f6\" }) }");

let validate_res = api.validate_ownership_proof(proof.clone());
assert!(validate_res.is_ok());
let validate_res = validate_res.unwrap();

assert_eq!(proof.message, validate_res.message);
assert_eq!(proof.network, validate_res.network);
assert_eq!(format!("{:?}", validate_res), "OwnershipProofValidation { network: \"floonet\", message: \"my message to sign\", viewing_key: Some(\"60a98a5d7d1823743b9c3993a31bec49fc7114d3cdbe6bf9e81f53a7f7e02727\"), tor_address: Some(\"7iyna4tlkbowgbgzhjhnnrfefdkgpmo2cotye3h52qjrarwke6saswid\"), mqs_address: Some(\"xmfmGjJU6hLUtndfwDSQmxtYckgDQEquMwXxxCW8D2zUN1uvdrkN\") }");

// Now let's try to adjust something and validate that it will fail
let mut invalid_proof = proof.clone();
invalid_proof.network = "mainnet".to_string();
let validate_res = api.validate_ownership_proof(invalid_proof);
assert!(validate_res.is_err());

let mut invalid_proof = proof.clone();
invalid_proof.message = "another message".to_string();
let validate_res = api.validate_ownership_proof(invalid_proof);
assert!(validate_res.is_err());

let mut invalid_proof = proof.clone();
invalid_proof.tor_address = None;
let validate_res = api.validate_ownership_proof(invalid_proof);
assert!(validate_res.is_err());

let mut invalid_proof = proof.clone();
invalid_proof.wallet_root = None;
let validate_res = api.validate_ownership_proof(invalid_proof);
assert!(validate_res.is_err());

let mut invalid_proof = proof.clone();
invalid_proof.mqs_address = None;
let validate_res = api.validate_ownership_proof(invalid_proof);
assert!(validate_res.is_err());

let mut invalid_proof = proof.clone();
invalid_proof.wallet_root = Some(PubKeySignature{
public_key: "022e4a08245fc03ca5da9c717c1b29c589413fac96150a400500eff613d15dd34d".to_string(), // PK is changed
signature: "3045022100e12d078b67446cc83ab46918b5a641a61b4a02bcdfac03b8978865ca6740def302202f463ec8dea18921942e20452d15ddd2fbcf590aa36b0c414d45078a83d38b92".to_string(),
});
let validate_res = api.validate_ownership_proof(invalid_proof);
assert!(validate_res.is_err());

let mut invalid_proof = proof.clone();
invalid_proof.wallet_root = Some(PubKeySignature{
public_key: "022e4a08245fc03ca5da9c717c1b29c589413fac96150a400500eff613d05dd34d".to_string(),
signature: "3045022100e12d078b67446cc83ab46918b5a641a61b4a02bcdfac03b8978865ca6740def302202f453ec8dea18921942e20452d15ddd2fbcf590aa36b0c414d45078a83d38b92".to_string(), // signature is changed
});
let validate_res = api.validate_ownership_proof(invalid_proof);
assert!(validate_res.is_err());

// Now let try not full proofs
let proof =
api.retrieve_ownership_proof(m, "my message to sign".to_string(), true, false, false)?;

assert_eq!(proof.message, "my message to sign");
assert_eq!(proof.network, "floonet");
assert!(proof.wallet_root.is_some());
assert!(proof.tor_address.is_none());
assert!(proof.mqs_address.is_none());

let validate_res = api.validate_ownership_proof(proof).unwrap();
assert_eq!("my message to sign", validate_res.message);
assert_eq!("floonet", validate_res.network);
assert!(validate_res.viewing_key.is_some());
assert!(validate_res.tor_address.is_none());
assert!(validate_res.mqs_address.is_none());

// Now let try not full proofs
let proof =
api.retrieve_ownership_proof(m, "my message to sign".to_string(), false, true, false)?;

assert_eq!(proof.message, "my message to sign");
assert_eq!(proof.network, "floonet");
assert!(proof.wallet_root.is_none());
assert!(proof.tor_address.is_some());
assert!(proof.mqs_address.is_none());

let validate_res = api.validate_ownership_proof(proof).unwrap();
assert_eq!("my message to sign", validate_res.message);
assert_eq!("floonet", validate_res.network);
assert!(validate_res.viewing_key.is_none());
assert!(validate_res.tor_address.is_some());
assert!(validate_res.mqs_address.is_none());

// Now let try not full proofs
let proof =
api.retrieve_ownership_proof(m, "my message to sign".to_string(), false, false, true)?;

assert_eq!(proof.message, "my message to sign");
assert_eq!(proof.network, "floonet");
assert!(proof.wallet_root.is_none());
assert!(proof.tor_address.is_none());
assert!(proof.mqs_address.is_some());

let validate_res = api.validate_ownership_proof(proof).unwrap();
assert_eq!("my message to sign", validate_res.message);
assert_eq!("floonet", validate_res.network);
assert!(validate_res.viewing_key.is_none());
assert!(validate_res.tor_address.is_none());
assert!(validate_res.mqs_address.is_some());

// Now let try not full proofs
let proof =
api.retrieve_ownership_proof(m, "my message to sign".to_string(), true, false, true)?;

assert_eq!(proof.message, "my message to sign");
assert_eq!(proof.network, "floonet");
assert!(proof.wallet_root.is_some());
assert!(proof.tor_address.is_none());
assert!(proof.mqs_address.is_some());

let validate_res = api.validate_ownership_proof(proof).unwrap();
assert_eq!("my message to sign", validate_res.message);
assert_eq!("floonet", validate_res.network);
assert!(validate_res.viewing_key.is_some());
assert!(validate_res.tor_address.is_none());
assert!(validate_res.mqs_address.is_some());

// Now let try not full proofs
let proof =
api.retrieve_ownership_proof(m, "my message to sign".to_string(), true, true, false)?;

assert_eq!(proof.message, "my message to sign");
assert_eq!(proof.network, "floonet");
assert!(proof.wallet_root.is_some());
assert!(proof.tor_address.is_some());
assert!(proof.mqs_address.is_none());

let validate_res = api.validate_ownership_proof(proof).unwrap();
assert_eq!("my message to sign", validate_res.message);
assert_eq!("floonet", validate_res.network);
assert!(validate_res.viewing_key.is_some());
assert!(validate_res.tor_address.is_some());
assert!(validate_res.mqs_address.is_none());

Ok(())
})?;

// let logging finish
stopper.store(false, Ordering::Relaxed);
thread::sleep(Duration::from_millis(1000));
Ok(())
}

#[test]
fn wallet_ownership_proof() {
let test_dir = "test_output/ownership_proof";
setup(test_dir);
if let Err(e) = ownership_proof_impl(test_dir) {
panic!("ownership_proof_impl Error: {}", e);
}
clean_output_dir(test_dir);
}
Loading

0 comments on commit 240afb3

Please sign in to comment.