diff --git a/Cargo.lock b/Cargo.lock index a24c2a2b2b..5e03749b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1300,7 +1300,7 @@ dependencies = [ "globwalk", "humantime 2.1.0", "inventory", - "itertools 0.12.0", + "itertools 0.12.1", "junit-report", "lazy-regex", "linked-hash-map", @@ -1321,7 +1321,7 @@ checksum = "01091e28d1f566c8b31b67948399d2efd6c0a8f6228a9785519ed7b73f7f0aef" dependencies = [ "cucumber-expressions", "inflections", - "itertools 0.12.0", + "itertools 0.12.1", "proc-macro2", "quote", "regex", @@ -2591,9 +2591,9 @@ checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "inventory" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8573b2b1fb643a372c73b23f4da5f888677feef3305146d68a539250a9bccc7" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "ipnet" @@ -2632,9 +2632,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] diff --git a/applications/minotari_console_wallet/src/automation/commands.rs b/applications/minotari_console_wallet/src/automation/commands.rs index ecbb5a67e9..8208168bad 100644 --- a/applications/minotari_console_wallet/src/automation/commands.rs +++ b/applications/minotari_console_wallet/src/automation/commands.rs @@ -94,6 +94,7 @@ pub enum WalletCommand { Whois, ExportUtxos, ExportTx, + ImportTx, ExportSpentUtxos, CountUtxos, SetBaseNode, @@ -817,6 +818,19 @@ pub async fn command_runner( }, Err(e) => eprintln!("ExportTx error! {}", e), }, + ImportTx(args) => { + match load_tx_from_csv_file(args.input_file) { + Ok(txs) => { + for tx in txs { + match transaction_service.import_transaction(tx).await { + Ok(id) => println!("imported tx: {}", id), + Err(e) => eprintln!("Could not import tx {}", e), + }; + } + }, + Err(e) => eprintln!("ImportTx error! {}", e), + }; + }, ExportSpentUtxos(args) => match output_service.get_spent_outputs().await { Ok(utxos) => { let utxos: Vec<(WalletOutput, Commitment)> = @@ -1107,6 +1121,19 @@ fn write_tx_to_csv_file(tx: WalletTransaction, file_path: PathBuf) -> Result<(), Ok(()) } +fn load_tx_from_csv_file(file_path: PathBuf) -> Result, CommandError> { + let file_contents = fs::read_to_string(file_path).map_err(|e| CommandError::CSVFile(e.to_string()))?; + let mut results = Vec::new(); + for line in file_contents.lines() { + if let Ok(tx) = serde_json::from_str(line) { + results.push(tx); + } else { + return Err(CommandError::CSVFile("Could not read json file".to_string())); + } + } + Ok(results) +} + #[allow(dead_code)] fn write_json_file, T: Serialize>(path: P, data: &T) -> Result<(), CommandError> { fs::create_dir_all(path.as_ref().parent().unwrap()).map_err(|e| CommandError::JsonFile(e.to_string()))?; diff --git a/applications/minotari_console_wallet/src/cli.rs b/applications/minotari_console_wallet/src/cli.rs index 4257a590f7..2bad5ae327 100644 --- a/applications/minotari_console_wallet/src/cli.rs +++ b/applications/minotari_console_wallet/src/cli.rs @@ -124,6 +124,7 @@ pub enum CliCommands { Whois(WhoisArgs), ExportUtxos(ExportUtxosArgs), ExportTx(ExportTxArgs), + ImportTx(ImportTxArgs), ExportSpentUtxos(ExportUtxosArgs), CountUtxos, SetBaseNode(SetBaseNodeArgs), @@ -249,6 +250,12 @@ pub struct ExportTxArgs { pub output_file: Option, } +#[derive(Debug, Args, Clone)] +pub struct ImportTxArgs { + #[clap(short, long)] + pub input_file: PathBuf, +} + #[derive(Debug, Args, Clone)] pub struct SetBaseNodeArgs { pub public_key: UniPublicKey, diff --git a/applications/minotari_console_wallet/src/wallet_modes.rs b/applications/minotari_console_wallet/src/wallet_modes.rs index e4175eb44d..70fe202652 100644 --- a/applications/minotari_console_wallet/src/wallet_modes.rs +++ b/applications/minotari_console_wallet/src/wallet_modes.rs @@ -475,6 +475,7 @@ async fn run_grpc( #[cfg(test)] mod test { + use std::path::Path; use crate::{cli::CliCommands, wallet_modes::parse_command_file}; @@ -501,6 +502,8 @@ mod test { export-tx 123456789 --output-file pie.txt + import-tx --input-file pie_this_message.txt + # End of script file " .to_string(); @@ -514,6 +517,7 @@ mod test { let mut coin_split = false; let mut discover_peer = false; let mut export_tx = false; + let mut import_tx = false; let mut whois = false; for command in commands { match command { @@ -532,6 +536,11 @@ mod test { export_tx = true } }, + CliCommands::ImportTx(args) => { + if args.input_file == Path::new("pie_this_message.txt") { + import_tx = true + } + }, CliCommands::ExportSpentUtxos(_) => {}, CliCommands::CountUtxos => {}, CliCommands::SetBaseNode(_) => {}, @@ -546,7 +555,15 @@ mod test { } } assert!( - get_balance && send_tari && burn_tari && make_it_rain && coin_split && discover_peer && whois && export_tx + get_balance && + send_tari && + burn_tari && + make_it_rain && + coin_split && + discover_peer && + whois && + export_tx && + import_tx ); } } diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index d1f1c8cccc..03cca0c01b 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -82,6 +82,7 @@ pub enum TransactionServiceRequest { GetCancelledCompletedTransactions, GetCompletedTransaction(TxId), GetAnyTransaction(TxId), + ImportTransaction(WalletTransaction), SendTransaction { destination: TariAddress, amount: MicroMinotari, @@ -165,6 +166,7 @@ impl fmt::Display for TransactionServiceRequest { Self::GetPendingInboundTransactions => write!(f, "GetPendingInboundTransactions"), Self::GetPendingOutboundTransactions => write!(f, "GetPendingOutboundTransactions"), Self::GetCompletedTransactions => write!(f, "GetCompletedTransactions"), + Self::ImportTransaction(tx) => write!(f, "ImportTransaction: {:?}", tx), Self::GetCancelledPendingInboundTransactions => write!(f, "GetCancelledPendingInboundTransactions"), Self::GetCancelledPendingOutboundTransactions => write!(f, "GetCancelledPendingOutboundTransactions"), Self::GetCancelledCompletedTransactions => write!(f, "GetCancelledCompletedTransactions"), @@ -243,6 +245,7 @@ impl fmt::Display for TransactionServiceRequest { #[derive(Debug)] pub enum TransactionServiceResponse { TransactionSent(TxId), + TransactionImported(TxId), BurntTransactionSent { tx_id: TxId, proof: Box, @@ -727,6 +730,17 @@ impl TransactionServiceHandle { } } + pub async fn import_transaction(&mut self, tx: WalletTransaction) -> Result { + match self + .handle + .call(TransactionServiceRequest::ImportTransaction(tx)) + .await?? + { + TransactionServiceResponse::TransactionImported(t) => Ok(t), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + pub async fn import_utxo_with_status( &mut self, amount: MicroMinotari, diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index d94897fcad..f03d0d8934 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -114,7 +114,11 @@ use crate::{ }, storage::{ database::{TransactionBackend, TransactionDatabase}, - models::{CompletedTransaction, TxCancellationReason}, + models::{ + CompletedTransaction, + TxCancellationReason, + WalletTransaction::{Completed, PendingInbound, PendingOutbound}, + }, }, tasks::{ check_faux_transaction_status::check_detected_transactions, @@ -774,6 +778,26 @@ where TransactionServiceRequest::GetAnyTransaction(tx_id) => Ok(TransactionServiceResponse::AnyTransaction( Box::new(self.db.get_any_transaction(tx_id)?), )), + TransactionServiceRequest::ImportTransaction(tx) => { + let tx_id = match tx { + PendingInbound(inbound_tx) => { + let tx_id = inbound_tx.tx_id; + self.db.insert_pending_inbound_transaction(tx_id, inbound_tx)?; + tx_id + }, + PendingOutbound(outbound_tx) => { + let tx_id = outbound_tx.tx_id; + self.db.insert_pending_outbound_transaction(tx_id, outbound_tx)?; + tx_id + }, + Completed(completed_tx) => { + let tx_id = completed_tx.tx_id; + self.db.insert_completed_transaction(tx_id, completed_tx)?; + tx_id + }, + }; + Ok(TransactionServiceResponse::TransactionImported(tx_id)) + }, TransactionServiceRequest::ImportUtxoWithStatus { amount, source_address, diff --git a/base_layer/wallet/src/transaction_service/storage/database.rs b/base_layer/wallet/src/transaction_service/storage/database.rs index 8622d6bc70..7e22f7133f 100644 --- a/base_layer/wallet/src/transaction_service/storage/database.rs +++ b/base_layer/wallet/src/transaction_service/storage/database.rs @@ -320,6 +320,30 @@ where T: TransactionBackend + 'static ))) } + pub fn insert_pending_inbound_transaction( + &self, + tx_id: TxId, + transaction: InboundTransaction, + ) -> Result, TransactionStorageError> { + self.db + .write(WriteOperation::Insert(DbKeyValuePair::PendingInboundTransaction( + tx_id, + Box::new(transaction), + ))) + } + + pub fn insert_pending_outbound_transaction( + &self, + tx_id: TxId, + transaction: OutboundTransaction, + ) -> Result, TransactionStorageError> { + self.db + .write(WriteOperation::Insert(DbKeyValuePair::PendingOutboundTransaction( + tx_id, + Box::new(transaction), + ))) + } + pub fn get_pending_outbound_transaction( &self, tx_id: TxId, diff --git a/base_layer/wallet/src/transaction_service/storage/models.rs b/base_layer/wallet/src/transaction_service/storage/models.rs index 9f02c60a87..77208cdbd0 100644 --- a/base_layer/wallet/src/transaction_service/storage/models.rs +++ b/base_layer/wallet/src/transaction_service/storage/models.rs @@ -303,7 +303,7 @@ impl From for CompletedTransaction { } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[allow(clippy::large_enum_variant)] pub enum WalletTransaction { PendingInbound(InboundTransaction),