Skip to content

Commit

Permalink
fix: implement cucumber tests for one-sided recovery and scanning (#2955
Browse files Browse the repository at this point in the history
)
  • Loading branch information
stringhandler committed May 26, 2021
2 parents 83eca21 + be6f3c5 commit b55d99f
Show file tree
Hide file tree
Showing 29 changed files with 1,144 additions and 1,986 deletions.
2 changes: 1 addition & 1 deletion applications/tari_console_wallet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "tari_console_wallet"
version = "0.8.11"
authors = ["Philip Robinson <[email protected]>"]
authors = ["The Tari Development Community"]
edition = "2018"

[dependencies]
Expand Down
47 changes: 8 additions & 39 deletions applications/tari_console_wallet/src/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,6 @@ use tari_wallet::{
output_manager_service::{
config::OutputManagerServiceConfig,
protocols::txo_validation_protocol::TxoValidationType,
storage::{
database::{
DbKeyValuePair as OutputDbKeyValuePair,
KeyManagerState,
OutputManagerBackend,
WriteOperation as OutputWriteOperation,
},
sqlite_db::OutputManagerSqliteDatabase,
},
},
storage::{database::WalletDatabase, sqlite_utilities::initialize_sqlite_database_backends},
transaction_service::{
Expand Down Expand Up @@ -191,7 +182,11 @@ pub async fn get_base_node_peer_config(
pub fn wallet_mode(bootstrap: &ConfigBootstrap, boot_mode: WalletBoot) -> WalletMode {
// Recovery mode
if matches!(boot_mode, WalletBoot::Recovery) {
return WalletMode::Recovery;
if bootstrap.daemon_mode {
return WalletMode::RecoveryDaemon;
} else {
return WalletMode::RecoveryTui;
}
}

match (
Expand Down Expand Up @@ -258,7 +253,7 @@ pub async fn init_wallet(
config: &GlobalConfig,
arg_password: Option<String>,
seed_words_file_name: Option<PathBuf>,
master_key: Option<PrivateKey>,
recovery_master_key: Option<PrivateKey>,
shutdown_signal: ShutdownSignal,
) -> Result<WalletSqlite, ExitCodes> {
fs::create_dir_all(
Expand Down Expand Up @@ -394,15 +389,14 @@ pub async fn init_wallet(
);
wallet_config.buffer_size = std::cmp::max(BASE_NODE_BUFFER_MIN_SIZE, config.buffer_size_base_node);

let recovery = set_master_key(&output_manager_backend, master_key).await?;

let mut wallet = Wallet::start(
wallet_config,
wallet_db,
transaction_backend,
output_manager_backend,
contacts_backend,
shutdown_signal,
recovery_master_key.clone(),
)
.await
.map_err(|e| {
Expand Down Expand Up @@ -444,7 +438,7 @@ pub async fn init_wallet(

debug!(target: LOG_TARGET, "Wallet encrypted.");

if interactive && !recovery {
if interactive && recovery_master_key.is_none() {
confirm_seed_words(&mut wallet).await?;
}
if let Some(file_name) = seed_words_file_name {
Expand All @@ -457,31 +451,6 @@ pub async fn init_wallet(
Ok(wallet)
}

/// If a master key is provided, set the initial key manager state to use that master key.
/// Returns true if the master key was provided, which means recovery is required.
async fn set_master_key(
output_manager_backend: &OutputManagerSqliteDatabase,
master_key: Option<PrivateKey>,
) -> Result<bool, ExitCodes> {
if let Some(master_key) = master_key {
let state = KeyManagerState {
master_key,
branch_seed: "".to_string(),
primary_key_index: 0,
};

output_manager_backend
.write(OutputWriteOperation::Insert(OutputDbKeyValuePair::KeyManagerState(
state,
)))
.map_err(|e| ExitCodes::IOError(e.to_string()))?;

Ok(true)
} else {
Ok(false)
}
}

/// Starts the wallet by setting the base node peer, and restarting the transaction and broadcast protocols.
pub async fn start_wallet(
wallet: &mut WalletSqlite,
Expand Down
13 changes: 8 additions & 5 deletions applications/tari_console_wallet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fn main_inner() -> Result<(), ExitCodes> {
// check for recovery based on existence of wallet file
let mut boot_mode = boot(&bootstrap, &config)?;

let master_key: Option<PrivateKey> = get_master_key(boot_mode, &bootstrap)?;
let recovery_master_key: Option<PrivateKey> = get_recovery_master_key(boot_mode, &bootstrap)?;

if bootstrap.init {
info!(target: LOG_TARGET, "Default configuration created. Done.");
Expand All @@ -107,7 +107,7 @@ fn main_inner() -> Result<(), ExitCodes> {
&config,
arg_password,
seed_words_file_name,
master_key,
recovery_master_key,
shutdown_signal,
))?;

Expand Down Expand Up @@ -144,14 +144,14 @@ fn main_inner() -> Result<(), ExitCodes> {
WalletMode::Grpc => grpc_mode(handle, wallet.clone(), config),
WalletMode::Script(path) => script_mode(handle, path, wallet.clone(), config),
WalletMode::Command(command) => command_mode(handle, command, wallet.clone(), config),
WalletMode::Recovery => recovery_mode(
WalletMode::RecoveryDaemon | WalletMode::RecoveryTui => recovery_mode(
handle,
config,
wallet.clone(),
base_node,
base_node_config,
notify_script,
&bootstrap,
wallet_mode,
),
WalletMode::Invalid => Err(ExitCodes::InputError(
"Invalid wallet mode - are you trying too many command options at once?".to_string(),
Expand All @@ -169,7 +169,10 @@ fn main_inner() -> Result<(), ExitCodes> {
result
}

fn get_master_key(boot_mode: WalletBoot, bootstrap: &ConfigBootstrap) -> Result<Option<PrivateKey>, ExitCodes> {
fn get_recovery_master_key(
boot_mode: WalletBoot,
bootstrap: &ConfigBootstrap,
) -> Result<Option<PrivateKey>, ExitCodes> {
if matches!(boot_mode, WalletBoot::Recovery) {
let private_key = if bootstrap.seed_words.is_some() {
let seed_words: Vec<String> = bootstrap
Expand Down
12 changes: 4 additions & 8 deletions applications/tari_console_wallet/src/recovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@ use chrono::offset::Local;
use futures::{FutureExt, StreamExt};
use log::*;
use rustyline::Editor;
use std::cmp;
use tari_app_utilities::utilities::ExitCodes;
use tari_comms::types::CommsPublicKey;
use tari_core::transactions::types::PrivateKey;
use tari_key_manager::mnemonic::to_secretkey;
use tari_shutdown::Shutdown;
use tari_wallet::{
storage::sqlite_db::WalletSqliteDatabase,
utxo_scanner_service::utxo_scanning::{UtxoScannerEvent, UtxoScannerService},
utxo_scanner_service::{handle::UtxoScannerEvent, utxo_scanning::UtxoScannerService},
WalletSqlite,
};

Expand Down Expand Up @@ -148,13 +147,10 @@ pub async fn wallet_recovery(wallet: &WalletSqlite, peer_seeds: Vec<CommsPublicK
value_received: total_amount,
time_taken: elapsed,
}) => {
let rate = (num_scanned as f32) * 1000f32 / (elapsed.as_millis() as f32);
let stats = format!(
"Recovery complete! Scanned = {} in {:.2?} ({} utxos/s), Recovered {} worth {}",
num_scanned,
elapsed,
num_scanned / cmp::max(elapsed.as_secs(), 1),
num_utxos,
total_amount
"Recovery complete! Scanned = {} in {:.2?} ({:.2?} utxos/s), Recovered {} worth {}",
num_scanned, elapsed, rate, num_utxos, total_amount
);
info!(target: LOG_TARGET, "{}", stats);
println!("{}", stats);
Expand Down
20 changes: 11 additions & 9 deletions applications/tari_console_wallet/src/wallet_modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use log::*;
use rand::{rngs::OsRng, seq::SliceRandom};
use std::{fs, io::Stdout, net::SocketAddr, path::PathBuf};
use tari_app_utilities::utilities::ExitCodes;
use tari_common::{ConfigBootstrap, GlobalConfig};
use tari_common::GlobalConfig;
use tari_comms::{peer_manager::Peer, types::CommsPublicKey};
use tari_wallet::WalletSqlite;
use tokio::runtime::Handle;
Expand All @@ -46,7 +46,8 @@ pub enum WalletMode {
Grpc,
Script(PathBuf),
Command(String),
Recovery,
RecoveryDaemon,
RecoveryTui,
Invalid,
}

Expand Down Expand Up @@ -179,7 +180,7 @@ pub fn recovery_mode(
base_node_selected: Peer,
base_node_config: PeerConfig,
notify_script: Option<PathBuf>,
bootstrap: &ConfigBootstrap,
wallet_mode: WalletMode,
) -> Result<(), ExitCodes> {
let peer_seed_public_keys: Vec<CommsPublicKey> = base_node_config
.peer_seeds
Expand All @@ -200,18 +201,19 @@ pub fn recovery_mode(
},
}

if bootstrap.daemon_mode {
grpc_mode(handle, wallet, config)
} else {
println!("Starting TUI.");
tui_mode(
println!("Starting TUI.");

match wallet_mode {
WalletMode::RecoveryDaemon => grpc_mode(handle, wallet, config),
WalletMode::RecoveryTui => tui_mode(
handle,
config,
wallet,
base_node_selected,
base_node_config,
notify_script,
)
),
_ => Err(ExitCodes::RecoveryError("Unsupported post recovery mode".to_string())),
}
}

Expand Down
5 changes: 3 additions & 2 deletions base_layer/wallet/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::{
output_manager_service::error::OutputManagerError,
storage::database::DbKey,
transaction_service::error::TransactionServiceError,
utxo_scanner_service::error::UtxoScannerError,
};
use diesel::result::Error as DieselError;
use log::SetLoggerError;
Expand Down Expand Up @@ -76,14 +77,14 @@ pub enum WalletError {
NodeIdentityError(#[from] NodeIdentityError),
#[error("Error performing wallet recovery: '{0}'")]
WalletRecoveryError(String),
#[error("Error performing wallet scanning: '{0}'")]
UtxoScannerError(String),
#[error("Shutdown Signal Received")]
Shutdown,
#[error("Transaction Error: {0}")]
TransactionError(#[from] TransactionError),
#[error("Byte array error")]
ByteArrayError(#[from] tari_crypto::tari_utilities::ByteArrayError),
#[error("Utxo Scanner Error: {0}")]
UtxoScannerError(#[from] UtxoScannerError),
}

#[derive(Debug, Error)]
Expand Down
52 changes: 41 additions & 11 deletions base_layer/wallet/src/output_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
base_node_service::handle::BaseNodeServiceHandle,
output_manager_service::{
config::OutputManagerServiceConfig,
error::{OutputManagerError, OutputManagerProtocolError},
error::{OutputManagerError, OutputManagerProtocolError, OutputManagerStorageError},
handle::{OutputManagerEventSender, OutputManagerRequest, OutputManagerResponse, PublicRewindKeys},
protocols::txo_validation_protocol::{TxoValidationProtocol, TxoValidationType},
storage::{
Expand All @@ -36,6 +36,7 @@ use crate::{
transaction_service::handle::TransactionServiceHandle,
types::{HashDigest, KeyDigest, ValidationRetryStrategy},
};
use diesel::result::{DatabaseErrorKind, Error as DieselError};
use digest::Digest;
use futures::{pin_mut, stream::FuturesUnordered, StreamExt};
use log::*;
Expand Down Expand Up @@ -76,7 +77,7 @@ use tari_crypto::{
keys::{DiffieHellmanSharedSecret, PublicKey as PublicKeyTrait, SecretKey},
range_proof::REWIND_USER_MESSAGE_LENGTH,
script,
script::{ExecutionStack, TariScript},
script::TariScript,
tari_utilities::{hex::Hex, ByteArray},
};
use tari_key_manager::{
Expand Down Expand Up @@ -1252,18 +1253,28 @@ where TBackend: OutputManagerBackend + 'static
&rewind_data.rewind_blinding_key,
)
.ok()
.map(|v| (v, output.features))
.map(|v| (v, output.features, output.script_hash, output.script_offset_public_key))
})
.map(|(output, features)| {
.map(|(output, features, script_hash, script_offset_public_key)| {
let beta_hash = Blake256::new()
.chain(script_hash.as_bytes())
.chain(features.to_bytes())
.chain(script_offset_public_key.as_bytes())
.result()
.to_vec();
let beta = PrivateKey::from_bytes(beta_hash.as_slice())
.expect("Should be able to construct a private key from a hash");
// TODO actually increment the keymanager with found private keys so that we don't reuse private keys
// once recovery is complete and use the correct script private key
UnblindedOutput::new(
output.committed_value,
output.blinding_factor.clone(),
output.blinding_factor.clone() - beta,
Some(features),
TariScript::default(),
ExecutionStack::default(),
script!(Nop),
inputs!(PublicKey::from_secret_key(&output.blinding_factor)),
height,
output.blinding_factor.clone(),
PublicKey::from_secret_key(&output.blinding_factor),
output.blinding_factor,
script_offset_public_key,
)
})
.collect();
Expand All @@ -1285,7 +1296,16 @@ where TBackend: OutputManagerBackend + 'static

async fn add_known_script(&mut self, known_script: KnownOneSidedPaymentScript) -> Result<(), OutputManagerError> {
debug!(target: LOG_TARGET, "Adding new script to output manager service");
Ok(self.resources.db.add_known_script(known_script).await?)
// It is not a problem if the script has already been persisted
match self.resources.db.add_known_script(known_script).await {
Ok(_) => (),
Err(OutputManagerStorageError::DieselError(DieselError::DatabaseError(
DatabaseErrorKind::UniqueViolation,
_,
))) => (),
Err(e) => return Err(e.into()),
}
Ok(())
}

/// Attempt to scan and then rewind all of the given transaction outputs into unblinded outputs based on known
Expand All @@ -1297,6 +1317,7 @@ where TBackend: OutputManagerBackend + 'static
) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
let known_one_sided_payment_scripts: Vec<KnownOneSidedPaymentScript> =
self.resources.db.get_all_known_one_sided_payment_scripts().await?;

let mut rewound_outputs: Vec<UnblindedOutput> = Vec::new();
for output in outputs {
let position = known_one_sided_payment_scripts
Expand All @@ -1314,11 +1335,20 @@ where TBackend: OutputManagerBackend + 'static
let blinding_key = PrivateKey::from_bytes(&hash_secret_key(&rewind_key))?;
let rewound =
output.full_rewind_range_proof(&self.resources.factories.range_proof, &rewind_key, &blinding_key);

let beta_hash = Blake256::new()
.chain(output.script_hash.as_bytes())
.chain(output.features.to_bytes())
.chain(output.script_offset_public_key.as_bytes())
.result()
.to_vec();
let beta = PrivateKey::from_bytes(beta_hash.as_slice())?;

if rewound.is_ok() {
let rewound_output = rewound.unwrap();
rewound_outputs.push(UnblindedOutput::new(
rewound_output.committed_value,
rewound_output.blinding_factor.clone(),
rewound_output.blinding_factor.clone() - beta,
Some(output.features),
known_one_sided_payment_scripts[i].script.clone(),
known_one_sided_payment_scripts[i].input.clone(),
Expand Down
1 change: 1 addition & 0 deletions base_layer/wallet/src/testnet_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ pub async fn create_wallet(
oms_backend,
ContactsServiceMemoryDatabase::new(),
shutdown_signal,
None,
)
.await
.expect("Could not create Wallet")
Expand Down
3 changes: 2 additions & 1 deletion base_layer/wallet/src/transaction_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ where
rewind_blinding_key: blinding_key.clone(),
proof_message: [0u8; 21],
};

let rtp = ReceiverTransactionProtocol::new_with_rewindable_output(
sender_message,
PrivateKey::random(&mut OsRng),
Expand All @@ -792,7 +793,7 @@ where
);
TransactionServiceProtocolError::new(tx_id, e.into())
})?;
trace!(target: LOG_TARGET, "Finalized one-side transaction TxId: {}", tx_id);
info!(target: LOG_TARGET, "Finalized one-side transaction TxId: {}", tx_id);

// This event being sent is important, but not critical to the protocol being successful. Send only fails if
// there are no subscribers.
Expand Down
Loading

0 comments on commit b55d99f

Please sign in to comment.