Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add wallet #18

Merged
merged 11 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
433 changes: 242 additions & 191 deletions src-tauri/Cargo.lock

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ tauri-build = { version = "1", features = ["isolation"] }

[dependencies]
tari_common = { git = "https://github.com/tari-project/tari.git", branch = "development" }
tari_common_types = { git = "https://github.com/tari-project/tari.git", branch = "development" }
tari_shutdown = { git = "https://github.com/tari-project/tari.git", branch = "development" }
minotari_node_grpc_client = { git = "https://github.com/tari-project/tari.git", branch = "development" }
minotari_wallet_grpc_client = { git = "https://github.com/tari-project/tari.git", branch = "development" }
tari_core = { git = "https://github.com/tari-project/tari.git", branch = "development", features = ["transactions"] }
tari_key_manager = { git = "https://github.com/tari-project/tari.git", branch = "development" }
tari_utilities = "0.7.0"
tari_crypto = "0.20.3"


anyhow = "1"
dirs-next = "2.0.0"
Expand All @@ -41,17 +47,22 @@ serde_json = "1"
futures-util = "0.3.30"
flate2 = "1.0.30"
tar = "0.4.26"
async_zip = { version ="0.0.17", features = ["full"] }
async_zip = { version = "0.0.17", features = ["full"] }
futures-lite = "2.3.0"
tokio-util = { version = "0.7.11", features = ["compat"] }
sanitize-filename = "0.5"
async-trait = "0.1.81"
sysinfo = "0.31.2"
log4rs = "1.3.0"
keyring = "3.0.5"
# static bind lzma
xz2 = { version = "0.1.7", features = ["static"] }

# needed for keymanager. TODO: Find a way of creating a keymanager without bundling sqlite
libsqlite3-sys = { version = "0.25.1", features = ["bundled"] }

log = "0.4.22"
rand = "0.8.5"


[features]
Expand Down
16 changes: 15 additions & 1 deletion src-tauri/src/binary_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl LatestVersionApiAdapter for XmrigVersionApiAdapter {

fn find_version_for_platform(
&self,
version: &VersionDownloadInfo,
_version: &VersionDownloadInfo,
) -> Result<VersionAsset, Error> {
todo!()
}
Expand Down Expand Up @@ -133,6 +133,13 @@ impl BinaryResolver {
owner: "tari-project".to_string(),
}),
);
adapters.insert(
Binaries::Wallet,
Box::new(GithubReleasesAdapter {
repo: "tari".to_string(),
owner: "tari-project".to_string(),
}),
);
Self { adapters }
}

Expand Down Expand Up @@ -162,6 +169,10 @@ impl BinaryResolver {
let minotari_node_bin = base_dir.join("minotari_node");
Ok(minotari_node_bin)
}
Binaries::Wallet => {
let wallet_bin = base_dir.join("minotari_console_wallet");
Ok(wallet_bin)
}
}
}

Expand Down Expand Up @@ -247,6 +258,7 @@ impl BinaryResolver {
return "freebsd-x64".to_string();
}

#[allow(unreachable_patterns)]
panic!("Unsupported OS");
}
}
Expand All @@ -256,6 +268,7 @@ pub enum Binaries {
Xmrig,
MergeMiningProxy,
MinotariNode,
Wallet,
}

