mirror of
https://code.gri.mw/GUI/grim.git
synced 2026-07-04 05:57:29 +00:00
wallet: do not scan outputs for new wallet
This commit is contained in:
Generated
+1
@@ -3551,6 +3551,7 @@ dependencies = [
|
||||
"qrcodegen",
|
||||
"rand 0.9.1",
|
||||
"rfd",
|
||||
"ring",
|
||||
"rkv",
|
||||
"rqrr",
|
||||
"rust-i18n",
|
||||
|
||||
@@ -76,6 +76,7 @@ ur = "0.4.1"
|
||||
gif = "0.13.1"
|
||||
rkv = { version = "0.19.0", features = ["lmdb"] }
|
||||
usvg = "0.45.1"
|
||||
ring = "0.16.20"
|
||||
|
||||
## tor
|
||||
arti-client = { version = "0.29.0", features = ["pt-client", "static", "onion-service-service", "onion-service-client"] }
|
||||
|
||||
@@ -73,7 +73,7 @@ impl Default for StratumSetup {
|
||||
// Setup mining rewards wallet name and identifier.
|
||||
let mut wallet_id = NodeConfig::get_stratum_wallet_id();
|
||||
let wallet_name = if let Some(id) = wallet_id {
|
||||
WalletConfig::name_by_id(id)
|
||||
WalletConfig::read_name_by_id(id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -115,7 +115,7 @@ impl ModalContainer for StratumSetup {
|
||||
self.wallets_modal.ui(ui, modal, &mut self.wallets, cb, |wallet, _| {
|
||||
let id = wallet.get_config().id;
|
||||
NodeConfig::save_stratum_wallet_id(id);
|
||||
self.wallet_name = WalletConfig::name_by_id(id);
|
||||
self.wallet_name = WalletConfig::read_name_by_id(id);
|
||||
})
|
||||
},
|
||||
STRATUM_PORT_MODAL => self.port_modal(ui, modal, cb),
|
||||
|
||||
+1
-1
@@ -588,7 +588,7 @@ impl Handler {
|
||||
let mut state = self.current_state.write();
|
||||
let wallet_listener_url = if !config.burn_reward {
|
||||
if let Ok(id) = config.wallet_listener_url.parse::<i64>() {
|
||||
if let Some(port) = WalletConfig::api_port_by_id(id) {
|
||||
if let Some(port) = WalletConfig::read_api_port_by_id(id) {
|
||||
let url = format!("http://127.0.0.1:{}", port);
|
||||
Some(url)
|
||||
} else {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use grin_core::global;
|
||||
use grin_core::global::ChainTypes;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use crate::gui::views::Content;
|
||||
@@ -118,6 +119,12 @@ impl AppConfig {
|
||||
*w_conn_config = ConnectionsConfig::for_chain_type(chain_type);
|
||||
}
|
||||
}
|
||||
if !global::GLOBAL_CHAIN_TYPE.is_init() {
|
||||
global::init_global_chain_type(*chain_type);
|
||||
} else {
|
||||
global::set_global_chain_type(*chain_type);
|
||||
global::set_local_chain_type(*chain_type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current [`ChainTypes`] for node and wallets.
|
||||
|
||||
@@ -21,7 +21,7 @@ use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use grin_config::ConfigError;
|
||||
|
||||
use grin_core::global;
|
||||
use crate::node::NodeConfig;
|
||||
use crate::settings::AppConfig;
|
||||
use crate::tor::TorConfig;
|
||||
@@ -62,7 +62,15 @@ impl Settings {
|
||||
let tor_config_path = Settings::config_path(TorConfig::FILE_NAME, None);
|
||||
let tor_config = Self::init_config::<TorConfig>(tor_config_path);
|
||||
|
||||
// Setup chain type.
|
||||
let chain_type = &app_config.chain_type;
|
||||
if !global::GLOBAL_CHAIN_TYPE.is_init() {
|
||||
global::init_global_chain_type(*chain_type);
|
||||
} else {
|
||||
global::set_global_chain_type(*chain_type);
|
||||
global::set_local_chain_type(*chain_type);
|
||||
}
|
||||
|
||||
Self {
|
||||
node_config: Arc::new(RwLock::new(NodeConfig::for_chain_type(chain_type))),
|
||||
conn_config: Arc::new(RwLock::new(ConnectionsConfig::for_chain_type(chain_type))),
|
||||
|
||||
+26
-7
@@ -50,10 +50,16 @@ pub struct WalletConfig {
|
||||
|
||||
/// Base wallets directory name.
|
||||
const BASE_DIR_NAME: &'static str = "wallets";
|
||||
/// Base wallets directory name.
|
||||
const DB_DIR_NAME: &'static str = "db";
|
||||
/// Wallet data directory name.
|
||||
const DATA_DIR_NAME: &'static str = "wallet_data";
|
||||
/// Wallet configuration file name.
|
||||
const CONFIG_FILE_NAME: &'static str = "grim-wallet.toml";
|
||||
/// Slatepacks directory name.
|
||||
const SLATEPACKS_DIR_NAME: &'static str = "slatepacks";
|
||||
/// Seed file name.
|
||||
const SEED_FILE: &str = "wallet.seed";
|
||||
|
||||
/// Default value of minimal amount of confirmations.
|
||||
const MIN_CONFIRMATIONS_DEFAULT: u64 = 10;
|
||||
@@ -98,7 +104,7 @@ impl WalletConfig {
|
||||
}
|
||||
|
||||
/// Get wallet name by provided identifier.
|
||||
pub fn name_by_id(id: i64) -> Option<String> {
|
||||
pub fn read_name_by_id(id: i64) -> Option<String> {
|
||||
let mut wallet_dir = WalletConfig::get_base_path(AppConfig::chain_type());
|
||||
wallet_dir.push(id.to_string());
|
||||
if let Some(cfg) = Self::load(wallet_dir) {
|
||||
@@ -108,7 +114,7 @@ impl WalletConfig {
|
||||
}
|
||||
|
||||
/// Get wallet API port by provided identifier.
|
||||
pub fn api_port_by_id(id: i64) -> Option<u16> {
|
||||
pub fn read_api_port_by_id(id: i64) -> Option<u16> {
|
||||
let mut wallet_dir = WalletConfig::get_base_path(AppConfig::chain_type());
|
||||
wallet_dir.push(id.to_string());
|
||||
if let Some(cfg) = Self::load(wallet_dir) {
|
||||
@@ -157,25 +163,38 @@ impl WalletConfig {
|
||||
config_path
|
||||
}
|
||||
|
||||
/// Get current wallet data path.
|
||||
pub fn get_data_path(&self) -> String {
|
||||
/// Get current wallet path.
|
||||
pub fn get_wallet_path(&self) -> String {
|
||||
let chain_type = AppConfig::chain_type();
|
||||
let mut data_path = Self::get_base_path(chain_type);
|
||||
data_path.push(self.id.to_string());
|
||||
data_path.to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// Get wallet data path.
|
||||
pub fn get_data_path(&self) -> String {
|
||||
let mut data_path = PathBuf::from(self.get_wallet_path());
|
||||
data_path.push(DATA_DIR_NAME);
|
||||
data_path.to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// Get wallet seed path.
|
||||
pub fn seed_path(&self) -> String {
|
||||
let mut path = PathBuf::from(self.get_data_path());
|
||||
path.push(SEED_FILE);
|
||||
path.to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// Get wallet database data path.
|
||||
pub fn get_db_path(&self) -> String {
|
||||
let mut path = PathBuf::from(self.get_data_path());
|
||||
path.push("wallet_data");
|
||||
path.push("db");
|
||||
path.push(DB_DIR_NAME);
|
||||
path.to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// Get Slatepacks data path for current wallet.
|
||||
pub fn get_slatepack_path(&self, slate: &Slate) -> PathBuf {
|
||||
let mut path = PathBuf::from(self.get_data_path());
|
||||
let mut path = PathBuf::from(self.get_wallet_path());
|
||||
path.push(SLATEPACKS_DIR_NAME);
|
||||
if !path.exists() {
|
||||
let _ = fs::create_dir_all(path.clone());
|
||||
|
||||
+2
-1
@@ -32,4 +32,5 @@ pub use list::*;
|
||||
mod utils;
|
||||
pub use utils::WalletUtils;
|
||||
|
||||
pub mod store;
|
||||
pub mod store;
|
||||
mod seed;
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2025 The Grim 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.
|
||||
|
||||
use core::num::NonZeroU32;
|
||||
use grin_util::{ToHex, ZeroingString};
|
||||
use grin_wallet_impls::Error;
|
||||
use rand::{rng, Rng};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use ring::aead;
|
||||
use ring::pbkdf2;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WalletSeed(Vec<u8>);
|
||||
|
||||
impl WalletSeed {
|
||||
pub fn from_bytes(bytes: &[u8]) -> WalletSeed {
|
||||
WalletSeed(bytes.to_vec())
|
||||
}
|
||||
|
||||
pub fn from_mnemonic(word_list: ZeroingString) -> Result<WalletSeed, Error> {
|
||||
let res = grin_keychain::mnemonic::to_entropy(&word_list);
|
||||
match res {
|
||||
Ok(s) => Ok(WalletSeed::from_bytes(&s)),
|
||||
Err(_) => Err(Error::Mnemonic.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_file(
|
||||
seed_file_path: &str,
|
||||
recovery_phrase: ZeroingString,
|
||||
password: ZeroingString,
|
||||
) -> Result<WalletSeed, Error> {
|
||||
let seed = WalletSeed::from_mnemonic(recovery_phrase)?;
|
||||
let enc_seed = EncryptedWalletSeed::from_seed(&seed, password)?;
|
||||
let enc_seed_json = serde_json::to_string_pretty(&enc_seed).map_err(|_| Error::Format)?;
|
||||
let mut file = File::create(seed_file_path).map_err(|_| Error::IO)?;
|
||||
file.write_all(&enc_seed_json.as_bytes())
|
||||
.map_err(|_| Error::IO)?;
|
||||
Ok(seed)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypted wallet seed, for storing on disk and decrypting with provided password.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub struct EncryptedWalletSeed {
|
||||
encrypted_seed: String,
|
||||
pub salt: String,
|
||||
pub nonce: String,
|
||||
}
|
||||
|
||||
impl EncryptedWalletSeed {
|
||||
/// Create a new encrypted seed from the given seed + password.
|
||||
pub fn from_seed(
|
||||
seed: &WalletSeed,
|
||||
password: ZeroingString,
|
||||
) -> Result<EncryptedWalletSeed, Error> {
|
||||
let salt: [u8; 8] = rng().random();
|
||||
let nonce: [u8; 12] = rng().random();
|
||||
let password = password.as_bytes();
|
||||
let mut key = [0; 32];
|
||||
pbkdf2::derive(
|
||||
pbkdf2::PBKDF2_HMAC_SHA512,
|
||||
NonZeroU32::new(100).unwrap(),
|
||||
&salt,
|
||||
password,
|
||||
&mut key,
|
||||
);
|
||||
let content = seed.0.to_vec();
|
||||
let mut enc_bytes = content;
|
||||
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
|
||||
let sealing_key: aead::LessSafeKey = aead::LessSafeKey::new(unbound_key);
|
||||
let aad = aead::Aad::from(&[]);
|
||||
let res = sealing_key.seal_in_place_append_tag(
|
||||
aead::Nonce::assume_unique_for_key(nonce),
|
||||
aad,
|
||||
&mut enc_bytes,
|
||||
);
|
||||
if let Err(_) = res {
|
||||
return Err(Error::Encryption);
|
||||
}
|
||||
Ok(EncryptedWalletSeed {
|
||||
encrypted_seed: enc_bytes.to_hex(),
|
||||
salt: salt.to_hex(),
|
||||
nonce: nonce.to_hex(),
|
||||
})
|
||||
}
|
||||
}
|
||||
+48
-38
@@ -12,42 +12,42 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{fs, thread};
|
||||
use futures::channel::oneshot;
|
||||
use parking_lot::RwLock;
|
||||
use rand::Rng;
|
||||
use serde_json::{json, Value};
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::net::{SocketAddr, TcpListener};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, mpsc};
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::thread::Thread;
|
||||
use std::time::Duration;
|
||||
use futures::channel::oneshot;
|
||||
use serde_json::{json, Value};
|
||||
use std::{fs, thread};
|
||||
|
||||
use grin_api::{ApiServer, Router};
|
||||
use grin_chain::SyncStatus;
|
||||
use grin_core::global;
|
||||
use grin_keychain::{ExtKeychain, Identifier, Keychain};
|
||||
use grin_util::{Mutex, ToHex};
|
||||
use grin_util::secp::SecretKey;
|
||||
use grin_util::types::ZeroingString;
|
||||
use grin_util::{Mutex, ToHex};
|
||||
use grin_wallet_api::Owner;
|
||||
use grin_wallet_controller::command::parse_slatepack;
|
||||
use grin_wallet_controller::controller;
|
||||
use grin_wallet_controller::controller::ForeignAPIHandlerV2;
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
|
||||
use grin_wallet_libwallet::{address, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlatepackAddress, SlateState, SlateVersion, StatusMessage, TxLogEntry, TxLogEntryType, VersionedSlate, WalletInst, WalletLCProvider};
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient, LMDBBackend};
|
||||
use grin_wallet_libwallet::api_impl::owner::{cancel_tx, retrieve_summary_info, retrieve_txs};
|
||||
use grin_wallet_libwallet::{address, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlateState, SlateVersion, SlatepackAddress, StatusMessage, TxLogEntry, TxLogEntryType, VersionedSlate, WalletBackend, WalletInitStatus, WalletInst, WalletLCProvider};
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::AppConfig;
|
||||
use crate::node::{Node, NodeConfig};
|
||||
use crate::tor::Tor;
|
||||
use crate::wallet::{ConnectionsConfig, Mnemonic, WalletConfig};
|
||||
use crate::wallet::seed::WalletSeed;
|
||||
use crate::wallet::store::TxHeightStore;
|
||||
use crate::wallet::types::{ConnectionMethod, WalletAccount, WalletData, WalletInstance, WalletTransaction};
|
||||
use crate::wallet::types::{ConnectionMethod, PhraseMode, WalletAccount, WalletData, WalletInstance, WalletTransaction};
|
||||
use crate::wallet::{ConnectionsConfig, Mnemonic, WalletConfig};
|
||||
use crate::AppConfig;
|
||||
|
||||
/// Contains wallet instance, configuration and state, handles wallet commands.
|
||||
#[derive(Clone)]
|
||||
@@ -132,18 +132,32 @@ impl Wallet {
|
||||
mnemonic: &Mnemonic,
|
||||
conn_method: &ConnectionMethod
|
||||
) -> Result<Wallet, Error> {
|
||||
let mut config = WalletConfig::create(name.clone(), conn_method);
|
||||
let config = WalletConfig::create(name.clone(), conn_method);
|
||||
let w = Wallet::new(config.clone());
|
||||
{
|
||||
let instance = Self::create_wallet_instance(&mut config)?;
|
||||
let mut w_lock = instance.lock();
|
||||
let p = w_lock.lc_provider()?;
|
||||
p.create_wallet(None,
|
||||
Some(ZeroingString::from(mnemonic.get_phrase())),
|
||||
mnemonic.size().entropy_size(),
|
||||
password.clone(),
|
||||
false,
|
||||
)?;
|
||||
// create directory if it doesn't exist
|
||||
fs::create_dir_all(config.get_data_path())
|
||||
.map_err(|_| Error::IO("Directory creation error".to_string()))?;
|
||||
// Create seed file.
|
||||
let _ = WalletSeed::init_file(config.seed_path().as_str(),
|
||||
ZeroingString::from(mnemonic.get_phrase()),
|
||||
password.clone())
|
||||
.map_err(|_| Error::IO("Seed file creation error".to_string()))?;
|
||||
let node_client = Self::create_node_client(&config)?;
|
||||
let mut wallet: LMDBBackend<'static, HTTPNodeClient, ExtKeychain> =
|
||||
match LMDBBackend::new(config.get_data_path().as_str(), node_client) {
|
||||
Err(_) => {
|
||||
return Err(Error::Lifecycle("DB creation error".to_string()).into());
|
||||
}
|
||||
Ok(d) => d,
|
||||
};
|
||||
// Save init status of this wallet, to determine whether it needs a full UTXO scan
|
||||
let mut batch = wallet.batch_no_mask()?;
|
||||
match mnemonic.mode() {
|
||||
PhraseMode::Generate => batch.save_init_status(WalletInitStatus::InitNoScanning)?,
|
||||
PhraseMode::Import => batch.save_init_status(WalletInitStatus::InitNeedsScanning)?,
|
||||
}
|
||||
batch.commit()?;
|
||||
}
|
||||
Ok(w)
|
||||
}
|
||||
@@ -157,18 +171,8 @@ impl Wallet {
|
||||
None
|
||||
}
|
||||
|
||||
/// Create [`WalletInstance`] from provided [`WalletConfig`].
|
||||
fn create_wallet_instance(config: &mut WalletConfig) -> Result<WalletInstance, Error> {
|
||||
// Assume global chain type has already been initialized.
|
||||
let chain_type = config.chain_type;
|
||||
if !global::GLOBAL_CHAIN_TYPE.is_init() {
|
||||
global::init_global_chain_type(chain_type);
|
||||
} else {
|
||||
global::set_global_chain_type(chain_type);
|
||||
global::set_local_chain_type(chain_type);
|
||||
}
|
||||
|
||||
// Setup node client.
|
||||
/// Create [`HTTPNodeClient`] from provided config.
|
||||
fn create_node_client(config: &WalletConfig) -> Result<HTTPNodeClient, Error> {
|
||||
let integrated = || {
|
||||
let api_url = format!("http://{}", NodeConfig::get_api_address());
|
||||
let api_secret = NodeConfig::get_api_secret(true);
|
||||
@@ -183,7 +187,13 @@ impl Wallet {
|
||||
} else {
|
||||
integrated()
|
||||
};
|
||||
let node_client = HTTPNodeClient::new(&node_api_url, node_secret)?;
|
||||
Ok(HTTPNodeClient::new(&node_api_url, node_secret)?)
|
||||
}
|
||||
|
||||
/// Create [`WalletInstance`] from provided [`WalletConfig`].
|
||||
fn create_wallet_instance(config: &mut WalletConfig) -> Result<WalletInstance, Error> {
|
||||
// Setup node client.
|
||||
let node_client = Self::create_node_client(config)?;
|
||||
|
||||
// Create wallet instance.
|
||||
let wallet = Self::inst_wallet::<
|
||||
@@ -208,7 +218,7 @@ impl Wallet {
|
||||
let mut wallet = Box::new(DefaultWalletImpl::<'static, C>::new(node_client).unwrap())
|
||||
as Box<dyn WalletInst<'static, L, C, K>>;
|
||||
let lc = wallet.lc_provider()?;
|
||||
lc.set_top_level_directory(config.get_data_path().as_str())?;
|
||||
lc.set_top_level_directory(config.get_wallet_path().as_str())?;
|
||||
Ok(Arc::new(Mutex::new(wallet)))
|
||||
}
|
||||
|
||||
@@ -1065,7 +1075,7 @@ impl Wallet {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
// Remove wallet files.
|
||||
let _ = fs::remove_dir_all(wallet_delete.get_config().get_data_path());
|
||||
let _ = fs::remove_dir_all(wallet_delete.get_config().get_wallet_path());
|
||||
// Mark wallet as deleted.
|
||||
wallet_delete.deleted.store(true, Ordering::Relaxed);
|
||||
// Start sync to close thread.
|
||||
|
||||
Reference in New Issue
Block a user