impl Binaries {
Expand All @@ -264,6 +277,7 @@ impl Binaries {
Binaries::Xmrig => "xmrig",
Binaries::MergeMiningProxy => "mmproxy",
Binaries::MinotariNode => "minotari_node",
Binaries::Wallet => "wallet",
}
}
}
6 changes: 5 additions & 1 deletion src-tauri/src/cpu_miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ impl CpuMiner {
let xmrig_node_connection = match cpu_miner_config.node_connection {
CpuMinerConnection::BuiltInProxy => {
local_mm_proxy
.start(app_shutdown.clone(), base_path)
.start(
app_shutdown.clone(),
base_path,
cpu_miner_config.tari_address.clone(),
)
.await?;
local_mm_proxy.wait_ready().await?;
XmrigNodeConnection::LocalMmproxy {
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/download_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ pub async fn extract_zip(archive: &Path, out_dir: &Path) -> Result<(), anyhow::E
Ok(())
}

#[allow(unused_variables)]
pub async fn set_permissions(file_path: &Path) -> Result<(), anyhow::Error> {
#[cfg(unix)]
{
Expand Down
16 changes: 8 additions & 8 deletions src-tauri/src/github/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ struct Asset {
}

pub async fn get_latest_release(
repo_owner: &str,
repo_name: &str,
tag: &str,
_repo_owner: &str,
_repo_name: &str,
_tag: &str,
) -> Result<String, anyhow::Error> {
let client = Client::new();
let url = format!(
"https://api.github.com/repos/{}/{}/releases/tags/{}",
repo_owner, repo_name, tag
);
// let client = Client::new();
// let url = format!(
// "https://api.github.com/repos/{}/{}/releases/tags/{}",
// repo_owner, repo_name, tag
// );
todo!("get_latest_release not implemented")
}

Expand Down
157 changes: 157 additions & 0 deletions src-tauri/src/internal_wallet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use anyhow::anyhow;
use keyring::Entry;
use log::{info, warn};
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tari_common::configuration::Network;
use tari_common_types::tari_address::{TariAddress, TariAddressFeatures};
use tari_crypto::keys::PublicKey;
use tari_crypto::ristretto::RistrettoPublicKey;
use tari_key_manager::cipher_seed::CipherSeed;
use tari_key_manager::key_manager::KeyManager;
use tari_key_manager::key_manager_service::KeyDigest;
use tari_key_manager::key_manager_service::KeyManagerInterface;
use tari_utilities::encoding::Base58;
use tari_utilities::SafePassword;
use tokio::fs;

use tari_core::transactions::key_manager::{
create_memory_db_key_manager_from_seed, SecretTransactionKeyManagerInterface,
TransactionKeyManagerInterface,
};
use tari_key_manager::mnemonic::{Mnemonic, MnemonicLanguage};
use tari_utilities::hex::Hex;

const KEY_MANAGER_COMMS_SECRET_KEY_BRANCH_KEY: &str = "comms";
const LOG_TARGET: &str = "tari::universe::internal_wallet";

pub struct InternalWallet {
tari_address: TariAddress,
config: WalletConfig,
}

impl InternalWallet {
pub async fn load_or_create(config_path: PathBuf) -> Result<Self, anyhow::Error> {
let file = config_path.join("wallet_config.json");

if file.exists() {
info!(target: LOG_TARGET, "Loading wallet from file: {:?}", file);
let config = fs::read_to_string(&file).await?;
match serde_json::from_str::<WalletConfig>(&config) {
Ok(config) => {
return Ok(Self {
tari_address: TariAddress::from_base58(&config.tari_address_base58)?,
config,
})
}
Err(e) => {
warn!(target: LOG_TARGET, "Failed to parse wallet config: {}", e.to_string());
}
}
}
info!(target: LOG_TARGET, "Wallet config does not exist or is corrupt. Creating new wallet");
let (wallet, config) = InternalWallet::create_new_wallet().await?;
let config = serde_json::to_string(&config)?;
fs::write(file, config).await?;
Ok(wallet)
}

pub fn get_tari_address(&self) -> TariAddress {
self.tari_address.clone()
}

async fn create_new_wallet() -> Result<(Self, WalletConfig), anyhow::Error> {
let mut config = WalletConfig {
tari_address_base58: "".to_string(),
view_key_private_hex: "".to_string(),
seed_words_encrypted_base58: "".to_string(),
spend_public_key_hex: "".to_string(),
};
let entry = Entry::new("com.tari.universe", "internal_wallet")?;

let passphrase = SafePassword::from(match entry.get_password() {
Ok(pass) => pass,
Err(_) => {
let passphrase = generate_password(32);
entry.set_password(&passphrase)?;
passphrase
}
});

let seed = CipherSeed::new();
// TODO: Don't print out the seed words lol
let seed_words = seed.to_mnemonic(MnemonicLanguage::English, None).unwrap();
for i in 0..seed_words.len() {
dbg!(seed_words.get_word(i).unwrap());
info!(target: LOG_TARGET, "Seed: {}:{}", i+1, seed_words.get_word(i).unwrap());
}
let seed_file = seed.encipher(Some(passphrase))?;
config.seed_words_encrypted_base58 = seed_file.to_base58();

let comms_key_manager = KeyManager::<RistrettoPublicKey, KeyDigest>::from(
seed.clone(),
KEY_MANAGER_COMMS_SECRET_KEY_BRANCH_KEY.to_string(),
0,
);
let comms_key = comms_key_manager
.derive_key(0)
.map_err(|e| anyhow!(e.to_string()))?
.key;
let comms_pub_key = RistrettoPublicKey::from_secret_key(&comms_key);
let network = Network::default();

let tx_key_manager = create_memory_db_key_manager_from_seed(seed.clone(), 64)?;
let view_key = tx_key_manager.get_view_key().await?;
let view_key_private = tx_key_manager.get_private_key(&view_key.key_id).await?;
let view_key_pub = view_key.pub_key;
let tari_address = TariAddress::new_dual_address(
view_key_pub.clone(),
comms_pub_key.clone(),
network,
TariAddressFeatures::create_one_sided_only(),
);

config.tari_address_base58 = tari_address.to_base58();
config.view_key_private_hex = view_key_private.to_hex();
config.spend_public_key_hex = comms_pub_key.to_hex();
Ok((
Self {
tari_address,
config: config.clone(),
},
config,
))
}

pub fn get_view_key(&self) -> String {
self.config.view_key_private_hex.clone()
}
pub fn get_spend_key(&self) -> String {
self.config.spend_public_key_hex.clone()
}
}

fn generate_password(length: usize) -> String {
let charset: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:',.<>?/`~"
.chars()
.collect();

let mut rng = rand::thread_rng();
let password: String = (0..length)
.map(|_| {
let idx = rng.gen_range(0..charset.len());
charset[idx]
})
.collect();

password
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct WalletConfig {
tari_address_base58: String,
view_key_private_hex: String,
spend_public_key_hex: String,
seed_words_encrypted_base58: String,
}
Loading
Loading