From 6179995fc58869af0e64c9d5aa237ed54a722ae4 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal <39146854+hansieodendaal@users.noreply.github.com> Date: Thu, 23 Nov 2023 08:47:10 +0200 Subject: [PATCH] feat!: add one-sided coinbase payments (#5967) Description --- Added normal one-sided and stealth one-sided coinbase transactions. Changes are: - Coinbases can now only be paid to a nominated wallet address directly. - The minotari miner and merge mining proxy will only start if a valid minotari wallet address has been supplied for the particular network. - The miner and merge mining proxy does not depend on the wallet anymore to construct the coinbase; it is done directly. - Corresponding gRPC method (`rpc GetCoinbase (GetCoinbaseRequest) returns (GetCoinbaseResponse)`) has been removed from the wallet. - A memory-based transactions key manager has been created for use by the miner and merge mining proxy as they do not need persistent storage of keys. This also ensures that new entropy for the generation of keys is created each time the miner or merge mining proxy is started effectively negating any collisions of private keys. - All unused code in the output manager and transaction manager have been removed. Motivation and Context --- See #5930 How Has This Been Tested? --- Unit tests Cucumber tests System-level tests - _Completed_ What process can a PR reviewer use to test or verify this change? --- - Code walk-through - Run cucumber `Scenario: Get Transaction Info` and review the log files for `WALLET_A` where the stealth one-sided coinbases will be imported and where payment is made to `WALLET_B`. Breaking Changes --- - [ ] None - [ ] Requires data directory on base node to be deleted - [ ] Requires hard fork - [x] Other - Please specify - Coinbases can now only be paid to a nominated wallet address directly. - Existing wallets need to be recovered into new wallets. --- Cargo.lock | 4 +- .../minotari_app_grpc/proto/wallet.proto | 26 +- .../src/conversions/transaction.rs | 2 +- .../minotari_console_wallet/Cargo.toml | 1 - .../minotari_console_wallet/src/grpc/mod.rs | 6 - .../src/grpc/wallet_grpc_server.rs | 21 - .../src/ui/components/transactions_tab.rs | 9 +- .../src/ui/state/app_state.rs | 32 +- .../minotari_merge_mining_proxy/Cargo.toml | 2 + .../src/block_template_protocol.rs | 82 +- .../src/common/merge_mining.rs | 11 +- .../minotari_merge_mining_proxy/src/config.rs | 23 +- .../minotari_merge_mining_proxy/src/error.rs | 18 +- .../minotari_merge_mining_proxy/src/proxy.rs | 9 +- .../src/run_merge_miner.rs | 51 +- applications/minotari_miner/README.md | 14 +- applications/minotari_miner/src/config.rs | 29 +- applications/minotari_miner/src/errors.rs | 11 +- applications/minotari_miner/src/lib.rs | 1 - applications/minotari_miner/src/main.rs | 8 +- applications/minotari_miner/src/run_miner.rs | 157 +- applications/minotari_miner/src/utils.rs | 80 - base_layer/common_types/src/transaction.rs | 5 - base_layer/core/benches/mempool.rs | 4 +- .../tests/blockchain_database.rs | 112 +- base_layer/core/src/covenants/covenant.rs | 8 +- base_layer/core/src/covenants/fields.rs | 23 +- .../src/covenants/filters/absolute_height.rs | 8 +- base_layer/core/src/covenants/filters/and.rs | 4 +- .../core/src/covenants/filters/field_eq.rs | 16 +- .../src/covenants/filters/fields_hashed_eq.rs | 7 +- .../src/covenants/filters/fields_preserved.rs | 4 +- .../core/src/covenants/filters/identity.rs | 4 +- base_layer/core/src/covenants/filters/not.rs | 4 +- base_layer/core/src/covenants/filters/or.rs | 4 +- .../src/covenants/filters/output_hash_eq.rs | 4 +- base_layer/core/src/covenants/filters/test.rs | 4 +- base_layer/core/src/covenants/filters/xor.rs | 4 +- base_layer/core/src/covenants/test.rs | 7 +- .../priority/prioritized_transaction.rs | 9 +- .../core/src/mempool/reorg_pool/reorg_pool.rs | 8 +- .../core/src/mempool/sync_protocol/test.rs | 5 +- .../unconfirmed_pool/unconfirmed_pool.rs | 19 +- .../core/src/test_helpers/blockchain.rs | 64 +- base_layer/core/src/test_helpers/mod.rs | 56 +- .../core/src/transactions/coinbase_builder.rs | 244 ++- .../src/transactions/key_manager/error.rs | 13 +- .../key_manager/memory_db_key_manager.rs | 64 + .../core/src/transactions/key_manager/mod.rs | 10 + base_layer/core/src/transactions/mod.rs | 7 +- .../core/src/transactions/test_helpers.rs | 92 +- .../transaction_components/output_type.rs | 2 +- .../transaction_components/test.rs | 42 +- .../transaction_output.rs | 22 +- .../wallet_output_builder.rs | 9 +- .../transaction_protocol/recipient.rs | 11 +- .../transaction_protocol/sender.rs | 29 +- .../transaction_protocol/single_receiver.rs | 10 +- .../transaction_initializer.rs | 23 +- .../aggregate_body_internal_validator.rs | 6 +- .../block_body_internal_validator.rs | 1 - .../core/src/validation/block_body/test.rs | 121 +- base_layer/core/src/validation/helpers.rs | 10 +- base_layer/core/src/validation/test.rs | 21 +- .../chain_storage_tests/chain_storage.rs | 2 +- .../core/tests/helpers/block_builders.rs | 34 +- .../core/tests/helpers/block_malleability.rs | 2 +- base_layer/core/tests/helpers/database.rs | 4 +- .../core/tests/helpers/sample_blockchains.rs | 16 +- base_layer/core/tests/helpers/sync.rs | 8 +- .../core/tests/helpers/test_blockchain.rs | 4 +- base_layer/core/tests/tests/base_node_rpc.rs | 14 +- .../core/tests/tests/block_validation.rs | 18 +- base_layer/core/tests/tests/mempool.rs | 21 +- .../core/tests/tests/node_comms_interface.rs | 27 +- base_layer/core/tests/tests/node_service.rs | 21 +- .../core/tests/tests/node_state_machine.rs | 6 +- base_layer/wallet/README.md | 1 + .../2023-11-14-131400_coinbase/down.sql | 1 + .../2023-11-14-131400_coinbase/up.sql | 69 + .../src/output_manager_service/error.rs | 3 - .../src/output_manager_service/handle.rs | 67 +- .../output_manager_service/input_selection.rs | 2 +- .../recovery/standard_outputs_recoverer.rs | 55 +- .../src/output_manager_service/service.rs | 101 +- .../storage/database/backend.rs | 2 - .../storage/database/mod.rs | 26 +- .../output_manager_service/storage/models.rs | 21 +- .../storage/output_source.rs | 26 +- .../storage/output_status.rs | 4 +- .../storage/sqlite_db/mod.rs | 83 +- .../storage/sqlite_db/new_output_sql.rs | 5 +- .../storage/sqlite_db/output_sql.rs | 26 +- base_layer/wallet/src/schema.rs | 2 - .../wallet/src/transaction_service/error.rs | 8 +- .../wallet/src/transaction_service/handle.rs | 51 +- .../protocols/transaction_receive_protocol.rs | 4 +- .../protocols/transaction_send_protocol.rs | 4 +- .../transaction_validation_protocol.rs | 122 +- .../wallet/src/transaction_service/service.rs | 130 +- .../transaction_service/storage/database.rs | 48 +- .../src/transaction_service/storage/models.rs | 27 +- .../transaction_service/storage/sqlite_db.rs | 322 +--- .../tasks/check_faux_transaction_status.rs | 134 +- .../utxo_scanner_service/utxo_scanner_task.rs | 54 +- base_layer/wallet/src/wallet.rs | 7 +- base_layer/wallet/tests/other/mod.rs | 7 +- .../output_manager_service_tests/service.rs | 201 +-- .../output_manager_service_tests/storage.rs | 26 +- .../support/output_manager_service_mock.rs | 2 + base_layer/wallet/tests/support/utils.rs | 10 +- .../transaction_service_tests/service.rs | 1395 +++-------------- .../transaction_service_tests/storage.rs | 31 +- .../transaction_protocols.rs | 145 +- base_layer/wallet/tests/utxo_scanner/mod.rs | 38 +- .../wallet_ffi/src/callback_handler_tests.rs | 12 +- base_layer/wallet_ffi/src/lib.rs | 28 +- .../config/presets/f_merge_mining_proxy.toml | 12 +- common/config/presets/g_miner.toml | 26 +- common/src/exit_codes.rs | 8 + integration_tests/Cargo.toml | 1 + integration_tests/log4rs/cucumber.yml | 33 + integration_tests/src/merge_mining_proxy.rs | 42 +- integration_tests/src/miner.rs | 359 ++--- integration_tests/src/transaction.rs | 18 +- integration_tests/src/wallet_process.rs | 2 +- integration_tests/src/world.rs | 35 +- .../tests/features/StressTest.feature | 16 +- integration_tests/tests/features/Sync.feature | 2 +- .../tests/features/TransactionInfo.feature | 21 +- .../tests/features/WalletMonitoring.feature | 20 +- .../tests/features/WalletQuery.feature | 4 +- .../features/WalletRoutingMechanism.feature | 4 +- .../tests/features/WalletTransactions.feature | 12 +- .../tests/features/WalletTransfer.feature | 4 +- .../tests/steps/merge_mining_steps.rs | 4 +- integration_tests/tests/steps/mining_steps.rs | 115 +- integration_tests/tests/steps/node_steps.rs | 39 +- integration_tests/tests/steps/wallet_steps.rs | 87 +- 139 files changed, 2364 insertions(+), 3694 deletions(-) delete mode 100644 applications/minotari_miner/src/utils.rs create mode 100644 base_layer/core/src/transactions/key_manager/memory_db_key_manager.rs create mode 100644 base_layer/wallet/migrations/2023-11-14-131400_coinbase/down.sql create mode 100644 base_layer/wallet/migrations/2023-11-14-131400_coinbase/up.sql diff --git a/Cargo.lock b/Cargo.lock index 40bdb58d79..295dfbfe07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3130,7 +3130,6 @@ dependencies = [ name = "minotari_console_wallet" version = "0.53.0-dan.0" dependencies = [ - "bitflags 2.4.1", "blake2", "chrono", "clap 3.2.25", @@ -3205,9 +3204,11 @@ dependencies = [ "serde", "serde_json", "tari_common", + "tari_common_types", "tari_comms", "tari_core", "tari_features", + "tari_key_manager", "tari_utilities", "thiserror", "tokio", @@ -5913,6 +5914,7 @@ dependencies = [ "tari_contacts", "tari_core", "tari_crypto", + "tari_key_manager", "tari_p2p", "tari_script", "tari_shutdown", diff --git a/applications/minotari_app_grpc/proto/wallet.proto b/applications/minotari_app_grpc/proto/wallet.proto index 6c5435b46a..d3de9f95cc 100644 --- a/applications/minotari_app_grpc/proto/wallet.proto +++ b/applications/minotari_app_grpc/proto/wallet.proto @@ -41,8 +41,6 @@ service Wallet { rpc Identify (GetIdentityRequest) returns (GetIdentityResponse); // This returns the tari address rpc GetAddress (Empty) returns (GetAddressResponse); - // This returns a coinbase transaction - rpc GetCoinbase (GetCoinbaseRequest) returns (GetCoinbaseResponse); // Send Minotari to a number of recipients rpc Transfer (TransferRequest) returns (TransferResponse); // Returns the transaction details for the given transaction IDs @@ -213,16 +211,16 @@ enum TransactionStatus { TRANSACTION_STATUS_COINBASE = 5; // This transaction is mined and confirmed at the current base node's height TRANSACTION_STATUS_MINED_CONFIRMED = 6; - // The transaction was not found by the wallet its in transaction database - TRANSACTION_STATUS_NOT_FOUND = 7; // The transaction was rejected by the mempool - TRANSACTION_STATUS_REJECTED = 8; + TRANSACTION_STATUS_REJECTED = 7; // This is faux transaction mainly for one-sided transaction outputs or wallet recovery outputs have been found - TRANSACTION_STATUS_FAUX_UNCONFIRMED = 9; + TRANSACTION_STATUS_FAUX_UNCONFIRMED = 8; // All Imported and FauxUnconfirmed transactions will end up with this status when the outputs have been confirmed - TRANSACTION_STATUS_FAUX_CONFIRMED = 10; + TRANSACTION_STATUS_FAUX_CONFIRMED = 9; // This transaction is still being queued for sending - TRANSACTION_STATUS_QUEUED = 11; + TRANSACTION_STATUS_QUEUED = 10; + // The transaction was not found by the wallet its in transaction database + TRANSACTION_STATUS_NOT_FOUND = 11; } message GetCompletedTransactionsRequest { } @@ -244,17 +242,6 @@ message GetUnspentAmountsResponse { repeated uint64 amount = 1; } -message GetCoinbaseRequest { - uint64 reward = 1; - uint64 fee = 2; - uint64 height = 3; - bytes extra = 4; -} - -message GetCoinbaseResponse { - Transaction transaction = 1; -} - message CoinSplitRequest { uint64 amount_per_split = 1; uint64 split_count = 2; @@ -329,7 +316,6 @@ message TransactionEvent { string direction = 6; uint64 amount = 7; string message = 8; - bool is_coinbase = 9; } message TransactionEventResponse { diff --git a/applications/minotari_app_grpc/src/conversions/transaction.rs b/applications/minotari_app_grpc/src/conversions/transaction.rs index 329e8ba570..77c9bb5176 100644 --- a/applications/minotari_app_grpc/src/conversions/transaction.rs +++ b/applications/minotari_app_grpc/src/conversions/transaction.rs @@ -95,10 +95,10 @@ impl From for grpc::TransactionStatus { Completed => grpc::TransactionStatus::Completed, Broadcast => grpc::TransactionStatus::Broadcast, MinedUnconfirmed => grpc::TransactionStatus::MinedUnconfirmed, - MinedConfirmed => grpc::TransactionStatus::MinedConfirmed, Imported => grpc::TransactionStatus::Imported, Pending => grpc::TransactionStatus::Pending, Coinbase => grpc::TransactionStatus::Coinbase, + MinedConfirmed => grpc::TransactionStatus::MinedConfirmed, Rejected => grpc::TransactionStatus::Rejected, FauxUnconfirmed => grpc::TransactionStatus::FauxUnconfirmed, FauxConfirmed => grpc::TransactionStatus::FauxConfirmed, diff --git a/applications/minotari_console_wallet/Cargo.toml b/applications/minotari_console_wallet/Cargo.toml index 8287816375..311c206336 100644 --- a/applications/minotari_console_wallet/Cargo.toml +++ b/applications/minotari_console_wallet/Cargo.toml @@ -28,7 +28,6 @@ console-subscriber = "0.1.8" # Uncomment for normal use (non tokio-console tracing) tokio = { version = "1.23", features = ["signal"] } -bitflags = { version = "2.4", features = ["serde"] } chrono = { version = "0.4.19", default-features = false } clap = { version = "3.2", features = ["derive", "env"] } config = "0.13.0" diff --git a/applications/minotari_console_wallet/src/grpc/mod.rs b/applications/minotari_console_wallet/src/grpc/mod.rs index 92cc7812ea..ecffc7ce38 100644 --- a/applications/minotari_console_wallet/src/grpc/mod.rs +++ b/applications/minotari_console_wallet/src/grpc/mod.rs @@ -29,7 +29,6 @@ pub fn convert_to_transaction_event(event: String, source: TransactionWrapper) - direction: completed.direction.to_string(), amount: completed.amount.as_u64(), message: completed.message.to_string(), - is_coinbase: completed.is_coinbase(), }, TransactionWrapper::Outbound(outbound) => TransactionEvent { event, @@ -40,7 +39,6 @@ pub fn convert_to_transaction_event(event: String, source: TransactionWrapper) - direction: "outbound".to_string(), amount: outbound.amount.as_u64(), message: outbound.message, - is_coinbase: false, }, TransactionWrapper::Inbound(inbound) => TransactionEvent { event, @@ -51,10 +49,6 @@ pub fn convert_to_transaction_event(event: String, source: TransactionWrapper) - direction: "inbound".to_string(), amount: inbound.amount.as_u64(), message: inbound.message.clone(), - // The coinbase are technically Inbound. - // To determine whether a transaction is coinbase - // we will check whether the message contains `Coinbase`. - is_coinbase: inbound.message.to_lowercase().contains("coinbase"), }, } } diff --git a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs index 97072caf68..762ded7b10 100644 --- a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -47,8 +47,6 @@ use minotari_app_grpc::tari_rpc::{ GetAddressResponse, GetBalanceRequest, GetBalanceResponse, - GetCoinbaseRequest, - GetCoinbaseResponse, GetCompletedTransactionsRequest, GetCompletedTransactionsResponse, GetConnectivityRequest, @@ -309,24 +307,6 @@ impl wallet_server::Wallet for WalletGrpcServer { Ok(Response::new(RevalidateResponse {})) } - async fn get_coinbase( - &self, - request: Request, - ) -> Result, Status> { - let request = request.into_inner(); - let mut tx_service = self.get_transaction_service(); - - let coinbase = tx_service - .generate_coinbase_transaction(request.reward.into(), request.fee.into(), request.height, request.extra) - .await - .map_err(|err| Status::unknown(err.to_string()))?; - - let coinbase = coinbase.try_into().map_err(Status::internal)?; - Ok(Response::new(GetCoinbaseResponse { - transaction: Some(coinbase), - })) - } - async fn send_sha_atomic_swap_transaction( &self, request: Request, @@ -1093,7 +1073,6 @@ fn simple_event(event: &str) -> TransactionEvent { direction: event.to_string(), amount: 0, message: String::default(), - is_coinbase: false, } } diff --git a/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs b/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs index b5814aefa7..50fa7ff213 100644 --- a/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs @@ -253,9 +253,7 @@ impl TransactionsTab { Style::default().fg(text_color), ))); - let status = if matches!(t.cancelled, Some(TxCancellationReason::AbandonedCoinbase)) { - "Abandoned".to_string() - } else if matches!(t.cancelled, Some(TxCancellationReason::UserCancelled)) { + let status = if matches!(t.cancelled, Some(TxCancellationReason::UserCancelled)) { "Cancelled".to_string() } else if t.cancelled.is_some() { "Rejected".to_string() @@ -364,9 +362,7 @@ impl TransactionsTab { let amount = tx.amount.to_string(); let content = &amount; let amount = Span::styled(content, Style::default().fg(Color::White)); - let fee_details = if tx.is_coinbase { - Span::raw("") - } else { + let fee_details = { Span::styled( format!( " (weight: {}g, #inputs: {}, #outputs: {})", @@ -602,7 +598,6 @@ impl Component for TransactionsTab { error!(target: LOG_TARGET, "Error rebroadcasting transactions: {}", e); } }, - 'a' => app_state.toggle_abandoned_coinbase_filter(), '\n' => match self.selected_tx_list { SelectedTransactionList::None => {}, SelectedTransactionList::PendingTxs => { diff --git a/applications/minotari_console_wallet/src/ui/state/app_state.rs b/applications/minotari_console_wallet/src/ui/state/app_state.rs index c2617b1e65..cc2a3b5196 100644 --- a/applications/minotari_console_wallet/src/ui/state/app_state.rs +++ b/applications/minotari_console_wallet/src/ui/state/app_state.rs @@ -27,7 +27,6 @@ use std::{ time::{Duration, Instant}, }; -use bitflags::bitflags; use chrono::{DateTime, Local, NaiveDateTime}; use log::*; use minotari_wallet::{ @@ -97,7 +96,6 @@ pub struct AppState { inner: Arc>, cached_data: AppStateData, cache_update_cooldown: Option, - completed_tx_filter: TransactionFilter, config: AppStateConfig, wallet_config: WalletConfig, wallet_connectivity: WalletConnectivityHandle, @@ -122,7 +120,6 @@ impl AppState { inner: inner.clone(), cached_data, cache_update_cooldown: None, - completed_tx_filter: TransactionFilter::ABANDONED_COINBASES, config: AppStateConfig::default(), wallet_connectivity, balance_enquiry_debouncer: BalanceEnquiryDebouncer::new( @@ -559,19 +556,7 @@ impl AppState { } pub fn get_completed_txs(&self) -> Vec<&CompletedTransactionInfo> { - if self - .completed_tx_filter - .contains(TransactionFilter::ABANDONED_COINBASES) - { - self.cached_data - .completed_txs - .iter() - .filter(|tx| !matches!(tx.cancelled, Some(TxCancellationReason::AbandonedCoinbase))) - .filter(|tx| !matches!(tx.status, TransactionStatus::Coinbase)) - .collect() - } else { - self.cached_data.completed_txs.iter().collect() - } + self.cached_data.completed_txs.iter().collect() } pub fn get_confirmations(&self, tx_id: TxId) -> Option<&u64> { @@ -657,10 +642,6 @@ impl AppState { self.wallet_config.num_required_confirmations } - pub fn toggle_abandoned_coinbase_filter(&mut self) { - self.completed_tx_filter.toggle(TransactionFilter::ABANDONED_COINBASES); - } - pub fn get_notifications(&self) -> &[(DateTime, String)] { &self.cached_data.notifications } @@ -1166,7 +1147,6 @@ pub struct CompletedTransactionInfo { pub cancelled: Option, pub direction: TransactionDirection, pub mined_height: Option, - pub is_coinbase: bool, pub weight: u64, pub inputs_count: usize, pub outputs_count: usize, @@ -1182,7 +1162,6 @@ impl CompletedTransactionInfo { .first_kernel_excess_sig() .map(|s| s.get_signature().to_hex()) .unwrap_or_default(); - let is_coinbase = tx.is_coinbase(); let weight = tx.transaction.calculate_weight(transaction_weighting)?; let inputs_count = tx.transaction.body.inputs().len(); let outputs_count = tx.transaction.body.outputs().len(); @@ -1208,7 +1187,6 @@ impl CompletedTransactionInfo { cancelled: tx.cancelled, direction: tx.direction, mined_height: tx.mined_height, - is_coinbase, weight, inputs_count, outputs_count, @@ -1344,14 +1322,6 @@ pub enum UiTransactionBurnStatus { Error(String), } -bitflags! { - #[derive(Clone)] - pub struct TransactionFilter: u8 { - const NONE = 0b0000_0000; - const ABANDONED_COINBASES = 0b0000_0001; - } -} - #[derive(Clone)] struct AppStateConfig { pub cache_update_cooldown: Duration, diff --git a/applications/minotari_merge_mining_proxy/Cargo.toml b/applications/minotari_merge_mining_proxy/Cargo.toml index 7942a31ac6..32c04b6e26 100644 --- a/applications/minotari_merge_mining_proxy/Cargo.toml +++ b/applications/minotari_merge_mining_proxy/Cargo.toml @@ -12,6 +12,7 @@ default = [] [dependencies] tari_common = { path = "../../common" } +tari_common_types = { path = "../../base_layer/common_types" } tari_comms = { path = "../../comms/core" } tari_core = { path = "../../base_layer/core", default-features = false, features = ["transactions"] } minotari_app_utilities = { path = "../minotari_app_utilities" } @@ -19,6 +20,7 @@ tari_utilities = { version = "0.6" } minotari_node_grpc_client = { path = "../../clients/rust/base_node_grpc_client" } minotari_wallet_grpc_client = { path = "../../clients/rust/wallet_grpc_client" } minotari_app_grpc = { path = "../minotari_app_grpc" } +tari_key_manager = { path = "../../base_layer/key_manager", features = ["key_manager_service"] } anyhow = "1.0.53" crossterm = { version = "0.25.0" } diff --git a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs index 1cb70122f7..914511a396 100644 --- a/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs +++ b/applications/minotari_merge_mining_proxy/src/block_template_protocol.rs @@ -22,15 +22,21 @@ //! Methods for seting up a new block. -use std::{cmp, sync::Arc}; +use std::{cmp, str::FromStr, sync::Arc}; use log::*; -use minotari_app_grpc::{ - authentication::ClientAuthenticationInterceptor, - tari_rpc::{base_node_client::BaseNodeClient, wallet_client::WalletClient}, -}; +use minotari_app_grpc::{authentication::ClientAuthenticationInterceptor, tari_rpc::base_node_client::BaseNodeClient}; use minotari_node_grpc_client::grpc; -use tari_core::proof_of_work::{monero_rx, monero_rx::FixedByteArray, Difficulty}; +use tari_common_types::tari_address::TariAddress; +use tari_core::{ + consensus::ConsensusManager, + proof_of_work::{monero_rx, monero_rx::FixedByteArray, Difficulty}, + transactions::{ + generate_coinbase, + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, + transaction_components::{TransactionKernel, TransactionOutput}, + }, +}; use tonic::{codegen::InterceptedService, transport::Channel}; use crate::{ @@ -46,20 +52,27 @@ const LOG_TARGET: &str = "minotari_mm_proxy::proxy::block_template_protocol"; pub struct BlockTemplateProtocol<'a> { config: Arc, base_node_client: &'a mut BaseNodeClient>, - wallet_client: &'a mut WalletClient>, + key_manager: MemoryDbKeyManager, + wallet_payment_address: TariAddress, + consensus_manager: ConsensusManager, } impl<'a> BlockTemplateProtocol<'a> { - pub fn new( + pub async fn new( base_node_client: &'a mut BaseNodeClient>, - wallet_client: &'a mut WalletClient>, config: Arc, - ) -> Self { - Self { - base_node_client, - wallet_client, + ) -> Result, MmProxyError> { + let key_manager = create_memory_db_key_manager(); + let wallet_payment_address = TariAddress::from_str(&config.wallet_payment_address) + .map_err(|err| MmProxyError::WalletPaymentAddress(err.to_string()))?; + let consensus_manager = ConsensusManager::builder(config.network).build()?; + Ok(Self { config, - } + base_node_client, + key_manager, + wallet_payment_address, + consensus_manager, + }) } } @@ -71,7 +84,7 @@ impl BlockTemplateProtocol<'_> { ) -> Result { loop { let new_template = self.get_new_block_template().await?; - let coinbase = self.get_coinbase(&new_template).await?; + let (coinbase_output, coinbase_kernel) = self.get_coinbase(&new_template).await?; let template_height = new_template.template.header.as_ref().map(|h| h.height).unwrap_or(0); if !self.check_expected_tip(template_height).await? { @@ -83,7 +96,8 @@ impl BlockTemplateProtocol<'_> { } debug!(target: LOG_TARGET, "Added coinbase to new block template"); - let block_template_with_coinbase = merge_mining::add_coinbase(coinbase, new_template.template.clone())?; + let block_template_with_coinbase = + merge_mining::add_coinbase(&coinbase_output, &coinbase_kernel, new_template.template.clone())?; info!( target: LOG_TARGET, "Received new block template from Minotari base node for height #{}", @@ -183,31 +197,27 @@ impl BlockTemplateProtocol<'_> { } /// Get coinbase transaction for the [template](NewBlockTemplateData). - async fn get_coinbase(&mut self, template: &NewBlockTemplateData) -> Result { + async fn get_coinbase( + &mut self, + template: &NewBlockTemplateData, + ) -> Result<(TransactionOutput, TransactionKernel), MmProxyError> { let miner_data = &template.miner_data; let tari_height = template.height(); let block_reward = miner_data.reward; let total_fees = miner_data.total_fees; - let extra = self.config.coinbase_extra.as_bytes().to_vec(); - let coinbase_response = self - .wallet_client - .get_coinbase(grpc::GetCoinbaseRequest { - reward: block_reward, - fee: total_fees, - height: tari_height, - extra, - }) - .await - .map_err(|status| MmProxyError::GrpcRequestError { - status, - details: "failed to get new block template".to_string(), - })?; - let coinbase = coinbase_response - .into_inner() - .transaction - .ok_or_else(|| MmProxyError::MissingDataError("Coinbase Invalid".to_string()))?; - Ok(coinbase) + let (coinbase_output, coinbase_kernel) = generate_coinbase( + total_fees.into(), + block_reward.into(), + tari_height, + self.config.coinbase_extra.as_bytes(), + &self.key_manager, + &self.wallet_payment_address, + self.config.stealth_payment, + self.consensus_manager.consensus_constants(tari_height), + ) + .await?; + Ok((coinbase_output, coinbase_kernel)) } /// Build the [FinalBlockTemplateData] from [template](NewBlockTemplateData) and with diff --git a/applications/minotari_merge_mining_proxy/src/common/merge_mining.rs b/applications/minotari_merge_mining_proxy/src/common/merge_mining.rs index e5d7ef03e3..b411e43cf6 100644 --- a/applications/minotari_merge_mining_proxy/src/common/merge_mining.rs +++ b/applications/minotari_merge_mining_proxy/src/common/merge_mining.rs @@ -34,16 +34,13 @@ use crate::error::MmProxyError; /// Add [coinbase](grpc::Transaction) to [block template](grpc::NewBlockTemplate) pub fn add_coinbase( - coinbase: grpc::Transaction, + coinbase_output: &TransactionOutput, + coinbase_kernel: &TransactionKernel, block_template: grpc::NewBlockTemplate, ) -> Result { let mut block_template = NewBlockTemplate::try_from(block_template) .map_err(|e| MmProxyError::MissingDataError(format!("GRPC Conversion Error: {}", e)))?; - let output = TransactionOutput::try_from(coinbase.body.as_ref().unwrap().outputs[0].clone()) - .map_err(MmProxyError::MissingDataError)?; - let kernel = TransactionKernel::try_from(coinbase.body.as_ref().unwrap().kernels[0].clone()) - .map_err(MmProxyError::MissingDataError)?; - block_template.body.add_output(output); - block_template.body.add_kernel(kernel); + block_template.body.add_output(coinbase_output.clone()); + block_template.body.add_kernel(coinbase_kernel.clone()); block_template.try_into().map_err(MmProxyError::ConversionError) } diff --git a/applications/minotari_merge_mining_proxy/src/config.rs b/applications/minotari_merge_mining_proxy/src/config.rs index 0684db75de..3d356b2d26 100644 --- a/applications/minotari_merge_mining_proxy/src/config.rs +++ b/applications/minotari_merge_mining_proxy/src/config.rs @@ -26,6 +26,7 @@ use tari_common::{ configuration::{Network, StringList}, SubConfigPath, }; +use tari_common_types::tari_address::TariAddress; use tari_comms::multiaddr::Multiaddr; #[derive(Clone, Debug, Deserialize, Serialize)] @@ -45,10 +46,6 @@ pub struct MergeMiningProxyConfig { pub base_node_grpc_address: Option, /// GRPC authentication for base node pub base_node_grpc_authentication: GrpcAuthentication, - /// The Minotari wallet's GRPC address - pub console_wallet_grpc_address: Option, - /// GRPC authentication for console wallet - pub console_wallet_grpc_authentication: GrpcAuthentication, /// Address of the minotari_merge_mining_proxy application pub listener_address: Multiaddr, /// In sole merged mining, the block solution is usually submitted to the Monero blockchain (monerod) as well as to @@ -71,6 +68,10 @@ pub struct MergeMiningProxyConfig { pub coinbase_extra: String, /// Selected network pub network: Network, + /// The Tari wallet address (valid address in hex) where the mining funds will be sent to - must be assigned + pub wallet_payment_address: String, + /// Stealth payment yes or no + pub stealth_payment: bool, } impl Default for MergeMiningProxyConfig { @@ -83,8 +84,6 @@ impl Default for MergeMiningProxyConfig { monerod_use_auth: false, base_node_grpc_address: None, base_node_grpc_authentication: GrpcAuthentication::default(), - console_wallet_grpc_address: None, - console_wallet_grpc_authentication: GrpcAuthentication::default(), listener_address: "/ip4/127.0.0.1/tcp/18081".parse().unwrap(), submit_to_origin: true, wait_for_initial_sync_at_startup: true, @@ -92,6 +91,8 @@ impl Default for MergeMiningProxyConfig { max_randomx_vms: 5, coinbase_extra: "tari_merge_mining_proxy".to_string(), network: Default::default(), + wallet_payment_address: TariAddress::default().to_hex(), + stealth_payment: true, } } } @@ -117,12 +118,10 @@ mod test { baz = "foo" [merge_mining_proxy] monerod_username = "cmot" - console_wallet_grpc_address = "/dns4/wallet/tcp/9000" [config_a.merge_mining_proxy] monerod_url = [ "http://network.a.org" ] monerod_password = "password_igor" base_node_grpc_address = "/dns4/base_node_a/tcp/8080" - console_wallet_grpc_address = "/dns4/wallet_a/tcp/9000" [config_b.merge_mining_proxy] submit_to_origin = false monerod_url = [ "http://network.b.org" ] @@ -150,10 +149,6 @@ mod test { config.base_node_grpc_address, Some(Multiaddr::from_str("/dns4/base_node_b/tcp/8080").unwrap()) ); - assert_eq!( - config.console_wallet_grpc_address, - Some(Multiaddr::from_str("/dns4/wallet/tcp/9000").unwrap()) - ); let cfg = get_config("config_a"); let config = MergeMiningProxyConfig::load_from(&cfg).expect("Failed to load config"); @@ -165,10 +160,6 @@ mod test { config.base_node_grpc_address, Some(Multiaddr::from_str("/dns4/base_node_a/tcp/8080").unwrap()) ); - assert_eq!( - config.console_wallet_grpc_address, - Some(Multiaddr::from_str("/dns4/wallet_a/tcp/9000").unwrap()) - ); } #[test] diff --git a/applications/minotari_merge_mining_proxy/src/error.rs b/applications/minotari_merge_mining_proxy/src/error.rs index 954e21c9e5..04d585aea6 100644 --- a/applications/minotari_merge_mining_proxy/src/error.rs +++ b/applications/minotari_merge_mining_proxy/src/error.rs @@ -29,9 +29,11 @@ use hyper::header::InvalidHeaderValue; use minotari_wallet_grpc_client::BasicAuthError; use tari_common::{ConfigError, ConfigurationError}; use tari_core::{ + consensus::ConsensusBuilderError, proof_of_work::{monero_rx::MergeMineError, DifficultyError}, - transactions::CoinbaseBuildError, + transactions::{key_manager::CoreKeyManagerError, CoinbaseBuildError}, }; +use tari_key_manager::key_manager_service::KeyManagerServiceError; use thiserror::Error; use tonic::{codegen::http::uri::InvalidUri, transport}; @@ -96,6 +98,14 @@ pub enum MmProxyError { ServersUnavailable, #[error("Invalid difficulty: {0}")] DifficultyError(#[from] DifficultyError), + #[error("Key manager service error: `{0}`")] + KeyManagerServiceError(String), + #[error("Key manager error: {0}")] + CoreKeyManagerError(#[from] CoreKeyManagerError), + #[error("Consensus build error: {0}")] + ConsensusBuilderError(#[from] ConsensusBuilderError), + #[error("Could not convert data:{0}")] + WalletPaymentAddress(String), } impl From for MmProxyError { @@ -107,6 +117,12 @@ impl From for MmProxyError { } } +impl From for MmProxyError { + fn from(err: KeyManagerServiceError) -> Self { + MmProxyError::KeyManagerServiceError(err.to_string()) + } +} + #[cfg(test)] pub mod test { use tonic::Code; diff --git a/applications/minotari_merge_mining_proxy/src/proxy.rs b/applications/minotari_merge_mining_proxy/src/proxy.rs index 17b4319c9a..85fb4dce4f 100644 --- a/applications/minotari_merge_mining_proxy/src/proxy.rs +++ b/applications/minotari_merge_mining_proxy/src/proxy.rs @@ -40,7 +40,7 @@ use hyper::{header::HeaderValue, service::Service, Body, Method, Request, Respon use json::json; use jsonrpc::error::StandardError; use minotari_node_grpc_client::{grpc, grpc::base_node_client::BaseNodeClient}; -use minotari_wallet_grpc_client::{grpc::wallet_client::WalletClient, ClientAuthenticationInterceptor}; +use minotari_wallet_grpc_client::ClientAuthenticationInterceptor; use reqwest::{ResponseBuilderExt, Url}; use serde_json as json; use tari_core::proof_of_work::{ @@ -77,7 +77,6 @@ impl MergeMiningProxyService { config: MergeMiningProxyConfig, http_client: reqwest::Client, base_node_client: BaseNodeClient>, - wallet_client: WalletClient>, block_templates: BlockTemplateRepository, randomx_factory: RandomXFactory, ) -> Self { @@ -88,7 +87,6 @@ impl MergeMiningProxyService { block_templates, http_client, base_node_client, - wallet_client, initial_sync_achieved: Arc::new(AtomicBool::new(false)), current_monerod_server: Arc::new(RwLock::new(None)), last_assigned_monerod_server: Arc::new(RwLock::new(None)), @@ -159,7 +157,6 @@ struct InnerService { block_templates: BlockTemplateRepository, http_client: reqwest::Client, base_node_client: BaseNodeClient>, - wallet_client: WalletClient>, initial_sync_achieved: Arc, current_monerod_server: Arc>>, last_assigned_monerod_server: Arc>>, @@ -395,7 +392,6 @@ impl InnerService { } let mut grpc_client = self.base_node_client.clone(); - let mut grpc_wallet_client = self.wallet_client.clone(); // Add merge mining tag on blocktemplate request debug!(target: LOG_TARGET, "Requested new block template from Minotari base node"); @@ -429,8 +425,7 @@ impl InnerService { } } - let new_block_protocol = - BlockTemplateProtocol::new(&mut grpc_client, &mut grpc_wallet_client, self.config.clone()); + let new_block_protocol = BlockTemplateProtocol::new(&mut grpc_client, self.config.clone()).await?; let seed_hash = FixedByteArray::from_hex(&monerod_resp["result"]["seed_hash"].to_string().replace('\"', "")) .map_err(|err| MmProxyError::InvalidMonerodResponse(format!("seed hash hex is invalid: {}", err)))?; diff --git a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs index 420f9b78d8..74a97556e7 100644 --- a/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs +++ b/applications/minotari_merge_mining_proxy/src/run_merge_miner.rs @@ -26,12 +26,13 @@ use futures::future; use hyper::{service::make_service_fn, Server}; use log::*; use minotari_node_grpc_client::grpc::base_node_client::BaseNodeClient; -use minotari_wallet_grpc_client::{grpc::wallet_client::WalletClient, ClientAuthenticationInterceptor}; +use minotari_wallet_grpc_client::ClientAuthenticationInterceptor; use tari_common::{ configuration::bootstrap::{grpc_default_port, ApplicationType}, load_configuration, DefaultConfigLoader, }; +use tari_common_types::tari_address::TariAddress; use tari_comms::utils::multiaddr::multiaddr_to_socketaddr; use tari_core::proof_of_work::randomx_factory::RandomXFactory; use tokio::time::Duration; @@ -55,6 +56,19 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { let mut config = MergeMiningProxyConfig::load_from(&cfg)?; setup_grpc_config(&mut config); + let wallet_payment_address = TariAddress::from_str(&config.wallet_payment_address) + .map_err(|err| MmProxyError::WalletPaymentAddress("'wallet_payment_address' ".to_owned() + &err.to_string()))?; + if wallet_payment_address == TariAddress::default() { + return Err(anyhow::Error::msg( + "'wallet_payment_address' may not have the default value", + )); + } + if wallet_payment_address.network() != config.network { + return Err(anyhow::Error::msg( + "'wallet_payment_address' network does not match miner network".to_string(), + )); + } + info!(target: LOG_TARGET, "Configuration: {:?}", config); let client = reqwest::Client::builder() .connect_timeout(Duration::from_secs(5)) @@ -64,7 +78,6 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { .map_err(MmProxyError::ReqwestError)?; let base_node_client = connect_base_node(&config).await?; - let wallet_client = connect_wallet(&config).await?; let listen_addr = multiaddr_to_socketaddr(&config.listener_address)?; let randomx_factory = RandomXFactory::new(config.max_randomx_vms); @@ -72,7 +85,6 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { config, client, base_node_client, - wallet_client, BlockTemplateRepository::new(), randomx_factory, ); @@ -98,28 +110,6 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> { } } -async fn connect_wallet( - config: &MergeMiningProxyConfig, -) -> Result>, MmProxyError> { - let wallet_addr = format!( - "http://{}", - multiaddr_to_socketaddr( - &config - .console_wallet_grpc_address - .clone() - .expect("Wallet grpc address not found") - )? - ); - info!(target: LOG_TARGET, "👛 Connecting to wallet at {}", wallet_addr); - let channel = Endpoint::from_str(&wallet_addr)?.connect().await?; - let wallet_conn = WalletClient::with_interceptor( - channel, - ClientAuthenticationInterceptor::create(&config.console_wallet_grpc_authentication)?, - ); - - Ok(wallet_conn) -} - async fn connect_base_node( config: &MergeMiningProxyConfig, ) -> Result>, MmProxyError> { @@ -153,15 +143,4 @@ fn setup_grpc_config(config: &mut MergeMiningProxyConfig) { .unwrap(), ); } - - if config.console_wallet_grpc_address.is_none() { - config.console_wallet_grpc_address = Some( - format!( - "/ip4/127.0.0.1/tcp/{}", - grpc_default_port(ApplicationType::ConsoleWallet, config.network) - ) - .parse() - .unwrap(), - ); - } } diff --git a/applications/minotari_miner/README.md b/applications/minotari_miner/README.md index c989c0c183..37cc22b185 100644 --- a/applications/minotari_miner/README.md +++ b/applications/minotari_miner/README.md @@ -20,12 +20,20 @@ is required. The Minotari Miner can also be located on a remote workstation. Configuration options for the Minotari Miner are as follows: - `base_node_grpc_address` - this is IPv4/IPv6 address including port number, by which the Minotari Base Node can be found; -- `wallet_grpc_address` - this is IPv4/IPv6 address including port number, by which the Minotari Wallet can be - found; - `num_mining_threads` - the number of mining threads, which defaults to the number of CPU cores; - `mine_on_tip_only` - mining will only start when the Minotari Base Node reports it is in the bootstrapped state; +- `proof_of_work_algo` - The proof of work algorithm to use - `validate_tip_timeout_sec` - the interval at which the current block height will be checked to determine if mining - must be restarted, whereby the tip might have advanced passed the block height that is in use in the current template. + must be restarted, whereby the tip might have advanced passed the block height that is in use in the current template. +- `mining_pool_address` - Stratum Mode configuration - mining pool address +- `mining_wallet_address` - `Stratum Mode configuration - mining wallet address/public key` +- `mining_worker_name` - `Stratum Mode configuration - mining worker name` +- `coinbase_extra` - Note that this data is publicly readable, but it is suggested you populate it so that pool + dominance can be seen before any one party has more than 51%. +- `network` - "Selected network" +- `wait_timeout_on_error` - "Base node reconnect timeout after any gRPC or miner error" +- `wallet_payment_address` - "The Tari wallet address where the mining funds will be sent to" +- `stealth_payment` - "Stealth payment yes or no" ### Caveats diff --git a/applications/minotari_miner/src/config.rs b/applications/minotari_miner/src/config.rs index aaef7be8b4..1a2d43b723 100644 --- a/applications/minotari_miner/src/config.rs +++ b/applications/minotari_miner/src/config.rs @@ -27,8 +27,6 @@ //! specific options: //! - base_node_grpc_address - is IPv4/IPv6 address including port //! number, by which Minotari Base Node can be found -//! - wallet_grpc_address - is IPv4/IPv6 address including port number, -//! where Minotari Wallet Node can be found //! - num_mining_threads - number of mining threads, defaults to number of cpu cores //! - mine_on_tip_only - will start mining only when node is reporting bootstrapped state //! - validate_tip_timeout_sec - will check tip with node every N seconds to validate that still @@ -41,20 +39,16 @@ use std::time::Duration; use minotari_app_grpc::tari_rpc::{pow_algo::PowAlgos, NewBlockTemplateRequest, PowAlgo}; use serde::{Deserialize, Serialize}; use tari_common::{configuration::Network, SubConfigPath}; -use tari_common_types::grpc_authentication::GrpcAuthentication; +use tari_common_types::{grpc_authentication::GrpcAuthentication, tari_address::TariAddress}; use tari_comms::multiaddr::Multiaddr; #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct MinerConfig { - /// GRPC address of base node + /// gRPC address of base node pub base_node_grpc_address: Option, /// GRPC authentication for base node pub base_node_grpc_authentication: GrpcAuthentication, - /// GRPC address of console wallet - pub wallet_grpc_address: Option, - /// GRPC authentication for console wallet - pub wallet_grpc_authentication: GrpcAuthentication, /// Number of mining threads pub num_mining_threads: usize, /// Start mining only when base node is bootstrapped and current block height is on the tip of network @@ -66,9 +60,9 @@ pub struct MinerConfig { /// `mine_on_tip_only` is set to true pub validate_tip_timeout_sec: u64, /// Stratum Mode configuration - mining pool address - pub mining_pool_address: String, + pub stratum_mining_pool_address: String, /// Stratum Mode configuration - mining wallet address/public key - pub mining_wallet_address: String, + pub stratum_mining_wallet_address: String, /// Stratum Mode configuration - mining worker name pub mining_worker_name: String, /// The extra data to store in the coinbase, usually some data about the mining pool. @@ -77,8 +71,12 @@ pub struct MinerConfig { pub coinbase_extra: String, /// Selected network pub network: Network, - /// Base node reconnect timeout after any GRPC or miner error + /// Base node reconnect timeout after any gRPC or miner error pub wait_timeout_on_error: u64, + /// The Tari wallet address (valid address in hex) where the mining funds will be sent to - must be assigned + pub wallet_payment_address: String, + /// Stealth payment yes or no + pub stealth_payment: bool, } /// The proof of work data structure that is included in the block header. For the Minotari miner only `Sha3x` is @@ -100,18 +98,18 @@ impl Default for MinerConfig { Self { base_node_grpc_address: None, base_node_grpc_authentication: GrpcAuthentication::default(), - wallet_grpc_address: None, - wallet_grpc_authentication: GrpcAuthentication::default(), num_mining_threads: num_cpus::get(), mine_on_tip_only: true, proof_of_work_algo: ProofOfWork::Sha3x, validate_tip_timeout_sec: 30, - mining_pool_address: String::new(), - mining_wallet_address: String::new(), + stratum_mining_pool_address: String::new(), + stratum_mining_wallet_address: String::new(), mining_worker_name: String::new(), coinbase_extra: "minotari_miner".to_string(), network: Default::default(), wait_timeout_on_error: 10, + wallet_payment_address: TariAddress::default().to_hex(), + stealth_payment: true, } } } @@ -158,7 +156,6 @@ mine_on_tip_only = false .unwrap(); let config = MinerConfig::load_from(&cfg).expect("Failed to load config"); assert_eq!(config.num_mining_threads, 2); - assert_eq!(config.wallet_grpc_address, MinerConfig::default().wallet_grpc_address); assert_eq!( config.base_node_grpc_address, Some(Multiaddr::from_str("/dns4/my_base_node/tcp/1234").unwrap()) diff --git a/applications/minotari_miner/src/errors.rs b/applications/minotari_miner/src/errors.rs index 902660eb51..e78749ca2e 100644 --- a/applications/minotari_miner/src/errors.rs +++ b/applications/minotari_miner/src/errors.rs @@ -21,6 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // use minotari_app_grpc::authentication::BasicAuthError; +use tari_core::consensus::ConsensusBuilderError; use thiserror::Error; use tonic::codegen::http::uri::InvalidUri; @@ -28,7 +29,7 @@ use tonic::codegen::http::uri::InvalidUri; pub enum MinerError { #[error("I/O error")] IOError(#[from] std::io::Error), - #[error("GRPC error: {0}")] + #[error("gRPC error: {0}")] GrpcStatus(#[from] tonic::Status), #[error("Connection error: {0}")] GrpcConnection(#[from] tonic::transport::Error), @@ -44,10 +45,14 @@ pub enum MinerError { BlockHeader(String), #[error("Conversion error: {0}")] Conversion(String), - #[error("Invalid grpc credentials: {0}")] + #[error("Invalid gRPC credentials: {0}")] BasicAuthError(#[from] BasicAuthError), - #[error("Invalid grpc url: {0}")] + #[error("Invalid gRPC url: {0}")] InvalidUri(#[from] InvalidUri), + #[error("Coinbase error: {0}")] + CoinbaseError(String), + #[error("Consensus build error: {0}")] + ConsensusBuilderError(#[from] ConsensusBuilderError), } pub fn err_empty(name: &str) -> MinerError { diff --git a/applications/minotari_miner/src/lib.rs b/applications/minotari_miner/src/lib.rs index 5d51320b1c..cc5f6b1cd4 100644 --- a/applications/minotari_miner/src/lib.rs +++ b/applications/minotari_miner/src/lib.rs @@ -33,7 +33,6 @@ mod difficulty; mod errors; mod miner; mod stratum; -mod utils; pub async fn run_miner(cli: Cli) -> Result<(), ExitError> { start_miner(cli).await diff --git a/applications/minotari_miner/src/main.rs b/applications/minotari_miner/src/main.rs index 036dec09cb..ad0d80c254 100644 --- a/applications/minotari_miner/src/main.rs +++ b/applications/minotari_miner/src/main.rs @@ -28,7 +28,6 @@ use log::*; use minotari_app_utilities::consts; use run_miner::start_miner; use tari_common::{exit_codes::ExitError, initialize_logging}; -use tokio::runtime::Runtime; use crate::cli::Cli; @@ -42,16 +41,15 @@ mod errors; mod miner; mod run_miner; mod stratum; -mod utils; /// Application entry point -fn main() { - let rt = Runtime::new().expect("Failed to start tokio runtime"); +#[tokio::main] +async fn main() { let terminal_title = format!("Minotari Miner - Version {}", consts::APP_VERSION); if let Err(e) = execute!(stdout(), SetTitle(terminal_title.as_str())) { println!("Error setting terminal title. {}", e) } - match rt.block_on(main_inner()) { + match main_inner().await { Ok(_) => std::process::exit(0), Err(err) => { eprintln!("Fatal error: {:?}", err); diff --git a/applications/minotari_miner/src/run_miner.rs b/applications/minotari_miner/src/run_miner.rs index 4c51695c56..87c0bdaa34 100644 --- a/applications/minotari_miner/src/run_miner.rs +++ b/applications/minotari_miner/src/run_miner.rs @@ -26,7 +26,7 @@ use futures::stream::StreamExt; use log::*; use minotari_app_grpc::{ authentication::ClientAuthenticationInterceptor, - tari_rpc::{base_node_client::BaseNodeClient, wallet_client::WalletClient}, + tari_rpc::{base_node_client::BaseNodeClient, TransactionOutput as GrpcTransactionOutput}, }; use tari_common::{ configuration::bootstrap::{grpc_default_port, ApplicationType}, @@ -34,8 +34,17 @@ use tari_common::{ load_configuration, DefaultConfigLoader, }; +use tari_common_types::tari_address::TariAddress; use tari_comms::utils::multiaddr::multiaddr_to_socketaddr; -use tari_core::blocks::BlockHeader; +use tari_core::{ + blocks::BlockHeader, + consensus::ConsensusManager, + transactions::{ + generate_coinbase, + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, + tari_amount::MicroMinotari, + }, +}; use tari_crypto::ristretto::RistrettoPublicKey; use tari_utilities::hex::Hex; use tokio::time::sleep; @@ -50,13 +59,11 @@ use crate::{ errors::{err_empty, MinerError}, miner::{Miner, MiningReport}, stratum::stratum_controller::controller::Controller, - utils::{coinbase_request, extract_outputs_and_kernels}, }; pub const LOG_TARGET: &str = "minotari::miner::main"; pub const LOG_TARGET_FILE: &str = "minotari::logging::miner::main"; -type WalletGrpcClient = WalletClient>; type BaseNodeGrpcClient = BaseNodeClient>; #[allow(clippy::too_many_lines)] @@ -66,10 +73,33 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> { let mut config = MinerConfig::load_from(&cfg).expect("Failed to load config"); debug!(target: LOG_TARGET_FILE, "{:?}", config); setup_grpc_config(&mut config); + let key_manager = create_memory_db_key_manager(); + let wallet_payment_address = TariAddress::from_str(&config.wallet_payment_address).map_err(|err| { + ExitError::new( + ExitCode::WalletPaymentAddress, + "'wallet_payment_address' ".to_owned() + &err.to_string(), + ) + })?; + debug!(target: LOG_TARGET_FILE, "wallet_payment_address: {}", wallet_payment_address); + if wallet_payment_address == TariAddress::default() { + return Err(ExitError::new( + ExitCode::WalletPaymentAddress, + "'wallet_payment_address' may not have the default value".to_string(), + )); + } + if wallet_payment_address.network() != config.network { + return Err(ExitError::new( + ExitCode::WalletPaymentAddress, + "'wallet_payment_address' network does not match miner network".to_string(), + )); + } + let consensus_manager = ConsensusManager::builder(config.network) + .build() + .map_err(|err| ExitError::new(ExitCode::ConsensusManagerBuilderError, err.to_string()))?; - if !config.mining_wallet_address.is_empty() && !config.mining_pool_address.is_empty() { - let url = config.mining_pool_address.clone(); - let mut miner_address = config.mining_wallet_address.clone(); + if !config.stratum_mining_wallet_address.is_empty() && !config.stratum_mining_pool_address.is_empty() { + let url = config.stratum_mining_pool_address.clone(); + let mut miner_address = config.stratum_mining_wallet_address.clone(); let _ = RistrettoPublicKey::from_hex(&miner_address).map_err(|_| { ExitError::new( ExitCode::ConfigError, @@ -105,7 +135,7 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> { Ok(()) } else { - let (mut node_conn, mut wallet_conn) = connect(&config).await.map_err(|e| { + let mut node_conn = connect(&config).await.map_err(|e| { ExitError::new( ExitCode::GrpcError, format!("Could not connect to wallet or base node: {}", e), @@ -115,7 +145,16 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> { let mut blocks_found: u64 = 0; loop { debug!(target: LOG_TARGET, "Starting new mining cycle"); - match mining_cycle(&mut node_conn, &mut wallet_conn, &config, &cli).await { + match mining_cycle( + &mut node_conn, + &config, + &cli, + &key_manager, + &wallet_payment_address, + &consensus_manager, + ) + .await + { err @ Err(MinerError::GrpcConnection(_)) | err @ Err(MinerError::GrpcStatus(_)) => { // Any GRPC error we will try to reconnect with a standard delay error!(target: LOG_TARGET, "Connection error: {:?}", err); @@ -123,9 +162,8 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> { info!(target: LOG_TARGET, "Holding for {:?}", config.wait_timeout()); sleep(config.wait_timeout()).await; match connect(&config).await { - Ok((nc, wc)) => { + Ok(nc) => { node_conn = nc; - wallet_conn = wc; break; }, Err(err) => { @@ -168,7 +206,7 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> { } } -async fn connect(config: &MinerConfig) -> Result<(BaseNodeGrpcClient, WalletGrpcClient), MinerError> { +async fn connect(config: &MinerConfig) -> Result { let node_conn = match connect_base_node(config).await { Ok(client) => client, Err(e) => { @@ -181,39 +219,7 @@ async fn connect(config: &MinerConfig) -> Result<(BaseNodeGrpcClient, WalletGrpc }, }; - let wallet_conn = match connect_wallet(config).await { - Ok(client) => client, - Err(e) => { - error!(target: LOG_TARGET, "Could not connect to wallet"); - error!( - target: LOG_TARGET, - "Is its grpc running? try running it with `--enable-grpc` or enable it in config" - ); - return Err(e); - }, - }; - - Ok((node_conn, wallet_conn)) -} - -async fn connect_wallet(config: &MinerConfig) -> Result { - let wallet_addr = format!( - "http://{}", - multiaddr_to_socketaddr( - &config - .wallet_grpc_address - .clone() - .expect("Wallet grpc address not found") - )? - ); - info!(target: LOG_TARGET, "👛 Connecting to wallet at {}", wallet_addr); - let channel = Endpoint::from_str(&wallet_addr)?.connect().await?; - let wallet_conn = WalletClient::with_interceptor( - channel, - ClientAuthenticationInterceptor::create(&config.wallet_grpc_authentication)?, - ); - - Ok(wallet_conn) + Ok(node_conn) } async fn connect_base_node(config: &MinerConfig) -> Result { @@ -236,49 +242,65 @@ async fn connect_base_node(config: &MinerConfig) -> Result Result { debug!(target: LOG_TARGET, "Getting new block template"); - let template = node_conn + let template_response = node_conn .get_new_block_template(config.pow_algo_request()) .await? .into_inner(); - let mut block_template = template + let mut block_template = template_response .new_block_template .clone() .ok_or_else(|| err_empty("new_block_template"))?; + let height = block_template + .header + .as_ref() + .ok_or_else(|| err_empty("header"))? + .height; if config.mine_on_tip_only { debug!( target: LOG_TARGET, "Checking if base node is synced, because mine_on_tip_only is true" ); - let height = block_template - .header - .as_ref() - .ok_or_else(|| err_empty("header"))? - .height; validate_tip(node_conn, height, cli.mine_until_height).await?; } debug!(target: LOG_TARGET, "Getting coinbase"); - let request = coinbase_request(&template, config.coinbase_extra.as_bytes().to_vec())?; - let coinbase = wallet_conn.get_coinbase(request).await?.into_inner(); - let (output, kernel) = extract_outputs_and_kernels(coinbase)?; + let miner_data = template_response.miner_data.ok_or_else(|| err_empty("miner_data"))?; + let fee = MicroMinotari::from(miner_data.total_fees); + let reward = MicroMinotari::from(miner_data.reward); + let (coinbase_output, coinbase_kernel) = generate_coinbase( + fee, + reward, + height, + config.coinbase_extra.as_bytes(), + key_manager, + wallet_payment_address, + config.stealth_payment, + consensus_manager.consensus_constants(height), + ) + .await + .map_err(|e| MinerError::CoinbaseError(e.to_string()))?; + debug!(target: LOG_TARGET, "Coinbase kernel: {}", coinbase_kernel); + debug!(target: LOG_TARGET, "Coinbase output: {}", coinbase_output); + let body = block_template .body .as_mut() .ok_or_else(|| err_empty("new_block_template.body"))?; - body.outputs.push(output); - body.kernels.push(kernel); - let target_difficulty = template - .miner_data - .ok_or_else(|| err_empty("miner_data"))? - .target_difficulty; + let grpc_output = GrpcTransactionOutput::try_from(coinbase_output.clone()).map_err(MinerError::Conversion)?; + body.outputs.push(grpc_output); + body.kernels.push(coinbase_kernel.into()); + let target_difficulty = miner_data.target_difficulty; debug!(target: LOG_TARGET, "Asking base node to assemble the MMR roots"); let block_result = node_conn.get_new_block(block_template).await?.into_inner(); @@ -394,15 +416,4 @@ fn setup_grpc_config(config: &mut MinerConfig) { .unwrap(), ); } - - if config.wallet_grpc_address.is_none() { - config.wallet_grpc_address = Some( - format!( - "/ip4/127.0.0.1/tcp/{}", - grpc_default_port(ApplicationType::ConsoleWallet, config.network) - ) - .parse() - .unwrap(), - ); - } } diff --git a/applications/minotari_miner/src/utils.rs b/applications/minotari_miner/src/utils.rs deleted file mode 100644 index a7f3794a4a..0000000000 --- a/applications/minotari_miner/src/utils.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -use minotari_app_grpc::tari_rpc::{ - GetCoinbaseRequest, - GetCoinbaseResponse, - NewBlockTemplateResponse, - TransactionKernel, - TransactionOutput, -}; - -use crate::errors::{err_empty, MinerError}; - -/// Convert NewBlockTemplateResponse to GetCoinbaseRequest -pub fn coinbase_request( - template_response: &NewBlockTemplateResponse, - extra: Vec, -) -> Result { - let template = template_response - .new_block_template - .as_ref() - .ok_or_else(|| err_empty("new_block_template"))?; - let miner_data = template_response - .miner_data - .as_ref() - .ok_or_else(|| err_empty("miner_data"))?; - let fee = miner_data.total_fees; - let reward = miner_data.reward; - let height = template - .header - .as_ref() - .ok_or_else(|| err_empty("template.header"))? - .height; - Ok(GetCoinbaseRequest { - reward, - fee, - height, - extra, - }) -} - -pub fn extract_outputs_and_kernels( - coinbase: GetCoinbaseResponse, -) -> Result<(TransactionOutput, TransactionKernel), MinerError> { - let transaction_body = coinbase - .transaction - .ok_or_else(|| err_empty("coinbase.transaction"))? - .body - .ok_or_else(|| err_empty("transaction.body"))?; - let output = transaction_body - .outputs - .get(0) - .cloned() - .ok_or_else(|| err_empty("transaction.body.outputs"))?; - let kernel = transaction_body - .kernels - .get(0) - .cloned() - .ok_or_else(|| err_empty("transaction.body.kernels"))?; - Ok((output, kernel)) -} diff --git a/base_layer/common_types/src/transaction.rs b/base_layer/common_types/src/transaction.rs index 56e2adbebb..63f483606c 100644 --- a/base_layer/common_types/src/transaction.rs +++ b/base_layer/common_types/src/transaction.rs @@ -103,8 +103,6 @@ pub enum ImportStatus { FauxUnconfirmed, /// This transaction import status is used when a one-sided transaction has been scanned and confirmed FauxConfirmed, - /// This is a coinbase that is imported - Coinbase, } impl TryFrom for TransactionStatus { @@ -115,7 +113,6 @@ impl TryFrom for TransactionStatus { ImportStatus::Imported => Ok(TransactionStatus::Imported), ImportStatus::FauxUnconfirmed => Ok(TransactionStatus::FauxUnconfirmed), ImportStatus::FauxConfirmed => Ok(TransactionStatus::FauxConfirmed), - ImportStatus::Coinbase => Ok(TransactionStatus::MinedConfirmed), } } } @@ -128,7 +125,6 @@ impl TryFrom for ImportStatus { TransactionStatus::Imported => Ok(ImportStatus::Imported), TransactionStatus::FauxUnconfirmed => Ok(ImportStatus::FauxUnconfirmed), TransactionStatus::FauxConfirmed => Ok(ImportStatus::FauxConfirmed), - TransactionStatus::Coinbase => Ok(ImportStatus::Coinbase), _ => Err(TransactionConversionError { code: i32::MAX }), } } @@ -140,7 +136,6 @@ impl fmt::Display for ImportStatus { ImportStatus::Imported => write!(f, "Imported"), ImportStatus::FauxUnconfirmed => write!(f, "FauxUnconfirmed"), ImportStatus::FauxConfirmed => write!(f, "FauxConfirmed"), - ImportStatus::Coinbase => write!(f, "Coinbase"), } } } diff --git a/base_layer/core/benches/mempool.rs b/base_layer/core/benches/mempool.rs index 24237ae43f..7bf8393368 100644 --- a/base_layer/core/benches/mempool.rs +++ b/base_layer/core/benches/mempool.rs @@ -38,8 +38,8 @@ mod benches { mempool::{Mempool, MempoolConfig}, test_helpers::blockchain::create_new_blockchain, transactions::{ + key_manager::create_memory_db_key_manager, tari_amount::{uT, T}, - test_helpers::create_test_core_key_manager_with_memory_db, transaction_components::{OutputFeatures, Transaction, MAX_TRANSACTION_OUTPUTS}, CryptoFactories, }, @@ -54,7 +54,7 @@ mod benches { num_outputs: usize, features: OutputFeatures, ) -> std::io::Result>> { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut txs = Vec::new(); for _ in 0..num_txs { let (tx, _, _) = diff --git a/base_layer/core/src/chain_storage/tests/blockchain_database.rs b/base_layer/core/src/chain_storage/tests/blockchain_database.rs index 5aab7f7dc1..e80ffb7f2f 100644 --- a/base_layer/core/src/chain_storage/tests/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/tests/blockchain_database.rs @@ -22,6 +22,8 @@ // DAMAGE. use std::sync::Arc; +use tari_common_types::tari_address::TariAddress; + use crate::{ blocks::{Block, BlockHeader, BlockHeaderAccumulatedData, ChainHeader, NewBlockTemplate}, chain_storage::{BlockchainDatabase, ChainStorageError}, @@ -29,11 +31,13 @@ use crate::{ test_helpers::{ blockchain::{create_new_blockchain, TempDatabase}, create_block, + default_coinbase_entities, BlockSpec, }, transactions::{ + key_manager::{MemoryDbKeyManager, TariKeyId}, tari_amount::T, - test_helpers::{create_test_core_key_manager_with_memory_db, schema_to_transaction, TestKeyManager}, + test_helpers::schema_to_transaction, transaction_components::{Transaction, WalletOutput}, }, txn_schema, @@ -47,7 +51,9 @@ async fn create_next_block( db: &BlockchainDatabase, prev_block: &Block, transactions: Vec>, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, ) -> (Arc, WalletOutput) { let rules = db.rules(); let (block, output) = create_block( @@ -57,6 +63,8 @@ async fn create_next_block( .with_transactions(transactions.into_iter().map(|t| (*t).clone()).collect()) .finish(), key_manager, + script_key_id, + wallet_payment_address, ) .await; let block = apply_mmr_to_block(db, block); @@ -78,14 +86,23 @@ fn apply_mmr_to_block(db: &BlockchainDatabase, block: Block) -> Bl async fn add_many_chained_blocks( size: usize, db: &BlockchainDatabase, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (Vec>, Vec) { let last_header = db.fetch_last_header().unwrap(); let mut prev_block = Arc::new(db.fetch_block(last_header.height, true).unwrap().into_block()); let mut blocks = Vec::with_capacity(size); let mut outputs = Vec::with_capacity(size); + let (script_key_id, wallet_payment_address) = default_coinbase_entities(key_manager).await; for _ in 1..=size { - let (block, coinbase_utxo) = create_next_block(db, &prev_block, vec![], key_manager).await; + let (block, coinbase_utxo) = create_next_block( + db, + &prev_block, + vec![], + key_manager, + &script_key_id, + &wallet_payment_address, + ) + .await; db.add_block(block.clone()).unwrap().assert_added(); prev_block = block.clone(); @@ -96,8 +113,8 @@ async fn add_many_chained_blocks( } mod fetch_blocks { - use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; #[test] fn it_returns_genesis() { @@ -109,7 +126,7 @@ mod fetch_blocks { #[tokio::test] async fn it_returns_all() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(4, &db, &key_manager).await; let blocks = db.fetch_blocks(.., true).unwrap(); assert_eq!(blocks.len(), 5); @@ -121,7 +138,7 @@ mod fetch_blocks { #[tokio::test] async fn it_returns_one() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (new_blocks, _) = add_many_chained_blocks(1, &db, &key_manager).await; let blocks = db.fetch_blocks(1..=1, true).unwrap(); assert_eq!(blocks.len(), 1); @@ -131,7 +148,7 @@ mod fetch_blocks { #[tokio::test] async fn it_returns_nothing_if_asking_for_blocks_out_of_range() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(1, &db, &key_manager).await; let blocks = db.fetch_blocks(2.., true).unwrap(); assert!(blocks.is_empty()); @@ -140,7 +157,7 @@ mod fetch_blocks { #[tokio::test] async fn it_returns_blocks_between_bounds_exclusive() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let blocks = db.fetch_blocks(3..5, true).unwrap(); assert_eq!(blocks.len(), 2); @@ -151,7 +168,7 @@ mod fetch_blocks { #[tokio::test] async fn it_returns_blocks_between_bounds_inclusive() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let blocks = db.fetch_blocks(3..=5, true).unwrap(); assert_eq!(blocks.len(), 3); @@ -163,7 +180,7 @@ mod fetch_blocks { #[tokio::test] async fn it_returns_blocks_to_the_tip() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let blocks = db.fetch_blocks(3.., true).unwrap(); assert_eq!(blocks.len(), 3); @@ -175,7 +192,7 @@ mod fetch_blocks { #[tokio::test] async fn it_returns_blocks_from_genesis() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let blocks = db.fetch_blocks(..=3, true).unwrap(); assert_eq!(blocks.len(), 4); @@ -188,6 +205,7 @@ mod fetch_blocks { mod fetch_headers { use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; #[test] fn it_returns_genesis() { @@ -205,7 +223,7 @@ mod fetch_headers { #[tokio::test] async fn it_returns_all() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(4, &db, &key_manager).await; let headers = db.fetch_headers(..).unwrap(); assert_eq!(headers.len(), 5); @@ -217,7 +235,7 @@ mod fetch_headers { #[tokio::test] async fn it_returns_nothing_if_asking_for_blocks_out_of_range() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(1, &db, &key_manager).await; let headers = db.fetch_headers(2..).unwrap(); assert!(headers.is_empty()); @@ -226,7 +244,7 @@ mod fetch_headers { #[tokio::test] async fn it_returns_blocks_between_bounds_exclusive() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let headers = db.fetch_headers(3..5).unwrap(); assert_eq!(headers.len(), 2); @@ -237,7 +255,7 @@ mod fetch_headers { #[tokio::test] async fn it_returns_blocks_between_bounds_inclusive() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let headers = db.fetch_headers(3..=5).unwrap(); assert_eq!(headers.len(), 3); @@ -248,7 +266,7 @@ mod fetch_headers { #[tokio::test] async fn it_returns_blocks_to_the_tip() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let headers = db.fetch_headers(3..).unwrap(); assert_eq!(headers.len(), 3); @@ -260,7 +278,7 @@ mod fetch_headers { #[tokio::test] async fn it_returns_blocks_from_genesis() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let headers = db.fetch_headers(..=3).unwrap(); assert_eq!(headers.len(), 4); @@ -275,6 +293,7 @@ mod find_headers_after_hash { use tari_common_types::types::FixedHash; use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; #[test] fn it_returns_none_given_empty_vec() { @@ -287,7 +306,7 @@ mod find_headers_after_hash { async fn it_returns_from_genesis() { let db = setup(); let genesis_hash = db.fetch_block(0, true).unwrap().block().hash(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(1, &db, &key_manager).await; let hashes = vec![genesis_hash]; let (index, headers) = db.find_headers_after_hash(hashes, 1).unwrap().unwrap(); @@ -298,7 +317,7 @@ mod find_headers_after_hash { #[tokio::test] async fn it_returns_the_first_headers_found() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let hashes = (1..=3) .rev() @@ -314,7 +333,7 @@ mod find_headers_after_hash { async fn fnit_ignores_unknown_hashes() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let hashes = (2..=4) .map(|i| db.fetch_block(i, true).unwrap().block().hash()) @@ -329,6 +348,7 @@ mod find_headers_after_hash { mod fetch_block_hashes_from_header_tip { use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; #[test] fn it_returns_genesis() { @@ -341,7 +361,7 @@ mod fetch_block_hashes_from_header_tip { #[tokio::test] async fn it_returns_empty_set_for_big_offset() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); add_many_chained_blocks(5, &db, &key_manager).await; let hashes = db.fetch_block_hashes_from_header_tip(3, 6).unwrap(); assert!(hashes.is_empty()); @@ -350,7 +370,7 @@ mod fetch_block_hashes_from_header_tip { #[tokio::test] async fn it_returns_n_hashes_from_tip() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (blocks, _) = add_many_chained_blocks(5, &db, &key_manager).await; let hashes = db.fetch_block_hashes_from_header_tip(3, 1).unwrap(); assert_eq!(hashes.len(), 3); @@ -362,7 +382,7 @@ mod fetch_block_hashes_from_header_tip { #[tokio::test] async fn it_returns_hashes_without_overlapping() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (blocks, _) = add_many_chained_blocks(3, &db, &key_manager).await; let hashes = db.fetch_block_hashes_from_header_tip(2, 0).unwrap(); assert_eq!(hashes[0], blocks[2].hash()); @@ -375,7 +395,7 @@ mod fetch_block_hashes_from_header_tip { async fn it_returns_all_hashes_from_tip() { let db = setup(); let genesis = db.fetch_tip_header().unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (blocks, _) = add_many_chained_blocks(5, &db, &key_manager).await; let hashes = db.fetch_block_hashes_from_header_tip(10, 0).unwrap(); assert_eq!(hashes.len(), 6); @@ -397,12 +417,13 @@ mod get_stats { mod fetch_total_size_stats { use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; #[tokio::test] async fn it_measures_the_number_of_entries() { let db = setup(); let genesis_output_count = db.fetch_header(0).unwrap().unwrap().output_smt_size; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let _block_and_outputs = add_many_chained_blocks(2, &db, &key_manager).await; let stats = db.fetch_total_size_stats().unwrap(); assert_eq!( @@ -454,6 +475,7 @@ mod prepare_new_block { mod fetch_header_containing_kernel_mmr { use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; #[test] fn it_returns_genesis() { @@ -474,7 +496,7 @@ mod fetch_header_containing_kernel_mmr { async fn it_returns_corresponding_header() { let db = setup(); let genesis = db.fetch_block(0, true).unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (blocks, outputs) = add_many_chained_blocks(1, &db, &key_manager).await; let num_genesis_kernels = genesis.block().body.kernels().len() as u64; @@ -484,7 +506,16 @@ mod fetch_header_containing_kernel_mmr { ) .await; - let (block, _) = create_next_block(&db, &blocks[0], txns, &key_manager).await; + let (script_key_id, wallet_payment_address) = default_coinbase_entities(&key_manager).await; + let (block, _) = create_next_block( + &db, + &blocks[0], + txns, + &key_manager, + &script_key_id, + &wallet_payment_address, + ) + .await; db.add_block(block).unwrap(); let _block_and_outputs = add_many_chained_blocks(3, &db, &key_manager).await; @@ -511,12 +542,13 @@ mod fetch_header_containing_kernel_mmr { mod clear_all_pending_headers { use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; #[tokio::test] async fn it_clears_no_headers() { let db = setup(); assert_eq!(db.clear_all_pending_headers().unwrap(), 0); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let _block_and_outputs = add_many_chained_blocks(2, &db, &key_manager).await; db.clear_all_pending_headers().unwrap(); let last_header = db.fetch_last_header().unwrap(); @@ -526,7 +558,7 @@ mod clear_all_pending_headers { #[tokio::test] async fn it_clears_headers_after_tip() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let _blocks_and_outputs = add_many_chained_blocks(2, &db, &key_manager).await; let prev_block = db.fetch_block(2, true).unwrap(); let mut prev_accum = prev_block.accumulated_data().clone(); @@ -577,13 +609,16 @@ mod validator_node_merkle_root { use super::*; use crate::{ chain_storage::calculate_validator_node_mr, - transactions::transaction_components::{OutputFeatures, ValidatorNodeSignature}, + transactions::{ + key_manager::create_memory_db_key_manager, + transaction_components::{OutputFeatures, ValidatorNodeSignature}, + }, ValidatorNodeBMT, }; #[tokio::test] async fn it_has_the_correct_genesis_merkle_root() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let vn_mmr = ValidatorNodeBMT::create(Vec::new()); let db = setup(); let (blocks, _outputs) = add_many_chained_blocks(1, &db, &key_manager).await; @@ -593,7 +628,7 @@ mod validator_node_merkle_root { #[tokio::test] async fn it_has_the_correct_merkle_root_for_current_vn_set() { let db = setup(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (blocks, outputs) = add_many_chained_blocks(1, &db, &key_manager).await; let (sk, public_key) = PublicKey::random_keypair(&mut OsRng); @@ -612,7 +647,16 @@ mod validator_node_merkle_root { &key_manager, ) .await; - let (block, _) = create_next_block(&db, &blocks[0], tx, &key_manager).await; + let (script_key_id, wallet_payment_address) = default_coinbase_entities(&key_manager).await; + let (block, _) = create_next_block( + &db, + &blocks[0], + tx, + &key_manager, + &script_key_id, + &wallet_payment_address, + ) + .await; db.add_block(block).unwrap().assert_added(); let consts = db.consensus_constants().unwrap(); diff --git a/base_layer/core/src/covenants/covenant.rs b/base_layer/core/src/covenants/covenant.rs index 9ef38befff..b9fc7ebf12 100644 --- a/base_layer/core/src/covenants/covenant.rs +++ b/base_layer/core/src/covenants/covenant.rs @@ -189,12 +189,12 @@ mod test { test::{create_input, create_outputs}, Covenant, }, - transactions::test_helpers::{create_test_core_key_manager_with_memory_db, UtxoTestParams}, + transactions::{key_manager::create_memory_db_key_manager, test_helpers::UtxoTestParams}, }; #[tokio::test] async fn it_succeeds_when_empty() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let outputs = create_outputs(10, UtxoTestParams::default(), &key_manager).await; let input = create_input(&key_manager).await; let covenant = covenant!(); @@ -204,7 +204,7 @@ mod test { #[tokio::test] async fn it_executes_the_covenant() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut outputs = create_outputs(10, UtxoTestParams::default(), &key_manager).await; outputs[4].features.maturity = 42; outputs[5].features.maturity = 42; @@ -221,7 +221,7 @@ mod test { #[tokio::test] async fn test_borsh_de_serialization() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut outputs = create_outputs(10, UtxoTestParams::default(), &key_manager).await; outputs[4].features.maturity = 42; outputs[5].features.maturity = 42; diff --git a/base_layer/core/src/covenants/fields.rs b/base_layer/core/src/covenants/fields.rs index f0fde63f02..d53293ffb3 100644 --- a/base_layer/core/src/covenants/fields.rs +++ b/base_layer/core/src/covenants/fields.rs @@ -393,14 +393,14 @@ mod test { mod is_eq { use super::*; use crate::transactions::{ + key_manager::create_memory_db_key_manager, tari_amount::MicroMinotari, - test_helpers::create_test_core_key_manager_with_memory_db, transaction_components::RangeProofType, }; #[tokio::test] async fn it_returns_true_if_eq() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let side_chain_features = make_sample_sidechain_feature(); let output = create_outputs( 1, @@ -443,7 +443,7 @@ mod test { #[tokio::test] async fn it_returns_false_if_not_eq() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let side_chain_features = make_sample_sidechain_feature(); let output = create_outputs( 1, @@ -490,11 +490,11 @@ mod test { mod is_eq_input { use super::*; - use crate::transactions::test_helpers::create_test_core_key_manager_with_memory_db; + use crate::transactions::key_manager::create_memory_db_key_manager; #[tokio::test] async fn it_returns_true_if_eq_input() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let output = create_outputs( 1, UtxoTestParams { @@ -561,6 +561,7 @@ mod test { mod output_fields { use super::*; + use crate::transactions::key_manager::create_memory_db_key_manager; mod construct_challenge_from { use blake2::Digest; @@ -568,15 +569,11 @@ mod test { use tari_crypto::hashing::DomainSeparation; use super::*; - use crate::transactions::{ - tari_amount::MicroMinotari, - test_helpers::create_test_core_key_manager_with_memory_db, - transaction_components::RangeProofType, - }; + use crate::transactions::{tari_amount::MicroMinotari, transaction_components::RangeProofType}; #[tokio::test] async fn it_constructs_challenge_using_consensus_encoding() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let features = OutputFeatures { maturity: 42, output_type: OutputType::Coinbase, @@ -621,14 +618,14 @@ mod test { mod get_field_value_ref { use super::*; use crate::transactions::{ + key_manager::create_memory_db_key_manager, tari_amount::MicroMinotari, - test_helpers::create_test_core_key_manager_with_memory_db, transaction_components::RangeProofType, }; #[tokio::test] async fn it_retrieves_the_value_as_ref() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let features = OutputFeatures { maturity: 42, range_proof_type: RangeProofType::RevealedValue, diff --git a/base_layer/core/src/covenants/filters/absolute_height.rs b/base_layer/core/src/covenants/filters/absolute_height.rs index c990ecf9e7..a01cfd3955 100644 --- a/base_layer/core/src/covenants/filters/absolute_height.rs +++ b/base_layer/core/src/covenants/filters/absolute_height.rs @@ -67,12 +67,12 @@ mod test { use crate::{ covenant, covenants::{filters::test::setup_filter_test, test::create_input}, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, }; #[tokio::test] async fn it_filters_all_out_if_height_not_reached() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(absolute_height(@uint(100))); let input = create_input(&key_manager).await; let (mut context, outputs) = setup_filter_test(&covenant, &input, 42, |_| {}, &key_manager).await; @@ -85,7 +85,7 @@ mod test { #[tokio::test] async fn it_filters_all_in_if_height_reached() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(absolute_height(@uint(100))); let input = create_input(&key_manager).await; let (mut context, outputs) = setup_filter_test(&covenant, &input, 100, |_| {}, &key_manager).await; @@ -98,7 +98,7 @@ mod test { #[tokio::test] async fn it_filters_all_in_if_height_exceeded() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(absolute_height(@uint(42))); let input = create_input(&key_manager).await; let (mut context, outputs) = setup_filter_test(&covenant, &input, 100, |_| {}, &key_manager).await; diff --git a/base_layer/core/src/covenants/filters/and.rs b/base_layer/core/src/covenants/filters/and.rs index cd4533244d..857ae2a5b0 100644 --- a/base_layer/core/src/covenants/filters/and.rs +++ b/base_layer/core/src/covenants/filters/and.rs @@ -48,12 +48,12 @@ mod test { use crate::{ covenant, covenants::{filters::test::setup_filter_test, test::create_input}, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, }; #[tokio::test] async fn it_filters_outputset_using_intersection() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let script = script!(CheckHeight(101)); let covenant = covenant!(and(field_eq(@field::features_maturity, @uint(42),), field_eq(@field::script, @script(script.clone())))); let input = create_input(&key_manager).await; diff --git a/base_layer/core/src/covenants/filters/field_eq.rs b/base_layer/core/src/covenants/filters/field_eq.rs index 9d8180c636..51e512e177 100644 --- a/base_layer/core/src/covenants/filters/field_eq.rs +++ b/base_layer/core/src/covenants/filters/field_eq.rs @@ -85,12 +85,12 @@ mod test { use crate::{ covenant, covenants::test::{create_context, create_input, create_outputs}, - transactions::{test_helpers::create_test_core_key_manager_with_memory_db, transaction_components::OutputType}, + transactions::{key_manager::create_memory_db_key_manager, transaction_components::OutputType}, }; #[tokio::test] async fn it_filters_uint() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(field_eq(@field::features_maturity, @uint(42))); let input = create_input(&key_manager).await; let mut context = create_context(&covenant, &input, 0); @@ -108,7 +108,7 @@ mod test { #[tokio::test] async fn it_filters_sender_offset_public_key() { let pk = PublicKey::from_hex("5615a327e1d19da34e5aa8bbd2ecc97addf29b158844b885bfc4efa0dab17052").unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(field_eq( @field::sender_offset_public_key, @public_key(pk.clone()) @@ -128,7 +128,7 @@ mod test { #[tokio::test] async fn it_filters_commitment() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let commitment = Commitment::from_hex("7ca31ba517d8b563609ed6707fedde5a2be64ac1d67b254cb5348bc2f680557f").unwrap(); let covenant = covenant!(field_eq( @@ -151,7 +151,7 @@ mod test { #[tokio::test] async fn it_filters_tari_script() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let script = script!(CheckHeight(100)); let covenant = covenant!(field_eq( @field::script, @@ -173,7 +173,7 @@ mod test { #[tokio::test] async fn it_filters_covenant() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let next_cov = covenant!(and(identity(), or(field_eq(@field::features_maturity, @uint(42))))); let covenant = covenant!(field_eq(@field::covenant, @covenant(next_cov.clone()))); let input = create_input(&key_manager).await; @@ -192,7 +192,7 @@ mod test { #[tokio::test] async fn it_filters_output_type() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(field_eq(@field::features_output_type, @output_type(Coinbase))); let input = create_input(&key_manager).await; let mut context = create_context(&covenant, &input, 0); @@ -210,7 +210,7 @@ mod test { #[tokio::test] async fn it_errors_if_field_has_an_incorrect_type() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(field_eq(@field::features, @uint(42))); let input = create_input(&key_manager).await; let mut context = create_context(&covenant, &input, 0); diff --git a/base_layer/core/src/covenants/filters/fields_hashed_eq.rs b/base_layer/core/src/covenants/filters/fields_hashed_eq.rs index fe26c56453..6cbb9a0d5c 100644 --- a/base_layer/core/src/covenants/filters/fields_hashed_eq.rs +++ b/base_layer/core/src/covenants/filters/fields_hashed_eq.rs @@ -58,15 +58,12 @@ mod test { BaseLayerCovenantsDomain, COVENANTS_FIELD_HASHER_LABEL, }, - transactions::{ - test_helpers::create_test_core_key_manager_with_memory_db, - transaction_components::OutputFeatures, - }, + transactions::{key_manager::create_memory_db_key_manager, transaction_components::OutputFeatures}, }; #[tokio::test] async fn it_filters_outputs_with_fields_that_hash_to_given_hash() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let features = OutputFeatures { maturity: 42, sidechain_feature: Some(make_sample_sidechain_feature()), diff --git a/base_layer/core/src/covenants/filters/fields_preserved.rs b/base_layer/core/src/covenants/filters/fields_preserved.rs index 2ecd2bacbe..06caf4881f 100644 --- a/base_layer/core/src/covenants/filters/fields_preserved.rs +++ b/base_layer/core/src/covenants/filters/fields_preserved.rs @@ -44,13 +44,13 @@ mod test { use crate::{ covenant, covenants::{filters::test::setup_filter_test, test::create_input}, - transactions::{test_helpers::create_test_core_key_manager_with_memory_db, transaction_components::OutputType}, + transactions::{key_manager::create_memory_db_key_manager, transaction_components::OutputType}, }; #[tokio::test] async fn it_filters_outputs_that_match_input_fields() { let covenant = covenant!(fields_preserved(@fields(@field::features_maturity, @field::features_output_type))); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut input = create_input(&key_manager).await; input.set_maturity(42).unwrap(); input.features_mut().unwrap().output_type = OutputType::ValidatorNodeRegistration; diff --git a/base_layer/core/src/covenants/filters/identity.rs b/base_layer/core/src/covenants/filters/identity.rs index 1785dd2a8c..88d1e5c03d 100644 --- a/base_layer/core/src/covenants/filters/identity.rs +++ b/base_layer/core/src/covenants/filters/identity.rs @@ -39,12 +39,12 @@ mod tests { use crate::{ covenant, covenants::{filters::test::setup_filter_test, test::create_input}, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, }; #[tokio::test] async fn it_returns_the_outputset_unchanged() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let covenant = covenant!(identity()); let input = create_input(&key_manager).await; let (mut context, outputs) = setup_filter_test(&covenant, &input, 0, |_| {}, &key_manager).await; diff --git a/base_layer/core/src/covenants/filters/not.rs b/base_layer/core/src/covenants/filters/not.rs index 0d5d71a58c..811b18854e 100644 --- a/base_layer/core/src/covenants/filters/not.rs +++ b/base_layer/core/src/covenants/filters/not.rs @@ -45,12 +45,12 @@ mod test { use crate::{ covenant, covenants::{filters::test::setup_filter_test, test::create_input}, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, }; #[tokio::test] async fn it_filters_compliment_of_filter() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let script = script!(CheckHeight(100)); let covenant = covenant!(not(or(field_eq(@field::features_maturity, @uint(42),), field_eq(@field::script, @script(script.clone()))))); let input = create_input(&key_manager).await; diff --git a/base_layer/core/src/covenants/filters/or.rs b/base_layer/core/src/covenants/filters/or.rs index 07ad183efd..6401ce4bc3 100644 --- a/base_layer/core/src/covenants/filters/or.rs +++ b/base_layer/core/src/covenants/filters/or.rs @@ -51,12 +51,12 @@ mod test { use crate::{ covenant, covenants::{filters::test::setup_filter_test, test::create_input}, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, }; #[tokio::test] async fn it_filters_outputset_using_union() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let script = script!(CheckHeight(100)); let covenant = covenant!(or(field_eq(@field::features_maturity, @uint(42),), field_eq(@field::script, @script(script.clone())))); let input = create_input(&key_manager).await; diff --git a/base_layer/core/src/covenants/filters/output_hash_eq.rs b/base_layer/core/src/covenants/filters/output_hash_eq.rs index 46cf3117a3..82c02117b3 100644 --- a/base_layer/core/src/covenants/filters/output_hash_eq.rs +++ b/base_layer/core/src/covenants/filters/output_hash_eq.rs @@ -47,12 +47,12 @@ mod test { filters::test::setup_filter_test, test::{create_input, create_outputs}, }, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, }; #[tokio::test] async fn it_filters_output_with_specific_hash() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let output = create_outputs(1, Default::default(), &key_manager).await.remove(0); let output_hash = output.hash(); let mut hash = [0u8; 32]; diff --git a/base_layer/core/src/covenants/filters/test.rs b/base_layer/core/src/covenants/filters/test.rs index a02f1445a8..65cc048473 100644 --- a/base_layer/core/src/covenants/filters/test.rs +++ b/base_layer/core/src/covenants/filters/test.rs @@ -27,7 +27,7 @@ use crate::{ Covenant, }, transactions::{ - test_helpers::TestKeyManager, + key_manager::MemoryDbKeyManager, transaction_components::{TransactionInput, TransactionOutput}, }, }; @@ -40,7 +40,7 @@ pub async fn setup_filter_test<'a, F>( input: &'a TransactionInput, block_height: u64, output_mod: F, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (CovenantContext<'a>, Vec) where F: FnOnce(&mut Vec), diff --git a/base_layer/core/src/covenants/filters/xor.rs b/base_layer/core/src/covenants/filters/xor.rs index c6520395b7..5ffa78c50f 100644 --- a/base_layer/core/src/covenants/filters/xor.rs +++ b/base_layer/core/src/covenants/filters/xor.rs @@ -53,12 +53,12 @@ mod test { use crate::{ covenant, covenants::{filters::test::setup_filter_test, test::create_input}, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, }; #[tokio::test] async fn it_filters_outputset_using_symmetric_difference() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let script = script!(CheckHeight(100)); let covenant = covenant!(and(field_eq(@field::features_maturity, @uint(42),), field_eq(@field::script, @script(script.clone())))); let input = create_input(&key_manager).await; diff --git a/base_layer/core/src/covenants/test.rs b/base_layer/core/src/covenants/test.rs index 37bb263636..91a3272289 100644 --- a/base_layer/core/src/covenants/test.rs +++ b/base_layer/core/src/covenants/test.rs @@ -25,7 +25,8 @@ use std::convert::TryInto; use crate::{ covenants::{context::CovenantContext, Covenant}, transactions::{ - test_helpers::{TestKeyManager, TestParams, UtxoTestParams}, + key_manager::MemoryDbKeyManager, + test_helpers::{TestParams, UtxoTestParams}, transaction_components::{ BuildInfo, CodeTemplateRegistration, @@ -40,7 +41,7 @@ use crate::{ pub async fn create_outputs( n: usize, utxo_params: UtxoTestParams, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Vec { let mut outputs = Vec::new(); for _i in 0..n { @@ -51,7 +52,7 @@ pub async fn create_outputs( outputs } -pub async fn create_input(key_manager: &TestKeyManager) -> TransactionInput { +pub async fn create_input(key_manager: &MemoryDbKeyManager) -> TransactionInput { let params = TestParams::new(key_manager).await; let output = params.create_output(Default::default(), key_manager).await.unwrap(); output.to_transaction_input(key_manager).await.unwrap() diff --git a/base_layer/core/src/mempool/priority/prioritized_transaction.rs b/base_layer/core/src/mempool/priority/prioritized_transaction.rs index 3cb3bb2661..d656a8cfd6 100644 --- a/base_layer/core/src/mempool/priority/prioritized_transaction.rs +++ b/base_layer/core/src/mempool/priority/prioritized_transaction.rs @@ -118,11 +118,12 @@ impl Display for PrioritizedTransaction { mod tests { use super::*; use crate::transactions::{ + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, tari_amount::{uT, MicroMinotari, T}, - test_helpers::{create_test_core_key_manager_with_memory_db, create_tx, TestKeyManager}, + test_helpers::create_tx, }; - async fn create_tx_with_fee(fee_per_gram: MicroMinotari, key_manager: &TestKeyManager) -> Transaction { + async fn create_tx_with_fee(fee_per_gram: MicroMinotari, key_manager: &MemoryDbKeyManager) -> Transaction { let (tx, _, _) = create_tx(10 * T, fee_per_gram, 0, 1, 0, 1, Default::default(), key_manager) .await .expect("Failed to get tx"); @@ -131,7 +132,7 @@ mod tests { #[tokio::test] async fn fee_increases_priority() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let weighting = TransactionWeight::latest(); let epoch = u64::MAX / 2; let tx = create_tx_with_fee(2 * uT, &key_manager).await; @@ -145,7 +146,7 @@ mod tests { #[tokio::test] async fn age_increases_priority() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let weighting = TransactionWeight::latest(); let epoch = u64::MAX / 2; let tx = create_tx_with_fee(2 * uT, &key_manager).await; diff --git a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs index 8770f712c1..b3b11e94c6 100644 --- a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs +++ b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs @@ -343,13 +343,13 @@ mod test { use crate::{ consensus::ConsensusManagerBuilder, test_helpers::create_orphan_block, - transactions::{tari_amount::MicroMinotari, test_helpers::create_test_core_key_manager_with_memory_db}, + transactions::{key_manager::create_memory_db_key_manager, tari_amount::MicroMinotari}, tx, }; #[tokio::test] async fn test_insert_expire_by_height() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let tx1 = Arc::new( tx!(MicroMinotari(100_000), fee: MicroMinotari(100), lock: 4000, inputs: 2, outputs: 1, &key_manager) .expect("Failed to get tx") @@ -409,7 +409,7 @@ mod test { #[tokio::test] async fn test_remove_all() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let tx1 = Arc::new( tx!(MicroMinotari(100_000), fee: MicroMinotari(100), lock: 4000, inputs: 2, outputs: 1, &key_manager) .expect("Failed to get tx") @@ -446,7 +446,7 @@ mod test { #[tokio::test] async fn remove_scan_for_and_remove_reorged_txs() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let network = Network::LocalNet; let consensus = ConsensusManagerBuilder::new(network).build().unwrap(); let tx1 = Arc::new( diff --git a/base_layer/core/src/mempool/sync_protocol/test.rs b/base_layer/core/src/mempool/sync_protocol/test.rs index 228adf9013..47b4719a86 100644 --- a/base_layer/core/src/mempool/sync_protocol/test.rs +++ b/base_layer/core/src/mempool/sync_protocol/test.rs @@ -52,15 +52,16 @@ use crate::{ Mempool, }, transactions::{ + key_manager::create_memory_db_key_manager, tari_amount::uT, - test_helpers::{create_test_core_key_manager_with_memory_db, create_tx}, + test_helpers::create_tx, transaction_components::Transaction, }, validation::mocks::MockValidator, }; pub async fn create_transactions(n: usize) -> Vec { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut transactions = Vec::new(); for _i in 0..n { let (transaction, _, _) = create_tx(5000 * uT, 3 * uT, 1, 2, 1, 3, Default::default(), &key_manager) diff --git a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs index 2ed8e7fe7d..8c7568a9be 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs @@ -883,8 +883,9 @@ mod test { transactions::{ aggregated_body::AggregateBody, fee::Fee, + key_manager::create_memory_db_key_manager, tari_amount::MicroMinotari, - test_helpers::{create_test_core_key_manager_with_memory_db, TestParams, UtxoTestParams}, + test_helpers::{TestParams, UtxoTestParams}, weight::TransactionWeight, SenderTransactionProtocol, }, @@ -893,7 +894,7 @@ mod test { #[tokio::test] async fn test_find_duplicate_input() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let tx1 = Arc::new( tx!(MicroMinotari(5000), fee: MicroMinotari(50), inputs: 2, outputs: 1, &key_manager) .expect("Failed to get tx") @@ -922,7 +923,7 @@ mod test { #[tokio::test] async fn test_insert_and_retrieve_highest_priority_txs() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let tx1 = Arc::new( tx!(MicroMinotari(5_000), fee: MicroMinotari(5), inputs: 2, outputs: 1, &key_manager) .expect("Failed to get tx") @@ -985,7 +986,7 @@ mod test { #[tokio::test] async fn test_double_spend_inputs() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (tx1, _, _) = tx!(MicroMinotari(5_000), fee: MicroMinotari(10), inputs: 1, outputs: 1, &key_manager) .expect("Failed to get tx"); const INPUT_AMOUNT: MicroMinotari = MicroMinotari(5_000); @@ -1072,7 +1073,7 @@ mod test { #[tokio::test] async fn test_remove_reorg_txs() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let network = Network::LocalNet; let consensus = ConsensusManagerBuilder::new(network).build().unwrap(); let tx1 = Arc::new( @@ -1144,7 +1145,7 @@ mod test { #[tokio::test] async fn test_discard_double_spend_txs() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus = create_consensus_rules(); let tx1 = Arc::new( tx!(MicroMinotari(5_000), fee: MicroMinotari(5), inputs:2, outputs:1, &key_manager) @@ -1219,7 +1220,7 @@ mod test { #[tokio::test] async fn test_multiple_transactions_with_same_outputs_in_mempool() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (tx1, _, _) = tx!(MicroMinotari(150_000), fee: MicroMinotari(50), inputs:5, outputs:5, &key_manager) .expect("Failed to get tx"); let (tx2, _, _) = tx!(MicroMinotari(250_000), fee: MicroMinotari(50), inputs:5, outputs:5, &key_manager) @@ -1322,7 +1323,7 @@ mod test { #[tokio::test] async fn it_compiles_correct_stats_for_single_block() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (tx1, _, _) = tx!(MicroMinotari(150_000), fee: MicroMinotari(5), inputs:5, outputs:1, &key_manager) .expect("Failed to get tx"); let (tx2, _, _) = tx!(MicroMinotari(250_000), fee: MicroMinotari(5), inputs:5, outputs:5, &key_manager) @@ -1352,7 +1353,7 @@ mod test { #[tokio::test] async fn it_compiles_correct_stats_for_multiple_blocks() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let expected_stats = [ FeePerGramStat { order: 0, diff --git a/base_layer/core/src/test_helpers/blockchain.rs b/base_layer/core/src/test_helpers/blockchain.rs index 31e126ad1f..bc4d084277 100644 --- a/base_layer/core/src/test_helpers/blockchain.rs +++ b/base_layer/core/src/test_helpers/blockchain.rs @@ -31,6 +31,7 @@ use std::{ use tari_common::configuration::Network; use tari_common_types::{ chain_metadata::ChainMetadata, + tari_address::TariAddress, types::{Commitment, FixedHash, HashOutput, PublicKey, Signature}, }; use tari_storage::lmdb_store::LMDBConfig; @@ -62,9 +63,9 @@ use crate::{ }, consensus::{chain_strength_comparer::ChainStrengthComparerBuilder, ConsensusConstantsBuilder, ConsensusManager}, proof_of_work::{AchievedTargetDifficulty, Difficulty, PowAlgorithm}, - test_helpers::{block_spec::BlockSpecs, create_consensus_rules, BlockSpec}, + test_helpers::{block_spec::BlockSpecs, create_consensus_rules, default_coinbase_entities, BlockSpec}, transactions::{ - test_helpers::{create_test_core_key_manager_with_memory_db, TestKeyManager}, + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager, TariKeyId}, transaction_components::{TransactionInput, TransactionKernel, TransactionOutput, WalletOutput}, CryptoFactories, }, @@ -422,16 +423,25 @@ pub async fn create_chained_blocks>( let mut block_hashes = HashMap::new(); block_hashes.insert("GB".to_string(), genesis_block); let rules = ConsensusManager::builder(Network::LocalNet).build().unwrap(); - let km = create_test_core_key_manager_with_memory_db(); + let km = create_memory_db_key_manager(); let blocks: BlockSpecs = blocks.into(); let mut block_names = Vec::with_capacity(blocks.len()); + let (script_key_id, wallet_payment_address) = default_coinbase_entities(&km).await; for block_spec in blocks { let prev_block = block_hashes .get(block_spec.parent) .unwrap_or_else(|| panic!("Could not find block {}", block_spec.parent)); let name = block_spec.name; let difficulty = block_spec.difficulty; - let (block, _) = create_block(&rules, prev_block.block(), block_spec, &km).await; + let (block, _) = create_block( + &rules, + prev_block.block(), + block_spec, + &km, + &script_key_id, + &wallet_payment_address, + ) + .await; let block = mine_block(block, prev_block.accumulated_data(), difficulty); block_names.push(name.to_string()); block_hashes.insert(name.to_string(), block); @@ -491,31 +501,36 @@ pub struct TestBlockchain { db: BlockchainDatabase, chain: Vec<(&'static str, Arc)>, rules: ConsensusManager, - pub km: TestKeyManager, + pub km: MemoryDbKeyManager, + script_key_id: TariKeyId, + wallet_payment_address: TariAddress, } impl TestBlockchain { - pub fn new(db: BlockchainDatabase, rules: ConsensusManager) -> Self { + pub async fn new(db: BlockchainDatabase, rules: ConsensusManager) -> Self { let genesis = db .fetch_block(0, true) .unwrap() .try_into_chain_block() .map(Arc::new) .unwrap(); - let km = create_test_core_key_manager_with_memory_db(); + let km = create_memory_db_key_manager(); + let (script_key_id, wallet_payment_address) = default_coinbase_entities(&km).await; let mut blockchain = Self { db, chain: Default::default(), rules, km, + script_key_id, + wallet_payment_address, }; blockchain.chain.push(("GB", genesis)); blockchain } - pub fn create(rules: ConsensusManager) -> Self { - Self::new(create_custom_blockchain(rules.clone()), rules) + pub async fn create(rules: ConsensusManager) -> Self { + Self::new(create_custom_blockchain(rules.clone()), rules).await } pub async fn append_chain( @@ -545,10 +560,10 @@ impl TestBlockchain { Ok(()) } - pub fn with_validators(validators: Validators) -> Self { + pub async fn with_validators(validators: Validators) -> Self { let rules = ConsensusManager::builder(Network::LocalNet).build().unwrap(); let db = create_store_with_consensus_and_validators(rules.clone(), validators); - Self::new(db, rules) + Self::new(db, rules).await } pub fn rules(&self) -> &ConsensusManager { @@ -605,7 +620,15 @@ impl TestBlockchain { .ok_or_else(|| format!("Parent block not found with name '{}'", block_spec.parent)) .unwrap(); let difficulty = block_spec.difficulty; - let (block, coinbase) = create_block(&self.rules, parent.block(), block_spec, &self.km).await; + let (block, coinbase) = create_block( + &self.rules, + parent.block(), + block_spec, + &self.km, + &self.script_key_id, + &self.wallet_payment_address, + ) + .await; let block = mine_block(block, parent.accumulated_data(), difficulty); (block, coinbase) } @@ -615,7 +638,15 @@ impl TestBlockchain { .get_block_by_name(block_spec.parent) .ok_or_else(|| format!("Parent block not found with name '{}'", block_spec.parent)) .unwrap(); - let (mut block, outputs) = create_block(&self.rules, parent.block(), block_spec, &self.km).await; + let (mut block, outputs) = create_block( + &self.rules, + parent.block(), + block_spec, + &self.km, + &self.script_key_id, + &self.wallet_payment_address, + ) + .await; block.body.sort(); (block, outputs) } @@ -649,10 +680,3 @@ impl TestBlockchain { self.chain.first().map(|(_, block)| block).unwrap().clone() } } - -impl Default for TestBlockchain { - fn default() -> Self { - let rules = ConsensusManager::builder(Network::LocalNet).build().unwrap(); - TestBlockchain::create(rules) - } -} diff --git a/base_layer/core/src/test_helpers/mod.rs b/base_layer/core/src/test_helpers/mod.rs index b8c462c679..be881c69bf 100644 --- a/base_layer/core/src/test_helpers/mod.rs +++ b/base_layer/core/src/test_helpers/mod.rs @@ -30,10 +30,13 @@ pub use block_spec::{BlockSpec, BlockSpecs}; use digest::consts::U32; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; use tari_common::configuration::Network; -use tari_common_types::types::PublicKey; +use tari_common_types::{ + tari_address::TariAddress, + types::{PrivateKey, PublicKey}, +}; use tari_comms::PeerManager; -use tari_crypto::keys::PublicKey as PublicKeyT; -use tari_key_manager::key_manager_service::KeyId; +use tari_crypto::keys::{PublicKey as PublicKeyT, SecretKey}; +use tari_key_manager::key_manager_service::KeyManagerInterface; use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; use tari_utilities::epoch_time::EpochTime; @@ -42,10 +45,10 @@ use crate::{ consensus::{ConsensusConstants, ConsensusManager}, proof_of_work::{difficulty::CheckedAdd, sha3x_difficulty, AchievedTargetDifficulty, Difficulty}, transactions::{ - key_manager::TransactionKeyManagerBranch, - test_helpers::TestKeyManager, + generate_coinbase_with_wallet_output, + key_manager::{MemoryDbKeyManager, TariKeyId}, + tari_amount::MicroMinotari, transaction_components::{Transaction, WalletOutput}, - CoinbaseBuilder, }, }; @@ -69,16 +72,24 @@ pub fn create_orphan_block(block_height: u64, transactions: Vec, co header.into_builder().with_transactions(transactions).build() } +pub async fn default_coinbase_entities(key_manager: &MemoryDbKeyManager) -> (TariKeyId, TariAddress) { + let wallet_private_key = PrivateKey::random(&mut OsRng); + let script_key_id = key_manager.import_key(wallet_private_key.clone()).await.unwrap(); + let wallet_payment_address = TariAddress::new(PublicKey::from_secret_key(&wallet_private_key), Network::LocalNet); + (script_key_id, wallet_payment_address) +} + pub async fn create_block( rules: &ConsensusManager, prev_block: &Block, spec: BlockSpec, - km: &TestKeyManager, + km: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, ) -> (Block, WalletOutput) { let mut header = BlockHeader::from_previous(&prev_block.header); let block_height = spec.height_override.unwrap_or(prev_block.header.height + 1); header.height = block_height; - // header.prev_hash = prev_block.hash(); let reward = spec.reward_override.unwrap_or_else(|| { rules .calculate_coinbase_and_fees( @@ -92,23 +103,24 @@ pub async fn create_block( .unwrap() }); - let spend_key_id = KeyId::Managed { - branch: TransactionKeyManagerBranch::Coinbase.get_branch_key(), - index: block_height, - }; - let (coinbase, coinbase_output) = CoinbaseBuilder::new(km.clone()) - .with_block_height(header.height) - .with_fees(0.into()) - .with_spend_key_id(spend_key_id.clone()) - .with_script_key_id(spend_key_id) - .build_with_reward(rules.consensus_constants(block_height), reward) - .await - .unwrap(); + let (coinbase_transaction, _, _, coinbase_wallet_output) = generate_coinbase_with_wallet_output( + MicroMinotari::from(0), + reward, + header.height, + &[], + km, + script_key_id, + wallet_payment_address, + false, + rules.consensus_constants(header.height), + ) + .await + .unwrap(); let mut block = header .into_builder() .with_transactions( - Some(coinbase) + Some(coinbase_transaction) .filter(|_| !spec.skip_coinbase) .into_iter() .chain(spec.transactions) @@ -125,7 +137,7 @@ pub async fn create_block( block.header.output_smt_size = prev_block.header.output_smt_size + block.body.outputs().len() as u64; block.header.kernel_mmr_size = prev_block.header.kernel_mmr_size + block.body.kernels().len() as u64; - (block, coinbase_output) + (block, coinbase_wallet_output) } pub fn mine_to_difficulty(mut block: Block, difficulty: Difficulty) -> Result { diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index d844cbe442..60e65bdea1 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -21,9 +21,16 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -use tari_common_types::types::{Commitment, PrivateKey}; -use tari_key_manager::key_manager_service::KeyManagerServiceError; -use tari_script::{inputs, script, TariScript}; +use chacha20poly1305::aead::OsRng; +use log::*; +use tari_common_types::{ + tari_address::TariAddress, + types::{Commitment, PrivateKey, PublicKey}, +}; +use tari_crypto::keys::PublicKey as PK; +use tari_key_manager::key_manager_service::{KeyManagerInterface, KeyManagerServiceError}; +use tari_script::{one_sided_payment_script, stealth_payment_script, ExecutionStack, TariScript}; +use tari_utilities::ByteArrayError; use thiserror::Error; use crate::{ @@ -32,8 +39,21 @@ use crate::{ ConsensusConstants, }, covenants::Covenant, + one_sided::{ + diffie_hellman_stealth_domain_hasher, + shared_secret_to_output_encryption_key, + shared_secret_to_output_spending_key, + stealth_address_script_spending_key, + }, transactions::{ - key_manager::{TariKeyId, TransactionKeyManagerBranch, TransactionKeyManagerInterface, TxoStage}, + key_manager::{ + CoreKeyManagerError, + MemoryDbKeyManager, + TariKeyId, + TransactionKeyManagerBranch, + TransactionKeyManagerInterface, + TxoStage, + }, tari_amount::{uT, MicroMinotari}, transaction_components::{ KernelBuilder, @@ -52,6 +72,8 @@ use crate::{ }, }; +pub const LOG_TARGET: &str = "c::tx::coinbase_builder"; + #[derive(Debug, Clone, Error, PartialEq, Eq)] pub enum CoinbaseBuildError { #[error("The block height for this coinbase transaction wasn't provided")] @@ -64,6 +86,14 @@ pub enum CoinbaseBuildError { MissingSpendKey, #[error("The script key for this coinbase transaction wasn't provided")] MissingScriptKey, + #[error("The script for this coinbase transaction wasn't provided")] + MissingScript, + #[error("The wallet public key for this coinbase transaction wasn't provided")] + MissingWalletPublicKey, + #[error("The encryption key for this coinbase transaction wasn't provided")] + MissingEncryptionKey, + #[error("The sender offset key for this coinbase transaction wasn't provided")] + MissingSenderOffsetKey, #[error("The value encryption was not succeed")] ValueEncryptionFailed, #[error("An error occurred building the final transaction: `{0}`")] @@ -74,8 +104,24 @@ pub enum CoinbaseBuildError { InvalidSenderOffsetKey, #[error("An invalid transaction has been encountered: {0}")] TransactionError(#[from] TransactionError), + #[error("Key manager error: {0}")] + CoreKeyManagerError(String), #[error("Key manager service error: `{0}`")] KeyManagerServiceError(String), + #[error("Conversion error: {0}")] + ByteArrayError(String), +} + +impl From for CoinbaseBuildError { + fn from(err: ByteArrayError) -> Self { + CoinbaseBuildError::ByteArrayError(err.to_string()) + } +} + +impl From for CoinbaseBuildError { + fn from(err: CoreKeyManagerError) -> Self { + CoinbaseBuildError::CoreKeyManagerError(err.to_string()) + } } impl From for CoinbaseBuildError { @@ -90,6 +136,8 @@ pub struct CoinbaseBuilder { fees: Option, spend_key_id: Option, script_key_id: Option, + encryption_key_id: Option, + sender_offset_key_id: Option, script: Option, covenant: Covenant, extra: Option>, @@ -107,6 +155,8 @@ where TKeyManagerInterface: TransactionKeyManagerInterface fees: None, spend_key_id: None, script_key_id: None, + encryption_key_id: None, + sender_offset_key_id: None, script: None, covenant: Covenant::default(), extra: None, @@ -125,19 +175,33 @@ where TKeyManagerInterface: TransactionKeyManagerInterface self } - /// Provides the spend key for this transaction. This will usually be provided by a miner's wallet instance. + /// Provides the spend key ID for this transaction. This will usually be provided by a miner's wallet instance. pub fn with_spend_key_id(mut self, key: TariKeyId) -> Self { self.spend_key_id = Some(key); self } - /// Provides the script key for this transaction. This will usually be provided by a miner's wallet + /// Provides the script key ID for this transaction. This will usually be provided by a miner's wallet /// instance. pub fn with_script_key_id(mut self, key: TariKeyId) -> Self { self.script_key_id = Some(key); self } + /// Provides the encryption key ID for this transaction. This will usually be provided by a Diffie-Hellman shared + /// secret. + pub fn with_encryption_key_id(mut self, key: TariKeyId) -> Self { + self.encryption_key_id = Some(key); + self + } + + /// Provides the sender offset key ID for this transaction. This will usually be provided by a miner's wallet + /// instance. + pub fn with_sender_offset_key_id(mut self, key: TariKeyId) -> Self { + self.sender_offset_key_id = Some(key); + self + } + /// Provides the script for this transaction, usually by a miner's wallet instance. pub fn with_script(mut self, script: TariScript) -> Self { self.script = Some(script); @@ -187,8 +251,12 @@ where TKeyManagerInterface: TransactionKeyManagerInterface let total_reward = block_reward + self.fees.ok_or(CoinbaseBuildError::MissingFees)?; let spending_key_id = self.spend_key_id.ok_or(CoinbaseBuildError::MissingSpendKey)?; let script_key_id = self.script_key_id.ok_or(CoinbaseBuildError::MissingScriptKey)?; + let encryption_key_id = self.encryption_key_id.ok_or(CoinbaseBuildError::MissingEncryptionKey)?; + let sender_offset_key_id = self + .sender_offset_key_id + .ok_or(CoinbaseBuildError::MissingSenderOffsetKey)?; let covenant = self.covenant; - let script = self.script.unwrap_or_else(|| script!(Nop)); + let script = self.script.ok_or(CoinbaseBuildError::MissingScript)?; let kernel_features = KernelFeatures::create_coinbase(); let metadata = TransactionMetadata::new_with_features(0.into(), 0, kernel_features); @@ -207,7 +275,6 @@ where TKeyManagerInterface: TransactionKeyManagerInterface .await?; let public_spend_key = self.key_manager.get_public_key_at_key_id(&spending_key_id).await?; - let public_script_key = self.key_manager.get_public_key_at_key_id(&script_key_id).await?; let kernel_signature = self .key_manager @@ -229,7 +296,7 @@ where TKeyManagerInterface: TransactionKeyManagerInterface let output_features = OutputFeatures::create_coinbase(height + constants.coinbase_min_maturity(), self.extra); let encrypted_data = self .key_manager - .encrypt_data_for_recovery(&spending_key_id, None, total_reward.into()) + .encrypt_data_for_recovery(&spending_key_id, Some(&encryption_key_id), total_reward.into()) .await?; let minimum_value_promise = MicroMinotari::zero(); @@ -243,17 +310,14 @@ where TKeyManagerInterface: TransactionKeyManagerInterface &minimum_value_promise, ); - let (sender_offset_public_key_id, sender_offset_public_key) = self - .key_manager - .get_next_key(TransactionKeyManagerBranch::SenderOffset.get_branch_key()) - .await?; + let sender_offset_public_key = self.key_manager.get_public_key_at_key_id(&sender_offset_key_id).await?; let metadata_sig = self .key_manager .get_metadata_signature( &spending_key_id, &value.into(), - &sender_offset_public_key_id, + &sender_offset_key_id, &output_version, &metadata_message, output_features.range_proof_type, @@ -266,7 +330,7 @@ where TKeyManagerInterface: TransactionKeyManagerInterface spending_key_id, output_features, script, - inputs!(public_script_key), + ExecutionStack::default(), script_key_id, sender_offset_public_key, metadata_sig, @@ -306,10 +370,102 @@ where TKeyManagerInterface: TransactionKeyManagerInterface } } +/// Clients that do not need to spend the wallet output must call this function to generate a coinbase transaction, +/// so that the only way to get access to the funds will be via the Diffie-Hellman shared secret. +pub async fn generate_coinbase( + fee: MicroMinotari, + reward: MicroMinotari, + height: u64, + extra: &[u8], + key_manager: &MemoryDbKeyManager, + wallet_payment_address: &TariAddress, + stealth_payment: bool, + consensus_constants: &ConsensusConstants, +) -> Result<(TransactionOutput, TransactionKernel), CoinbaseBuildError> { + // The script key is not used in the Diffie-Hellmann protocol, so we assign default. + let script_key_id = TariKeyId::default(); + let (_, coinbase_output, coinbase_kernel, _) = generate_coinbase_with_wallet_output( + fee, + reward, + height, + extra, + key_manager, + &script_key_id, + wallet_payment_address, + stealth_payment, + consensus_constants, + ) + .await?; + Ok((coinbase_output, coinbase_kernel)) +} + +/// Clients that need to spend the wallet output must call this function to generate a coinbase transaction, +/// so that the only way to get access to the funds will be via the Diffie-Hellman shared secret. +pub async fn generate_coinbase_with_wallet_output( + fee: MicroMinotari, + reward: MicroMinotari, + height: u64, + extra: &[u8], + key_manager: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, + stealth_payment: bool, + consensus_constants: &ConsensusConstants, +) -> Result<(Transaction, TransactionOutput, TransactionKernel, WalletOutput), CoinbaseBuildError> { + let (sender_offset_key_id, _) = key_manager + .get_next_key(TransactionKeyManagerBranch::SenderOffset.get_branch_key()) + .await?; + let shared_secret = key_manager + .get_diffie_hellman_shared_secret(&sender_offset_key_id, wallet_payment_address.public_key()) + .await?; + let spending_key = shared_secret_to_output_spending_key(&shared_secret)?; + + let encryption_private_key = shared_secret_to_output_encryption_key(&shared_secret)?; + let encryption_key_id = key_manager.import_key(encryption_private_key).await?; + + let spending_key_id = key_manager.import_key(spending_key).await?; + + let script = if stealth_payment { + let (nonce_private_key, nonce_public_key) = PublicKey::random_keypair(&mut OsRng); + let c = diffie_hellman_stealth_domain_hasher(&nonce_private_key, wallet_payment_address.public_key()); + let script_spending_key = stealth_address_script_spending_key(&c, wallet_payment_address.public_key()); + stealth_payment_script(&nonce_public_key, &script_spending_key) + } else { + one_sided_payment_script(wallet_payment_address.public_key()) + }; + + let (transaction, wallet_output) = CoinbaseBuilder::new(key_manager.clone()) + .with_block_height(height) + .with_fees(fee) + .with_spend_key_id(spending_key_id) + .with_encryption_key_id(encryption_key_id) + .with_sender_offset_key_id(sender_offset_key_id) + .with_script_key_id(script_key_id.clone()) + .with_script(script) + .with_extra(extra.to_vec()) + .build_with_reward(consensus_constants, reward) + .await?; + + let output = transaction + .body() + .outputs() + .first() + .ok_or(CoinbaseBuildError::BuildError("No output found".to_string()))?; + let kernel = transaction + .body() + .kernels() + .first() + .ok_or(CoinbaseBuildError::BuildError("No kernel found".to_string()))?; + + debug!(target: LOG_TARGET, "Coinbase kernel: {}", kernel.clone()); + debug!(target: LOG_TARGET, "Coinbase output: {}", output.clone()); + Ok((transaction.clone(), output.clone(), kernel.clone(), wallet_output)) +} + #[cfg(test)] mod test { use tari_common::configuration::Network; - use tari_common_types::types::Commitment; + use tari_common_types::{tari_address::TariAddress, types::Commitment}; use crate::{ consensus::{emission::Emission, ConsensusManager, ConsensusManagerBuilder}, @@ -325,14 +481,14 @@ mod test { }; fn get_builder() -> ( - CoinbaseBuilder, + CoinbaseBuilder, ConsensusManager, CryptoFactories, - TestKeyManager, + MemoryDbKeyManager, ) { let network = Network::LocalNet; let rules = ConsensusManagerBuilder::new(network).build().unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let factories = CryptoFactories::default(); (CoinbaseBuilder::new(key_manager.clone()), rules, factories, key_manager) } @@ -382,11 +538,15 @@ mod test { async fn valid_coinbase() { let (builder, rules, factories, key_manager) = get_builder(); let p = TestParams::new(&key_manager).await; + let wallet_payment_address = TariAddress::default(); let builder = builder .with_block_height(42) .with_fees(145 * uT) .with_spend_key_id(p.spend_key_id.clone()) - .with_script_key_id(p.script_key_id); + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(p.sender_offset_key_id) + .with_script_key_id(p.script_key_id) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())); let (tx, _unblinded_output) = builder .build(rules.consensus_constants(42), rules.emission_schedule()) .await @@ -428,11 +588,15 @@ mod test { let (builder, rules, factories, key_manager) = get_builder(); let p = TestParams::new(&key_manager).await; let block_reward = rules.emission_schedule().block_reward(42) + 145 * uT; + let wallet_payment_address = TariAddress::default(); let builder = builder .with_block_height(42) .with_fees(145 * uT) .with_spend_key_id(p.spend_key_id) - .with_script_key_id(p.script_key_id); + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(p.sender_offset_key_id) + .with_script_key_id(p.script_key_id) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())); let (mut tx, _) = builder .build(rules.consensus_constants(42), rules.emission_schedule()) .await @@ -458,11 +622,15 @@ mod test { let p = TestParams::new(&key_manager).await; // We just want some small amount here. let missing_fee = rules.emission_schedule().block_reward(4200000) + (2 * uT); + let wallet_payment_address = TariAddress::default(); let builder = builder .with_block_height(42) .with_fees(1 * uT) .with_spend_key_id(p.spend_key_id.clone()) - .with_script_key_id(p.script_key_id.clone()); + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(p.sender_offset_key_id.clone()) + .with_script_key_id(p.script_key_id.clone()) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())); let (mut tx, _) = builder .build(rules.consensus_constants(0), rules.emission_schedule()) .await @@ -473,7 +641,10 @@ mod test { .with_block_height(4200000) .with_fees(1 * uT) .with_spend_key_id(p.spend_key_id.clone()) - .with_script_key_id(p.script_key_id.clone()); + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(p.sender_offset_key_id.clone()) + .with_script_key_id(p.script_key_id.clone()) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())); let (tx2, _) = builder .build(rules.consensus_constants(0), rules.emission_schedule()) .await @@ -501,7 +672,10 @@ mod test { .with_block_height(42) .with_fees(missing_fee) .with_spend_key_id(p.spend_key_id) - .with_script_key_id(p.script_key_id); + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(p.sender_offset_key_id) + .with_script_key_id(p.script_key_id) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())); let (tx3, _) = builder .build(rules.consensus_constants(0), rules.emission_schedule()) .await @@ -517,11 +691,18 @@ mod test { .is_ok()); } use tari_key_manager::key_manager_service::KeyManagerInterface; + use tari_script::one_sided_payment_script; use crate::transactions::{ aggregated_body::AggregateBody, - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface, TxoStage}, - test_helpers::{create_test_core_key_manager_with_memory_db, TestKeyManager}, + key_manager::{ + create_memory_db_key_manager, + MemoryDbKeyManager, + TariKeyId, + TransactionKeyManagerBranch, + TransactionKeyManagerInterface, + TxoStage, + }, transaction_components::TransactionKernelVersion, }; @@ -535,11 +716,15 @@ mod test { let p = TestParams::new(&key_manager).await; // We just want some small amount here. let missing_fee = rules.emission_schedule().block_reward(4200000) + (2 * uT); + let wallet_payment_address = TariAddress::default(); let builder = builder .with_block_height(42) .with_fees(1 * uT) .with_spend_key_id(p.spend_key_id.clone()) - .with_script_key_id(p.script_key_id.clone()); + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(p.sender_offset_key_id.clone()) + .with_script_key_id(p.script_key_id.clone()) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())); let (mut tx, _) = builder .build(rules.consensus_constants(0), rules.emission_schedule()) .await @@ -552,7 +737,10 @@ mod test { .with_block_height(4200000) .with_fees(1 * uT) .with_spend_key_id(p.spend_key_id.clone()) - .with_script_key_id(p.script_key_id); + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(p.sender_offset_key_id) + .with_script_key_id(p.script_key_id) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())); let (tx2, output) = builder .build(rules.consensus_constants(0), rules.emission_schedule()) .await diff --git a/base_layer/core/src/transactions/key_manager/error.rs b/base_layer/core/src/transactions/key_manager/error.rs index 71f668fc4c..ef92873c28 100644 --- a/base_layer/core/src/transactions/key_manager/error.rs +++ b/base_layer/core/src/transactions/key_manager/error.rs @@ -20,10 +20,10 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - use tari_crypto::signatures::CommitmentAndPublicKeySignatureError; -use thiserror::Error; use tari_key_manager::error::KeyManagerError; +use thiserror::Error; + use crate::transactions::transaction_components::TransactionError; #[derive(Debug, Error, PartialEq)] @@ -31,8 +31,13 @@ pub enum CoreKeyManagerError { #[error("KeyManagerError: `{0}`")] KeyManagerError(#[from] KeyManagerError), #[error("Error generating Commitment and PublicKey signature: `{0}`")] - CommitmentAndPublicKeySignatureError(#[from] CommitmentAndPublicKeySignatureError), + CommitmentAndPublicKeySignatureError(String), #[error("Transaction error: `{0}`")] TransactionError(#[from] TransactionError), +} -} \ No newline at end of file +impl From for CoreKeyManagerError { + fn from(err: CommitmentAndPublicKeySignatureError) -> Self { + CoreKeyManagerError::CommitmentAndPublicKeySignatureError(err.to_string()) + } +} diff --git a/base_layer/core/src/transactions/key_manager/memory_db_key_manager.rs b/base_layer/core/src/transactions/key_manager/memory_db_key_manager.rs new file mode 100644 index 0000000000..35e6776036 --- /dev/null +++ b/base_layer/core/src/transactions/key_manager/memory_db_key_manager.rs @@ -0,0 +1,64 @@ +// Copyright 2023 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{iter, mem::size_of}; + +use chacha20poly1305::{Key, KeyInit, XChaCha20Poly1305}; +use rand::{distributions::Alphanumeric, rngs::OsRng, Rng, RngCore}; +use tari_common_sqlite::connection::{DbConnection, DbConnectionUrl}; +use tari_key_manager::{ + cipher_seed::CipherSeed, + key_manager_service::storage::{database::KeyManagerDatabase, sqlite_db::KeyManagerSqliteDatabase}, +}; + +use crate::transactions::{key_manager::TransactionKeyManagerWrapper, CryptoFactories}; + +pub type MemoryDbKeyManager = TransactionKeyManagerWrapper>; + +fn random_string(len: usize) -> String { + iter::repeat(()) + .map(|_| OsRng.sample(Alphanumeric) as char) + .take(len) + .collect() +} + +pub fn create_memory_db_key_manager_with_range_proof_size(size: usize) -> MemoryDbKeyManager { + let connection = DbConnection::connect_url(&DbConnectionUrl::MemoryShared(random_string(8))).unwrap(); + let cipher = CipherSeed::new(); + + let mut key = [0u8; size_of::()]; + OsRng.fill_bytes(&mut key); + let key_ga = Key::from_slice(&key); + let db_cipher = XChaCha20Poly1305::new(key_ga); + let factory = CryptoFactories::new(size); + + TransactionKeyManagerWrapper::>::new( + cipher, + KeyManagerDatabase::new(KeyManagerSqliteDatabase::init(connection, db_cipher)), + factory, + ) + .unwrap() +} + +pub fn create_memory_db_key_manager() -> MemoryDbKeyManager { + create_memory_db_key_manager_with_range_proof_size(64) +} diff --git a/base_layer/core/src/transactions/key_manager/mod.rs b/base_layer/core/src/transactions/key_manager/mod.rs index afa8aa808e..2d803f06f0 100644 --- a/base_layer/core/src/transactions/key_manager/mod.rs +++ b/base_layer/core/src/transactions/key_manager/mod.rs @@ -36,4 +36,14 @@ mod initializer; pub use initializer::TransactionKeyManagerInitializer; mod inner; +/// This is a memory database implementation of the `TransactionKeyManager` trait. +mod memory_db_key_manager; pub use inner::TransactionKeyManagerInner; +pub use memory_db_key_manager::{ + create_memory_db_key_manager, + create_memory_db_key_manager_with_range_proof_size, + MemoryDbKeyManager, +}; + +mod error; +pub use error::CoreKeyManagerError; diff --git a/base_layer/core/src/transactions/mod.rs b/base_layer/core/src/transactions/mod.rs index a2dce5b429..632aaefd22 100644 --- a/base_layer/core/src/transactions/mod.rs +++ b/base_layer/core/src/transactions/mod.rs @@ -9,7 +9,12 @@ pub use crypto_factories::CryptoFactories; use tari_crypto::hash_domain; mod coinbase_builder; -pub use coinbase_builder::{CoinbaseBuildError, CoinbaseBuilder}; +pub use coinbase_builder::{ + generate_coinbase, + generate_coinbase_with_wallet_output, + CoinbaseBuildError, + CoinbaseBuilder, +}; pub mod fee; pub mod tari_amount; diff --git a/base_layer/core/src/transactions/test_helpers.rs b/base_layer/core/src/transactions/test_helpers.rs index 92b6a61ec8..4a4c412626 100644 --- a/base_layer/core/src/transactions/test_helpers.rs +++ b/base_layer/core/src/transactions/test_helpers.rs @@ -20,21 +20,13 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{iter, mem::size_of, sync::Arc}; +use std::sync::Arc; -use chacha20poly1305::{Key, KeyInit, XChaCha20Poly1305}; -use rand::{distributions::Alphanumeric, rngs::OsRng, Rng, RngCore}; +use rand::rngs::OsRng; use tari_common::configuration::Network; -use tari_common_sqlite::connection::{DbConnection, DbConnectionUrl}; use tari_common_types::types::{Commitment, PrivateKey, PublicKey, Signature}; use tari_crypto::keys::{PublicKey as PK, SecretKey}; -use tari_key_manager::{ - cipher_seed::CipherSeed, - key_manager_service::{ - storage::{database::KeyManagerDatabase, sqlite_db::KeyManagerSqliteDatabase}, - KeyManagerInterface, - }, -}; +use tari_key_manager::key_manager_service::KeyManagerInterface; use tari_script::{inputs, script, ExecutionStack, TariScript}; use super::transaction_components::{TransactionInputVersion, TransactionOutputVersion}; @@ -46,10 +38,11 @@ use crate::{ crypto_factories::CryptoFactories, fee::Fee, key_manager::{ + create_memory_db_key_manager, + MemoryDbKeyManager, TariKeyId, TransactionKeyManagerBranch, TransactionKeyManagerInterface, - TransactionKeyManagerWrapper, TxoStage, }, tari_amount::MicroMinotari, @@ -71,7 +64,7 @@ use crate::{ }, }; -pub async fn create_test_input(amount: MicroMinotari, maturity: u64, key_manager: &TestKeyManager) -> WalletOutput { +pub async fn create_test_input(amount: MicroMinotari, maturity: u64, key_manager: &MemoryDbKeyManager) -> WalletOutput { let params = TestParams::new(key_manager).await; params .create_input( @@ -106,7 +99,7 @@ pub struct TestParams { } impl TestParams { - pub async fn new(key_manager: &TestKeyManager) -> TestParams { + pub async fn new(key_manager: &MemoryDbKeyManager) -> TestParams { let (spend_key_id, spend_key_pk, script_key_id, script_key_pk) = key_manager.get_next_spend_and_script_key_ids().await.unwrap(); let (sender_offset_key_id, sender_offset_key_pk) = key_manager @@ -150,7 +143,7 @@ impl TestParams { pub async fn create_output( &self, params: UtxoTestParams, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { let version = match params.output_version { Some(v) => v, @@ -182,7 +175,7 @@ impl TestParams { /// Create a random transaction input for the given amount and maturity period. The input's wallet /// parameters are returned. - pub async fn create_input(&self, params: UtxoTestParams, key_manager: &TestKeyManager) -> WalletOutput { + pub async fn create_input(&self, params: UtxoTestParams, key_manager: &MemoryDbKeyManager) -> WalletOutput { self.create_output(params, key_manager).await.unwrap() } @@ -271,7 +264,7 @@ pub fn create_signature(k: PrivateKey, fee: MicroMinotari, lock_height: u64, fea /// Generate a random transaction signature given a key, returning the public key (excess) and the signature. pub async fn create_random_signature_from_secret_key( - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, secret_key_id: TariKeyId, fee: MicroMinotari, lock_height: u64, @@ -318,7 +311,7 @@ pub async fn create_coinbase_wallet_output( extra: Option>, ) -> WalletOutput { let rules = create_consensus_manager(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let constants = rules.consensus_constants(height); test_params .create_output( @@ -338,7 +331,7 @@ pub async fn create_wallet_output_with_data( output_features: OutputFeatures, test_params: &TestParams, value: MicroMinotari, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { test_params .create_output( @@ -399,9 +392,9 @@ macro_rules! tx { /// The output of this macro is intended to be used in [spend_utxos]. #[macro_export] macro_rules! txn_schema { - (from: $input:expr, to: $outputs:expr, fee: $fee:expr, lock: $lock:expr, features: $features:expr, input_version: $input_version:expr, output_version: $output_version:expr) => {{ + (from: $inputs:expr, to: $outputs:expr, fee: $fee:expr, lock: $lock:expr, features: $features:expr, input_version: $input_version:expr, output_version: $output_version:expr) => {{ $crate::transactions::test_helpers::TransactionSchema { - from: $input.clone(), + from: $inputs.clone(), to: $outputs.clone(), to_outputs: vec![], fee: $fee, @@ -500,7 +493,7 @@ pub async fn create_tx( input_maturity: u64, output_count: usize, output_features: OutputFeatures, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> std::io::Result<(Transaction, Vec, Vec)> { let (inputs, outputs) = create_wallet_outputs( amount, @@ -527,7 +520,7 @@ pub async fn create_wallet_outputs( output_features: &OutputFeatures, output_script: &TariScript, output_covenant: &Covenant, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> std::io::Result<(Vec, Vec<(WalletOutput, TariKeyId)>)> { let weighting = TransactionWeight::latest(); // This is a best guess to not underestimate metadata size @@ -604,7 +597,7 @@ pub async fn create_transaction_with( fee_per_gram: MicroMinotari, inputs: Vec, outputs: Vec<(WalletOutput, TariKeyId)>, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Transaction { let rules = ConsensusManager::builder(Network::LocalNet).build().unwrap(); let constants = rules.consensus_constants(0).clone(); @@ -639,7 +632,10 @@ pub async fn create_transaction_with( /// You only need to provide the wallet outputs to spend. This function will calculate the commitment for you. /// This is obviously less efficient, but is offered as a convenience. /// The output features will be applied to every output -pub async fn spend_utxos(schema: TransactionSchema, key_manager: &TestKeyManager) -> (Transaction, Vec) { +pub async fn spend_utxos( + schema: TransactionSchema, + key_manager: &MemoryDbKeyManager, +) -> (Transaction, Vec) { let (mut stx_protocol, outputs) = create_stx_protocol(schema, key_manager).await; stx_protocol.finalize(key_manager).await.unwrap(); let txn = stx_protocol.get_transaction().unwrap().clone(); @@ -649,7 +645,7 @@ pub async fn spend_utxos(schema: TransactionSchema, key_manager: &TestKeyManager #[allow(clippy::too_many_lines)] pub async fn create_stx_protocol( schema: TransactionSchema, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (SenderTransactionProtocol, Vec) { let mut outputs = Vec::with_capacity(schema.to.len()); let stx_builder = create_stx_protocol_internal(schema, key_manager, &mut outputs).await; @@ -664,9 +660,9 @@ pub async fn create_stx_protocol( #[allow(clippy::too_many_lines)] pub async fn create_stx_protocol_internal( schema: TransactionSchema, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, outputs: &mut Vec, -) -> SenderTransactionInitializer { +) -> SenderTransactionInitializer { let constants = ConsensusManager::builder(Network::LocalNet) .build() .unwrap() @@ -759,7 +755,10 @@ pub async fn create_stx_protocol_internal( stx_builder } -pub async fn create_coinbase_kernel(spending_key_id: &TariKeyId, key_manager: &TestKeyManager) -> TransactionKernel { +pub async fn create_coinbase_kernel( + spending_key_id: &TariKeyId, + key_manager: &MemoryDbKeyManager, +) -> TransactionKernel { let kernel_version = TransactionKernelVersion::get_current_version(); let kernel_features = KernelFeatures::COINBASE_KERNEL; let kernel_message = @@ -808,7 +807,7 @@ pub fn create_test_kernel(fee: MicroMinotari, lock_height: u64, features: Kernel /// Create a new UTXO for the specified value and return the output and spending key pub async fn create_utxo( value: MicroMinotari, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, features: &OutputFeatures, script: &TariScript, covenant: &Covenant, @@ -878,7 +877,7 @@ pub async fn create_utxo( pub async fn schema_to_transaction( txns: &[TransactionSchema], - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (Vec>, Vec) { let mut txs = Vec::new(); let mut utxos = Vec::new(); @@ -890,34 +889,3 @@ pub async fn schema_to_transaction( (txs, utxos) } - -pub type TestKeyManager = TransactionKeyManagerWrapper>; - -fn random_string(len: usize) -> String { - iter::repeat(()) - .map(|_| OsRng.sample(Alphanumeric) as char) - .take(len) - .collect() -} - -pub fn create_test_core_key_manager_with_memory_db_with_range_proof_size(size: usize) -> TestKeyManager { - let connection = DbConnection::connect_url(&DbConnectionUrl::MemoryShared(random_string(8))).unwrap(); - let cipher = CipherSeed::new(); - - let mut key = [0u8; size_of::()]; - OsRng.fill_bytes(&mut key); - let key_ga = Key::from_slice(&key); - let db_cipher = XChaCha20Poly1305::new(key_ga); - let factory = CryptoFactories::new(size); - - TransactionKeyManagerWrapper::>::new( - cipher, - KeyManagerDatabase::new(KeyManagerSqliteDatabase::init(connection, db_cipher)), - factory, - ) - .unwrap() -} - -pub fn create_test_core_key_manager_with_memory_db() -> TestKeyManager { - create_test_core_key_manager_with_memory_db_with_range_proof_size(64) -} diff --git a/base_layer/core/src/transactions/transaction_components/output_type.rs b/base_layer/core/src/transactions/transaction_components/output_type.rs index 59a84d49a0..9eb15402ed 100644 --- a/base_layer/core/src/transactions/transaction_components/output_type.rs +++ b/base_layer/core/src/transactions/transaction_components/output_type.rs @@ -45,7 +45,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; )] #[repr(u8)] pub enum OutputType { - /// An standard non-coinbase output. + /// An standard output. Standard = 0, /// Output is a coinbase output, must not be spent until maturity. Coinbase = 1, diff --git a/base_layer/core/src/transactions/transaction_components/test.rs b/base_layer/core/src/transactions/transaction_components/test.rs index dfb5a993af..3265bfe9e8 100644 --- a/base_layer/core/src/transactions/transaction_components/test.rs +++ b/base_layer/core/src/transactions/transaction_components/test.rs @@ -37,15 +37,14 @@ use crate::{ consensus::ConsensusManager, transactions::{ aggregated_body::AggregateBody, - key_manager::TransactionKeyManagerInterface, + key_manager::{ + create_memory_db_key_manager, + create_memory_db_key_manager_with_range_proof_size, + TransactionKeyManagerInterface, + }, tari_amount::{uT, MicroMinotari, T}, test_helpers, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_test_core_key_manager_with_memory_db_with_range_proof_size, - TestParams, - UtxoTestParams, - }, + test_helpers::{TestParams, UtxoTestParams}, transaction_components::{transaction_output::batch_verify_range_proofs, EncryptedData, OutputFeatures}, transaction_protocol::TransactionProtocolError, CryptoFactories, @@ -56,7 +55,7 @@ use crate::{ #[tokio::test] async fn input_and_output_and_wallet_output_hash_match() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let i = test_params @@ -80,7 +79,7 @@ fn test_smt_hashes() { #[tokio::test] async fn key_manager_input() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let i = test_params @@ -105,7 +104,7 @@ async fn key_manager_input() { #[tokio::test] async fn range_proof_verification() { let factories = CryptoFactories::new(32); - let key_manager = create_test_core_key_manager_with_memory_db_with_range_proof_size(32); + let key_manager = create_memory_db_key_manager_with_range_proof_size(32); // Directly test the tx_output verification let test_params_1 = TestParams::new(&key_manager).await; let test_params_2 = TestParams::new(&key_manager).await; @@ -165,7 +164,7 @@ async fn range_proof_verification() { #[tokio::test] async fn range_proof_verification_batch() { let factories = CryptoFactories::new(64); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let wallet_output1 = TestParams::new(&key_manager) .await .create_output( @@ -256,7 +255,7 @@ async fn range_proof_verification_batch() { #[tokio::test] async fn sender_signature_verification() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let wallet_output = test_params .create_output(Default::default(), &key_manager) @@ -383,7 +382,7 @@ fn check_timelocks() { #[tokio::test] async fn test_validate_internal_consistency() { let features = OutputFeatures { ..Default::default() }; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (tx, _, _) = test_helpers::create_tx(5000.into(), 3.into(), 1, 2, 1, 4, features, &key_manager) .await .expect("Failed to create tx"); @@ -396,7 +395,7 @@ async fn test_validate_internal_consistency() { #[tokio::test] #[allow(clippy::identity_op)] async fn check_cut_through() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (tx, _, outputs) = test_helpers::create_tx(50000000.into(), 3.into(), 1, 2, 1, 2, Default::default(), &key_manager) .await @@ -453,7 +452,7 @@ async fn check_cut_through() { #[tokio::test] async fn check_duplicate_inputs_outputs() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (tx, _, _outputs) = test_helpers::create_tx(50000000.into(), 3.into(), 1, 2, 1, 2, Default::default(), &key_manager) .await @@ -476,7 +475,7 @@ async fn check_duplicate_inputs_outputs() { #[tokio::test] async fn inputs_not_malleable() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (inputs, outputs) = test_helpers::create_wallet_outputs( 5000.into(), 1, @@ -511,7 +510,7 @@ async fn inputs_not_malleable() { #[tokio::test] async fn test_output_recover_openings() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let v = MicroMinotari::from(42); @@ -543,14 +542,17 @@ mod validate_internal_consistency { use super::*; use crate::{ covenants::{BaseLayerCovenantsDomain, COVENANTS_FIELD_HASHER_LABEL}, - transactions::test_helpers::{create_transaction_with, create_wallet_outputs, TestKeyManager}, + transactions::{ + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, + test_helpers::{create_transaction_with, create_wallet_outputs}, + }, }; async fn test_case( input_params: &UtxoTestParams, utxo_params: &UtxoTestParams, height: u64, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result<(), TransactionProtocolError> { let (mut inputs, outputs) = create_wallet_outputs( 100 * T, @@ -584,7 +586,7 @@ mod validate_internal_consistency { //---------------------------------- Case1 - PASS --------------------------------------------// let covenant = covenant!(fields_preserved(@fields( @field::covenant))); let features = OutputFeatures { ..Default::default() }; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); test_case( &UtxoTestParams { features: features.clone(), diff --git a/base_layer/core/src/transactions/transaction_components/transaction_output.rs b/base_layer/core/src/transactions/transaction_components/transaction_output.rs index 5b4f24fd05..0b35a48bf7 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_output.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_output.rs @@ -589,9 +589,9 @@ mod test { use super::{batch_verify_range_proofs, TransactionOutput}; use crate::transactions::{ - key_manager::TransactionKeyManagerInterface, + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager, TransactionKeyManagerInterface}, tari_amount::MicroMinotari, - test_helpers::{create_test_core_key_manager_with_memory_db, TestKeyManager, TestParams, UtxoTestParams}, + test_helpers::{TestParams, UtxoTestParams}, transaction_components::{OutputFeatures, RangeProofType}, CryptoFactories, }; @@ -599,7 +599,7 @@ mod test { #[tokio::test] async fn it_builds_correctly() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let value = MicroMinotari(10); @@ -623,7 +623,7 @@ mod test { #[tokio::test] async fn it_does_not_verify_incorrect_minimum_value() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let value = MicroMinotari(10); @@ -643,7 +643,7 @@ mod test { #[tokio::test] async fn it_does_batch_verify_correct_minimum_values() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let outputs = [ @@ -681,7 +681,7 @@ mod test { #[tokio::test] async fn it_does_batch_verify_with_mixed_range_proof_types() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let factories = CryptoFactories::default(); let test_params = TestParams::new(&key_manager).await; @@ -729,7 +729,7 @@ mod test { #[tokio::test] async fn invalid_revealed_value_proofs_are_blocked() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; assert!(create_output( &test_params, @@ -760,7 +760,7 @@ mod test { #[tokio::test] async fn revealed_value_proofs_only_succeed_with_valid_metadata_signatures() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let mut output = create_output( &test_params, @@ -787,7 +787,7 @@ mod test { #[tokio::test] async fn it_does_not_batch_verify_incorrect_minimum_values() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let outputs = [ @@ -818,7 +818,7 @@ mod test { value: MicroMinotari, minimum_value_promise: MicroMinotari, range_proof_type: RangeProofType, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { let utxo = test_params .create_output( @@ -845,7 +845,7 @@ mod test { value: MicroMinotari, minimum_value_promise: MicroMinotari, range_proof_type: RangeProofType, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> TransactionOutput { // we need first to create a valid minimum value, regardless of the minimum_value_promise // because this test function should allow creating an invalid proof for later testing diff --git a/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs b/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs index 3ab34ac40e..2eaf9ebb9a 100644 --- a/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs +++ b/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs @@ -229,14 +229,11 @@ mod test { use tari_key_manager::key_manager_service::KeyManagerInterface; use super::*; - use crate::transactions::{ - key_manager::TransactionKeyManagerBranch, - test_helpers::create_test_core_key_manager_with_memory_db, - }; + use crate::transactions::key_manager::{create_memory_db_key_manager, TransactionKeyManagerBranch}; #[tokio::test] async fn test_try_build() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (spending_key_id, _, script_key_id, _) = key_manager.get_next_spend_and_script_key_ids().await.unwrap(); let value = MicroMinotari(100); let kmob = WalletOutputBuilder::new(value, spending_key_id.clone()); @@ -278,7 +275,7 @@ mod test { #[tokio::test] async fn test_partial_metadata_signatures() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (spending_key_id, _, script_key_id, _) = key_manager.get_next_spend_and_script_key_ids().await.unwrap(); let value = MicroMinotari(100); let kmob = WalletOutputBuilder::new(value, spending_key_id.clone()); diff --git a/base_layer/core/src/transactions/transaction_protocol/recipient.rs b/base_layer/core/src/transactions/transaction_protocol/recipient.rs index 04c52c66ba..115e5edba8 100644 --- a/base_layer/core/src/transactions/transaction_protocol/recipient.rs +++ b/base_layer/core/src/transactions/transaction_protocol/recipient.rs @@ -194,9 +194,14 @@ mod test { test_helpers::create_consensus_constants, transactions::{ crypto_factories::CryptoFactories, - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface, TxoStage}, + key_manager::{ + create_memory_db_key_manager, + TransactionKeyManagerBranch, + TransactionKeyManagerInterface, + TxoStage, + }, tari_amount::*, - test_helpers::{create_test_core_key_manager_with_memory_db, TestParams, UtxoTestParams}, + test_helpers::{TestParams, UtxoTestParams}, transaction_components::{ OutputFeatures, TransactionKernel, @@ -213,7 +218,7 @@ mod test { #[tokio::test] async fn single_round_recipient() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let factories = CryptoFactories::default(); let sender_test_params = TestParams::new(&key_manager).await; let m = TransactionMetadata::new(MicroMinotari(125), 0); diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 8df23ac0a8..776fe40e28 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -879,14 +879,9 @@ mod test { test_helpers::{create_consensus_constants, create_consensus_rules}, transactions::{ crypto_factories::CryptoFactories, - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface}, + key_manager::{create_memory_db_key_manager, TransactionKeyManagerBranch, TransactionKeyManagerInterface}, tari_amount::*, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_test_input, - create_wallet_output_with_data, - TestParams, - }, + test_helpers::{create_test_input, create_wallet_output_with_data, TestParams}, transaction_components::{ EncryptedData, OutputFeatures, @@ -911,7 +906,7 @@ mod test { #[tokio::test] async fn test_errors() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let stp = SenderTransactionProtocol { state: SenderState::Failed(TransactionProtocolError::InvalidStateError), }; @@ -956,7 +951,7 @@ mod test { #[tokio::test] async fn test_metadata_signature_finalize() { // Defaults - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); // Sender data let (ephemeral_pubkey_id, ephemeral_pubkey) = key_manager @@ -1045,7 +1040,7 @@ mod test { #[tokio::test] async fn zero_recipients() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p1 = TestParams::new(&key_manager).await; let p2 = TestParams::new(&key_manager).await; let input = create_test_input(MicroMinotari(1200), 0, &key_manager).await; @@ -1107,7 +1102,7 @@ mod test { let rules = create_consensus_rules(); let factories = CryptoFactories::default(); // Alice's parameters - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let a_change_key = TestParams::new(&key_manager).await; // Bob's parameters let bob_key = TestParams::new(&key_manager).await; @@ -1212,7 +1207,7 @@ mod test { #[tokio::test] async fn single_recipient_with_change() { let rules = create_consensus_rules(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let factories = CryptoFactories::default(); // Alice's parameters let alice_key = TestParams::new(&key_manager).await; @@ -1325,7 +1320,7 @@ mod test { #[tokio::test] async fn single_recipient_multiple_inputs_with_change() { let rules = create_consensus_rules(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let factories = CryptoFactories::default(); // Bob's parameters let bob_key = TestParams::new(&key_manager).await; @@ -1434,7 +1429,7 @@ mod test { #[tokio::test] async fn disallow_fee_larger_than_amount() { // Alice's parameters - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (utxo_amount, fee_per_gram, amount) = (MicroMinotari(2500), MicroMinotari(10), MicroMinotari(500)); let input = create_test_input(utxo_amount, 0, &key_manager).await; let script = script!(Nop); @@ -1472,7 +1467,7 @@ mod test { #[tokio::test] async fn allow_fee_larger_than_amount() { // Alice's parameters - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (utxo_amount, fee_per_gram, amount) = (MicroMinotari(2500), MicroMinotari(10), MicroMinotari(500)); let input = create_test_input(utxo_amount, 0, &key_manager).await; let script = script!(Nop); @@ -1511,8 +1506,8 @@ mod test { #[tokio::test] async fn single_recipient_with_rewindable_change_and_receiver_outputs_bulletproofs() { // Alice's parameters - let key_manager_alice = create_test_core_key_manager_with_memory_db(); - let key_manager_bob = create_test_core_key_manager_with_memory_db(); + let key_manager_alice = create_memory_db_key_manager(); + let key_manager_bob = create_memory_db_key_manager(); // Bob's parameters let bob_test_params = TestParams::new(&key_manager_bob).await; let alice_value = MicroMinotari(25000); diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 393c2f1748..8fb6feee3f 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -149,9 +149,9 @@ mod test { covenants::Covenant, test_helpers::create_consensus_constants, transactions::{ - key_manager::TransactionKeyManagerInterface, + key_manager::{create_memory_db_key_manager, TransactionKeyManagerInterface}, tari_amount::*, - test_helpers::{create_test_core_key_manager_with_memory_db, TestParams}, + test_helpers::TestParams, transaction_components::{ EncryptedData, OutputFeatures, @@ -172,7 +172,7 @@ mod test { #[tokio::test] async fn zero_amount_fails() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let consensus_constants = create_consensus_constants(0); let info = SingleRoundSenderData::default(); @@ -204,7 +204,7 @@ mod test { #[tokio::test] async fn invalid_version_fails() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let consensus_constants = create_consensus_constants(0); @@ -250,7 +250,7 @@ mod test { tari_key_manager::key_manager_service::storage::sqlite_db::KeyManagerSqliteDatabase< tari_common_sqlite::connection::DbConnection, >, - > = create_test_core_key_manager_with_memory_db(); + > = create_memory_db_key_manager(); let consensus_constants = create_consensus_constants(0); let m = TransactionMetadata::new(MicroMinotari(100), 0); let test_params = TestParams::new(&key_manager).await; diff --git a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs index 05ff1af532..4bdb2e36df 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -600,14 +600,9 @@ mod test { test_helpers::create_consensus_constants, transactions::{ fee::Fee, + key_manager::create_memory_db_key_manager, tari_amount::*, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_test_input, - create_wallet_output_with_data, - TestParams, - UtxoTestParams, - }, + test_helpers::{create_test_input, create_wallet_output_with_data, TestParams, UtxoTestParams}, transaction_components::{OutputFeatures, MAX_TRANSACTION_INPUTS}, transaction_protocol::{sender::SenderState, transaction_initializer::SenderTransactionInitializer}, }, @@ -617,7 +612,7 @@ mod test { #[tokio::test] async fn no_receivers() -> std::io::Result<()> { // Create some inputs - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager).await; // Start the builder let builder = SenderTransactionInitializer::new(&create_consensus_constants(0), key_manager.clone()); @@ -694,7 +689,7 @@ mod test { #[tokio::test] async fn no_change_or_receivers() { // Create some inputs - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager).await; let input = create_test_input(MicroMinotari(5000), 0, &key_manager).await; let constants = create_consensus_constants(0); @@ -745,7 +740,7 @@ mod test { #[allow(clippy::identity_op)] async fn change_edge_case() { // Create some inputs - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager).await; let constants = create_consensus_constants(0); let weighting = constants.transaction_weight_params(); @@ -799,7 +794,7 @@ mod test { #[tokio::test] async fn too_many_inputs() { // Create some inputs - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager).await; let output = create_wallet_output_with_data( @@ -831,7 +826,7 @@ mod test { #[tokio::test] async fn fee_too_low() { // Create some inputs - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager).await; let tx_fee = p.fee().calculate( MicroMinotari(1), @@ -876,7 +871,7 @@ mod test { #[tokio::test] async fn not_enough_funds() { // Create some inputs - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager).await; let input = create_test_input(MicroMinotari(400), 0, &key_manager).await; let script = script!(Nop); @@ -928,7 +923,7 @@ mod test { #[tokio::test] async fn single_recipient() { // Create some inputs - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager).await; let input1 = create_test_input(MicroMinotari(2000), 0, &key_manager).await; let input2 = create_test_input(MicroMinotari(3000), 0, &key_manager).await; diff --git a/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs b/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs index 4b4a899ead..0edff76f0e 100644 --- a/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs +++ b/base_layer/core/src/validation/aggregate_body/aggregate_body_internal_validator.rs @@ -433,8 +433,8 @@ mod test { use crate::{ covenants::Covenant, transactions::{ + key_manager::create_memory_db_key_manager, test_helpers, - test_helpers::create_test_core_key_manager_with_memory_db, transaction_components::{KernelFeatures, OutputFeatures, TransactionInputVersion}, }, }; @@ -499,7 +499,7 @@ mod test { let mut kernel1 = test_helpers::create_test_kernel(0.into(), 0, KernelFeatures::create_burn()); let mut kernel2 = test_helpers::create_test_kernel(0.into(), 0, KernelFeatures::create_burn()); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (output1, _, _) = test_helpers::create_utxo( 100.into(), &key_manager, @@ -561,7 +561,7 @@ mod test { // Sort the kernels, we'll check that the outputs fail the sorting check kernels.sort(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut outputs = futures::stream::unfold((), |_| async { let (o, _, _) = test_helpers::create_utxo( 100.into(), diff --git a/base_layer/core/src/validation/block_body/block_body_internal_validator.rs b/base_layer/core/src/validation/block_body/block_body_internal_validator.rs index 2357a8988f..ad75e45498 100644 --- a/base_layer/core/src/validation/block_body/block_body_internal_validator.rs +++ b/base_layer/core/src/validation/block_body/block_body_internal_validator.rs @@ -85,7 +85,6 @@ fn validate_block_specific_checks( warn!(target: LOG_TARGET, "Attempt to validate genesis block"); return Err(ValidationError::ValidatingGenesis); } - check_coinbase_output(block, consensus_manager, factories)?; check_coinbase_output_features(&block.body, constants)?; diff --git a/base_layer/core/src/validation/block_body/test.rs b/base_layer/core/src/validation/block_body/test.rs index 166c0c1625..1906acacac 100644 --- a/base_layer/core/src/validation/block_body/test.rs +++ b/base_layer/core/src/validation/block_body/test.rs @@ -22,8 +22,9 @@ use std::sync::Arc; use tari_common::configuration::Network; +use tari_common_types::tari_address::TariAddress; use tari_key_manager::key_manager_service::KeyId; -use tari_script::script; +use tari_script::{one_sided_payment_script, script}; use tari_test_utils::unpack_enum; use tokio::time::Instant; @@ -36,7 +37,7 @@ use crate::{ test_helpers::{blockchain::TestBlockchain, BlockSpec}, transactions::{ aggregated_body::AggregateBody, - key_manager::TransactionKeyManagerBranch, + key_manager::{TariKeyId, TransactionKeyManagerBranch}, tari_amount::{uT, T}, test_helpers::schema_to_transaction, transaction_components::TransactionError, @@ -47,13 +48,13 @@ use crate::{ validation::{BlockBodyValidator, ValidationError}, }; -fn setup_with_rules(rules: ConsensusManager, check_rangeproof: bool) -> (TestBlockchain, BlockBodyFullValidator) { - let blockchain = TestBlockchain::create(rules.clone()); +async fn setup_with_rules(rules: ConsensusManager, check_rangeproof: bool) -> (TestBlockchain, BlockBodyFullValidator) { + let blockchain = TestBlockchain::create(rules.clone()).await; let validator = BlockBodyFullValidator::new(rules, check_rangeproof); (blockchain, validator) } -fn setup(check_rangeproof: bool) -> (TestBlockchain, BlockBodyFullValidator) { +async fn setup(check_rangeproof: bool) -> (TestBlockchain, BlockBodyFullValidator) { let rules = ConsensusManager::builder(Network::LocalNet) .add_consensus_constants( ConsensusConstantsBuilder::new(Network::LocalNet) @@ -63,13 +64,13 @@ fn setup(check_rangeproof: bool) -> (TestBlockchain, BlockBodyFullValidator) { ) .build() .unwrap(); - setup_with_rules(rules, check_rangeproof) + setup_with_rules(rules, check_rangeproof).await } #[tokio::test] async fn it_passes_if_large_output_block_is_valid() { // we use this test to benchmark a block with multiple outputs - let (mut blockchain, validator) = setup(false); + let (mut blockchain, validator) = setup(false).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); let mut outs = Vec::new(); // create 498 outputs, so we have a block with 500 outputs, 498 + change + coinbase @@ -77,7 +78,7 @@ async fn it_passes_if_large_output_block_is_valid() { outs.push(9000 * uT); } - let schema1 = txn_schema!(from: vec![coinbase_a], to: outs); + let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: outs); let (txs, _outputs) = schema_to_transaction(&[schema1], &blockchain.km).await; let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); @@ -106,12 +107,41 @@ async fn it_passes_if_large_output_block_is_valid() { println!("finished validating in: {}", finished.as_millis()); } +#[tokio::test] +async fn it_validates_when_a_coinbase_is_spent() { + // we use this test to benchmark a block with multiple outputs + let (mut blockchain, validator) = setup(false).await; + let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); + + let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![9000 * uT]); + let (txs, _outputs) = schema_to_transaction(&[schema1], &blockchain.km).await; + + let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); + let (chain_block, _coinbase_b) = blockchain + .create_next_tip(block_spec!("B",parent: "A", transactions: txs)) + .await; + let (mut block, mmr_roots) = blockchain + .db() + .calculate_mmr_roots(chain_block.block().clone()) + .unwrap(); + block.header.input_mr = mmr_roots.input_mr; + block.header.output_mr = mmr_roots.output_mr; + block.header.output_smt_size = mmr_roots.output_smt_size; + block.header.kernel_mr = mmr_roots.kernel_mr; + block.header.kernel_mmr_size = mmr_roots.kernel_mmr_size; + block.header.validator_node_mr = mmr_roots.validator_node_mr; + block.header.validator_node_size = mmr_roots.validator_node_size; + + let txn = blockchain.db().db_read_access().unwrap(); + assert!(validator.validate_body(&*txn, &block).is_ok()); +} + #[tokio::test] async fn it_passes_if_large_block_is_valid() { // we use this test to benchmark a block with multiple inputs and outputs - let (mut blockchain, validator) = setup(false); + let (mut blockchain, validator) = setup(false).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); - let schema1 = txn_schema!(from: vec![coinbase_a], to: vec![5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T]); + let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T, 5 * T]); let (txs, outputs) = schema_to_transaction(&[schema1], &blockchain.km).await; let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); @@ -122,7 +152,7 @@ async fn it_passes_if_large_block_is_valid() { let mut schemas = Vec::new(); for output in outputs { - let new_schema = txn_schema!(from: vec![output], to: vec![1 * T, 1 * T, 1 * T, 1 * T]); + let new_schema = txn_schema!(from: vec![output.clone()], to: vec![1 * T, 1 * T, 1 * T, 1 * T]); schemas.push(new_schema); } let (txs, _) = schema_to_transaction(&schemas, &blockchain.km).await; @@ -145,7 +175,8 @@ async fn it_passes_if_large_block_is_valid() { let txn = blockchain.db().db_read_access().unwrap(); let start = Instant::now(); - assert!(validator.validate_body(&*txn, &block).is_ok()); + validator.validate_body(&*txn, &block).unwrap(); + // assert!(validator.validate_body(&*txn, &block).is_ok()); let finished = start.elapsed(); // this here here for benchmarking purposes. // we can extrapolate full block validation by multiplying the time by 32.9, this we get from the max_weight /weight @@ -155,7 +186,7 @@ async fn it_passes_if_large_block_is_valid() { #[tokio::test] async fn it_passes_if_block_is_valid() { - let (blockchain, validator) = setup(true); + let (blockchain, validator) = setup(true).await; let (chain_block, _) = blockchain.create_next_tip(BlockSpec::default()).await; @@ -177,7 +208,7 @@ async fn it_passes_if_block_is_valid() { #[tokio::test] async fn it_checks_the_coinbase_reward() { - let (blockchain, validator) = setup(true); + let (blockchain, validator) = setup(true).await; let (block, _) = blockchain .create_chained_block(block_spec!("A", parent: "GB", reward: 10 * T, )) @@ -195,18 +226,22 @@ async fn it_checks_the_coinbase_reward() { #[tokio::test] async fn it_allows_multiple_coinbases() { - let (blockchain, validator) = setup(true); + let (blockchain, validator) = setup(true).await; let (mut block, coinbase) = blockchain.create_unmined_block(block_spec!("A1", parent: "GB")).await; let spend_key_id = KeyId::Managed { branch: TransactionKeyManagerBranch::Coinbase.get_branch_key(), index: 42, }; + let wallet_payment_address = TariAddress::default(); let (_, coinbase_output) = CoinbaseBuilder::new(blockchain.km.clone()) .with_block_height(1) .with_fees(0.into()) .with_spend_key_id(spend_key_id.clone()) - .with_script_key_id(spend_key_id) + .with_encryption_key_id(TariKeyId::default()) + .with_sender_offset_key_id(TariKeyId::default()) + .with_script_key_id(TariKeyId::default()) + .with_script(one_sided_payment_script(wallet_payment_address.public_key())) .build_with_reward(blockchain.rules().consensus_constants(1), coinbase.value) .await .unwrap(); @@ -231,11 +266,14 @@ async fn it_allows_multiple_coinbases() { #[tokio::test] async fn it_checks_duplicate_kernel() { - let (mut blockchain, validator) = setup(true); + let (mut blockchain, validator) = setup(true).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); - let (txs, _) = - schema_to_transaction(&[txn_schema!(from: vec![coinbase_a], to: vec![50 * T])], &blockchain.km).await; + let (txs, _) = schema_to_transaction( + &[txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T])], + &blockchain.km, + ) + .await; blockchain .add_next_tip(block_spec!("1", transactions: txs.iter().map(|t| (**t).clone()).collect())) @@ -255,7 +293,7 @@ async fn it_checks_duplicate_kernel() { #[tokio::test] async fn it_checks_double_spends() { - let (mut blockchain, validator) = setup(true); + let (mut blockchain, validator) = setup(true).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); let (txs, _) = schema_to_transaction( @@ -269,8 +307,11 @@ async fn it_checks_double_spends() { .await .unwrap(); // lets create a new transction from the same input - let (txs2, _) = - schema_to_transaction(&[txn_schema!(from: vec![coinbase_a], to: vec![50 * T])], &blockchain.km).await; + let (txs2, _) = schema_to_transaction( + &[txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T])], + &blockchain.km, + ) + .await; let (block, _) = blockchain .create_next_tip( BlockSpec::new() @@ -285,10 +326,10 @@ async fn it_checks_double_spends() { #[tokio::test] async fn it_checks_input_maturity() { - let (mut blockchain, validator) = setup(true); + let (mut blockchain, validator) = setup(true).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); - let mut schema = txn_schema!(from: vec![coinbase_a], to: vec![50 * T]); + let mut schema = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T]); schema.from[0].features.maturity = 100; let (txs, _) = schema_to_transaction(&[schema], &blockchain.km).await; @@ -310,11 +351,11 @@ async fn it_checks_input_maturity() { #[tokio::test] async fn it_checks_txo_sort_order() { - let (mut blockchain, validator) = setup(true); + let (mut blockchain, validator) = setup(true).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); - let schema1 = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 12 * T]); + let schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]); let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await; let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); @@ -343,11 +384,11 @@ async fn it_limits_the_script_byte_size() { ) .build() .unwrap(); - let (mut blockchain, validator) = setup_with_rules(rules, true); + let (mut blockchain, validator) = setup_with_rules(rules, true).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); - let mut schema1 = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 12 * T]); + let mut schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]); schema1.script = script!(Nop Nop Nop); let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await; let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); @@ -368,11 +409,11 @@ async fn it_rejects_invalid_input_metadata() { ) .build() .unwrap(); - let (mut blockchain, validator) = setup_with_rules(rules, true); + let (mut blockchain, validator) = setup_with_rules(rules.clone(), true).await; let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); - let mut schema1 = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 12 * T]); + let mut schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]); schema1.from[0].sender_offset_public_key = Default::default(); let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await; let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); @@ -385,10 +426,10 @@ async fn it_rejects_invalid_input_metadata() { #[tokio::test] async fn it_rejects_zero_conf_double_spends() { - let (mut blockchain, validator) = setup(true); + let (mut blockchain, validator) = setup(true).await; let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap(); - let schema = txn_schema!(from: vec![coinbase], to: vec![201 * T]); + let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]); let (initial_tx, outputs) = schema_to_transaction(&[schema], &blockchain.km).await; let schema = txn_schema!(from: vec![outputs[0].clone()], to: vec![200 * T]); @@ -426,12 +467,12 @@ mod body_only { ) .build() .unwrap(); - let mut blockchain = TestBlockchain::create(rules.clone()); + let mut blockchain = TestBlockchain::create(rules.clone()).await; let validator = BlockBodyFullValidator::new(rules, true); let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("A")).await.unwrap(); - let mut schema1 = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 12 * T]); + let mut schema1 = txn_schema!(from: vec![coinbase_a.clone()], to: vec![50 * T, 12 * T]); schema1.from[0].sender_offset_public_key = Default::default(); let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await; let txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::>(); @@ -465,11 +506,11 @@ mod orphan_validator { ) .build() .unwrap(); - let mut blockchain = TestBlockchain::create(rules.clone()); + let mut blockchain = TestBlockchain::create(rules.clone()).await; let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default()); let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap(); - let schema = txn_schema!(from: vec![coinbase], to: vec![201 * T]); + let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]); let (initial_tx, outputs) = schema_to_transaction(&[schema], &blockchain.km).await; let schema = txn_schema!(from: vec![outputs[0].clone()], to: vec![200 * T]); @@ -503,11 +544,11 @@ mod orphan_validator { ) .build() .unwrap(); - let mut blockchain = TestBlockchain::create(rules.clone()); + let mut blockchain = TestBlockchain::create(rules.clone()).await; let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default()); let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap(); - let schema = txn_schema!(from: vec![coinbase], to: vec![201 * T]); + let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]); let (tx, _) = schema_to_transaction(&[schema], &blockchain.km).await; let transactions = tx.into_iter().map(|b| Arc::try_unwrap(b).unwrap()).collect::>(); @@ -531,11 +572,11 @@ mod orphan_validator { ) .build() .unwrap(); - let mut blockchain = TestBlockchain::create(rules.clone()); + let mut blockchain = TestBlockchain::create(rules.clone()).await; let validator = BlockBodyInternalConsistencyValidator::new(rules, false, CryptoFactories::default()); let (_, coinbase) = blockchain.append(block_spec!("1", parent: "GB")).await.unwrap(); - let schema = txn_schema!(from: vec![coinbase], to: vec![201 * T]); + let schema = txn_schema!(from: vec![coinbase.clone()], to: vec![201 * T]); let (tx, _) = schema_to_transaction(&[schema], &blockchain.km).await; let transactions = tx.into_iter().map(|b| Arc::try_unwrap(b).unwrap()).collect::>(); diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs index 8011f17faf..aae8d66b25 100644 --- a/base_layer/core/src/validation/helpers.rs +++ b/base_layer/core/src/validation/helpers.rs @@ -534,17 +534,17 @@ mod test { use super::*; use crate::transactions::{ aggregated_body::AggregateBody, - test_helpers::create_test_core_key_manager_with_memory_db, + key_manager::create_memory_db_key_manager, transaction_components::TransactionError, }; #[tokio::test] async fn it_succeeds_for_valid_coinbase() { let height = 1; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let rules = test_helpers::create_consensus_manager(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let coinbase = block_on(test_helpers::create_coinbase_wallet_output(&test_params, height, None)); let coinbase_output = coinbase.to_transaction_output(&key_manager).await.unwrap(); let coinbase_kernel = test_helpers::create_coinbase_kernel(&coinbase.spending_key_id, &key_manager).await; @@ -560,7 +560,7 @@ mod test { #[tokio::test] async fn it_returns_error_for_invalid_coinbase_maturity() { let height = 1; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let rules = test_helpers::create_consensus_manager(); let mut coinbase = test_helpers::create_coinbase_wallet_output(&test_params, height, None).await; @@ -582,7 +582,7 @@ mod test { #[tokio::test] async fn it_returns_error_for_invalid_coinbase_reward() { let height = 1; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let test_params = TestParams::new(&key_manager).await; let rules = test_helpers::create_consensus_manager(); let mut coinbase = test_helpers::create_coinbase_wallet_output(&test_params, height, None).await; diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index 2a8166aeb4..7ac7af1d49 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -36,13 +36,9 @@ use crate::{ proof_of_work::AchievedTargetDifficulty, test_helpers::{blockchain::create_store_with_consensus, create_chain_header}, transactions::{ - key_manager::TxoStage, + key_manager::{create_memory_db_key_manager, TxoStage}, tari_amount::{uT, MicroMinotari}, - test_helpers::{ - create_random_signature_from_secret_key, - create_test_core_key_manager_with_memory_db, - create_utxo, - }, + test_helpers::{create_random_signature_from_secret_key, create_utxo}, transaction_components::{KernelBuilder, KernelFeatures, OutputFeatures, TransactionKernel}, CryptoFactories, }, @@ -177,7 +173,7 @@ async fn chain_balance_validation() { let consensus_manager = ConsensusManagerBuilder::new(Network::Esmeralda).build().unwrap(); let genesis = consensus_manager.get_genesis_block(); let faucet_value = 5000 * uT; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (faucet_utxo, faucet_key_id, _) = create_utxo( faucet_value, &key_manager, @@ -360,7 +356,7 @@ async fn chain_balance_validation_burned() { let consensus_manager = ConsensusManagerBuilder::new(Network::Esmeralda).build().unwrap(); let genesis = consensus_manager.get_genesis_block(); let faucet_value = 5000 * uT; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (faucet_utxo, faucet_key_id, _) = create_utxo( faucet_value, &key_manager, @@ -512,16 +508,13 @@ async fn chain_balance_validation_burned() { mod transaction_validator { use super::*; use crate::{ - transactions::{ - test_helpers::create_test_core_key_manager_with_memory_db, - transaction_components::{OutputType, TransactionError}, - }, + transactions::transaction_components::{OutputType, TransactionError}, validation::transaction::TransactionInternalConsistencyValidator, }; #[tokio::test] async fn it_rejects_coinbase_outputs() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build().unwrap(); let db = create_store_with_consensus(consensus_manager.clone()); let factories = CryptoFactories::default(); @@ -543,7 +536,7 @@ mod transaction_validator { #[tokio::test] async fn coinbase_extra_must_be_empty() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build().unwrap(); let db = create_store_with_consensus(consensus_manager.clone()); let factories = CryptoFactories::default(); diff --git a/base_layer/core/tests/chain_storage_tests/chain_storage.rs b/base_layer/core/tests/chain_storage_tests/chain_storage.rs index c6893dbb5c..c5e35ff216 100644 --- a/base_layer/core/tests/chain_storage_tests/chain_storage.rs +++ b/base_layer/core/tests/chain_storage_tests/chain_storage.rs @@ -1920,7 +1920,7 @@ fn test_fails_validation() { let mut blocks = vec![block0]; let mut outputs = vec![vec![]]; - let schemas = vec![txn_schema!(from: vec![output], to: vec![2 * T, 500_000 * uT])]; + let schemas = vec![txn_schema!(from: vec![output.clone()], to: vec![2 * T, 500_000 * uT])]; let err = generate_new_block_with_achieved_difficulty( &mut store, &mut blocks, diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs index 3a7407e747..be2afeca9e 100644 --- a/base_layer/core/tests/helpers/block_builders.rs +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -35,9 +35,9 @@ use tari_core::{ consensus::{emission::Emission, ConsensusConstants, ConsensusManager}, proof_of_work::{sha3x_difficulty, AccumulatedDifficulty, AchievedTargetDifficulty, Difficulty}, transactions::{ - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface, TxoStage}, + key_manager::{MemoryDbKeyManager, TransactionKeyManagerBranch, TransactionKeyManagerInterface, TxoStage}, tari_amount::MicroMinotari, - test_helpers::{create_wallet_output_with_data, spend_utxos, TestKeyManager, TestParams, TransactionSchema}, + test_helpers::{create_wallet_output_with_data, spend_utxos, TestParams, TransactionSchema}, transaction_components::{ KernelBuilder, KernelFeatures, @@ -61,7 +61,7 @@ pub async fn create_coinbase( value: MicroMinotari, maturity_height: u64, extra: Option>, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (TransactionOutput, TransactionKernel, WalletOutput) { let p = TestParams::new(key_manager).await; let public_exess = key_manager.get_public_key_at_key_id(&p.spend_key_id).await.unwrap(); @@ -117,7 +117,7 @@ pub async fn create_coinbase( async fn genesis_template( coinbase_value: MicroMinotari, consensus_constants: &ConsensusConstants, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (NewBlockTemplate, WalletOutput) { let header = BlockHeader::new(consensus_constants.blockchain_version()); let (utxo, kernel, output) = create_coinbase( @@ -164,7 +164,7 @@ fn print_new_genesis_block_values() { /// value, and the maturity is zero. pub async fn create_genesis_block( consensus_constants: &ConsensusConstants, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (ChainBlock, WalletOutput) { create_genesis_block_with_coinbase_value( consensus_constants.emission_amounts().0, @@ -199,7 +199,7 @@ fn update_genesis_block_mmr_roots(template: NewBlockTemplate) -> Result (ChainBlock, WalletOutput) { let (template, output) = genesis_template(coinbase_value, consensus_constants, key_manager).await; let mut block = update_genesis_block_mmr_roots(template).unwrap(); @@ -226,7 +226,7 @@ pub async fn create_genesis_block_with_coinbase_value( pub async fn create_genesis_block_with_utxos( values: &[MicroMinotari], consensus_constants: &ConsensusConstants, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (ChainBlock, Vec) { let (mut template, coinbase) = genesis_template(100_000_000.into(), consensus_constants, key_manager).await; let script = script!(Nop); @@ -268,7 +268,7 @@ pub async fn chain_block( prev_block: &Block, transactions: Vec, consensus: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> NewBlockTemplate { let mut header = BlockHeader::from_previous(&prev_block.header); header.version = consensus.consensus_constants(header.height).blockchain_version(); @@ -323,7 +323,7 @@ pub async fn chain_block_with_new_coinbase( transactions: Vec, consensus_manager: &ConsensusManager, extra: Option>, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (NewBlockTemplate, WalletOutput) { let height = prev_block.height() + 1; let mut coinbase_value = consensus_manager.emission_schedule().block_reward(height); @@ -364,7 +364,7 @@ pub async fn append_block( txns: Vec, consensus: &ConsensusManager, achieved_difficulty: Difficulty, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { append_block_with_coinbase(db, prev_block, txns, consensus, achieved_difficulty, key_manager) .await @@ -379,7 +379,7 @@ pub async fn append_block_with_coinbase( txns: Vec, consensus_manager: &ConsensusManager, achieved_difficulty: Difficulty, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result<(ChainBlock, WalletOutput), ChainStorageError> { let height = prev_block.height() + 1; let mut coinbase_value = consensus_manager.emission_schedule().block_reward(height); @@ -423,7 +423,7 @@ pub async fn generate_new_block( outputs: &mut Vec>, schemas: Vec, consensus: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { let coinbase_value = consensus.emission_schedule().block_reward(db.get_height().unwrap() + 1); generate_new_block_with_coinbase(db, blocks, outputs, schemas, coinbase_value, consensus, key_manager).await @@ -437,7 +437,7 @@ pub async fn generate_new_block_with_achieved_difficulty( schemas: Vec, achieved_difficulty: Difficulty, consensus: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { let mut txns = Vec::new(); let mut block_utxos = Vec::new(); @@ -459,7 +459,7 @@ pub async fn generate_new_block_with_coinbase( schemas: Vec, coinbase_value: MicroMinotari, consensus: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { let mut txns = Vec::new(); let mut block_utxos = Vec::new(); @@ -502,7 +502,7 @@ pub async fn generate_block( blocks: &mut Vec, transactions: Vec, consensus: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { let prev_block = blocks.last().unwrap(); let template = chain_block_with_new_coinbase(prev_block, transactions, consensus, None, key_manager) @@ -523,7 +523,7 @@ pub async fn generate_block_with_achieved_difficulty( transactions: Vec, achieved_difficulty: Difficulty, consensus: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Result { let template = chain_block_with_new_coinbase(blocks.last().unwrap(), transactions, consensus, None, key_manager) .await @@ -571,7 +571,7 @@ pub async fn construct_chained_blocks( block0: ChainBlock, consensus: &ConsensusManager, n: usize, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Vec { let mut prev_block = block0; let mut blocks = Vec::new(); diff --git a/base_layer/core/tests/helpers/block_malleability.rs b/base_layer/core/tests/helpers/block_malleability.rs index 8eb6888e41..7593c4b416 100644 --- a/base_layer/core/tests/helpers/block_malleability.rs +++ b/base_layer/core/tests/helpers/block_malleability.rs @@ -66,7 +66,7 @@ async fn check_block_changes_are_detected(field: MerkleMountainRangeField, block let (txs, _) = schema_to_transaction( &[txn_schema!( - from: vec![output], + from: vec![output.clone()], to: vec![50 * T], input_version: TransactionInputVersion::V0, output_version: TransactionOutputVersion::V0 diff --git a/base_layer/core/tests/helpers/database.rs b/base_layer/core/tests/helpers/database.rs index 2bb0ebc03c..1ec525373c 100644 --- a/base_layer/core/tests/helpers/database.rs +++ b/base_layer/core/tests/helpers/database.rs @@ -26,7 +26,7 @@ use tari_core::{ blocks::{Block, BlockHeader, NewBlockTemplate}, consensus::{emission::Emission, ConsensusManager}, proof_of_work::Difficulty, - transactions::{tari_amount::MicroMinotari, test_helpers::TestKeyManager, transaction_components::Transaction}, + transactions::{key_manager::MemoryDbKeyManager, tari_amount::MicroMinotari, transaction_components::Transaction}, }; use crate::helpers::block_builders::create_coinbase; @@ -38,7 +38,7 @@ pub async fn create_orphan_block( block_height: u64, transactions: Vec, consensus: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> Block { let mut coinbase_value = consensus.emission_schedule().block_reward(block_height); let lock_height = consensus.consensus_constants(block_height).coinbase_min_maturity(); diff --git a/base_layer/core/tests/helpers/sample_blockchains.rs b/base_layer/core/tests/helpers/sample_blockchains.rs index 19b4868080..a958186ec3 100644 --- a/base_layer/core/tests/helpers/sample_blockchains.rs +++ b/base_layer/core/tests/helpers/sample_blockchains.rs @@ -28,8 +28,8 @@ use tari_core::{ consensus::{ConsensusConstants, ConsensusConstantsBuilder, ConsensusManager, ConsensusManagerBuilder}, test_helpers::blockchain::{create_store_with_consensus, TempDatabase}, transactions::{ + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, tari_amount::{uT, T}, - test_helpers::{create_test_core_key_manager_with_memory_db, TestKeyManager}, transaction_components::WalletOutput, }, txn_schema, @@ -84,7 +84,7 @@ pub async fn create_blockchain_db_no_cut_through() -> ( Vec, Vec>, ConsensusManager, - TestKeyManager, + MemoryDbKeyManager, ) { let network = Network::LocalNet; let (mut db, mut blocks, mut outputs, consensus_manager, key_manager) = create_new_blockchain(network).await; @@ -179,9 +179,9 @@ pub async fn create_new_blockchain( Vec, Vec>, ConsensusManager, - TestKeyManager, + MemoryDbKeyManager, ) { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = ConsensusConstantsBuilder::new(network) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .with_coinbase_lockheight(1) @@ -211,9 +211,9 @@ pub async fn create_new_blockchain_with_constants( Vec, Vec>, ConsensusManager, - TestKeyManager, + MemoryDbKeyManager, ) { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (block0, output) = create_genesis_block(&constants, &key_manager).await; let consensus_manager = ConsensusManagerBuilder::new(network) .add_consensus_constants(constants) @@ -240,9 +240,9 @@ pub async fn create_new_blockchain_lmdb( Vec, Vec>, ConsensusManager, - TestKeyManager, + MemoryDbKeyManager, ) { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = ConsensusConstantsBuilder::new(network) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .with_coinbase_lockheight(1) diff --git a/base_layer/core/tests/helpers/sync.rs b/base_layer/core/tests/helpers/sync.rs index e3041b5bc9..c3af805031 100644 --- a/base_layer/core/tests/helpers/sync.rs +++ b/base_layer/core/tests/helpers/sync.rs @@ -40,7 +40,7 @@ use tari_core::{ mempool::MempoolServiceConfig, proof_of_work::{randomx_factory::RandomXFactory, Difficulty}, test_helpers::blockchain::TempDatabase, - transactions::test_helpers::{create_test_core_key_manager_with_memory_db, TestKeyManager}, + transactions::key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, validation::mocks::MockValidator, }; use tari_p2p::{services::liveness::LivenessConfig, P2pConfig}; @@ -97,11 +97,11 @@ pub async fn create_network_with_local_and_peer_nodes() -> ( NodeInterfaces, ChainBlock, ConsensusManager, - TestKeyManager, + MemoryDbKeyManager, ) { let network = Network::LocalNet; let temp_dir = tempdir().unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = ConsensusConstantsBuilder::new(network) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); @@ -254,7 +254,7 @@ pub async fn create_and_add_some_blocks( start_block: &ChainBlock, number_of_blocks: usize, consensus_manager: &ConsensusManager, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, difficulties: &[u64], ) -> Vec { if number_of_blocks != difficulties.len() { diff --git a/base_layer/core/tests/helpers/test_blockchain.rs b/base_layer/core/tests/helpers/test_blockchain.rs index 865578e610..fb9fbedcae 100644 --- a/base_layer/core/tests/helpers/test_blockchain.rs +++ b/base_layer/core/tests/helpers/test_blockchain.rs @@ -33,7 +33,7 @@ use tari_core::{ consensus::ConsensusManager, proof_of_work::Difficulty, test_helpers::blockchain::TempDatabase, - transactions::{test_helpers::TestKeyManager, transaction_components::WalletOutput}, + transactions::{key_manager::MemoryDbKeyManager, transaction_components::WalletOutput}, }; use crate::helpers::{ @@ -51,7 +51,7 @@ pub struct TestBlockchain { hash_to_block: HashMap, consensus_manager: ConsensusManager, outputs: Vec>, - pub key_manager: TestKeyManager, + pub key_manager: MemoryDbKeyManager, } #[allow(dead_code)] diff --git a/base_layer/core/tests/tests/base_node_rpc.rs b/base_layer/core/tests/tests/base_node_rpc.rs index 571c40e168..ec11eff7c3 100644 --- a/base_layer/core/tests/tests/base_node_rpc.rs +++ b/base_layer/core/tests/tests/base_node_rpc.rs @@ -48,8 +48,9 @@ use tari_core::{ }, test_helpers::blockchain::TempDatabase, transactions::{ + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, tari_amount::{uT, T}, - test_helpers::{create_test_core_key_manager_with_memory_db, schema_to_transaction, TestKeyManager}, + test_helpers::schema_to_transaction, transaction_components::{TransactionOutput, WalletOutput}, }, txn_schema, @@ -77,13 +78,13 @@ async fn setup() -> ( ChainBlock, WalletOutput, TempDir, - TestKeyManager, + MemoryDbKeyManager, ) { let network = NetworkConsensus::from(Network::LocalNet); let consensus_constants = ConsensusConstantsBuilder::new(Network::LocalNet) .with_coinbase_lockheight(1) .build(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let temp_dir = tempdir().unwrap(); let (block0, utxo0) = create_genesis_block_with_coinbase_value(100_000_000.into(), &consensus_constants, &key_manager).await; @@ -212,8 +213,11 @@ async fn test_base_node_wallet_rpc() { assert_eq!(resp.rejection_reason, TxSubmissionRejectionReason::AlreadyMined); // Now create a different tx that uses the same input as Tx1 to produce a DoubleSpend rejection - let (txs1b, _utxos1) = - schema_to_transaction(&[txn_schema!(from: vec![utxo0], to: vec![2 * T, 1 * T])], &key_manager).await; + let (txs1b, _utxos1) = schema_to_transaction( + &[txn_schema!(from: vec![utxo0.clone()], to: vec![2 * T, 1 * T])], + &key_manager, + ) + .await; let tx1b = (*txs1b[0]).clone(); // Now if we submit Tx1 is should return as rejected as AlreadyMined diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index 9e2facf69f..d7542cac2b 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -49,7 +49,6 @@ use tari_core::{ key_manager::TransactionKeyManagerInterface, tari_amount::{uT, MicroMinotari, T}, test_helpers::{ - create_test_core_key_manager_with_memory_db, create_wallet_output_with_data, schema_to_transaction, spend_utxos, @@ -98,7 +97,7 @@ async fn test_monero_blocks() { let seed1 = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad97"; let seed2 = "9f02e032f9b15d2aded991e0f68cc3c3427270b568b782e55fbd269ead0bad98"; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let network = Network::Esmeralda; let cc = ConsensusConstantsBuilder::new(network) .with_max_randomx_seed_height(1) @@ -312,7 +311,7 @@ async fn inputs_are_not_malleable() { #[allow(clippy::too_many_lines)] async fn test_orphan_validator() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let network = Network::Igor; let consensus_constants = ConsensusConstantsBuilder::new(network) .with_max_block_transaction_weight(325) @@ -460,7 +459,7 @@ async fn test_orphan_body_validation() { .clear_proof_of_work() .add_proof_of_work(PowAlgorithm::Sha3x, sha3x_constants) .build(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (genesis, outputs) = create_genesis_block_with_utxos(&[T, T, T], &consensus_constants, &key_manager).await; let network = Network::LocalNet; let rules = ConsensusManager::builder(network) @@ -665,7 +664,7 @@ OutputFeatures::default()), #[allow(clippy::too_many_lines)] async fn test_header_validation() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let network = Network::Igor; // we dont want localnet's 1 difficulty or the full mined difficulty of weather wax but we want some. let sha3x_constants = PowAlgorithmConstants { @@ -790,7 +789,7 @@ async fn test_block_sync_body_validator() { let consensus_constants = ConsensusConstantsBuilder::new(network) .with_max_block_transaction_weight(400) .build(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (genesis, outputs) = create_genesis_block_with_utxos(&[T, T, T], &consensus_constants, &key_manager).await; let network = Network::LocalNet; let rules = ConsensusManager::builder(network) @@ -1050,7 +1049,7 @@ async fn add_block_with_large_block() { let factories = CryptoFactories::default(); let network = Network::LocalNet; let consensus_constants = ConsensusConstantsBuilder::new(network).build(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (genesis, outputs) = create_genesis_block_with_utxos( &[ 5 * T, @@ -1095,7 +1094,7 @@ async fn add_block_with_large_block() { // lets make our big block (1 -> 5) * 12 let mut schemas = Vec::new(); for output in outputs.into_iter().skip(1) { - let new_schema = txn_schema!(from: vec![output], to: vec![1 * T, 1 * T, 1 * T, 1 * T]); + let new_schema = txn_schema!(from: vec![output.clone()], to: vec![1 * T, 1 * T, 1 * T, 1 * T]); schemas.push(new_schema); } @@ -1127,7 +1126,7 @@ async fn add_block_with_large_many_output_block() { let consensus_constants = ConsensusConstantsBuilder::new(network) .with_max_block_transaction_weight(127_795) .build(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (genesis, outputs) = create_genesis_block_with_utxos(&[501 * T], &consensus_constants, &key_manager).await; let network = Network::LocalNet; let rules = ConsensusManager::builder(network) @@ -1183,6 +1182,7 @@ async fn add_block_with_large_many_output_block() { use tari_core::{ blocks::{BlockHeader, NewBlockTemplate}, transactions::{ + key_manager::create_memory_db_key_manager, test_helpers::create_stx_protocol_internal, transaction_components::{Transaction, TransactionKernel}, }, diff --git a/base_layer/core/tests/tests/mempool.rs b/base_layer/core/tests/tests/mempool.rs index 04d9133e29..0bb1d7a6d3 100644 --- a/base_layer/core/tests/tests/mempool.rs +++ b/base_layer/core/tests/tests/mempool.rs @@ -34,10 +34,14 @@ use tari_core::{ proto, transactions::{ fee::Fee, - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface, TxoStage}, + key_manager::{ + create_memory_db_key_manager, + TransactionKeyManagerBranch, + TransactionKeyManagerInterface, + TxoStage, + }, tari_amount::{uT, MicroMinotari, T}, test_helpers::{ - create_test_core_key_manager_with_memory_db, create_wallet_output_with_data, schema_to_transaction, spend_utxos, @@ -1042,7 +1046,7 @@ async fn receive_and_propagate_transaction() { .with_coinbase_lockheight(100) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (block0, utxo) = create_genesis_block(&consensus_constants, &key_manager).await; let consensus_manager = ConsensusManager::builder(network) .add_consensus_constants(consensus_constants) @@ -1077,7 +1081,7 @@ async fn receive_and_propagate_transaction() { }); let (tx, _) = spend_utxos( - txn_schema!(from: vec![utxo], to: vec![2 * T, 2 * T, 2 * T]), + txn_schema!(from: vec![utxo.clone()], to: vec![2 * T, 2 * T, 2 * T]), &key_manager, ) .await; @@ -1705,7 +1709,7 @@ async fn block_event_and_reorg_event_handling() { // When block B2A is submitted, then both nodes have TX2A and TX3A in their reorg pools // When block B2B is submitted with TX2B, TX3B, then TX2A, TX3A are discarded (Not Stored) let network = Network::LocalNet; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = ConsensusConstantsBuilder::new(Network::LocalNet) .with_coinbase_lockheight(1) .build(); @@ -1735,8 +1739,11 @@ async fn block_event_and_reorg_event_handling() { // Bob creates Block 1 and sends it to Alice. Alice adds it to her chain and creates a block event that the Mempool // service will receive. - let (tx1, utxos1) = - schema_to_transaction(&[txn_schema!(from: vec![utxos0], to: vec![1 * T, 1 * T])], &key_manager).await; + let (tx1, utxos1) = schema_to_transaction( + &[txn_schema!(from: vec![utxos0.clone()], to: vec![1 * T, 1 * T])], + &key_manager, + ) + .await; let (txs_a, _utxos2) = schema_to_transaction( &[ txn_schema!(from: vec![utxos1[0].clone()], to: vec![400_000 * uT, 590_000 * uT]), diff --git a/base_layer/core/tests/tests/node_comms_interface.rs b/base_layer/core/tests/tests/node_comms_interface.rs index 854d3becde..f8edda06c9 100644 --- a/base_layer/core/tests/tests/node_comms_interface.rs +++ b/base_layer/core/tests/tests/node_comms_interface.rs @@ -39,15 +39,14 @@ use tari_core::{ create_consensus_rules, }, transactions::{ - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface}, - tari_amount::MicroMinotari, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_utxo, - TestKeyManager, - TestParams, - TransactionSchema, + key_manager::{ + create_memory_db_key_manager, + MemoryDbKeyManager, + TransactionKeyManagerBranch, + TransactionKeyManagerInterface, }, + tari_amount::MicroMinotari, + test_helpers::{create_utxo, TestParams, TransactionSchema}, transaction_components::{ OutputFeatures, TransactionOutput, @@ -203,7 +202,7 @@ async fn inbound_fetch_utxos() { let utxo_1 = block.body.outputs()[0].clone(); let hash_1 = utxo_1.hash(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (utxo_2, _, _) = create_utxo( MicroMinotari(10_000), &key_manager, @@ -265,9 +264,9 @@ async fn inbound_fetch_blocks() { } async fn initialize_sender_transaction_protocol_for_overflow_test( - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, txn_schema: TransactionSchema, -) -> SenderTransactionInitializer { +) -> SenderTransactionInitializer { let constants = ConsensusManager::builder(Network::LocalNet) .build() .unwrap() @@ -361,7 +360,7 @@ async fn initialize_sender_transaction_protocol_for_overflow_test( #[tokio::test] async fn test_sender_transaction_protocol_for_overflow() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let script = script!(Nop); let amount = MicroMinotari(u64::MAX); // This is the adversary's attack! let output_features = OutputFeatures::default(); @@ -419,7 +418,7 @@ async fn test_sender_transaction_protocol_for_overflow() { // Test overflow in total input value (inputs + outputs to self + fee) let txn_schema = // This is the adversary's attack! - txn_schema!(from: vec![wallet_output], to: vec![MicroMinotari(u64::MAX)]); + txn_schema!(from: vec![wallet_output.clone()], to: vec![MicroMinotari(u64::MAX)]); let stx_builder = initialize_sender_transaction_protocol_for_overflow_test(&key_manager, txn_schema).await; assert_eq!( format!("{:?}", stx_builder.build().await.unwrap_err()), @@ -432,7 +431,7 @@ async fn test_sender_transaction_protocol_for_overflow() { async fn inbound_fetch_blocks_before_horizon_height() { let consensus_manager = ConsensusManager::builder(Network::LocalNet).build().unwrap(); let block0 = consensus_manager.get_genesis_block(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let validators = Validators::new( MockValidator::new(true), MockValidator::new(true), diff --git a/base_layer/core/tests/tests/node_service.rs b/base_layer/core/tests/tests/node_service.rs index 4a710f0c72..9877d99b44 100644 --- a/base_layer/core/tests/tests/node_service.rs +++ b/base_layer/core/tests/tests/node_service.rs @@ -35,8 +35,9 @@ use tari_core::{ mempool::TxStorageResponse, proof_of_work::{randomx_factory::RandomXFactory, Difficulty, PowAlgorithm}, transactions::{ + key_manager::create_memory_db_key_manager, tari_amount::{uT, T}, - test_helpers::{create_test_core_key_manager_with_memory_db, schema_to_transaction, spend_utxos}, + test_helpers::{schema_to_transaction, spend_utxos}, transaction_components::OutputFeatures, CryptoFactories, }, @@ -71,7 +72,7 @@ use crate::{ #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn propagate_and_forward_many_valid_blocks() { let temp_dir = tempdir().unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); // Alice will propagate a number of block hashes to bob, bob will receive it, request the full block, verify and // then propagate the hash to carol and dan. Dan and Carol will also try to propagate the block hashes to each // other, but the block should not be re-requested. These duplicate blocks will be discarded and wont be @@ -220,7 +221,7 @@ async fn propagate_and_forward_invalid_block_hash() { let bob_node_identity = random_node_identity(); let carol_node_identity = random_node_identity(); let network = Network::LocalNet; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = ConsensusConstantsBuilder::new(network) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); @@ -270,7 +271,7 @@ async fn propagate_and_forward_invalid_block_hash() { // Add a transaction that Bob does not have to force a request let (txs, _) = schema_to_transaction( - &[txn_schema!(from: vec![genesis_coinbase], to: vec![5 * T], fee: 5.into())], + &[txn_schema!(from: vec![genesis_coinbase.clone()], to: vec![5 * T], fee: 5.into())], &key_manager, ) .await; @@ -343,7 +344,7 @@ async fn propagate_and_forward_invalid_block() { let bob_node_identity = random_node_identity(); let carol_node_identity = random_node_identity(); let dan_node_identity = random_node_identity(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let network = Network::LocalNet; let consensus_constants = ConsensusConstantsBuilder::new(network) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) @@ -482,7 +483,7 @@ async fn propagate_and_forward_invalid_block() { async fn local_get_metadata() { let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (mut node, consensus_manager) = BaseNodeBuilder::new(network.into()) .start(temp_dir.path().to_str().unwrap()) .await; @@ -506,7 +507,7 @@ async fn local_get_metadata() { async fn local_get_new_block_template_and_get_new_block() { let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = NetworkConsensus::from(network).create_consensus_constants(); let (block0, outputs) = create_genesis_block_with_utxos(&[T, T], &consensus_constants[0], &key_manager).await; let rules = ConsensusManager::builder(network) @@ -549,7 +550,7 @@ async fn local_get_new_block_with_zero_conf() { let factories = CryptoFactories::default(); let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = NetworkConsensus::from(network).create_consensus_constants(); let (block0, outputs) = create_genesis_block_with_utxos(&[T, T], &consensus_constants[0], &key_manager).await; let rules = ConsensusManagerBuilder::new(network) @@ -635,7 +636,7 @@ async fn local_get_new_block_with_combined_transaction() { let factories = CryptoFactories::default(); let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = NetworkConsensus::from(network).create_consensus_constants(); let (block0, outputs) = create_genesis_block_with_utxos(&[T, T], &consensus_constants[0], &key_manager).await; let rules = ConsensusManagerBuilder::new(network) @@ -715,7 +716,7 @@ async fn local_get_new_block_with_combined_transaction() { async fn local_submit_block() { let temp_dir = tempdir().unwrap(); let network = Network::LocalNet; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (mut node, consensus_manager) = BaseNodeBuilder::new(network.into()) .start(temp_dir.path().to_str().unwrap()) .await; diff --git a/base_layer/core/tests/tests/node_state_machine.rs b/base_layer/core/tests/tests/node_state_machine.rs index db52c208e7..55e68c79de 100644 --- a/base_layer/core/tests/tests/node_state_machine.rs +++ b/base_layer/core/tests/tests/node_state_machine.rs @@ -40,7 +40,7 @@ use tari_core::{ mempool::MempoolServiceConfig, proof_of_work::{randomx_factory::RandomXFactory, Difficulty}, test_helpers::blockchain::create_test_blockchain_db, - transactions::test_helpers::create_test_core_key_manager_with_memory_db, + transactions::key_manager::create_memory_db_key_manager, validation::mocks::MockValidator, }; use tari_p2p::{services::liveness::config::LivenessConfig, P2pConfig}; @@ -71,7 +71,7 @@ static EMISSION: [u64; 2] = [10, 10]; async fn test_listening_lagging() { let network = Network::LocalNet; let temp_dir = tempdir().unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = ConsensusConstantsBuilder::new(network) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); @@ -147,7 +147,7 @@ async fn test_listening_lagging() { async fn test_listening_initial_fallen_behind() { let network = Network::LocalNet; let temp_dir = tempdir().unwrap(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = ConsensusConstantsBuilder::new(network) .with_emission_amounts(100_000_000.into(), &EMISSION, 100.into()) .build(); diff --git a/base_layer/wallet/README.md b/base_layer/wallet/README.md index de4b41259b..29f38ad1ad 100644 --- a/base_layer/wallet/README.md +++ b/base_layer/wallet/README.md @@ -18,6 +18,7 @@ See README.md in wallet_ffi crate - Ensure that you installed diesel with the sqlite feature flag: - `cargo install diesel_cli --no-default-features --features sqlite` - If you updated the tables the following needs to be run from the `base_layer/wallet/` folder: + - manually delete `base_layer/wallet/test.sqlite3` if present - `diesel setup --database-url test.sqlite3` - `diesel migration run --database-url test.sqlite3` - After running this, make sure that the diesel update did not change BigInt to Integer in `schema.rs` (check for diff --git a/base_layer/wallet/migrations/2023-11-14-131400_coinbase/down.sql b/base_layer/wallet/migrations/2023-11-14-131400_coinbase/down.sql new file mode 100644 index 0000000000..291a97c5ce --- /dev/null +++ b/base_layer/wallet/migrations/2023-11-14-131400_coinbase/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/base_layer/wallet/migrations/2023-11-14-131400_coinbase/up.sql b/base_layer/wallet/migrations/2023-11-14-131400_coinbase/up.sql new file mode 100644 index 0000000000..10fb885562 --- /dev/null +++ b/base_layer/wallet/migrations/2023-11-14-131400_coinbase/up.sql @@ -0,0 +1,69 @@ +-- Any old 'outputs' will not be valid due to the removal of 'coinbase_block_height' and removal of default value for +-- 'spending_priority', so we drop and recreate the table. + +DROP TABLE outputs; +CREATE TABLE outputs +( + id INTEGER PRIMARY KEY NOT NULL, + commitment BLOB NOT NULL, + rangeproof BLOB NULL, + spending_key TEXT NOT NULL, + value BIGINT NOT NULL, + output_type INTEGER NOT NULL, + maturity BIGINT NOT NULL, + status INTEGER NOT NULL, + hash BLOB NOT NULL, + script BLOB NOT NULL, + input_data BLOB NOT NULL, + script_private_key TEXT NOT NULL, + script_lock_height UNSIGNED BIGINT NOT NULL DEFAULT 0, + sender_offset_public_key BLOB NOT NULL, + metadata_signature_ephemeral_commitment BLOB NOT NULL, + metadata_signature_ephemeral_pubkey BLOB NOT NULL, + metadata_signature_u_a BLOB NOT NULL, + metadata_signature_u_x BLOB NOT NULL, + metadata_signature_u_y BLOB NOT NULL, + mined_height UNSIGNED BIGINT NULL, + mined_in_block BLOB NULL, + marked_deleted_at_height BIGINT NULL, + marked_deleted_in_block BLOB NULL, + received_in_tx_id BIGINT NULL, + spent_in_tx_id BIGINT NULL, + coinbase_extra BLOB NULL, + features_json TEXT NOT NULL DEFAULT '{}', + spending_priority UNSIGNED INTEGER NOT NULL, + covenant BLOB NOT NULL, + mined_timestamp DATETIME NULL, + encrypted_data BLOB NOT NULL, + minimum_value_promise BIGINT NOT NULL, + source INTEGER NOT NULL DEFAULT 0, + last_validation_timestamp DATETIME NULL, + CONSTRAINT unique_commitment UNIQUE (commitment) +); + +-- Any old 'completed_transactions' will not be valid due to the removal of 'coinbase_block_height' and coinbase +-- transactions no longer being supported, so we drop and recreate the table. + +DROP TABLE completed_transactions; +CREATE TABLE completed_transactions +( + tx_id BIGINT PRIMARY KEY NOT NULL, + source_address BLOB NOT NULL, + destination_address BLOB NOT NULL, + amount BIGINT NOT NULL, + fee BIGINT NOT NULL, + transaction_protocol BLOB NOT NULL, + status INTEGER NOT NULL, + message TEXT NOT NULL, + timestamp DATETIME NOT NULL, + cancelled INTEGER NULL, + direction INTEGER NULL, + send_count INTEGER DEFAULT 0 NOT NULL, + last_send_timestamp DATETIME NULL, + confirmations BIGINT NULL, + mined_height BIGINT NULL, + mined_in_block BLOB NULL, + mined_timestamp DATETIME NULL, + transaction_signature_nonce BLOB DEFAULT 0 NOT NULL, + transaction_signature_key BLOB DEFAULT 0 NOT NULL +); diff --git a/base_layer/wallet/src/output_manager_service/error.rs b/base_layer/wallet/src/output_manager_service/error.rs index 0f0f31332b..f7f7c3ea64 100644 --- a/base_layer/wallet/src/output_manager_service/error.rs +++ b/base_layer/wallet/src/output_manager_service/error.rs @@ -28,7 +28,6 @@ use tari_comms_dht::outbound::DhtOutboundError; use tari_core::transactions::{ transaction_components::{EncryptedDataError, TransactionError}, transaction_protocol::TransactionProtocolError, - CoinbaseBuildError, }; use tari_crypto::errors::RangeProofError; use tari_key_manager::{ @@ -102,8 +101,6 @@ pub enum OutputManagerError { BaseNodeChanged, #[error("Invalid Sender Message Type")] InvalidSenderMessage, - #[error("Coinbase build error: `{0}`")] - CoinbaseBuildError(#[from] CoinbaseBuildError), #[error("TXO Validation protocol cancelled")] Cancellation, #[error("Base NodeService Error: `{0}`")] diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index 12cd02b847..4340d95d36 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -24,7 +24,7 @@ use std::{fmt, fmt::Formatter, sync::Arc}; use tari_common_types::{ transaction::TxId, - types::{Commitment, HashOutput, PublicKey}, + types::{Commitment, FixedHash, HashOutput, PublicKey}, }; use tari_core::{ covenants::Covenant, @@ -44,7 +44,7 @@ use tower::Service; use crate::output_manager_service::{ error::OutputManagerError, - service::{Balance, OutputStatusesByTxId}, + service::{Balance, OutputInfoByTxId}, storage::{ database::OutputBackendQuery, models::{DbWalletOutput, KnownOneSidedPaymentScript, SpendingPriority}, @@ -61,13 +61,6 @@ pub enum OutputManagerRequest { AddUnvalidatedOutput((TxId, Box, Option)), UpdateOutputMetadataSignature(Box), GetRecipientTransaction(TransactionSenderMessage), - GetCoinbaseTransaction { - tx_id: TxId, - reward: MicroMinotari, - fees: MicroMinotari, - block_height: u64, - extra: Vec, - }, ConfirmPendingTransaction(TxId), PrepareToSendTransaction { tx_id: TxId, @@ -126,10 +119,9 @@ pub enum OutputManagerRequest { }, ReinstateCancelledInboundTx(TxId), - SetCoinbaseAbandoned(TxId, bool), CreateClaimShaAtomicSwapTransaction(HashOutput, PublicKey, MicroMinotari), CreateHtlcRefundTransaction(HashOutput, MicroMinotari), - GetOutputStatusesByTxId(TxId), + GetOutputInfoByTxId(TxId), } impl fmt::Display for OutputManagerRequest { @@ -184,7 +176,6 @@ impl fmt::Display for OutputManagerRequest { "CreateCoinJoin: commitments={:#?}, fee_per_gram={}", commitments, fee_per_gram, ), - GetCoinbaseTransaction { .. } => write!(f, "GetCoinbaseTransaction"), FeeEstimate { amount, selection_criteria, @@ -204,7 +195,6 @@ impl fmt::Display for OutputManagerRequest { }, CreatePayToSelfWithOutputs { .. } => write!(f, "CreatePayToSelfWithOutputs"), ReinstateCancelledInboundTx(_) => write!(f, "ReinstateCancelledInboundTx"), - SetCoinbaseAbandoned(_, _) => write!(f, "SetCoinbaseAbandoned"), CreateClaimShaAtomicSwapTransaction(output, pre_image, fee_per_gram) => write!( f, "ClaimShaAtomicSwap(output hash: {}, pre_image: {}, fee_per_gram: {} )", @@ -219,7 +209,7 @@ impl fmt::Display for OutputManagerRequest { fee_per_gram, ), - GetOutputStatusesByTxId(t) => write!(f, "GetOutputStatusesByTxId: {}", t), + GetOutputInfoByTxId(t) => write!(f, "GetOutputInfoByTxId: {}", t), } } } @@ -232,7 +222,6 @@ pub enum OutputManagerResponse { ConvertedToTransactionOutput(Box), OutputMetadataSignatureUpdated, RecipientTransactionGenerated(ReceiverTransactionProtocol), - CoinbaseTransaction(Transaction), OutputConfirmed, PendingTransactionConfirmed, PayToSelfTransaction((MicroMinotari, Transaction)), @@ -254,9 +243,8 @@ pub enum OutputManagerResponse { CreateOutputWithFeatures { output: Box }, CreatePayToSelfWithOutputs { transaction: Box, tx_id: TxId }, ReinstatedCancelledInboundTx, - CoinbaseAbandonedSet, ClaimHtlcTransaction((TxId, MicroMinotari, MicroMinotari, Transaction)), - OutputStatusesByTxId(OutputStatusesByTxId), + OutputInfoByTxId(OutputInfoByTxId), CoinPreview((Vec, MicroMinotari)), } @@ -300,6 +288,7 @@ pub struct PublicRewindKeys { pub struct RecoveredOutput { pub tx_id: TxId, pub output: WalletOutput, + pub hash: FixedHash, } #[derive(Clone)] @@ -438,30 +427,6 @@ impl OutputManagerHandle { } } - pub async fn get_coinbase_transaction( - &mut self, - tx_id: TxId, - reward: MicroMinotari, - fees: MicroMinotari, - block_height: u64, - extra: Vec, - ) -> Result { - match self - .handle - .call(OutputManagerRequest::GetCoinbaseTransaction { - tx_id, - reward, - fees, - block_height, - extra, - }) - .await?? - { - OutputManagerResponse::CoinbaseTransaction(tx) => Ok(tx), - _ => Err(OutputManagerError::UnexpectedApiResponse), - } - } - pub async fn prepare_transaction_to_send( &mut self, tx_id: TxId, @@ -800,27 +765,13 @@ impl OutputManagerHandle { } } - pub async fn set_coinbase_abandoned(&mut self, tx_id: TxId, abandoned: bool) -> Result<(), OutputManagerError> { - match self - .handle - .call(OutputManagerRequest::SetCoinbaseAbandoned(tx_id, abandoned)) - .await?? - { - OutputManagerResponse::CoinbaseAbandonedSet => Ok(()), - _ => Err(OutputManagerError::UnexpectedApiResponse), - } - } - - pub async fn get_output_statuses_by_tx_id( - &mut self, - tx_id: TxId, - ) -> Result { + pub async fn get_output_info_for_tx_id(&mut self, tx_id: TxId) -> Result { match self .handle - .call(OutputManagerRequest::GetOutputStatusesByTxId(tx_id)) + .call(OutputManagerRequest::GetOutputInfoByTxId(tx_id)) .await?? { - OutputManagerResponse::OutputStatusesByTxId(output_statuses_by_tx_id) => Ok(output_statuses_by_tx_id), + OutputManagerResponse::OutputInfoByTxId(output_info_by_tx_id) => Ok(output_info_by_tx_id), _ => Err(OutputManagerError::UnexpectedApiResponse), } } diff --git a/base_layer/wallet/src/output_manager_service/input_selection.rs b/base_layer/wallet/src/output_manager_service/input_selection.rs index fdd0f2cc11..895c2d69e5 100644 --- a/base_layer/wallet/src/output_manager_service/input_selection.rs +++ b/base_layer/wallet/src/output_manager_service/input_selection.rs @@ -106,7 +106,7 @@ impl Display for UtxoSelectionOrdering { #[derive(Default, Debug, Clone)] pub enum UtxoSelectionFilter { - /// Select OutputType::Standard or OutputType::Coinbase outputs only + /// Select OutputType::Standard outputs only #[default] Standard, /// Selects specific outputs. All outputs must be exist and be spendable. diff --git a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs index 623a8ba948..949b255d06 100644 --- a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs +++ b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs @@ -23,11 +23,11 @@ use std::time::Instant; use log::*; -use tari_common_types::transaction::TxId; +use tari_common_types::{transaction::TxId, types::FixedHash}; use tari_core::transactions::{ key_manager::{TariKeyId, TransactionKeyManagerBranch, TransactionKeyManagerInterface}, tari_amount::MicroMinotari, - transaction_components::{TransactionError, TransactionOutput, WalletOutput}, + transaction_components::{OutputType, TransactionError, TransactionOutput, WalletOutput}, }; use tari_script::{inputs, script, ExecutionStack, Opcode, TariScript}; use tari_utilities::hex::Hex; @@ -69,7 +69,7 @@ where let known_scripts = self.db.get_all_known_one_sided_payment_scripts()?; - let mut rewound_outputs: Vec = Vec::new(); + let mut rewound_outputs: Vec<(WalletOutput, bool, FixedHash)> = Vec::new(); let push_pub_key_script = script!(PushPubKey(Box::default())); for output in outputs { let known_script_index = known_scripts.iter().position(|s| s.script == output.script); @@ -92,6 +92,7 @@ where None => continue, }; + let hash = output.hash(); let uo = WalletOutput::new_with_rangeproof( output.version, committed_value, @@ -109,7 +110,7 @@ where output.proof.clone(), ); - rewound_outputs.push(uo); + rewound_outputs.push((uo, known_script_index.is_some(), hash)); } let rewind_time = start.elapsed(); @@ -121,20 +122,12 @@ where ); let mut rewound_outputs_with_tx_id: Vec = Vec::new(); - for output in &mut rewound_outputs { - // Attempting to recognize output source by i.e., standard MimbleWimble, simple or stealth one-sided - let output_source = match *output.script.as_slice() { - [Opcode::Nop] => OutputSource::Standard, - [Opcode::PushPubKey(_), Opcode::Drop, Opcode::PushPubKey(_)] => OutputSource::StealthOneSided, - [Opcode::PushPubKey(_)] => OutputSource::OneSided, - _ => OutputSource::RecoveredButUnrecognized, - }; - + for (output, has_known_script, hash) in &mut rewound_outputs { let db_output = DbWalletOutput::from_wallet_output( output.clone(), &self.master_key_manager, None, - output_source, + Self::output_source(output, *has_known_script), None, None, ) @@ -153,6 +146,7 @@ where rewound_outputs_with_tx_id.push(RecoveredOutput { output: output.clone(), tx_id, + hash: *hash, }); self.update_outputs_script_private_key_and_update_key_manager_index(output) .await?; @@ -168,6 +162,28 @@ where Ok(rewound_outputs_with_tx_id) } + // Helper function to get the output source for a given output + fn output_source(output: &WalletOutput, has_known_script: bool) -> OutputSource { + match output.features.output_type { + OutputType::Standard => match *output.script.as_slice() { + [Opcode::Nop] => OutputSource::Standard, + [Opcode::PushPubKey(_), Opcode::Drop, Opcode::PushPubKey(_)] => OutputSource::StealthOneSided, + [Opcode::PushPubKey(_)] => { + if has_known_script { + OutputSource::OneSided + } else { + OutputSource::Standard + } + }, + _ => OutputSource::NonStandardScript, + }, + OutputType::Coinbase => OutputSource::Coinbase, + OutputType::Burn => OutputSource::Burn, + OutputType::ValidatorNodeRegistration => OutputSource::ValidatorNodeRegistration, + OutputType::CodeTemplateRegistration => OutputSource::CodeTemplateRegistration, + } + } + async fn find_script_key( &self, script: &TariScript, @@ -244,16 +260,7 @@ where .master_key_manager .get_public_key_at_key_id(&output.spending_key_id) .await?; - let script_key = if output.features.is_coinbase() { - let found_index = self - .master_key_manager - .find_key_index(TransactionKeyManagerBranch::Coinbase.get_branch_key(), &public_key) - .await?; - TariKeyId::Managed { - branch: TransactionKeyManagerBranch::CoinbaseScript.get_branch_key(), - index: found_index, - } - } else { + let script_key = { let found_index = self .master_key_manager .find_key_index( diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 497c901692..ed734105d9 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -53,7 +53,6 @@ use tari_core::{ WalletOutputBuilder, }, transaction_protocol::{sender::TransactionSenderMessage, TransactionMetadata}, - CoinbaseBuilder, CryptoFactories, ReceiverTransactionProtocol, SenderTransactionProtocol, @@ -237,16 +236,6 @@ where .get_default_recipient_transaction(tsm) .await .map(OutputManagerResponse::RecipientTransactionGenerated), - OutputManagerRequest::GetCoinbaseTransaction { - tx_id, - reward, - fees, - block_height, - extra, - } => self - .get_coinbase_transaction(tx_id, reward, fees, block_height, extra) - .await - .map(OutputManagerResponse::CoinbaseTransaction), OutputManagerRequest::PrepareToSendTransaction { tx_id, amount, @@ -411,9 +400,6 @@ where tx_id, }) }, - OutputManagerRequest::SetCoinbaseAbandoned(tx_id, abandoned) => self - .set_coinbase_abandoned(tx_id, abandoned) - .map(|_| OutputManagerResponse::CoinbaseAbandonedSet), OutputManagerRequest::CreateClaimShaAtomicSwapTransaction(output_hash, pre_image, fee_per_gram) => { self.claim_sha_atomic_swap_with_hash(output_hash, pre_image, fee_per_gram) .await @@ -422,14 +408,14 @@ where .create_htlc_refund_transaction(output, fee_per_gram) .await .map(OutputManagerResponse::ClaimHtlcTransaction), - OutputManagerRequest::GetOutputStatusesByTxId(tx_id) => { - let output_statuses_by_tx_id = self.get_output_status_by_tx_id(tx_id)?; - Ok(OutputManagerResponse::OutputStatusesByTxId(output_statuses_by_tx_id)) + OutputManagerRequest::GetOutputInfoByTxId(tx_id) => { + let output_statuses_by_tx_id = self.get_output_info_by_tx_id(tx_id)?; + Ok(OutputManagerResponse::OutputInfoByTxId(output_statuses_by_tx_id)) }, } } - fn get_output_status_by_tx_id(&self, tx_id: TxId) -> Result { + fn get_output_info_by_tx_id(&self, tx_id: TxId) -> Result { let outputs = self.resources.db.fetch_outputs_by_tx_id(tx_id)?; let statuses = outputs.clone().into_iter().map(|uo| uo.status).collect(); // We need the maximum mined height and corresponding block hash (faux transactions outputs can have different @@ -444,7 +430,7 @@ where } } } - Ok(OutputStatusesByTxId { + Ok(OutputInfoByTxId { statuses, mined_height: max_mined_height, block_hash, @@ -782,7 +768,7 @@ where self.resources .db - .add_output_to_be_received(single_round_sender_data.tx_id, output, None)?; + .add_output_to_be_received(single_round_sender_data.tx_id, output)?; let rtp = ReceiverTransactionProtocol::new( sender_message.clone(), @@ -1000,70 +986,6 @@ where Ok(stp) } - /// Request a Coinbase transaction for a specific block height. All existing pending transactions with - /// the corresponding output hash will be cancelled. - /// The key will be derived from the coinbase specific keychain using the blockheight as an index. The coinbase - /// keychain is based on the wallets master_key and the "coinbase" branch. - async fn get_coinbase_transaction( - &mut self, - tx_id: TxId, - reward: MicroMinotari, - fees: MicroMinotari, - block_height: u64, - extra: Vec, - ) -> Result { - debug!( - target: LOG_TARGET, - "Building coinbase transaction for block_height {} with TxId: {}", block_height, tx_id - ); - - let (coinbase_spending_key, _) = self - .resources - .key_manager - .get_next_key(TransactionKeyManagerBranch::Coinbase.get_branch_key()) - .await?; - let (coinbase_script_key, _) = self - .resources - .key_manager - .get_next_key(TransactionKeyManagerBranch::CoinbaseScript.get_branch_key()) - .await?; - - let (tx, wallet_output) = CoinbaseBuilder::new(self.resources.key_manager.clone()) - .with_block_height(block_height) - .with_fees(fees) - .with_spend_key_id(coinbase_spending_key) - .with_script_key_id(coinbase_script_key) - .with_script(script!(Nop)) - .with_extra(extra) - .build_with_reward(&self.resources.consensus_constants, reward) - .await?; - - let output = DbWalletOutput::from_wallet_output( - wallet_output, - &self.resources.key_manager, - None, - OutputSource::Coinbase, - Some(tx_id), - None, - ) - .await?; - - // If there is no existing output available, we store the one we produced. - match self.resources.db.fetch_by_commitment(output.commitment.clone()) { - Ok(_) => {}, - Err(OutputManagerStorageError::ValueNotFound) => { - self.resources - .db - .add_output_to_be_received(tx_id, output, Some(block_height))?; - - self.confirm_encumberance(tx_id)?; - }, - Err(e) => return Err(e.into()), - }; - - Ok(tx) - } - #[allow(clippy::too_many_lines)] async fn create_pay_to_self_containing_outputs( &mut self, @@ -1465,11 +1387,6 @@ where Ok(self.resources.db.get_invalid_outputs()?) } - pub fn set_coinbase_abandoned(&self, tx_id: TxId, abandoned: bool) -> Result<(), OutputManagerError> { - self.resources.db.set_coinbase_abandoned(tx_id, abandoned)?; - Ok(()) - } - fn default_features_and_scripts_size(&self) -> Result { Ok(self .resources @@ -2299,7 +2216,7 @@ where wallet_output, &self.resources.key_manager, None, - OutputSource::Refund, + OutputSource::HtlcRefund, Some(tx_id), None, ) @@ -2451,6 +2368,7 @@ where committed_value.into(), )? { let spending_key_id = self.resources.key_manager.import_key(spending_key).await?; + let hash = output.hash(); let rewound_output = WalletOutput::new_with_rangeproof( output.version, committed_value, @@ -2491,6 +2409,7 @@ where rewound_outputs.push(RecoveredOutput { output: rewound_output, tx_id, + hash, }) }, Err(OutputManagerStorageError::DuplicateOutput) => { @@ -2593,7 +2512,7 @@ impl UtxoSelection { } #[derive(Debug, Clone)] -pub struct OutputStatusesByTxId { +pub struct OutputInfoByTxId { pub statuses: Vec, pub(crate) mined_height: Option, pub(crate) block_hash: Option, diff --git a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs index c14deb2986..702e6f2caf 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs @@ -88,8 +88,6 @@ pub trait OutputManagerBackend: Send + Sync + Clone { fn get_last_mined_output(&self) -> Result, OutputManagerStorageError>; /// Get the output that was most recently spent, ordered descending by mined height fn get_last_spent_output(&self) -> Result, OutputManagerStorageError>; - /// Set if a coinbase output is abandoned or not - fn set_coinbase_abandoned(&self, tx_id: TxId, abandoned: bool) -> Result<(), OutputManagerStorageError>; /// Reinstate a cancelled inbound output fn reinstate_cancelled_inbound_output(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; /// Return the available, time locked, pending incoming and pending outgoing balance diff --git a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs index 0f7d78b478..20ec2edc8e 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs @@ -110,7 +110,7 @@ pub enum DbValue { pub enum DbKeyValuePair { UnspentOutput(Commitment, Box), UnspentOutputWithTxId(Commitment, (TxId, Box)), - OutputToBeReceived(Commitment, (TxId, Box, Option)), + OutputToBeReceived(Commitment, (TxId, Box)), KnownOneSidedPaymentScripts(KnownOneSidedPaymentScript), } @@ -162,31 +162,15 @@ where T: OutputManagerBackend + 'static Ok(()) } - pub fn add_output_to_be_received_remove_this( - &self, - tx_id: TxId, - output: DbWalletOutput, - coinbase_block_height: Option, - ) -> Result<(), OutputManagerStorageError> { - self.db - .write(WriteOperation::Insert(DbKeyValuePair::OutputToBeReceived( - output.commitment.clone(), - (tx_id, Box::new(output), coinbase_block_height), - )))?; - - Ok(()) - } - pub fn add_output_to_be_received( &self, tx_id: TxId, output: DbWalletOutput, - coinbase_block_height: Option, ) -> Result<(), OutputManagerStorageError> { self.db .write(WriteOperation::Insert(DbKeyValuePair::OutputToBeReceived( output.commitment.clone(), - (tx_id, Box::new(output), coinbase_block_height), + (tx_id, Box::new(output)), )))?; Ok(()) @@ -445,12 +429,6 @@ where T: OutputManagerBackend + 'static Ok(()) } - pub fn set_coinbase_abandoned(&self, tx_id: TxId, abandoned: bool) -> Result<(), OutputManagerStorageError> { - let db = self.db.clone(); - db.set_coinbase_abandoned(tx_id, abandoned)?; - Ok(()) - } - pub fn fetch_outputs_by_tx_id(&self, tx_id: TxId) -> Result, OutputManagerStorageError> { let outputs = self.db.fetch_outputs_by_tx_id(tx_id)?; Ok(outputs) diff --git a/base_layer/wallet/src/output_manager_service/storage/models.rs b/base_layer/wallet/src/output_manager_service/storage/models.rs index 2a77e54b82..522483572c 100644 --- a/base_layer/wallet/src/output_manager_service/storage/models.rs +++ b/base_layer/wallet/src/output_manager_service/storage/models.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cmp::Ordering; +use std::{cmp::Ordering, convert::TryFrom}; use chrono::NaiveDateTime; use derivative::Derivative; @@ -114,27 +114,28 @@ impl Eq for DbWalletOutput {} // --------------------------------------------------------------------------- -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum SpendingPriority { Normal, HtlcSpendAsap, - Unknown, } -impl From for SpendingPriority { - fn from(value: u32) -> Self { +impl TryFrom for SpendingPriority { + type Error = String; + + fn try_from(value: u32) -> Result { match value { - 0 => SpendingPriority::Normal, - 1 => SpendingPriority::HtlcSpendAsap, - _ => SpendingPriority::Unknown, + 0 => Ok(SpendingPriority::Normal), + 1 => Ok(SpendingPriority::HtlcSpendAsap), + _ => Err(format!("Invalid spending priority value: {}", value)), } } } -impl From for u32 { +impl From for i32 { fn from(value: SpendingPriority) -> Self { match value { - SpendingPriority::Normal | SpendingPriority::Unknown => 0, + SpendingPriority::Normal => 0, SpendingPriority::HtlcSpendAsap => 1, } } diff --git a/base_layer/wallet/src/output_manager_service/storage/output_source.rs b/base_layer/wallet/src/output_manager_service/storage/output_source.rs index 51a85d03aa..d64b7f8d26 100644 --- a/base_layer/wallet/src/output_manager_service/storage/output_source.rs +++ b/base_layer/wallet/src/output_manager_service/storage/output_source.rs @@ -34,15 +34,17 @@ use crate::output_manager_service::error::OutputManagerStorageError; // The source of where the output came from #[derive(Copy, Clone, Debug, PartialEq, Display, Default)] pub enum OutputSource { - Unknown, - Coinbase, - RecoveredButUnrecognized, #[default] Standard, + Coinbase, + NonStandardScript, OneSided, StealthOneSided, - Refund, + HtlcRefund, AtomicSwap, + Burn, + ValidatorNodeRegistration, + CodeTemplateRegistration, } impl TryFrom for OutputSource { @@ -50,14 +52,16 @@ impl TryFrom for OutputSource { fn try_from(value: i32) -> Result { Ok(match value { - 0 => OutputSource::Unknown, + 0 => OutputSource::Standard, 1 => OutputSource::Coinbase, - 2 => OutputSource::RecoveredButUnrecognized, - 3 => OutputSource::Standard, - 4 => OutputSource::OneSided, - 5 => OutputSource::StealthOneSided, - 6 => OutputSource::Refund, - 7 => OutputSource::AtomicSwap, + 2 => OutputSource::NonStandardScript, + 3 => OutputSource::OneSided, + 4 => OutputSource::StealthOneSided, + 5 => OutputSource::HtlcRefund, + 6 => OutputSource::AtomicSwap, + 7 => OutputSource::Burn, + 8 => OutputSource::ValidatorNodeRegistration, + 9 => OutputSource::CodeTemplateRegistration, _ => { return Err(OutputManagerStorageError::ConversionError { reason: "Was expecting value between 0 and 7 for OutputSource".to_string(), diff --git a/base_layer/wallet/src/output_manager_service/storage/output_status.rs b/base_layer/wallet/src/output_manager_service/storage/output_status.rs index 774e11106a..495cc72e37 100644 --- a/base_layer/wallet/src/output_manager_service/storage/output_status.rs +++ b/base_layer/wallet/src/output_manager_service/storage/output_status.rs @@ -44,7 +44,6 @@ pub enum OutputStatus { ShortTermEncumberedToBeReceived, ShortTermEncumberedToBeSpent, SpentMinedUnconfirmed, - AbandonedCoinbase, NotStored, } @@ -63,8 +62,7 @@ impl TryFrom for OutputStatus { 7 => Ok(OutputStatus::ShortTermEncumberedToBeReceived), 8 => Ok(OutputStatus::ShortTermEncumberedToBeSpent), 9 => Ok(OutputStatus::SpentMinedUnconfirmed), - 10 => Ok(OutputStatus::AbandonedCoinbase), - 11 => Ok(OutputStatus::NotStored), + 10 => Ok(OutputStatus::NotStored), _ => Err(OutputManagerStorageError::ConversionError { reason: "Was expecting value between 0 and 11 for OutputStatus".to_string(), }), diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index a393588d71..aba1729e6c 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -85,26 +85,21 @@ impl OutputManagerSqliteDatabase { if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, conn).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } - let new_output = NewOutputSql::new(*o, OutputStatus::Unspent, None, None)?; + let new_output = NewOutputSql::new(*o, OutputStatus::Unspent, None)?; new_output.commit(conn)? }, DbKeyValuePair::UnspentOutputWithTxId(c, (tx_id, o)) => { if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, conn).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } - let new_output = NewOutputSql::new(*o, OutputStatus::Unspent, Some(tx_id), None)?; + let new_output = NewOutputSql::new(*o, OutputStatus::Unspent, Some(tx_id))?; new_output.commit(conn)? }, - DbKeyValuePair::OutputToBeReceived(c, (tx_id, o, coinbase_block_height)) => { + DbKeyValuePair::OutputToBeReceived(c, (tx_id, o)) => { if OutputSql::find_by_commitment_and_cancelled(&c.to_vec(), false, conn).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } - let new_output = NewOutputSql::new( - *o, - OutputStatus::EncumberedToBeReceived, - Some(tx_id), - coinbase_block_height, - )?; + let new_output = NewOutputSql::new(*o, OutputStatus::EncumberedToBeReceived, Some(tx_id))?; new_output.commit(conn)? }, @@ -663,12 +658,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { })?; for co in outputs_to_receive { - let new_output = NewOutputSql::new( - co.clone(), - OutputStatus::ShortTermEncumberedToBeReceived, - Some(tx_id), - None, - )?; + let new_output = NewOutputSql::new(co.clone(), OutputStatus::ShortTermEncumberedToBeReceived, Some(tx_id))?; new_output.commit(&mut conn)?; } if start.elapsed().as_millis() > 0 { @@ -955,47 +945,6 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { Ok(()) } - fn set_coinbase_abandoned(&self, tx_id: TxId, abandoned: bool) -> Result<(), OutputManagerStorageError> { - let start = Instant::now(); - let mut conn = self.database_connection.get_pooled_connection()?; - let acquire_lock = start.elapsed(); - - if abandoned { - debug!( - target: LOG_TARGET, - "set_coinbase_abandoned(TxID: {}) as {}", tx_id, abandoned - ); - diesel::update( - outputs::table.filter( - outputs::received_in_tx_id - .eq(Some(tx_id.as_u64() as i64)) - .and(outputs::coinbase_block_height.is_not_null()), - ), - ) - .set((outputs::status.eq(OutputStatus::AbandonedCoinbase as i32),)) - .execute(&mut conn) - .num_rows_affected_or_not_found(1)?; - } else { - update_outputs_with_tx_id_and_status_to_new_status( - &mut conn, - tx_id, - OutputStatus::AbandonedCoinbase, - OutputStatus::EncumberedToBeReceived, - )?; - }; - if start.elapsed().as_millis() > 0 { - trace!( - target: LOG_TARGET, - "sqlite profile - set_coinbase_abandoned: lock {} + db_op {} = {} ms", - acquire_lock.as_millis(), - (start.elapsed() - acquire_lock).as_millis(), - start.elapsed().as_millis() - ); - } - - Ok(()) - } - fn reinstate_cancelled_inbound_output(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError> { let start = Instant::now(); let mut conn = self.database_connection.get_pooled_connection()?; @@ -1028,7 +977,7 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { if OutputSql::find_by_commitment_and_cancelled(&output.commitment.to_vec(), false, &mut conn).is_ok() { return Err(OutputManagerStorageError::DuplicateOutput); } - let new_output = NewOutputSql::new(output, OutputStatus::EncumberedToBeReceived, Some(tx_id), None)?; + let new_output = NewOutputSql::new(output, OutputStatus::EncumberedToBeReceived, Some(tx_id))?; new_output.commit(&mut conn)?; if start.elapsed().as_millis() > 0 { @@ -1307,13 +1256,9 @@ mod test { use diesel_migrations::{EmbeddedMigrations, MigrationHarness}; use rand::{rngs::OsRng, RngCore}; use tari_core::transactions::{ + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, tari_amount::MicroMinotari, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_wallet_output_with_data, - TestKeyManager, - TestParams, - }, + test_helpers::{create_wallet_output_with_data, TestParams}, transaction_components::{OutputFeatures, TransactionInput, WalletOutput}, }; use tari_script::script; @@ -1326,7 +1271,7 @@ mod test { OutputSource, }; - pub async fn make_input(val: MicroMinotari, key_manager: &TestKeyManager) -> (TransactionInput, WalletOutput) { + pub async fn make_input(val: MicroMinotari, key_manager: &MemoryDbKeyManager) -> (TransactionInput, WalletOutput) { let test_params = TestParams::new(key_manager).await; let wallet_output = @@ -1371,13 +1316,13 @@ mod test { let mut outputs_spent = Vec::new(); let mut outputs_unspent = Vec::new(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); for _i in 0..2 { let (_, uo) = make_input(MicroMinotari::from(100 + OsRng.next_u64() % 1000), &key_manager).await; - let uo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + let uo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); - let o = NewOutputSql::new(uo, OutputStatus::Unspent, None, None).unwrap(); + let o = NewOutputSql::new(uo, OutputStatus::Unspent, None).unwrap(); outputs.push(o.clone()); outputs_unspent.push(o.clone()); o.commit(&mut conn).unwrap(); @@ -1385,10 +1330,10 @@ mod test { for _i in 0..3 { let (_, uo) = make_input(MicroMinotari::from(100 + OsRng.next_u64() % 1000), &key_manager).await; - let uo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + let uo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); - let o = NewOutputSql::new(uo, OutputStatus::Spent, None, None).unwrap(); + let o = NewOutputSql::new(uo, OutputStatus::Spent, None).unwrap(); outputs.push(o.clone()); outputs_spent.push(o.clone()); o.commit(&mut conn).unwrap(); diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index 7eb606bc3b..3719ddc81d 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -59,12 +59,12 @@ pub struct NewOutputSql { pub metadata_signature_u_x: Vec, pub metadata_signature_u_y: Vec, pub received_in_tx_id: Option, - pub coinbase_block_height: Option, pub features_json: String, pub covenant: Vec, pub encrypted_data: Vec, pub minimum_value_promise: i64, pub source: i32, + pub spending_priority: i32, } impl NewOutputSql { @@ -73,7 +73,6 @@ impl NewOutputSql { output: DbWalletOutput, status: OutputStatus, received_in_tx_id: Option, - coinbase_block_height: Option, ) -> Result { let mut covenant = Vec::new(); BorshSerialize::serialize(&output.wallet_output.covenant, &mut covenant)?; @@ -102,7 +101,6 @@ impl NewOutputSql { metadata_signature_u_a: output.wallet_output.metadata_signature.u_a().to_vec(), metadata_signature_u_x: output.wallet_output.metadata_signature.u_x().to_vec(), metadata_signature_u_y: output.wallet_output.metadata_signature.u_y().to_vec(), - coinbase_block_height: coinbase_block_height.map(|bh| bh as i64), features_json: serde_json::to_string(&output.wallet_output.features).map_err(|s| { OutputManagerStorageError::ConversionError { reason: format!("Could not parse features from JSON:{}", s), @@ -112,6 +110,7 @@ impl NewOutputSql { encrypted_data: output.wallet_output.encrypted_data.to_byte_vec(), minimum_value_promise: output.wallet_output.minimum_value_promise.as_u64() as i64, source: output.source as i32, + spending_priority: output.spending_priority.into(), }; Ok(output) diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index 9a932c365e..8d722bb046 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -50,7 +50,7 @@ use crate::{ service::Balance, storage::{ database::{OutputBackendQuery, SortDirection}, - models::DbWalletOutput, + models::{DbWalletOutput, SpendingPriority}, sqlite_db::{UpdateOutput, UpdateOutputSql}, OutputSource, OutputStatus, @@ -91,7 +91,6 @@ pub struct OutputSql { pub marked_deleted_in_block: Option>, pub received_in_tx_id: Option, pub spent_in_tx_id: Option, - pub coinbase_block_height: Option, pub coinbase_extra: Option>, pub features_json: String, pub spending_priority: i32, @@ -264,12 +263,6 @@ impl OutputSql { }, }; - // debug!( - // target: LOG_TARGET, - // "Executing UTXO select query: {}", - // diesel::debug_query(&query) - // ); - Ok(query.load(conn)?) } @@ -603,17 +596,6 @@ impl OutputSql { .first::(conn)?) } - /// Find a particular Output, if it exists and is in the specified Spent state - pub fn find_pending_coinbase_at_block_height( - block_height: u64, - conn: &mut SqliteConnection, - ) -> Result { - Ok(outputs::table - .filter(outputs::status.ne(OutputStatus::Unspent as i32)) - .filter(outputs::coinbase_block_height.eq(block_height as i64)) - .first::(conn)?) - } - pub fn delete(&self, conn: &mut SqliteConnection) -> Result<(), OutputManagerStorageError> { let num_deleted = diesel::delete(outputs::table.filter(outputs::spending_key.eq(&self.spending_key))).execute(conn)?; @@ -756,7 +738,11 @@ impl OutputSql { }); }, }; - let spending_priority = (self.spending_priority as u32).into(); + let spending_priority = SpendingPriority::try_from(self.spending_priority as u32).map_err(|e| { + OutputManagerStorageError::ConversionError { + reason: format!("Could not convert spending priority from i32: {}", e), + } + })?; let mined_in_block = match self.mined_in_block { Some(v) => match v.try_into() { Ok(v) => Some(v), diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index 5d734c2795..fb49078d48 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -29,7 +29,6 @@ diesel::table! { timestamp -> Timestamp, cancelled -> Nullable, direction -> Nullable, - coinbase_block_height -> Nullable, send_count -> Integer, last_send_timestamp -> Nullable, confirmations -> Nullable, @@ -109,7 +108,6 @@ diesel::table! { marked_deleted_in_block -> Nullable, received_in_tx_id -> Nullable, spent_in_tx_id -> Nullable, - coinbase_block_height -> Nullable, coinbase_extra -> Nullable, features_json -> Text, spending_priority -> Integer, diff --git a/base_layer/wallet/src/transaction_service/error.rs b/base_layer/wallet/src/transaction_service/error.rs index c7f4bf10c0..1743fa8a44 100644 --- a/base_layer/wallet/src/transaction_service/error.rs +++ b/base_layer/wallet/src/transaction_service/error.rs @@ -96,8 +96,6 @@ pub enum TransactionServiceError { DiscoveryProcessFailed(TxId), #[error("Invalid Completed Transaction provided")] InvalidCompletedTransaction, - #[error("Attempted to broadcast a coinbase transaction. TxId `{0}`")] - AttemptedToBroadcastCoinbaseTransaction(TxId), #[error("No Base Node public keys are provided for Base chain broadcast and monitoring")] NoBaseNodeKeysProvided, #[error("Base node changed during {task_name}")] @@ -112,8 +110,6 @@ pub enum TransactionServiceError { UnexpectedBaseNodeResponse, #[error("The current transaction has been cancelled")] TransactionCancelled, - #[error("Chain tip has moved beyond this coinbase before it was mined so it must be cancelled")] - ChainTipHigherThanCoinbaseHeight, #[error("DHT outbound error: `{0}`")] DhtOutboundError(#[from] DhtOutboundError), #[error("Output manager error: `{0}`")] @@ -271,10 +267,10 @@ pub enum TransactionStorageError { ByteArrayError(String), #[error("Tari address error: `{0}`")] TariAddressError(#[from] TariAddressError), - #[error("Not a coinbase transaction so cannot be abandoned")] - NotCoinbase, #[error("Db error: `{0}`")] SqliteStorageError(#[from] SqliteStorageError), + #[error("Coinbase transactions are not supported in the wallet")] + CoinbaseNotSupported, } impl From for TransactionStorageError { diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index fa90791478..5c85d60db0 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -139,21 +139,15 @@ pub enum TransactionServiceRequest { amount: MicroMinotari, source_address: TariAddress, message: String, - maturity: Option, import_status: ImportStatus, tx_id: Option, current_height: Option, mined_timestamp: Option, + scanned_output: TransactionOutput, }, SubmitTransactionToSelf(TxId, Transaction, MicroMinotari, MicroMinotari, String), SetLowPowerMode, SetNormalPowerMode, - GenerateCoinbaseTransaction { - reward: MicroMinotari, - fees: MicroMinotari, - block_height: u64, - extra: Vec, - }, RestartTransactionProtocols, RestartBroadcastProtocols, GetNumConfirmationsRequired, @@ -216,29 +210,19 @@ impl fmt::Display for TransactionServiceRequest { amount, source_address, message, - maturity, import_status, tx_id, current_height, mined_timestamp, + .. } => write!( f, - "ImportUtxo (from {}, {}, {} with maturity {} and {:?} and {:?} and {:?} and {:?})", - source_address, - amount, - message, - maturity.unwrap_or(0), - import_status, - tx_id, - current_height, - mined_timestamp + "ImportUtxo (from {}, {}, {} and {:?} and {:?} and {:?} and {:?}", + source_address, amount, message, import_status, tx_id, current_height, mined_timestamp ), Self::SubmitTransactionToSelf(tx_id, _, _, _, _) => write!(f, "SubmitTransaction ({})", tx_id), Self::SetLowPowerMode => write!(f, "SetLowPowerMode "), Self::SetNormalPowerMode => write!(f, "SetNormalPowerMode"), - Self::GenerateCoinbaseTransaction { block_height, .. } => { - write!(f, "GenerateCoinbaseTransaction (Blockheight {})", block_height) - }, Self::RestartTransactionProtocols => write!(f, "RestartTransactionProtocols"), Self::RestartBroadcastProtocols => write!(f, "RestartBroadcastProtocols"), Self::GetNumConfirmationsRequired => write!(f, "GetNumConfirmationsRequired"), @@ -278,7 +262,6 @@ pub enum TransactionServiceResponse { TransactionSubmitted, LowPowerModeSet, NormalPowerModeSet, - CoinbaseTransactionGenerated(Box), ProtocolsRestarted, AnyTransaction(Box>), NumConfirmationsRequired(u64), @@ -752,11 +735,11 @@ impl TransactionServiceHandle { amount: MicroMinotari, source_address: TariAddress, message: String, - maturity: Option, import_status: ImportStatus, tx_id: Option, current_height: Option, mined_timestamp: Option, + scanned_output: TransactionOutput, ) -> Result { match self .handle @@ -764,11 +747,11 @@ impl TransactionServiceHandle { amount, source_address, message, - maturity, import_status, tx_id, current_height, mined_timestamp, + scanned_output, }) .await?? { @@ -848,28 +831,6 @@ impl TransactionServiceHandle { } } - pub async fn generate_coinbase_transaction( - &mut self, - reward: MicroMinotari, - fees: MicroMinotari, - block_height: u64, - extra: Vec, - ) -> Result { - match self - .handle - .call(TransactionServiceRequest::GenerateCoinbaseTransaction { - reward, - fees, - block_height, - extra, - }) - .await?? - { - TransactionServiceResponse::CoinbaseTransactionGenerated(tx) => Ok(*tx), - _ => Err(TransactionServiceError::UnexpectedApiResponse), - } - } - pub async fn restart_transaction_protocols(&mut self) -> Result<(), TransactionServiceError> { match self .handle diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs index b7942e337e..38d948a5f7 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs @@ -435,8 +435,8 @@ where TransactionDirection::Inbound, None, None, - None, - ); + ) + .map_err(|e| TransactionServiceProtocolError::new(self.id, TransactionServiceError::from(e)))?; self.resources .db diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs index 24f5b332ba..30eed38c44 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_send_protocol.rs @@ -578,8 +578,8 @@ where TransactionDirection::Outbound, None, None, - None, - ); + ) + .map_err(|e| TransactionServiceProtocolError::new(self.id, TransactionServiceError::from(e)))?; self.resources .db diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs index 3b8f7834b9..f910bd114a 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_validation_protocol.rs @@ -44,14 +44,12 @@ use tari_utilities::hex::Hex; use crate::{ connectivity_service::WalletConnectivityInterface, - output_manager_service::handle::OutputManagerHandle, transaction_service::{ config::TransactionServiceConfig, error::{TransactionServiceError, TransactionServiceProtocolError, TransactionServiceProtocolErrorExt}, handle::{TransactionEvent, TransactionEventSender}, storage::{ database::{TransactionBackend, TransactionDatabase}, - models::TxCancellationReason, sqlite_db::UnconfirmedTransactionInfo, }, }, @@ -66,7 +64,6 @@ pub struct TransactionValidationProtocol Self { Self { operation_id, @@ -89,7 +85,6 @@ where connectivity, config, event_publisher, - output_manager_handle, } } @@ -147,43 +142,13 @@ where } if let Some((tip_height, tip_block, tip_mined_timestamp)) = tip_info { for unmined_tx in &unmined { - // Treat coinbases separately - if unmined_tx.is_coinbase() { - if unmined_tx.coinbase_block_height.unwrap_or_default() <= tip_height { - debug!( - target: LOG_TARGET, - "Updated coinbase {} as abandoned (Operation ID: {})", - unmined_tx.tx_id, - self.operation_id - ); - self.update_coinbase_as_abandoned( - unmined_tx.tx_id, - &tip_block, - tip_height, - tip_mined_timestamp, - tip_height.saturating_sub(unmined_tx.coinbase_block_height.unwrap_or_default()), - ) - .await?; - state_changed = true; - } else { - debug!( - target: LOG_TARGET, - "Coinbase not found, but it is for a block that is not yet in the chain. Coinbase \ - height: {}, tip height:{} (Operation ID: {})", - unmined_tx.coinbase_block_height.unwrap_or_default(), - tip_height, - self.operation_id - ); - } - } else { - debug!( - target: LOG_TARGET, - "Updated transaction {} as unmined (Operation ID: {})", unmined_tx.tx_id, self.operation_id - ); - self.update_transaction_as_unmined(unmined_tx.tx_id, &unmined_tx.status) - .await?; - self.publish_event(TransactionEvent::NewBlockMined(unmined_tx.tx_id)); - } + debug!( + target: LOG_TARGET, + "Updated transaction {} as unmined (Operation ID: {})", unmined_tx.tx_id, self.operation_id + ); + self.update_transaction_as_unmined(unmined_tx.tx_id, &unmined_tx.status) + .await?; + self.publish_event(TransactionEvent::NewBlockMined(unmined_tx.tx_id)); } } } @@ -430,67 +395,6 @@ where }) } - if *status == TransactionStatus::Coinbase { - if let Err(e) = self.output_manager_handle.set_coinbase_abandoned(tx_id, false).await { - warn!( - target: LOG_TARGET, - "Could not mark coinbase output for TxId: {} as not abandoned: {} (Operation ID: {})", - tx_id, - e, - self.operation_id - ); - }; - } - - Ok(()) - } - - #[allow(clippy::ptr_arg)] - async fn update_coinbase_as_abandoned( - &mut self, - tx_id: TxId, - mined_in_block: &BlockHash, - mined_height: u64, - mined_timestamp: u64, - num_confirmations: u64, - ) -> Result<(), TransactionServiceProtocolError> { - // This updates the OMS first before we update the TMS. If we update the TMS first and operation fail inside of - // the OMS, we have two databases that are out of sync, as the TMS would have been updated and OMS will be stuck - // forever as pending_incoming. - self.output_manager_handle - .set_coinbase_abandoned(tx_id, true) - .await - .map_err(|e| { - warn!( - target: LOG_TARGET, - "Could not mark coinbase output for TxId: {} as abandoned: {} (Operation ID: {})", - tx_id, - e, - self.operation_id - ); - e - }) - .for_protocol(self.operation_id)?; - self.db - .set_transaction_mined_height( - tx_id, - mined_height, - *mined_in_block, - mined_timestamp, - num_confirmations, - num_confirmations >= self.config.num_confirmations_required, - false, - ) - .for_protocol(self.operation_id)?; - - self.db - .abandon_coinbase_transaction(tx_id) - .for_protocol(self.operation_id)?; - - self.publish_event(TransactionEvent::TransactionCancelled( - tx_id, - TxCancellationReason::AbandonedCoinbase, - )); Ok(()) } @@ -499,18 +403,6 @@ where tx_id: TxId, status: &TransactionStatus, ) -> Result<(), TransactionServiceProtocolError> { - if *status == TransactionStatus::Coinbase { - if let Err(e) = self.output_manager_handle.set_coinbase_abandoned(tx_id, false).await { - warn!( - target: LOG_TARGET, - "Could not mark coinbase output for TxId: {} as not abandoned: {} (Operation ID: {})", - tx_id, - e, - self.operation_id - ); - }; - } - self.db .set_transaction_as_unmined(tx_id) .for_protocol(self.operation_id)?; diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 45d13d145a..29211cf55c 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -780,37 +780,27 @@ where amount, source_address, message, - maturity, import_status, tx_id, current_height, mined_timestamp, + scanned_output, } => self .add_utxo_import_transaction_with_status( amount, source_address, message, - maturity, import_status, tx_id, current_height, mined_timestamp, - transaction_validation_join_handles, + scanned_output, ) .await .map(TransactionServiceResponse::UtxoImported), TransactionServiceRequest::SubmitTransactionToSelf(tx_id, tx, fee, amount, message) => self .submit_transaction_to_self(transaction_broadcast_join_handles, tx_id, tx, fee, amount, message) .map(|_| TransactionServiceResponse::TransactionSubmitted), - TransactionServiceRequest::GenerateCoinbaseTransaction { - reward, - fees, - block_height, - extra, - } => self - .generate_coinbase_transaction(reward, fees, block_height, extra) - .await - .map(|tx| TransactionServiceResponse::CoinbaseTransactionGenerated(Box::new(tx))), TransactionServiceRequest::SetLowPowerMode => { self.set_power_mode(PowerMode::Low).await?; Ok(TransactionServiceResponse::LowPowerModeSet) @@ -1015,8 +1005,7 @@ where TransactionDirection::Inbound, None, None, - None, - ), + )?, )?; let _result = reply_channel @@ -1265,8 +1254,7 @@ where TransactionDirection::Outbound, None, None, - None, - ), + )?, )?; let tx_output = output @@ -1454,8 +1442,7 @@ where TransactionDirection::Outbound, None, None, - None, - ), + )?, )?; Ok(tx_id) @@ -1716,8 +1703,7 @@ where TransactionDirection::Outbound, None, None, - None, - ), + )?, )?; info!(target: LOG_TARGET, "Submitted burning transaction - TxId: {}", tx_id); @@ -2536,7 +2522,7 @@ where .map_err(|resp| { error!( target: LOG_TARGET, - "Error restarting protocols for all coinbase transactions: {:?}", resp + "Error restarting protocols for all pending inbound transactions: {:?}", resp ); resp })?; @@ -2575,7 +2561,6 @@ where self.resources.connectivity.clone(), self.resources.config.clone(), self.event_publisher.clone(), - self.resources.output_manager_service.clone(), ); let mut base_node_watch = self.connectivity().get_current_base_node_watcher(); @@ -2692,11 +2677,6 @@ where { return Err(TransactionServiceError::InvalidCompletedTransaction); } - if completed_tx.is_coinbase() { - return Err(TransactionServiceError::AttemptedToBroadcastCoinbaseTransaction( - completed_tx.tx_id, - )); - } if !self.resources.connectivity.is_base_node_set() { return Err(TransactionServiceError::NoBaseNodeKeysProvided); @@ -2819,14 +2799,11 @@ where value: MicroMinotari, source_address: TariAddress, message: String, - maturity: Option, import_status: ImportStatus, tx_id: Option, current_height: Option, mined_timestamp: Option, - transaction_validation_join_handles: &mut FuturesUnordered< - JoinHandle>>, - >, + scanned_output: TransactionOutput, ) -> Result { let tx_id = if let Some(id) = tx_id { id } else { TxId::new_random() }; self.db.add_utxo_import_transaction_with_status( @@ -2835,10 +2812,10 @@ where source_address, self.resources.wallet_identity.address.clone(), message, - maturity, import_status.clone(), current_height, mined_timestamp, + scanned_output, )?; let transaction_event = match import_status { ImportStatus::Imported => TransactionEvent::TransactionImported(tx_id), @@ -2847,9 +2824,7 @@ where num_confirmations: 0, is_valid: true, }, - ImportStatus::FauxConfirmed | ImportStatus::Coinbase => { - TransactionEvent::FauxTransactionConfirmed { tx_id, is_valid: true } - }, + ImportStatus::FauxConfirmed => TransactionEvent::FauxTransactionConfirmed { tx_id, is_valid: true }, }; let _size = self.event_publisher.send(Arc::new(transaction_event)).map_err(|e| { trace!( @@ -2859,9 +2834,6 @@ where ); e }); - // Because we added new transactions, let try to trigger a validation for them - self.start_transaction_validation_protocol(transaction_validation_join_handles) - .await?; Ok(tx_id) } @@ -2919,84 +2891,11 @@ where TransactionDirection::Inbound, None, None, - None, - ), + )?, )?; Ok(()) } - async fn generate_coinbase_transaction( - &mut self, - reward: MicroMinotari, - fees: MicroMinotari, - block_height: u64, - extra: Vec, - ) -> Result { - let amount = reward + fees; - - // first check if we already have a coinbase tx for this height and amount - let find_result = self - .db - .find_coinbase_transaction_at_block_height(block_height, amount)?; - - let mut completed_transaction = None; - if let Some(tx) = find_result { - if let Some(coinbase) = tx.transaction.body.outputs().first() { - if coinbase.features.coinbase_extra == extra { - completed_transaction = Some(tx.transaction); - } - } - }; - if completed_transaction.is_none() { - // otherwise create a new coinbase tx - let tx_id = TxId::new_random(); - let tx = self - .resources - .output_manager_service - .get_coinbase_transaction(tx_id, reward, fees, block_height, extra) - .await?; - self.db.insert_completed_transaction( - tx_id, - CompletedTransaction::new( - tx_id, - self.resources.wallet_identity.address.clone(), - self.resources.wallet_identity.address.clone(), - amount, - MicroMinotari::from(0), - tx.clone(), - TransactionStatus::Coinbase, - format!("Coinbase Transaction for Block #{}", block_height), - Utc::now().naive_utc(), - TransactionDirection::Inbound, - Some(block_height), - None, - None, - ), - )?; - - let _size = self - .resources - .event_publisher - .send(Arc::new(TransactionEvent::ReceivedFinalizedTransaction(tx_id))) - .map_err(|e| { - trace!( - target: LOG_TARGET, - "Error sending event because there are no subscribers: {:?}", - e - ); - e - }); - - info!( - target: LOG_TARGET, - "Coinbase transaction (TxId: {}) for Block Height: {} added", tx_id, block_height - ); - completed_transaction = Some(tx); - }; - - Ok(completed_transaction.unwrap()) - } - /// Check if a Recovery Status is currently stored in the databse, this indicates that a wallet recovery is in /// progress fn check_recovery_status(&self) -> Result<(), TransactionServiceError> { @@ -3035,13 +2934,6 @@ enum PowerMode { Normal, } -/// Contains the generated TxId and SpendingKey for a Pending Coinbase transaction -#[derive(Debug)] -pub struct PendingCoinbaseSpendingKey { - pub tx_id: TxId, - pub spending_key: PrivateKey, -} - /// Contains the generated TxId and TransactionStatus transaction send result #[derive(Debug)] pub struct TransactionSendResult { diff --git a/base_layer/wallet/src/transaction_service/storage/database.rs b/base_layer/wallet/src/transaction_service/storage/database.rs index 17105d7cf5..7e22032e45 100644 --- a/base_layer/wallet/src/transaction_service/storage/database.rs +++ b/base_layer/wallet/src/transaction_service/storage/database.rs @@ -35,7 +35,10 @@ use tari_common_types::{ transaction::{ImportStatus, TransactionDirection, TransactionStatus, TxId}, types::{BlockHash, PrivateKey}, }; -use tari_core::transactions::{tari_amount::MicroMinotari, transaction_components::Transaction}; +use tari_core::transactions::{ + tari_amount::MicroMinotari, + transaction_components::{Transaction, TransactionOutput}, +}; use crate::transaction_service::{ error::TransactionStorageError, @@ -115,20 +118,10 @@ pub trait TransactionBackend: Send + Sync + Clone { ) -> Result; /// Mark a pending transaction direct send attempt as a success fn mark_direct_send_success(&self, tx_id: TxId) -> Result<(), TransactionStorageError>; - /// Cancel coinbase transactions at a specific block height - fn cancel_coinbase_transactions_at_block_height(&self, block_height: u64) -> Result<(), TransactionStorageError>; - /// Find coinbase transaction at a specific block height for a given amount - fn find_coinbase_transaction_at_block_height( - &self, - block_height: u64, - amount: MicroMinotari, - ) -> Result, TransactionStorageError>; /// Increment the send counter and timestamp of a transaction fn increment_send_count(&self, tx_id: TxId) -> Result<(), TransactionStorageError>; /// Update a transactions mined height. A transaction can either be mined as valid or mined as invalid /// A normal transaction can only be mined with valid = true, - /// A coinbase transaction can either be mined as valid = true, meaning that it is the coinbase in that block - /// or valid =false, meaning that the coinbase has been awarded to another tx, but this has been confirmed by blocks /// The mined height and block are used to determine reorgs fn update_mined_height( &self, @@ -137,7 +130,7 @@ pub trait TransactionBackend: Send + Sync + Clone { mined_in_block: BlockHash, mined_timestamp: u64, num_confirmations: u64, - is_confirmed: bool, + must_be_confirmed: bool, is_faux: bool, ) -> Result<(), TransactionStorageError>; /// Clears the mined block and height of a transaction @@ -154,7 +147,6 @@ pub trait TransactionBackend: Send + Sync + Clone { &self, height: u64, ) -> Result, TransactionStorageError>; - fn abandon_coinbase_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError>; } #[derive(Clone, PartialEq)] @@ -649,10 +641,10 @@ where T: TransactionBackend + 'static source_address: TariAddress, comms_address: TariAddress, message: String, - maturity: Option, import_status: ImportStatus, current_height: Option, mined_timestamp: Option, + scanned_output: TransactionOutput, ) -> Result<(), TransactionStorageError> { let transaction = CompletedTransaction::new( tx_id, @@ -662,7 +654,7 @@ where T: TransactionBackend + 'static MicroMinotari::from(0), Transaction::new( Vec::new(), - Vec::new(), + vec![scanned_output], Vec::new(), PrivateKey::default(), PrivateKey::default(), @@ -671,10 +663,9 @@ where T: TransactionBackend + 'static message, mined_timestamp.unwrap_or_else(|| Utc::now().naive_utc()), TransactionDirection::Inbound, - maturity, current_height, mined_timestamp, - ); + )?; self.db .write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( @@ -684,21 +675,6 @@ where T: TransactionBackend + 'static Ok(()) } - pub fn cancel_coinbase_transaction_at_block_height( - &self, - block_height: u64, - ) -> Result<(), TransactionStorageError> { - self.db.cancel_coinbase_transactions_at_block_height(block_height) - } - - pub fn find_coinbase_transaction_at_block_height( - &self, - block_height: u64, - amount: MicroMinotari, - ) -> Result, TransactionStorageError> { - self.db.find_coinbase_transaction_at_block_height(block_height, amount) - } - pub fn increment_send_count(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { self.db.increment_send_count(tx_id) } @@ -718,7 +694,7 @@ where T: TransactionBackend + 'static mined_in_block: BlockHash, mined_timestamp: u64, num_confirmations: u64, - is_confirmed: bool, + must_be_confirmed: bool, is_faux: bool, ) -> Result<(), TransactionStorageError> { self.db.update_mined_height( @@ -727,7 +703,7 @@ where T: TransactionBackend + 'static mined_in_block, mined_timestamp, num_confirmations, - is_confirmed, + must_be_confirmed, is_faux, ) } @@ -741,10 +717,6 @@ where T: TransactionBackend + 'static }?; Ok(t) } - - pub fn abandon_coinbase_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { - self.db.abandon_coinbase_transaction(tx_id) - } } impl Display for DbKey { diff --git a/base_layer/wallet/src/transaction_service/storage/models.rs b/base_layer/wallet/src/transaction_service/storage/models.rs index bd2a63b753..1dbc29ab03 100644 --- a/base_layer/wallet/src/transaction_service/storage/models.rs +++ b/base_layer/wallet/src/transaction_service/storage/models.rs @@ -39,6 +39,8 @@ use tari_core::transactions::{ SenderTransactionProtocol, }; +use crate::transaction_service::error::TransactionStorageError; + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct InboundTransaction { pub tx_id: TxId, @@ -138,7 +140,6 @@ pub struct CompletedTransaction { pub timestamp: NaiveDateTime, pub cancelled: Option, pub direction: TransactionDirection, - pub coinbase_block_height: Option, pub send_count: u32, pub last_send_timestamp: Option, pub transaction_signature: Signature, @@ -160,16 +161,18 @@ impl CompletedTransaction { message: String, timestamp: NaiveDateTime, direction: TransactionDirection, - coinbase_block_height: Option, mined_height: Option, mined_timestamp: Option, - ) -> Self { + ) -> Result { + if status == TransactionStatus::Coinbase { + return Err(TransactionStorageError::CoinbaseNotSupported); + } let transaction_signature = if let Some(excess_sig) = transaction.first_kernel_excess_sig() { excess_sig.clone() } else { Signature::default() }; - Self { + Ok(Self { tx_id, source_address, destination_address, @@ -181,7 +184,6 @@ impl CompletedTransaction { timestamp, cancelled: None, direction, - coinbase_block_height, send_count: 0, last_send_timestamp: None, transaction_signature, @@ -189,15 +191,7 @@ impl CompletedTransaction { mined_height, mined_in_block: None, mined_timestamp, - } - } - - pub fn is_coinbase(&self) -> bool { - if let Some(height) = self.coinbase_block_height { - height > 0 - } else { - false - } + }) } } @@ -269,7 +263,6 @@ impl From for CompletedTransaction { }, transaction, direction: TransactionDirection::Outbound, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature, @@ -299,7 +292,6 @@ impl From for CompletedTransaction { }, transaction: Transaction::new(vec![], vec![], vec![], PrivateKey::default(), PrivateKey::default()), direction: TransactionDirection::Inbound, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: Signature::default(), @@ -338,7 +330,6 @@ pub enum TxCancellationReason { Orphan, // 4 TimeLocked, // 5 InvalidTransaction, // 6 - AbandonedCoinbase, // 7 } impl TryFrom for TxCancellationReason { @@ -353,7 +344,6 @@ impl TryFrom for TxCancellationReason { 4 => Ok(TxCancellationReason::Orphan), 5 => Ok(TxCancellationReason::TimeLocked), 6 => Ok(TxCancellationReason::InvalidTransaction), - 7 => Ok(TxCancellationReason::AbandonedCoinbase), code => Err(TransactionConversionError { code: code as i32 }), } } @@ -371,7 +361,6 @@ impl Display for TxCancellationReason { Orphan => "Orphan", TimeLocked => "TimeLocked", InvalidTransaction => "Invalid Transaction", - AbandonedCoinbase => "Abandoned Coinbase", }; fmt.write_str(response) } diff --git a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs index 30e17796d2..d392187c02 100644 --- a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -44,7 +44,7 @@ use tari_common_types::{ types::{BlockHash, PrivateKey, PublicKey, Signature}, }; use tari_core::transactions::tari_amount::MicroMinotari; -use tari_utilities::{ByteArray, Hidden}; +use tari_utilities::{hex::Hex, ByteArray, Hidden}; use thiserror::Error; use tokio::time::Instant; use zeroize::Zeroize; @@ -754,59 +754,6 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { Ok(()) } - fn cancel_coinbase_transactions_at_block_height(&self, block_height: u64) -> Result<(), TransactionStorageError> { - let start = Instant::now(); - let mut conn = self.database_connection.get_pooled_connection()?; - let acquire_lock = start.elapsed(); - - CompletedTransactionSql::reject_coinbases_at_block_height( - block_height as i64, - TxCancellationReason::AbandonedCoinbase, - &mut conn, - )?; - if start.elapsed().as_millis() > 0 { - trace!( - target: LOG_TARGET, - "sqlite profile - cancel_coinbase_transaction_at_block_height: lock {} + db_op {} = {} ms", - acquire_lock.as_millis(), - (start.elapsed() - acquire_lock).as_millis(), - start.elapsed().as_millis() - ); - } - - Ok(()) - } - - fn find_coinbase_transaction_at_block_height( - &self, - block_height: u64, - amount: MicroMinotari, - ) -> Result, TransactionStorageError> { - let start = Instant::now(); - let mut conn = self.database_connection.get_pooled_connection()?; - let acquire_lock = start.elapsed(); - let cipher = acquire_read_lock!(self.cipher); - - let coinbase_txs = CompletedTransactionSql::index_coinbase_at_block_height(block_height as i64, &mut conn)?; - for c in coinbase_txs { - let completed_tx = CompletedTransaction::try_from(c, &cipher)?; - if completed_tx.amount == amount { - return Ok(Some(completed_tx)); - } - } - if start.elapsed().as_millis() > 0 { - trace!( - target: LOG_TARGET, - "sqlite profile - find_coinbase_transaction_at_block_height: lock {} + db_op {} = {} ms", - acquire_lock.as_millis(), - (start.elapsed() - acquire_lock).as_millis(), - start.elapsed().as_millis() - ); - } - - Ok(None) - } - fn increment_send_count(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { let start = Instant::now(); let mut conn = self.database_connection.get_pooled_connection()?; @@ -839,13 +786,13 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { mined_in_block: BlockHash, mined_timestamp: u64, num_confirmations: u64, - is_confirmed: bool, + must_be_confirmed: bool, is_faux: bool, ) -> Result<(), TransactionStorageError> { let start = Instant::now(); let mut conn = self.database_connection.get_pooled_connection()?; let acquire_lock = start.elapsed(); - let status = if is_confirmed { + let status = if must_be_confirmed { if is_faux { TransactionStatus::FauxConfirmed } else { @@ -956,11 +903,6 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { .eq(TransactionStatus::Completed as i32) .or(completed_transactions::status.eq(TransactionStatus::Broadcast as i32)), ) - .filter( - completed_transactions::coinbase_block_height - .is_null() - .or(completed_transactions::coinbase_block_height.eq(0)), - ) .filter(completed_transactions::cancelled.is_null()) .order_by(completed_transactions::tx_id) .load::(&mut conn)?; @@ -1102,21 +1044,6 @@ impl TransactionBackend for TransactionServiceSqliteDatabase { }) .collect::, TransactionStorageError>>() } - - fn abandon_coinbase_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { - let mut conn = self.database_connection.get_pooled_connection()?; - match CompletedTransactionSql::find_and_abandon_coinbase(tx_id, &mut conn) { - Ok(_) => {}, - Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { - return Err(TransactionStorageError::ValueNotFound(DbKey::CompletedTransaction( - tx_id, - ))); - }, - Err(e) => return Err(e), - }; - - Ok(()) - } } #[derive(Debug, PartialEq)] @@ -1688,7 +1615,6 @@ pub struct CompletedTransactionSql { timestamp: NaiveDateTime, cancelled: Option, direction: Option, - coinbase_block_height: Option, send_count: i32, last_send_timestamp: Option, confirmations: Option, @@ -1762,33 +1688,6 @@ impl CompletedTransactionSql { .load::(conn)?) } - pub fn index_coinbase_at_block_height( - block_height: i64, - conn: &mut SqliteConnection, - ) -> Result, TransactionStorageError> { - Ok(completed_transactions::table - .filter(completed_transactions::status.eq(TransactionStatus::Coinbase as i32)) - .filter(completed_transactions::coinbase_block_height.eq(block_height)) - .load::(conn)?) - } - - pub fn find_and_abandon_coinbase(tx_id: TxId, conn: &mut SqliteConnection) -> Result<(), TransactionStorageError> { - let _ = diesel::update( - completed_transactions::table - .filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64)) - .filter(completed_transactions::cancelled.is_null()) - .filter(completed_transactions::coinbase_block_height.is_not_null()), - ) - .set(UpdateCompletedTransactionSql { - cancelled: Some(Some(TxCancellationReason::AbandonedCoinbase as i32)), - ..Default::default() - }) - .execute(conn) - .num_rows_affected_or_not_found(1)?; - - Ok(()) - } - pub fn find(tx_id: TxId, conn: &mut SqliteConnection) -> Result { Ok(completed_transactions::table .filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64)) @@ -1859,24 +1758,6 @@ impl CompletedTransactionSql { Ok(()) } - pub fn reject_coinbases_at_block_height( - block_height: i64, - reason: TxCancellationReason, - conn: &mut SqliteConnection, - ) -> Result { - Ok(diesel::update( - completed_transactions::table - .filter(completed_transactions::status.eq(TransactionStatus::Coinbase as i32)) - .filter(completed_transactions::coinbase_block_height.eq(block_height)), - ) - .set(UpdateCompletedTransactionSql { - cancelled: Some(Some(reason as i32)), - status: Some(TransactionStatus::Rejected as i32), - ..Default::default() - }) - .execute(conn)?) - } - pub fn delete(&self, conn: &mut SqliteConnection) -> Result<(), TransactionStorageError> { let num_deleted = diesel::delete(completed_transactions::table.filter(completed_transactions::tx_id.eq(&self.tx_id))) @@ -1916,6 +1797,17 @@ impl CompletedTransactionSql { mined_timestamp )) })?; + trace!( + target: LOG_TARGET, + "update_mined_height: tx_id '{}', status '{:?}', confirmations '{}', mined height '{}', \ + mined timestamp '{}', mined block hash '{}'", + tx_id, + status, + num_confirmations, + mined_height, + timestamp, + mined_in_block.to_hex(), + ); diesel::update(completed_transactions::table.filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64))) .set(UpdateCompletedTransactionSql { confirmations: Some(Some(num_confirmations as i64)), @@ -1938,21 +1830,18 @@ impl CompletedTransactionSql { diesel::update(completed_transactions::table.filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64))) .set(UpdateCompletedTransactionSql { status: { - if let Some(Some(_coinbase_block_height)) = completed_transactions::table - .filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64)) - .select(completed_transactions::coinbase_block_height) - .load::>(conn)? - .first() - { - Some(TransactionStatus::Coinbase as i32) - } else if let Some(status) = completed_transactions::table + if let Some(status) = completed_transactions::table .filter(completed_transactions::tx_id.eq(tx_id.as_u64() as i64)) .select(completed_transactions::status) .load::(conn)? .first() { - if *status == TransactionStatus::FauxConfirmed as i32 { + if *status == TransactionStatus::FauxConfirmed as i32 || + *status == TransactionStatus::FauxUnconfirmed as i32 + { Some(TransactionStatus::FauxUnconfirmed as i32) + } else if *status == TransactionStatus::Imported as i32 { + Some(TransactionStatus::Imported as i32) } else if *status == TransactionStatus::Broadcast as i32 { Some(TransactionStatus::Broadcast as i32) } else { @@ -2007,7 +1896,6 @@ impl CompletedTransactionSql { timestamp: c.timestamp, cancelled: c.cancelled.map(|v| v as i32), direction: Some(c.direction as i32), - coinbase_block_height: c.coinbase_block_height.map(|b| b as i64), send_count: c.send_count as i32, last_send_timestamp: c.last_send_timestamp, confirmations: c.confirmations.map(|ic| ic as i64), @@ -2106,7 +1994,6 @@ impl CompletedTransaction { .cancelled .map(|v| TxCancellationReason::try_from(v as u32).unwrap_or(TxCancellationReason::Unknown)), direction: TransactionDirection::try_from(c.direction.unwrap_or(2i32))?, - coinbase_block_height: c.coinbase_block_height.map(|b| b as u64), send_count: c.send_count as u32, last_send_timestamp: c.last_send_timestamp, transaction_signature, @@ -2146,17 +2033,7 @@ pub struct UnconfirmedTransactionInfo { pub tx_id: TxId, pub signature: Signature, pub status: TransactionStatus, - pub coinbase_block_height: Option, -} - -impl UnconfirmedTransactionInfo { - pub fn is_coinbase(&self) -> bool { - if let Some(height) = self.coinbase_block_height { - height > 0 - } else { - false - } - } + pub message: String, } impl TryFrom for UnconfirmedTransactionInfo { @@ -2170,7 +2047,7 @@ impl TryFrom for UnconfirmedTransactionInfo { PrivateKey::from_vec(&i.transaction_signature_key)?, ), status: TransactionStatus::try_from(i.status)?, - coinbase_block_height: i.coinbase_block_height.map(|b| b as u64), + message: i.message, }) } } @@ -2181,7 +2058,7 @@ pub struct UnconfirmedTransactionInfoSql { pub status: i32, pub transaction_signature_nonce: Vec, pub transaction_signature_key: Vec, - pub coinbase_block_height: Option, + pub message: String, } impl UnconfirmedTransactionInfoSql { @@ -2195,13 +2072,18 @@ impl UnconfirmedTransactionInfoSql { completed_transactions::status, completed_transactions::transaction_signature_nonce, completed_transactions::transaction_signature_key, - completed_transactions::coinbase_block_height, + completed_transactions::message, )) .filter( completed_transactions::status + // Filter out imported or scanned transactions .ne(TransactionStatus::Imported as i32) .and(completed_transactions::status.ne(TransactionStatus::FauxUnconfirmed as i32)) .and(completed_transactions::status.ne(TransactionStatus::FauxConfirmed as i32)) + // Filter out any transaction without a kernel signature + .and(completed_transactions::transaction_signature_nonce.ne(vec![0u8; 32])) + .and(completed_transactions::transaction_signature_key.ne(vec![0u8; 32])) + // Allow the remainder of the unconfirmed transactions .and( completed_transactions::mined_height .is_null() @@ -2233,8 +2115,9 @@ mod test { types::{PrivateKey, PublicKey, Signature}, }; use tari_core::transactions::{ + key_manager::create_memory_db_key_manager, tari_amount::MicroMinotari, - test_helpers::{create_test_core_key_manager_with_memory_db, create_wallet_output_with_data, TestParams}, + test_helpers::{create_wallet_output_with_data, TestParams}, transaction_components::{OutputFeatures, Transaction}, transaction_protocol::sender::TransactionSenderMessage, ReceiverTransactionProtocol, @@ -2265,7 +2148,7 @@ mod test { #[tokio::test] #[allow(clippy::too_many_lines)] async fn test_crud() { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let consensus_constants = create_consensus_constants(0); let db_name = format!("{}.sqlite3", string(8).as_str()); let temp_dir = tempdir().unwrap(); @@ -2512,7 +2395,6 @@ mod test { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Unknown, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), @@ -2541,7 +2423,6 @@ mod test { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Unknown, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), @@ -2661,116 +2542,6 @@ mod test { .unwrap(); assert!(CompletedTransactionSql::find_by_cancelled(completed_tx1.tx_id, false, &mut conn).is_err()); assert!(CompletedTransactionSql::find_by_cancelled(completed_tx1.tx_id, true, &mut conn).is_ok()); - - let source_address = TariAddress::new( - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - Network::LocalNet, - ); - let destination_address = TariAddress::new( - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - Network::LocalNet, - ); - let coinbase_tx1 = CompletedTransaction { - tx_id: 101u64.into(), - source_address, - destination_address, - amount, - fee: MicroMinotari::from(100), - transaction: tx.clone(), - status: TransactionStatus::Coinbase, - message: "Hey!".to_string(), - timestamp: Utc::now().naive_utc(), - cancelled: None, - direction: TransactionDirection::Unknown, - coinbase_block_height: Some(2), - send_count: 0, - last_send_timestamp: None, - transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), - confirmations: None, - mined_height: None, - mined_in_block: None, - mined_timestamp: None, - }; - - let source_address = TariAddress::new( - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - Network::LocalNet, - ); - let destination_address = TariAddress::new( - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - Network::LocalNet, - ); - let coinbase_tx2 = CompletedTransaction { - tx_id: 102u64.into(), - source_address, - destination_address, - amount, - fee: MicroMinotari::from(100), - transaction: tx.clone(), - status: TransactionStatus::Coinbase, - message: "Hey!".to_string(), - timestamp: Utc::now().naive_utc(), - cancelled: None, - direction: TransactionDirection::Unknown, - coinbase_block_height: Some(2), - send_count: 0, - last_send_timestamp: None, - transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), - confirmations: None, - mined_height: None, - mined_in_block: None, - mined_timestamp: None, - }; - - let source_address = TariAddress::new( - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - Network::LocalNet, - ); - let destination_address = TariAddress::new( - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - Network::LocalNet, - ); - let coinbase_tx3 = CompletedTransaction { - tx_id: 103u64.into(), - source_address, - destination_address, - amount, - fee: MicroMinotari::from(100), - transaction: tx.clone(), - status: TransactionStatus::Coinbase, - message: "Hey!".to_string(), - timestamp: Utc::now().naive_utc(), - cancelled: None, - direction: TransactionDirection::Unknown, - coinbase_block_height: Some(3), - send_count: 0, - last_send_timestamp: None, - transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), - confirmations: None, - mined_height: None, - mined_in_block: None, - mined_timestamp: None, - }; - - CompletedTransactionSql::try_from(coinbase_tx1, &cipher) - .unwrap() - .commit(&mut conn) - .unwrap(); - CompletedTransactionSql::try_from(coinbase_tx2, &cipher) - .unwrap() - .commit(&mut conn) - .unwrap(); - CompletedTransactionSql::try_from(coinbase_tx3, &cipher) - .unwrap() - .commit(&mut conn) - .unwrap(); - - let coinbase_txs = CompletedTransactionSql::index_coinbase_at_block_height(2, &mut conn).unwrap(); - - assert_eq!(coinbase_txs.len(), 2); - assert!(coinbase_txs.iter().any(|c| c.tx_id == 101)); - assert!(coinbase_txs.iter().any(|c| c.tx_id == 102)); - assert!(!coinbase_txs.iter().any(|c| c.tx_id == 103)); } #[test] @@ -2887,7 +2658,6 @@ mod test { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Unknown, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: Signature::default(), @@ -3016,7 +2786,6 @@ mod test { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Unknown, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: Signature::default(), @@ -3090,7 +2859,7 @@ mod test { let mut info_list_reference: Vec = vec![]; for i in 0..1000 { - let (cancelled, status, coinbase_block_height) = match i % 13 { + let (cancelled, status) = match i % 13 { 0 => ( if i % 3 == 0 { Some(TxCancellationReason::Unknown) @@ -3098,7 +2867,6 @@ mod test { None }, TransactionStatus::Completed, - None, ), 1 => ( if i % 5 == 0 { @@ -3107,7 +2875,6 @@ mod test { None }, TransactionStatus::Broadcast, - None, ), 2 => ( if i % 7 == 0 { @@ -3116,7 +2883,6 @@ mod test { None }, TransactionStatus::Completed, - Some(i % 2), ), 3 => ( if i % 11 == 0 { @@ -3125,16 +2891,14 @@ mod test { None }, TransactionStatus::Broadcast, - Some(i % 2), ), - 4 => (None, TransactionStatus::Completed, None), - 5 => (None, TransactionStatus::Broadcast, None), - 6 => (None, TransactionStatus::Pending, None), - 7 => (None, TransactionStatus::Coinbase, None), - 8 => (None, TransactionStatus::MinedUnconfirmed, None), - 9 => (None, TransactionStatus::Imported, None), - 10 => (None, TransactionStatus::MinedConfirmed, None), - _ => (None, TransactionStatus::Completed, Some(i)), + 4 => (None, TransactionStatus::Completed), + 5 => (None, TransactionStatus::Broadcast), + 6 => (None, TransactionStatus::Pending), + 8 => (None, TransactionStatus::MinedUnconfirmed), + 9 => (None, TransactionStatus::Imported), + 10 => (None, TransactionStatus::MinedConfirmed), + _ => (None, TransactionStatus::Completed), }; let source_address = TariAddress::new( PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), @@ -3145,7 +2909,7 @@ mod test { Network::LocalNet, ); let completed_tx = CompletedTransaction { - tx_id: TxId::from(i), + tx_id: TxId::from(i as u64), source_address, destination_address, amount: MicroMinotari::from(100), @@ -3162,7 +2926,6 @@ mod test { timestamp: Utc::now().naive_utc(), cancelled, direction: TransactionDirection::Unknown, - coinbase_block_height, send_count: 0, last_send_timestamp: None, transaction_signature: Signature::default(), @@ -3192,11 +2955,10 @@ mod test { let db1 = TransactionServiceSqliteDatabase::new(connection, cipher); let txn_list = db1.get_transactions_to_be_broadcast().unwrap(); - assert_eq!(txn_list.len(), 335); + assert_eq!(txn_list.len(), 633); for txn in &txn_list { assert!(txn.status == TransactionStatus::Completed || txn.status == TransactionStatus::Broadcast); assert!(txn.cancelled.is_none()); - assert!(txn.coinbase_block_height.is_none() || txn.coinbase_block_height == Some(0)); } let info_list = db1.get_pending_inbound_transaction_sender_info().unwrap(); diff --git a/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs b/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs index 41ed276bc6..75156e173d 100644 --- a/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs +++ b/base_layer/wallet/src/transaction_service/tasks/check_faux_transaction_status.rs @@ -29,7 +29,7 @@ use tari_common_types::{ }; use crate::{ - output_manager_service::{handle::OutputManagerHandle, storage::OutputStatus}, + output_manager_service::handle::OutputManagerHandle, transaction_service::{ config::TransactionServiceConfig, handle::{TransactionEvent, TransactionEventSender}, @@ -97,83 +97,79 @@ pub async fn check_faux_transactions( all_faux_transactions.len() ); for tx in all_faux_transactions { - let output_statuses_by_tx_id = match output_manager.get_output_statuses_by_tx_id(tx.tx_id).await { + let output_info_for_tx_id = match output_manager.get_output_info_for_tx_id(tx.tx_id).await { Ok(s) => s, Err(e) => { error!(target: LOG_TARGET, "Problem retrieving output statuses: {}", e); return; }, }; - if !output_statuses_by_tx_id - .statuses - .iter() - .any(|s| s != &OutputStatus::Unspent) - { - let mined_height = if let Some(height) = output_statuses_by_tx_id.mined_height { - height - } else { - tip_height - }; - let mined_in_block: BlockHash = if let Some(hash) = output_statuses_by_tx_id.block_hash { - hash - } else { - FixedHash::zero() - }; - let is_valid = tip_height >= mined_height; - let was_confirmed = tx.status == TransactionStatus::FauxConfirmed; - let is_confirmed = tip_height.saturating_sub(mined_height) >= - TransactionServiceConfig::default().num_confirmations_required; - let num_confirmations = tip_height - mined_height; - debug!( + let output_status = output_info_for_tx_id.statuses[0]; + let mined_height = if let Some(height) = output_info_for_tx_id.mined_height { + height + } else { + tip_height + }; + let mined_in_block: BlockHash = if let Some(hash) = output_info_for_tx_id.block_hash { + hash + } else { + FixedHash::zero() + }; + let is_valid = tip_height >= mined_height; + let previously_confirmed = tx.status == TransactionStatus::FauxConfirmed; + let must_be_confirmed = + tip_height.saturating_sub(mined_height) >= TransactionServiceConfig::default().num_confirmations_required; + let num_confirmations = tip_height - mined_height; + debug!( + target: LOG_TARGET, + "Updating faux transaction: TxId({}), mined_height({}), must_be_confirmed({}), num_confirmations({}), \ + output_status({}), is_valid({})", + tx.tx_id, + mined_height, + must_be_confirmed, + num_confirmations, + output_status, + is_valid, + ); + let result = db.set_transaction_mined_height( + tx.tx_id, + mined_height, + mined_in_block, + tx.mined_timestamp + .map_or(0, |mined_timestamp| mined_timestamp.timestamp() as u64), + num_confirmations, + must_be_confirmed, + true, + ); + if let Err(e) = result { + error!( target: LOG_TARGET, - "Updating faux transaction: TxId({}), mined_height({}), is_confirmed({}), num_confirmations({}), \ - is_valid({})", - tx.tx_id, - mined_height, - is_confirmed, - num_confirmations, - is_valid, - ); - let result = db.set_transaction_mined_height( - tx.tx_id, - mined_height, - mined_in_block, - tx.mined_timestamp - .map_or(0, |mined_timestamp| mined_timestamp.timestamp() as u64), - num_confirmations, - is_confirmed, - is_valid, + "Error setting faux transaction to mined confirmed: {}", e ); - if let Err(e) = result { - error!( - target: LOG_TARGET, - "Error setting faux transaction to mined confirmed: {}", e - ); - } else { - // Only send an event if the transaction was not previously confirmed OR was previously confirmed and is - // now not confirmed (i.e. confirmation changed) - if !(was_confirmed && is_confirmed) { - let transaction_event = if is_confirmed { - TransactionEvent::FauxTransactionConfirmed { - tx_id: tx.tx_id, - is_valid, - } - } else { - TransactionEvent::FauxTransactionUnconfirmed { - tx_id: tx.tx_id, - num_confirmations: 0, - is_valid, - } - }; - let _size = event_publisher.send(Arc::new(transaction_event)).map_err(|e| { - trace!( - target: LOG_TARGET, - "Error sending event, usually because there are no subscribers: {:?}", - e - ); + } else { + // Only send an event if the transaction was not previously confirmed OR was previously confirmed and is + // now not confirmed (i.e. confirmation changed) + if !(previously_confirmed && must_be_confirmed) { + let transaction_event = if must_be_confirmed { + TransactionEvent::FauxTransactionConfirmed { + tx_id: tx.tx_id, + is_valid, + } + } else { + TransactionEvent::FauxTransactionUnconfirmed { + tx_id: tx.tx_id, + num_confirmations: 0, + is_valid, + } + }; + let _size = event_publisher.send(Arc::new(transaction_event)).map_err(|e| { + trace!( + target: LOG_TARGET, + "Error sending event, usually because there are no subscribers: {:?}", e - }); - } + ); + e + }); } } } diff --git a/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs b/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs index 53e425a72a..d3d5d7557e 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs @@ -553,8 +553,8 @@ where async fn scan_for_outputs( &mut self, outputs: Vec, - ) -> Result, UtxoScannerError> { - let mut found_outputs: Vec<(WalletOutput, String, ImportStatus, TxId)> = Vec::new(); + ) -> Result, UtxoScannerError> { + let mut found_outputs: Vec<(WalletOutput, String, ImportStatus, TxId, TransactionOutput)> = Vec::new(); found_outputs.append( &mut self .resources @@ -562,15 +562,18 @@ where .scan_for_recoverable_outputs(outputs.clone()) .await? .into_iter() - .map(|ro| { - let status = if ro.output.features.is_coinbase() { - ImportStatus::Coinbase + .map(|ro| -> Result<_, UtxoScannerError> { + let message = if ro.output.features.is_coinbase() { + "**COINBASE** ".to_owned() + &self.resources.recovery_message } else { - ImportStatus::Imported + self.resources.recovery_message.clone() }; - (ro.output, self.resources.recovery_message.clone(), status, ro.tx_id) + let output = outputs.iter().find(|o| o.hash() == ro.hash).ok_or_else(|| { + UtxoScannerError::UtxoScanningError(format!("Output '{}' not found", ro.hash.to_hex())) + })?; + Ok((ro.output, message, ImportStatus::Imported, ro.tx_id, output.clone())) }) - .collect(), + .collect::, _>>()?, ); found_outputs.append( @@ -580,30 +583,39 @@ where .scan_outputs_for_one_sided_payments(outputs.clone()) .await? .into_iter() - .map(|ro| { - ( + .map(|ro| -> Result<_, UtxoScannerError> { + let message = if ro.output.features.is_coinbase() { + "**COINBASE** ".to_owned() + &self.resources.one_sided_payment_message + } else { + self.resources.one_sided_payment_message.clone() + }; + let output = outputs.iter().find(|o| o.hash() == ro.hash).ok_or_else(|| { + UtxoScannerError::UtxoScanningError(format!("Output '{}' not found", ro.hash.to_hex())) + })?; + Ok(( ro.output, - self.resources.one_sided_payment_message.clone(), + message, ImportStatus::FauxUnconfirmed, ro.tx_id, - ) + output.clone(), + )) }) - .collect(), + .collect::, _>>()?, ); Ok(found_outputs) } async fn import_utxos_to_transaction_service( &mut self, - utxos: Vec<(WalletOutput, String, ImportStatus, TxId)>, + utxos: Vec<(WalletOutput, String, ImportStatus, TxId, TransactionOutput)>, current_height: u64, mined_timestamp: NaiveDateTime, ) -> Result<(u64, MicroMinotari), UtxoScannerError> { let mut num_recovered = 0u64; let mut total_amount = MicroMinotari::from(0); - for (uo, message, import_status, tx_id) in utxos { - let source_address = if uo.features.is_coinbase() { - // its a coinbase, so we know we mined it and it comes from us. + for (wo, message, import_status, tx_id, to) in utxos { + let source_address = if wo.features.is_coinbase() { + // It's a coinbase, so we know we mined it (we do mining with cold wallets). self.resources.wallet_identity.address.clone() } else { // Because we do not know the source public key we are making it the default key of zeroes to make it @@ -612,19 +624,20 @@ where }; match self .import_key_manager_utxo_to_transaction_service( - uo.clone(), + wo.clone(), source_address, message, import_status, tx_id, current_height, mined_timestamp, + to.clone(), ) .await { Ok(_) => { num_recovered = num_recovered.saturating_add(1); - total_amount += uo.value; + total_amount += wo.value; }, Err(WalletError::TransactionServiceError(TransactionServiceError::TransactionStorageError( TransactionStorageError::DuplicateOutput, @@ -677,6 +690,7 @@ where tx_id: TxId, current_height: u64, mined_timestamp: NaiveDateTime, + scanned_output: TransactionOutput, ) -> Result { let tx_id = self .resources @@ -685,11 +699,11 @@ where wallet_output.value, source_address, message, - Some(wallet_output.features.maturity), import_status.clone(), Some(tx_id), Some(current_height), Some(mined_timestamp), + scanned_output, ) .await?; diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index 0f818efa5a..ce2d79f9e1 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -482,11 +482,16 @@ where unblinded_output.value, source_address, message, - Some(unblinded_output.features.maturity), ImportStatus::Imported, None, None, None, + unblinded_output + .clone() + .to_wallet_output(&self.key_manager_service) + .await? + .to_transaction_output(&self.key_manager_service) + .await?, ) .await?; let wallet_output = unblinded_output.to_wallet_output(&self.key_manager_service).await?; diff --git a/base_layer/wallet/tests/other/mod.rs b/base_layer/wallet/tests/other/mod.rs index 2bb7ba5dd9..b6ac783f17 100644 --- a/base_layer/wallet/tests/other/mod.rs +++ b/base_layer/wallet/tests/other/mod.rs @@ -102,7 +102,6 @@ use minotari_wallet::{ }; use tempfile::tempdir; use tokio::{sync::mpsc, time::sleep}; -use tari_core::test_helpers::create_test_core_key_manager_with_memory_db; use crate::support::utils::make_input; @@ -284,7 +283,7 @@ async fn test_wallet() { let mut alice_event_stream = alice_wallet.transaction_service.get_event_stream(); let value = MicroMinotari::from(1000); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (_utxo, uo1) = make_non_recoverable_input(&mut OsRng, MicroMinotari(2500), &OutputFeatures::default(), &key_manager).await; alice_wallet.output_manager_service.add_output(uo1, None).await.unwrap(); @@ -590,7 +589,7 @@ async fn test_store_and_forward_send_tx() { .unwrap(); let value = MicroMinotari::from(1000); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let (_utxo, uo1) = make_non_recoverable_input(&mut OsRng, MicroMinotari(2500), &OutputFeatures::default(), &key_manager).await; alice_wallet.output_manager_service.add_output(uo1, None).await.unwrap(); @@ -734,7 +733,7 @@ async fn test_import_utxo() { let input = inputs!(claim); let temp_features = OutputFeatures::create_coinbase(50, None); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let p = TestParams::new(&key_manager); let utxo = create_wallet_output_with_data(script.clone(), temp_features, &p, 20000 * uT, &key_manager).await.unwrap(); let output = utxo.as_transaction_output(&key_manager).unwrap(); diff --git a/base_layer/wallet/tests/output_manager_service_tests/service.rs b/base_layer/wallet/tests/output_manager_service_tests/service.rs index a25bdcb42c..15c8214cd8 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/service.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/service.rs @@ -61,15 +61,15 @@ use tari_core::{ proto::base_node::{QueryDeletedData, QueryDeletedResponse, UtxoQueryResponse, UtxoQueryResponses}, transactions::{ fee::Fee, - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface}, - tari_amount::{uT, MicroMinotari, T}, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_wallet_output_with_data, - TestKeyManager, - TestParams, + key_manager::{ + create_memory_db_key_manager, + MemoryDbKeyManager, + TransactionKeyManagerBranch, + TransactionKeyManagerInterface, }, - transaction_components::{OutputFeatures, OutputType, TransactionOutput, WalletOutput}, + tari_amount::{uT, MicroMinotari, T}, + test_helpers::{create_wallet_output_with_data, TestParams}, + transaction_components::{OutputFeatures, TransactionOutput, WalletOutput}, transaction_protocol::{sender::TransactionSenderMessage, TransactionMetadata}, weight::TransactionWeight, CryptoFactories, @@ -108,7 +108,7 @@ struct TestOmsService { pub node_id: Arc, pub base_node_wallet_rpc_mock_state: BaseNodeWalletRpcMockState, pub node_event: broadcast::Sender>, - pub key_manager_handle: TestKeyManager, + pub key_manager_handle: MemoryDbKeyManager, } #[allow(clippy::type_complexity)] @@ -159,7 +159,7 @@ async fn setup_output_manager_service( wallet_connectivity_mock.set_base_node_wallet_rpc_client(connect_rpc_client(&mut connection).await); } - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let wallet_identity = WalletIdentity::new(server_node_identity.clone(), Network::LocalNet); let output_manager_service = OutputManagerService::new( @@ -204,7 +204,7 @@ pub async fn setup_oms_with_bn_state( TransactionServiceHandle, BaseNodeServiceHandle, broadcast::Sender>, - TestKeyManager, + MemoryDbKeyManager, ) { let shutdown = Shutdown::new(); let factories = CryptoFactories::default(); @@ -226,7 +226,7 @@ pub async fn setup_oms_with_bn_state( mock_base_node_service.set_base_node_state(height); task::spawn(mock_base_node_service.run()); let connectivity = create_wallet_connectivity_mock(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let wallet_identity = WalletIdentity::new(node_identity.clone(), Network::LocalNet); let output_manager_service = OutputManagerService::new( OutputManagerServiceConfig { ..Default::default() }, @@ -259,7 +259,7 @@ pub async fn setup_oms_with_bn_state( async fn generate_sender_transaction_message( amount: MicroMinotari, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (TxId, TransactionSenderMessage) { let input = make_input(&mut OsRng, 2 * amount, &OutputFeatures::default(), key_manager).await; let mut builder = SenderTransactionProtocol::builder(create_consensus_constants(0), key_manager.clone()); @@ -678,16 +678,34 @@ async fn test_utxo_selection_with_tx_priority() { let amount = MicroMinotari::from(2000); let fee_per_gram = MicroMinotari::from(2); - // we create two outputs, one as coinbase-high priority one as normal so we can track them - let uo = make_input_with_features( + // Low priority + let uo_low_1 = make_input_with_features( + &mut OsRng.clone(), + amount, + OutputFeatures { + maturity: 1, + ..Default::default() + }, + &key_manager, + ) + .await; + oms.add_output(uo_low_1.clone(), None).await.unwrap(); + // High priority + let uo_high = make_input_with_features( &mut OsRng.clone(), amount, - OutputFeatures::create_coinbase(1, None), + OutputFeatures { + maturity: 1, + ..Default::default() + }, &key_manager, ) .await; - oms.add_output(uo, Some(SpendingPriority::HtlcSpendAsap)).await.unwrap(); - let uo = make_input_with_features( + oms.add_output(uo_high.clone(), Some(SpendingPriority::HtlcSpendAsap)) + .await + .unwrap(); + // Low priority + let uo_low_2 = make_input_with_features( &mut OsRng.clone(), amount, OutputFeatures { @@ -697,10 +715,17 @@ async fn test_utxo_selection_with_tx_priority() { &key_manager, ) .await; - oms.add_output(uo, None).await.unwrap(); + oms.add_output(uo_low_2.clone(), None).await.unwrap(); let utxos = oms.get_unspent_outputs().await.unwrap(); - assert_eq!(utxos.len(), 2); + assert_eq!(utxos.len(), 3); + + assert_eq!(utxos[0].spending_priority, SpendingPriority::Normal); + assert_eq!(utxos[0].wallet_output.spending_key_id, uo_low_1.spending_key_id); + assert_eq!(utxos[1].spending_priority, SpendingPriority::HtlcSpendAsap); + assert_eq!(utxos[1].wallet_output.spending_key_id, uo_high.spending_key_id); + assert_eq!(utxos[2].spending_priority, SpendingPriority::Normal); + assert_eq!(utxos[2].wallet_output.spending_key_id, uo_low_2.spending_key_id); // test transactions let stp = oms @@ -720,11 +745,11 @@ async fn test_utxo_selection_with_tx_priority() { .unwrap(); assert!(stp.get_tx_id().is_ok()); - // test that the utxo with the lowest priority was left + // Test that the UTXOs with the lowest priority was left let utxos = oms.get_unspent_outputs().await.unwrap(); - assert_eq!(utxos.len(), 1); - - assert_ne!(utxos[0].wallet_output.features.output_type, OutputType::Coinbase); + assert_eq!(utxos.len(), 2); + assert_ne!(utxos[0].wallet_output.spending_key_id, uo_high.spending_key_id); + assert_ne!(utxos[1].wallet_output.spending_key_id, uo_high.spending_key_id); } #[tokio::test] @@ -783,7 +808,7 @@ async fn send_no_change() { .expect("Failed to get default features and scripts size byte size"), ); let value1 = 5000; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); oms.output_manager_handle .add_output( create_wallet_output_with_data( @@ -800,7 +825,7 @@ async fn send_no_change() { .await .unwrap(); let value2 = 8000; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); oms.output_manager_handle .add_output( create_wallet_output_with_data( @@ -855,7 +880,7 @@ async fn send_not_enough_for_change() { let constants = create_consensus_constants(0); let fee_without_change = Fee::new(*constants.transaction_weight_params()).calculate(fee_per_gram, 1, 2, 1, 0); let value1 = MicroMinotari(500); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); oms.output_manager_handle .add_output( create_wallet_output_with_data( @@ -1257,75 +1282,6 @@ async fn it_handles_large_coin_splits() { assert_eq!(coin_split_tx.body.outputs().len(), split_count + 1); } -#[tokio::test] -async fn handle_coinbase_with_bulletproofs_rewinding() { - let (connection, _tempdir) = get_temp_sqlite_database_connection(); - let backend = OutputManagerSqliteDatabase::new(connection.clone()); - let mut oms = setup_output_manager_service(backend, true).await; - - let reward1 = MicroMinotari::from(1000); - let fees1 = MicroMinotari::from(500); - let reward2 = MicroMinotari::from(2000); - let fees2 = MicroMinotari::from(500); - let reward3 = MicroMinotari::from(3000); - let fees3 = MicroMinotari::from(500); - let value3 = reward3 + fees3; - - let _transaction = oms - .output_manager_handle - .get_coinbase_transaction(1u64.into(), reward1, fees1, 1, b"test".to_vec()) - .await - .unwrap(); - assert_eq!(oms.output_manager_handle.get_unspent_outputs().await.unwrap().len(), 0); - // pending coinbases should not show up as pending incoming - assert_eq!( - oms.output_manager_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - let _tx2 = oms - .output_manager_handle - .get_coinbase_transaction(2u64.into(), reward2, fees2, 1, b"test".to_vec()) - .await - .unwrap(); - assert_eq!(oms.output_manager_handle.get_unspent_outputs().await.unwrap().len(), 0); - assert_eq!( - oms.output_manager_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - let tx3 = oms - .output_manager_handle - .get_coinbase_transaction(3u64.into(), reward3, fees3, 2, b"test".to_vec()) - .await - .unwrap(); - assert_eq!(oms.output_manager_handle.get_unspent_outputs().await.unwrap().len(), 0); - assert_eq!( - oms.output_manager_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - let output = tx3.body.outputs()[0].clone(); - - let (_, decrypted_value) = oms - .key_manager_handle - .try_output_key_recovery(&output, None) - .await - .unwrap(); - assert_eq!(decrypted_value, value3); -} - #[tokio::test] #[allow(clippy::too_many_lines)] async fn test_txo_validation() { @@ -1482,30 +1438,14 @@ async fn test_txo_validation() { .await .unwrap(); - oms.output_manager_handle - .get_coinbase_transaction( - 6u64.into(), - MicroMinotari::from(15_000_000), - MicroMinotari::from(1_000_000), - 2, - b"test".to_vec(), - ) - .await - .unwrap(); - let mut outputs = oms_db.fetch_pending_incoming_outputs().unwrap(); - assert_eq!(outputs.len(), 3); + assert_eq!(outputs.len(), 2); let o5_pos = outputs .iter() .position(|o| o.wallet_output.value == MicroMinotari::from(8_000_000)) .unwrap(); let output5 = outputs.remove(o5_pos); - let o6_pos = outputs - .iter() - .position(|o| o.wallet_output.value == MicroMinotari::from(16_000_000)) - .unwrap(); - let output6 = outputs.remove(o6_pos); let output4 = outputs[0].clone(); let output4_tx_output = output4 @@ -1518,11 +1458,6 @@ async fn test_txo_validation() { .to_transaction_output(&oms.key_manager_handle) .await .unwrap(); - let output6_tx_output = output6 - .wallet_output - .to_transaction_output(&oms.key_manager_handle) - .await - .unwrap(); let balance = oms.output_manager_handle.get_balance().await.unwrap(); @@ -1581,13 +1516,6 @@ async fn test_txo_validation() { output_hash: output5_tx_output.hash().to_vec(), mined_timestamp: 0, }, - UtxoQueryResponse { - output: Some(output6_tx_output.clone().try_into().unwrap()), - mined_at_height: 5, - mined_in_block: block5_header.hash().to_vec(), - output_hash: output6_tx_output.hash().to_vec(), - mined_timestamp: 0, - }, ]; let mut utxo_query_responses = UtxoQueryResponses { @@ -1648,14 +1576,14 @@ async fn test_txo_validation() { .await .unwrap(); - assert_eq!(utxo_query_calls[0].len(), 4); + assert_eq!(utxo_query_calls[0].len(), 3); let query_deleted_calls = oms .base_node_wallet_rpc_mock_state .wait_pop_query_deleted(1, Duration::from_secs(60)) .await .unwrap(); - assert_eq!(query_deleted_calls[0].hashes.len(), 5); + assert_eq!(query_deleted_calls[0].hashes.len(), 4); let balance = oms.output_manager_handle.get_balance().await.unwrap(); assert_eq!( @@ -1664,7 +1592,7 @@ async fn test_txo_validation() { ); assert_eq!(MicroMinotari::from(0), balance.time_locked_balance.unwrap()); - assert_eq!(oms.output_manager_handle.get_unspent_outputs().await.unwrap().len(), 5); + assert_eq!(oms.output_manager_handle.get_unspent_outputs().await.unwrap().len(), 4); assert!(oms.output_manager_handle.get_spent_outputs().await.unwrap().is_empty()); @@ -1695,7 +1623,7 @@ async fn test_txo_validation() { .unwrap(); // The spent transaction is not checked during this second validation - assert_eq!(utxo_query_calls[0].len(), 4); + assert_eq!(utxo_query_calls[0].len(), 3); let query_deleted_calls = oms .base_node_wallet_rpc_mock_state @@ -1703,7 +1631,7 @@ async fn test_txo_validation() { .await .unwrap(); - assert_eq!(query_deleted_calls[0].hashes.len(), 5); + assert_eq!(query_deleted_calls[0].hashes.len(), 4); let balance = oms.output_manager_handle.get_balance().await.unwrap(); assert_eq!( @@ -1711,10 +1639,9 @@ async fn test_txo_validation() { MicroMinotari::from(output2_value) + MicroMinotari::from(output3_value) + MicroMinotari::from(output1_value) - MicroMinotari::from(900_000) - MicroMinotari::from(1320) + //spent 900_000 and 1320 for fees - MicroMinotari::from(8_000_000) + //output 5 - MicroMinotari::from(16_000_000) // output 6 + MicroMinotari::from(8_000_000) // output 5 ); - assert_eq!(balance.pending_outgoing_balance, MicroMinotari::from(0)); + assert_eq!(balance.pending_outgoing_balance, MicroMinotari::from(1000000)); assert_eq!(balance.pending_incoming_balance, MicroMinotari::from(0)); assert_eq!(MicroMinotari::from(0), balance.time_locked_balance.unwrap()); @@ -1954,7 +1881,7 @@ async fn test_txo_revalidation() { .set_base_node_wallet_rpc_client(connect_rpc_client(&mut connection).await); let output1_value = 1_000_000; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let output1 = create_wallet_output_with_data( script!(Nop), OutputFeatures::default(), @@ -2168,7 +2095,7 @@ async fn test_get_status_by_tx_id() { let output_statuses_by_tx_id = oms .output_manager_handle - .get_output_statuses_by_tx_id(TxId::from(1u64)) + .get_output_info_for_tx_id(TxId::from(1u64)) .await .unwrap(); @@ -2232,7 +2159,7 @@ async fn scan_for_recovery_test() { let mut non_recoverable_wallet_outputs = Vec::new(); // we need to create a new key_manager to make the outputs non recoverable - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); for i in 1..=NUM_NON_RECOVERABLE { let uo = make_input( &mut OsRng, @@ -2287,7 +2214,7 @@ async fn recovered_output_key_not_in_keychain() { let backend = OutputManagerSqliteDatabase::new(connection.clone()); let mut oms = setup_output_manager_service(backend.clone(), true).await; // we need to create a new key manager here as we dont want the input be recoverable from oms key chain - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let uo = make_input( &mut OsRng, MicroMinotari::from(1000u64), diff --git a/base_layer/wallet/tests/output_manager_service_tests/storage.rs b/base_layer/wallet/tests/output_manager_service_tests/storage.rs index 411e974e0d..88f2616f15 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/storage.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/storage.rs @@ -33,8 +33,8 @@ use minotari_wallet::output_manager_service::{ use rand::{rngs::OsRng, RngCore}; use tari_common_types::{transaction::TxId, types::FixedHash}; use tari_core::transactions::{ + key_manager::create_memory_db_key_manager, tari_amount::MicroMinotari, - test_helpers::create_test_core_key_manager_with_memory_db, transaction_components::OutputFeatures, }; @@ -46,7 +46,7 @@ pub async fn test_db_backend(backend: T) { // Add some unspent outputs let mut unspent_outputs = Vec::new(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); for i in 0..5 { let uo = make_input( &mut OsRng, @@ -55,7 +55,7 @@ pub async fn test_db_backend(backend: T) { &key_manager, ) .await; - let mut kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + let mut kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); kmo.wallet_output.features.maturity = i; @@ -106,7 +106,7 @@ pub async fn test_db_backend(backend: T) { &key_manager, ) .await; - let kmo = DbWalletOutput::from_wallet_output(kmo, &key_manager, None, OutputSource::Unknown, None, None) + let kmo = DbWalletOutput::from_wallet_output(kmo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); db.add_unspent_output(kmo.clone()).unwrap(); @@ -120,7 +120,7 @@ pub async fn test_db_backend(backend: T) { &key_manager, ) .await; - let kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + let kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); pending_tx.outputs_to_be_received.push(kmo); @@ -257,10 +257,10 @@ pub async fn test_db_backend(backend: T) { ) .await; let output_to_be_received = - DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); - db.add_output_to_be_received(TxId::from(11u64), output_to_be_received.clone(), None) + db.add_output_to_be_received(TxId::from(11u64), output_to_be_received.clone()) .unwrap(); pending_incoming_balance += output_to_be_received.wallet_output.value; @@ -346,7 +346,7 @@ pub async fn test_short_term_encumberance() { let db = OutputManagerDatabase::new(backend); let mut unspent_outputs = Vec::new(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); for i in 0..5 { let kmo = make_input( &mut OsRng, @@ -355,7 +355,7 @@ pub async fn test_short_term_encumberance() { &key_manager, ) .await; - let mut kmo = DbWalletOutput::from_wallet_output(kmo, &key_manager, None, OutputSource::Unknown, None, None) + let mut kmo = DbWalletOutput::from_wallet_output(kmo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); kmo.wallet_output.features.maturity = i; @@ -406,7 +406,7 @@ pub async fn test_no_duplicate_outputs() { let db = OutputManagerDatabase::new(backend); // create an output - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let uo = make_input( &mut OsRng, MicroMinotari::from(1000), @@ -414,7 +414,7 @@ pub async fn test_no_duplicate_outputs() { &key_manager, ) .await; - let kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + let kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); @@ -448,7 +448,7 @@ pub async fn test_mark_as_unmined() { let db = OutputManagerDatabase::new(backend); // create an output - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let uo = make_input( &mut OsRng, MicroMinotari::from(1000), @@ -456,7 +456,7 @@ pub async fn test_mark_as_unmined() { &key_manager, ) .await; - let kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + let kmo = DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(); diff --git a/base_layer/wallet/tests/support/output_manager_service_mock.rs b/base_layer/wallet/tests/support/output_manager_service_mock.rs index 736c474ba6..87cc1b4b1c 100644 --- a/base_layer/wallet/tests/support/output_manager_service_mock.rs +++ b/base_layer/wallet/tests/support/output_manager_service_mock.rs @@ -107,6 +107,7 @@ impl OutputManagerServiceMock { Some(RecoveredOutput { output: dbuo.wallet_output, tx_id: TxId::new_random(), + hash: dbuo.hash, }) } else { None @@ -131,6 +132,7 @@ impl OutputManagerServiceMock { Some(RecoveredOutput { output: dbuo.wallet_output, tx_id: TxId::new_random(), + hash: dbuo.hash, }) } else { None diff --git a/base_layer/wallet/tests/support/utils.rs b/base_layer/wallet/tests/support/utils.rs index b91be5482c..704ff911f6 100644 --- a/base_layer/wallet/tests/support/utils.rs +++ b/base_layer/wallet/tests/support/utils.rs @@ -24,9 +24,9 @@ use rand::{CryptoRng, Rng}; use tari_core::{ covenants::Covenant, transactions::{ - key_manager::TransactionKeyManagerInterface, + key_manager::{MemoryDbKeyManager, TransactionKeyManagerInterface}, tari_amount::MicroMinotari, - test_helpers::{create_wallet_output_with_data, TestKeyManager, TestParams}, + test_helpers::{create_wallet_output_with_data, TestParams}, transaction_components::{ OutputFeatures, RangeProofType, @@ -44,7 +44,7 @@ pub async fn make_input( _rng: &mut R, val: MicroMinotari, features: &OutputFeatures, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> WalletOutput { let test_params = TestParams::new(key_manager).await; create_wallet_output_with_data(TariScript::default(), features.clone(), &test_params, val, key_manager) @@ -54,7 +54,7 @@ pub async fn make_input( pub async fn create_wallet_output_from_sender_data( info: &TransactionSenderMessage, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> WalletOutput { let test_params = TestParams::new(key_manager).await; let sender_data = info.single().unwrap(); @@ -104,7 +104,7 @@ pub async fn make_input_with_features( _rng: &mut R, value: MicroMinotari, features: OutputFeatures, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> WalletOutput { let test_params = TestParams::new(key_manager).await; create_wallet_output_with_data(script!(Nop), features, &test_params, value, key_manager) diff --git a/base_layer/wallet/tests/transaction_service_tests/service.rs b/base_layer/wallet/tests/transaction_service_tests/service.rs index d922c4d92d..17b58c1218 100644 --- a/base_layer/wallet/tests/transaction_service_tests/service.rs +++ b/base_layer/wallet/tests/transaction_service_tests/service.rs @@ -21,7 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::{ - collections::HashMap, convert::{TryFrom, TryInto}, mem::size_of, path::Path, @@ -50,7 +49,7 @@ use minotari_wallet::{ output_manager_service::{ config::OutputManagerServiceConfig, handle::{OutputManagerEvent, OutputManagerHandle}, - service::{Balance, OutputManagerService}, + service::OutputManagerService, storage::{ database::OutputManagerDatabase, models::KnownOneSidedPaymentScript, @@ -108,29 +107,20 @@ use tari_core::{ proto::wallet_rpc::{TxLocation, TxQueryResponse, TxSubmissionRejectionReason, TxSubmissionResponse}, rpc::BaseNodeWalletRpcServer, }, - blocks::BlockHeader, consensus::{ConsensusConstantsBuilder, ConsensusManager}, covenants::Covenant, one_sided::shared_secret_to_output_encryption_key, - proto::{ - base_node as base_node_proto, - base_node::{ - TxLocation as TxLocationProto, - TxQueryBatchResponse as TxQueryBatchResponseProto, - TxQueryBatchResponses as TxQueryBatchResponsesProto, - }, - types::Signature as SignatureProto, - }, + proto::base_node as base_node_proto, transactions::{ fee::Fee, - key_manager::{TransactionKeyManagerInitializer, TransactionKeyManagerInterface}, - tari_amount::*, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_wallet_output_with_data, - TestKeyManager, - TestParams, + key_manager::{ + create_memory_db_key_manager, + MemoryDbKeyManager, + TransactionKeyManagerInitializer, + TransactionKeyManagerInterface, }, + tari_amount::*, + test_helpers::{create_wallet_output_with_data, TestParams}, transaction_components::{KernelBuilder, OutputFeatures, Transaction}, transaction_protocol::{ proto::protocol as proto, @@ -159,7 +149,7 @@ use tari_script::{inputs, one_sided_payment_script, script, ExecutionStack}; use tari_service_framework::{reply_channel, RegisterHandle, StackBuilder}; use tari_shutdown::{Shutdown, ShutdownSignal}; use tari_test_utils::{comms_and_services::get_next_memory_address, random}; -use tari_utilities::{epoch_time::EpochTime, ByteArray, SafePassword}; +use tari_utilities::{ByteArray, SafePassword}; use tempfile::tempdir; use tokio::{ sync::{broadcast, broadcast::channel}, @@ -188,7 +178,7 @@ async fn setup_transaction_service>( OutputManagerHandle, CommsNode, WalletConnectivityHandle, - TestKeyManager, + MemoryDbKeyManager, ) { let (publisher, subscription_factory) = pubsub_connector(100); let subscription_factory = Arc::new(subscription_factory); @@ -230,7 +220,7 @@ async fn setup_transaction_service>( .add_initializer(RegisterHandle::new(comms.connectivity())) .add_initializer(OutputManagerServiceInitializer::< OutputManagerSqliteDatabase, - TestKeyManager, + MemoryDbKeyManager, >::new( OutputManagerServiceConfig::default(), oms_backend, @@ -243,7 +233,7 @@ async fn setup_transaction_service>( cipher, factories.clone(), )) - .add_initializer(TransactionServiceInitializer::<_, _, TestKeyManager>::new( + .add_initializer(TransactionServiceInitializer::<_, _, MemoryDbKeyManager>::new( TransactionServiceConfig { broadcast_monitoring_timeout: Duration::from_secs(5), chain_monitoring_timeout: Duration::from_secs(5), @@ -265,7 +255,7 @@ async fn setup_transaction_service>( .unwrap(); let output_manager_handle = handles.expect_handle::(); - let key_manager_handle = handles.expect_handle::(); + let key_manager_handle = handles.expect_handle::(); let transaction_service_handle = handles.expect_handle::(); let connectivity_service_handle = handles.expect_handle::(); @@ -283,7 +273,7 @@ async fn setup_transaction_service>( pub struct TransactionServiceNoCommsInterface { transaction_service_handle: TransactionServiceHandle, output_manager_service_handle: OutputManagerHandle, - key_manager_handle: TestKeyManager, + key_manager_handle: MemoryDbKeyManager, outbound_service_mock_state: OutboundServiceMockState, transaction_send_message_channel: Sender>>, @@ -373,7 +363,7 @@ async fn setup_transaction_service_no_comms( let ts_service_db = TransactionServiceSqliteDatabase::new(db_connection.clone(), cipher.clone()); let ts_db = TransactionDatabase::new(ts_service_db.clone()); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let oms_db = OutputManagerDatabase::new(OutputManagerSqliteDatabase::new(db_connection)); let wallet_identity = WalletIdentity::new(node_identity.clone(), Network::LocalNet); let output_manager_service = OutputManagerService::new( @@ -1980,7 +1970,7 @@ async fn test_accepting_unknown_tx_id_and_malformed_reply() { #[tokio::test] async fn finalize_tx_with_incorrect_pubkey() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let temp_dir = tempdir().unwrap(); let path_string = temp_dir.path().to_str().unwrap().to_string(); @@ -2101,7 +2091,7 @@ async fn finalize_tx_with_incorrect_pubkey() { #[tokio::test] async fn finalize_tx_with_missing_output() { let factories = CryptoFactories::default(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let temp_dir = tempdir().unwrap(); let path_string = temp_dir.path().to_str().unwrap().to_string(); @@ -2463,7 +2453,6 @@ async fn test_power_mode_updates() { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Outbound, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), @@ -2493,7 +2482,6 @@ async fn test_power_mode_updates() { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Outbound, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), @@ -2749,7 +2737,7 @@ async fn test_transaction_cancellation() { .remove(&tx_id) .is_none()); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let input = create_wallet_output_with_data( script!(Nop), OutputFeatures::default(), @@ -2761,7 +2749,7 @@ async fn test_transaction_cancellation() { .unwrap(); let constants = create_consensus_constants(0); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut builder = SenderTransactionProtocol::builder(constants, key_manager.clone()); let amount = MicroMinotari::from(10_000); let change = TestParams::new(&key_manager).await; @@ -3567,7 +3555,7 @@ async fn test_restarting_transaction_protocols() { .await; let constants = create_consensus_constants(0); let fee_calc = Fee::new(*constants.transaction_weight_params()); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut builder = SenderTransactionProtocol::builder(constants.clone(), key_manager.clone()); let fee = fee_calc.calculate(MicroMinotari(4), 1, 1, 1, 0); let change = TestParams::new(&key_manager).await; @@ -3749,1150 +3737,205 @@ async fn test_restarting_transaction_protocols() { } #[tokio::test] -async fn test_coinbase_transactions_rejection_same_hash_but_accept_on_same_height() { +async fn test_transaction_resending() { let factories = CryptoFactories::default(); - let (connection, _temp_dir) = make_wallet_database_connection(None); - - let mut alice_ts_interface = setup_transaction_service_no_comms(factories, connection, None).await; - - let block_height_a = 10; - let block_height_b = block_height_a + 1; + let alice_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE); + let bob_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE); + // Setup Alice wallet with no comms stack + let (connection, _tempdir) = make_wallet_database_connection(None); - let fees1 = 1000 * uT; - let reward1 = 1_000_000 * uT; + let mut alice_ts_interface = setup_transaction_service_no_comms( + factories.clone(), + connection, + Some(TransactionServiceConfig { + transaction_resend_period: Duration::from_secs(20), + resend_response_cooldown: Duration::from_secs(10), + ..Default::default() + }), + ) + .await; - let fees2 = 2000 * uT; - let reward2 = 2_000_000 * uT; + // Send a transaction to Bob + let alice_total_available = 250000 * uT; + let uo = make_input( + &mut OsRng, + alice_total_available, + &OutputFeatures::default(), + &alice_ts_interface.key_manager_handle, + ) + .await; + alice_ts_interface + .output_manager_service_handle + .add_output(uo, None) + .await + .unwrap(); - let fees3 = 4000 * uT; - let reward3 = 4_000_000 * uT; + let amount_sent = 100000 * uT; - // Create a coinbase Txn at the first block height - let _tx1 = alice_ts_interface + let bob_address = TariAddress::new(bob_node_identity.public_key().clone(), Network::LocalNet); + let tx_id = alice_ts_interface .transaction_service_handle - .generate_coinbase_transaction(reward1, fees1, block_height_a, b"test".to_vec()) + .send_transaction( + bob_address, + amount_sent, + UtxoSelectionCriteria::default(), + OutputFeatures::default(), + 100 * uT, + "Testing Message".to_string(), + ) .await .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() + + // Check that there were repeats + alice_ts_interface + .outbound_service_mock_state + .wait_call_count(2, Duration::from_secs(60)) .await - .unwrap(); - assert_eq!(transactions.len(), 1); - let _tx_id1 = transactions - .values() - .find(|tx| tx.amount == fees1 + reward1) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); + .expect("Alice call wait 1"); - // Create a second coinbase txn at the first block height, with same output hash as the previous one - // the previous one should be cancelled - let _tx1b = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward1, fees1, block_height_a, b"test".to_vec()) + let mut alice_sender_message = TransactionSenderMessage::None; + for _ in 0..2 { + let call = alice_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); + alice_sender_message = try_decode_sender_message(call.1.to_vec().clone()).unwrap(); + if let TransactionSenderMessage::Single(data) = alice_sender_message.clone() { + assert_eq!(data.tx_id, tx_id); + } else { + panic!("Should be a Single Transaction Sender Message") + } + } + + // Setup Bob's wallet with no comms stack + let (connection, _tempdir) = make_wallet_database_connection(None); + + let mut bob_ts_interface = setup_transaction_service_no_comms( + factories, + connection, + Some(TransactionServiceConfig { + transaction_resend_period: Duration::from_secs(20), + resend_response_cooldown: Duration::from_secs(10), + ..Default::default() + }), + ) + .await; + + // Pass sender message to Bob's wallet + bob_ts_interface + .transaction_send_message_channel + .send(create_dummy_message( + alice_sender_message.clone().try_into().unwrap(), + alice_node_identity.public_key(), + )) .await .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() + + // Check that the reply was repeated + bob_ts_interface + .outbound_service_mock_state + .wait_call_count(2, Duration::from_secs(60)) .await - .unwrap(); - assert_eq!(transactions.len(), 1); - let _tx_id1b = transactions - .values() - .find(|tx| tx.amount == fees1 + reward1) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); + .expect("Bob call wait 1"); - // Create another coinbase Txn at the same block height; the previous one should not be cancelled - let _tx2 = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward2, fees2, block_height_a, b"test".to_vec()) + let mut bob_reply_message; + for _ in 0..2 { + let call = bob_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); + bob_reply_message = try_decode_transaction_reply_message(call.1.to_vec().clone()).unwrap(); + assert_eq!(bob_reply_message.tx_id, tx_id); + } + + sleep(Duration::from_secs(2)).await; + // See if sending a second message too soon is ignored + bob_ts_interface + .transaction_send_message_channel + .send(create_dummy_message( + alice_sender_message.clone().try_into().unwrap(), + alice_node_identity.public_key(), + )) .await .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() + + assert!(bob_ts_interface + .outbound_service_mock_state + .wait_call_count(1, Duration::from_secs(2)) .await - .unwrap(); // Only one valid coinbase txn remains - assert_eq!(transactions.len(), 2); - let _tx_id2 = transactions - .values() - .find(|tx| tx.amount == fees2 + reward2) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); + .is_err()); - // Create a third coinbase Txn at the second block height; all the three should be valid - let _tx3 = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward3, fees3, block_height_b, b"test".to_vec()) + // Wait for the cooldown to expire but before the resend period has elapsed see if a repeat illicits a response. + sleep(Duration::from_secs(8)).await; + bob_ts_interface + .transaction_send_message_channel + .send(create_dummy_message( + alice_sender_message.try_into().unwrap(), + alice_node_identity.public_key(), + )) .await .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() + bob_ts_interface + .outbound_service_mock_state + .wait_call_count(2, Duration::from_secs(60)) .await - .unwrap(); - assert_eq!(transactions.len(), 3); - let _tx_id3 = transactions - .values() - .find(|tx| tx.amount == fees3 + reward3) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - assert!(transactions.values().any(|tx| tx.amount == fees1 + reward1)); - assert!(transactions.values().any(|tx| tx.amount == fees2 + reward2)); - assert!(transactions.values().any(|tx| tx.amount == fees3 + reward3)); -} - -#[tokio::test] -async fn test_coinbase_generation_and_monitoring() { - let factories = CryptoFactories::default(); + .expect("Bob call wait 2"); + let _result = bob_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); + let call = bob_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); + bob_reply_message = try_decode_transaction_reply_message(call.1.to_vec()).unwrap(); + assert_eq!(bob_reply_message.tx_id, tx_id); - let (connection, _temp_dir) = make_wallet_database_connection(None); - let mut alice_ts_interface = setup_transaction_service_no_comms(factories, connection, None).await; + let _result = alice_ts_interface.outbound_service_mock_state.take_calls().await; - let tx_backend = alice_ts_interface.ts_db; - let db = TransactionDatabase::new(tx_backend); - let mut alice_event_stream = alice_ts_interface.transaction_service_handle.get_event_stream(); + // Send the reply to Alice alice_ts_interface - .base_node_rpc_mock_state - .set_response_delay(Some(Duration::from_secs(1))); - - let block_height_a = 10; - let block_height_b = block_height_a + 1; + .transaction_ack_message_channel + .send(create_dummy_message( + bob_reply_message.clone().try_into().unwrap(), + bob_node_identity.public_key(), + )) + .await + .unwrap(); - let fees1 = 1000 * uT; - let reward1 = 1_000_000 * uT; + alice_ts_interface + .outbound_service_mock_state + .wait_call_count(2, Duration::from_secs(60)) + .await + .expect("Alice call wait 2"); - let fees2 = 2000 * uT; - let fees2b = 5000 * uT; - let reward2 = 2_000_000 * uT; + let _result = alice_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); + let call = alice_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); + let alice_finalize_message = try_decode_finalized_transaction_message(call.1.to_vec()).unwrap(); + assert_eq!(alice_finalize_message.tx_id, tx_id.as_u64()); - // Create a coinbase Txn at the first block height - let _tx1 = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward1, fees1, block_height_a, b"test".to_vec()) + // See if sending a second message before cooldown and see if it is ignored + alice_ts_interface + .transaction_ack_message_channel + .send(create_dummy_message( + bob_reply_message.clone().try_into().unwrap(), + bob_node_identity.public_key(), + )) .await .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() + + assert!(alice_ts_interface + .outbound_service_mock_state + .wait_call_count(1, Duration::from_secs(8)) .await - .unwrap(); - assert_eq!(transactions.len(), 1); - let tx_id1 = transactions - .values() - .find(|tx| tx.amount == fees1 + reward1) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); + .is_err()); - // Create another coinbase Txn at the next block height - let _tx2 = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward2, fees2, block_height_b, b"test".to_vec()) + // Wait for the cooldown to expire but before the resend period has elapsed see if a repeat illicts a response. + sleep(Duration::from_secs(6)).await; + + alice_ts_interface + .transaction_ack_message_channel + .send(create_dummy_message( + bob_reply_message.try_into().unwrap(), + bob_node_identity.public_key(), + )) .await .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - assert_eq!(transactions.len(), 2); - let tx_id2 = transactions - .values() - .find(|tx| tx.amount == fees2 + reward2) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - // Take out a second one at the second height which should not overwrite the initial one - let _tx2b = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward2, fees2b, block_height_b, b"test".to_vec()) - .await - .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - assert_eq!(transactions.len(), 3); - let tx_id2b = transactions - .values() - .find(|tx| tx.amount == fees2b + reward2) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - assert!(transactions.values().any(|tx| tx.amount == fees1 + reward1)); - assert!(transactions.values().any(|tx| tx.amount == fees2b + reward2)); - - let delay = sleep(Duration::from_secs(30)); - tokio::pin!(delay); - let mut count = 0usize; - loop { - tokio::select! { - event = alice_event_stream.recv() => { - if let TransactionEvent::ReceivedFinalizedTransaction(tx_id) = &*event.unwrap() { - if tx_id == &tx_id1 || tx_id == &tx_id2 || tx_id == &tx_id2b { - count += 1; - } - if count == 3 { - break; - } - } - }, - () = &mut delay => { - break; - }, - } - } - assert_eq!( - count, 3, - "Expected exactly two 'ReceivedFinalizedTransaction(_)' events" - ); - - // Now we will test validation where tx1 will not be found but tx2b will be unconfirmed, then confirmed. - let tx1 = db.get_completed_transaction(tx_id1).unwrap(); - let tx2b = db.get_completed_transaction(tx_id2b).unwrap(); - - let timestamp = EpochTime::now().as_u64(); - let mut block_headers = HashMap::new(); - for i in 0..=4 { - let mut block_header = BlockHeader::new(1); - block_header.height = i; - block_headers.insert(i, block_header.clone()); - } - alice_ts_interface - .base_node_rpc_mock_state - .set_blocks(block_headers.clone()); - let mut transaction_query_batch_responses = vec![ - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from( - tx1.transaction.first_kernel_excess_sig().unwrap().clone(), - )), - location: TxLocationProto::from(TxLocation::NotStored) as i32, - block_hash: vec![], - confirmations: 0, - block_height: 0, - mined_timestamp: 0, - }, - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from( - tx2b.transaction.first_kernel_excess_sig().unwrap().clone(), - )), - location: TxLocationProto::from(TxLocation::Mined) as i32, - block_hash: block_headers.get(&1).unwrap().hash().to_vec(), - confirmations: 0, - block_height: 1, - mined_timestamp: timestamp, - }, - ]; - let batch_query_response = TxQueryBatchResponsesProto { - responses: transaction_query_batch_responses.clone(), - is_synced: true, - tip_hash: block_headers.get(&1).unwrap().hash().to_vec(), - height_of_longest_chain: 1, - tip_mined_timestamp: timestamp, - }; - - alice_ts_interface - .base_node_rpc_mock_state - .set_transaction_query_batch_responses(batch_query_response); - - alice_ts_interface - .transaction_service_handle - .validate_transactions() - .await - .expect("Validation should start"); - - let _tx_batch_query_calls = alice_ts_interface - .base_node_rpc_mock_state - .wait_pop_transaction_batch_query_calls(2, Duration::from_secs(30)) - .await - .unwrap(); - - let completed_txs = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - - assert_eq!(completed_txs.len(), 3); - - let tx = completed_txs.get(&tx_id1).unwrap(); - assert_eq!(tx.status, TransactionStatus::Coinbase); - - let tx = completed_txs.get(&tx_id2b).unwrap(); - assert_eq!(tx.status, TransactionStatus::MinedUnconfirmed); - - // Now we will have tx_id2b becoming confirmed - let _tx_query_batch_responses = transaction_query_batch_responses.pop(); - transaction_query_batch_responses.push(TxQueryBatchResponseProto { - signature: Some(SignatureProto::from( - tx2b.transaction.first_kernel_excess_sig().unwrap().clone(), - )), - location: TxLocationProto::from(TxLocation::Mined) as i32, - block_hash: block_headers.get(&4).unwrap().hash().to_vec(), - confirmations: 3, - block_height: 4, - mined_timestamp: timestamp, - }); - - let batch_query_response = TxQueryBatchResponsesProto { - responses: transaction_query_batch_responses, - is_synced: true, - tip_hash: block_headers.get(&4).unwrap().hash().to_vec(), - height_of_longest_chain: 4, - tip_mined_timestamp: timestamp, - }; - alice_ts_interface - .base_node_rpc_mock_state - .set_transaction_query_batch_responses(batch_query_response); - - alice_ts_interface - .transaction_service_handle - .validate_transactions() - .await - .expect("Validation should start"); - - let _tx_batch_query_calls = alice_ts_interface - .base_node_rpc_mock_state - .wait_pop_transaction_batch_query_calls(2, Duration::from_secs(30)) - .await - .unwrap(); - - let completed_txs = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - - let tx = completed_txs.get(&tx_id2b).unwrap(); - assert_eq!(tx.status, TransactionStatus::MinedConfirmed); -} - -#[tokio::test] -async fn test_coinbase_abandoned() { - let factories = CryptoFactories::default(); - - let (connection, _temp_dir) = make_wallet_database_connection(None); - - let mut alice_ts_interface = setup_transaction_service_no_comms(factories, connection, None).await; - let mut alice_event_stream = alice_ts_interface.transaction_service_handle.get_event_stream(); - - let block_height_a = 10; - - // First we create un unmined coinbase and then abandon it - let fees1 = 1000 * uT; - let reward1 = 1_000_000 * uT; - - let tx1 = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward1, fees1, block_height_a, b"test".to_vec()) - .await - .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - assert_eq!(transactions.len(), 1); - let tx_id1 = transactions - .values() - .find(|tx| tx.amount == fees1 + reward1) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - let timestamp = EpochTime::now().as_u64(); - - let transaction_query_batch_responses = vec![TxQueryBatchResponseProto { - signature: Some(SignatureProto::from(tx1.first_kernel_excess_sig().unwrap().clone())), - location: TxLocationProto::from(TxLocation::InMempool) as i32, - block_hash: vec![], - confirmations: 0, - block_height: 0, - mined_timestamp: 0, - }]; - - let batch_query_response = TxQueryBatchResponsesProto { - responses: transaction_query_batch_responses, - is_synced: true, - tip_hash: [5u8; 32].to_vec(), - height_of_longest_chain: block_height_a + TransactionServiceConfig::default().num_confirmations_required + 1, - tip_mined_timestamp: timestamp, - }; - - alice_ts_interface - .base_node_rpc_mock_state - .set_transaction_query_batch_responses(batch_query_response); - - let balance = alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap(); - assert_eq!(balance.pending_incoming_balance, MicroMinotari::from(0)); - - let validation_id = alice_ts_interface - .transaction_service_handle - .validate_transactions() - .await - .expect("Validation should start"); - - let delay = sleep(Duration::from_secs(30)); - tokio::pin!(delay); - let mut cancelled = false; - let mut completed = false; - loop { - tokio::select! { - event = alice_event_stream.recv() => { - match &*event.unwrap() { - TransactionEvent::TransactionValidationCompleted(id) => { - if id == &validation_id { - completed = true; - } - }, - TransactionEvent::TransactionCancelled(tx_id, _) => { - if tx_id == &tx_id1 { - cancelled = true; - } - }, - _ => (), - } - - if cancelled && completed { - break; - } - }, - () = &mut delay => { - break; - }, - } - } - assert!(cancelled, "Expected a TransactionCancelled event"); - assert!(completed, "Expected a TransactionValidationCompleted event"); - - let txs = alice_ts_interface - .transaction_service_handle - .get_cancelled_completed_transactions() - .await - .unwrap(); - assert!(txs.get(&tx_id1).is_some()); - - let balance = alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap(); - assert_eq!(balance, Balance { - available_balance: MicroMinotari(0), - time_locked_balance: Some(MicroMinotari(0)), - pending_incoming_balance: MicroMinotari(0), - pending_outgoing_balance: MicroMinotari(0) - }); - - let invalid_txs = alice_ts_interface - .output_manager_service_handle - .get_invalid_outputs() - .await - .unwrap(); - assert!(invalid_txs.is_empty()); - - // Now we will make a coinbase that will be mined, reorged out and then reorged back in - let fees2 = 2000 * uT; - let reward2 = 2_000_000 * uT; - let block_height_b = 11; - - let tx2 = alice_ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward2, fees2, block_height_b, b"test".to_vec()) - .await - .unwrap(); - let transactions = alice_ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - assert_eq!(transactions.len(), 1); - let tx_id2 = transactions - .values() - .find(|tx| tx.amount == fees2 + reward2) - .unwrap() - .tx_id; - assert_eq!( - alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - let transaction_query_batch_responses = vec![ - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from(tx1.first_kernel_excess_sig().unwrap().clone())), - location: TxLocationProto::from(TxLocation::NotStored) as i32, - block_hash: vec![], - confirmations: 0, - block_height: 0, - mined_timestamp: 0, - }, - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from(tx2.first_kernel_excess_sig().unwrap().clone())), - location: TxLocationProto::from(TxLocation::Mined) as i32, - block_hash: [11u8; 32].to_vec(), - confirmations: 2, - block_height: block_height_b, - mined_timestamp: timestamp, - }, - ]; - - let batch_query_response = TxQueryBatchResponsesProto { - responses: transaction_query_batch_responses, - is_synced: true, - tip_hash: [13u8; 32].to_vec(), - height_of_longest_chain: block_height_b + 2, - tip_mined_timestamp: timestamp, - }; - - alice_ts_interface - .base_node_rpc_mock_state - .set_transaction_query_batch_responses(batch_query_response); - - let mut block_headers = HashMap::new(); - for i in 0..=(block_height_b + 2) { - let mut block_header = BlockHeader::new(1); - block_header.height = i; - block_headers.insert(i, block_header.clone()); - } - alice_ts_interface.base_node_rpc_mock_state.set_blocks(block_headers); - - let validation_id = alice_ts_interface - .transaction_service_handle - .validate_transactions() - .await - .expect("Validation should start"); - - let delay = sleep(Duration::from_secs(30)); - tokio::pin!(delay); - let mut completed = false; - let mut mined_unconfirmed = false; - loop { - tokio::select! { - event = alice_event_stream.recv() => { - match &*event.unwrap() { - TransactionEvent::TransactionValidationCompleted(id) => { - if id == &validation_id { - completed = true; - } - }, - TransactionEvent::TransactionMinedUnconfirmed{tx_id, num_confirmations:_, is_valid: _} => { - if tx_id == &tx_id2 { - mined_unconfirmed = true; - } - }, - _ => (), - } - - if mined_unconfirmed && completed { - break; - } - }, - () = &mut delay => { - break; - }, - } - } - assert!(mined_unconfirmed, "Expected a TransactionMinedUnconfirmed event"); - assert!(completed, "Expected a TransactionValidationCompleted event"); - - let tx = alice_ts_interface - .transaction_service_handle - .get_completed_transaction(tx_id2) - .await - .unwrap(); - assert_eq!(tx.status, TransactionStatus::MinedUnconfirmed); - - // Now we create a reorg - let transaction_query_batch_responses = vec![ - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from(tx1.first_kernel_excess_sig().unwrap().clone())), - location: TxLocationProto::from(TxLocation::NotStored) as i32, - block_hash: vec![], - confirmations: 0, - block_height: 0, - mined_timestamp: 0, - }, - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from(tx2.first_kernel_excess_sig().unwrap().clone())), - location: TxLocationProto::from(TxLocation::NotStored) as i32, - block_hash: vec![], - confirmations: 0, - block_height: 0, - mined_timestamp: 0, - }, - ]; - - let batch_query_response = TxQueryBatchResponsesProto { - responses: transaction_query_batch_responses, - is_synced: true, - tip_hash: [12u8; 32].to_vec(), - height_of_longest_chain: block_height_b + TransactionServiceConfig::default().num_confirmations_required + 1, - tip_mined_timestamp: timestamp, - }; - - alice_ts_interface - .base_node_rpc_mock_state - .set_transaction_query_batch_responses(batch_query_response); - - let mut block_headers = HashMap::new(); - for i in 0..=(block_height_b + TransactionServiceConfig::default().num_confirmations_required + 1) { - let mut block_header = BlockHeader::new(2); - block_header.height = i; - block_headers.insert(i, block_header.clone()); - } - alice_ts_interface.base_node_rpc_mock_state.set_blocks(block_headers); - - let validation_id = alice_ts_interface - .transaction_service_handle - .validate_transactions() - .await - .expect("Validation should start"); - - let delay = sleep(Duration::from_secs(30)); - tokio::pin!(delay); - let mut completed = false; - let mut broadcast = false; - let mut cancelled = false; - loop { - tokio::select! { - event = alice_event_stream.recv() => { - match &*event.unwrap() { - TransactionEvent::TransactionBroadcast(tx_id) => { - if tx_id == &tx_id2 { - broadcast = true; - } - }, - TransactionEvent::TransactionCancelled(tx_id, _) => { - if tx_id == &tx_id2 { - cancelled = true; - } - }, - TransactionEvent::TransactionValidationCompleted(id) => { - if id == &validation_id { - completed = true; - } - }, - _ => (), - } - - if cancelled && broadcast && completed { - break; - } - }, - () = &mut delay => { - break; - }, - } - } - assert!(cancelled, "Expected a TransactionCancelled event"); - assert!(broadcast, "Expected a TransactionBroadcast event"); - assert!(completed, "Expected a TransactionValidationCompleted event"); - - let txs = alice_ts_interface - .transaction_service_handle - .get_cancelled_completed_transactions() - .await - .unwrap(); - - assert!(txs.get(&tx_id1).is_some()); - assert!(txs.get(&tx_id2).is_some()); - - let balance = alice_ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap(); - assert_eq!(balance, Balance { - available_balance: MicroMinotari(0), - time_locked_balance: Some(MicroMinotari(0)), - pending_incoming_balance: MicroMinotari(0), - pending_outgoing_balance: MicroMinotari(0) - }); - - // Now reorg again and have tx2 be mined - let mut block_headers = HashMap::new(); - for i in 0..=15 { - let mut block_header = BlockHeader::new(1); - block_header.height = i; - block_headers.insert(i, block_header.clone()); - } - alice_ts_interface - .base_node_rpc_mock_state - .set_blocks(block_headers.clone()); - - let transaction_query_batch_responses = vec![ - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from(tx1.first_kernel_excess_sig().unwrap().clone())), - location: TxLocationProto::from(TxLocation::NotStored) as i32, - block_hash: vec![], - confirmations: 0, - block_height: 0, - mined_timestamp: 0, - }, - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from(tx2.first_kernel_excess_sig().unwrap().clone())), - location: TxLocationProto::from(TxLocation::Mined) as i32, - block_hash: block_headers.get(&10).unwrap().hash().to_vec(), - confirmations: 5, - block_height: 10, - mined_timestamp: timestamp, - }, - ]; - - let batch_query_response = TxQueryBatchResponsesProto { - responses: transaction_query_batch_responses, - is_synced: true, - tip_hash: [20u8; 32].to_vec(), - height_of_longest_chain: 20, - tip_mined_timestamp: timestamp, - }; - - alice_ts_interface - .base_node_rpc_mock_state - .set_transaction_query_batch_responses(batch_query_response); - - let validation_id = alice_ts_interface - .transaction_service_handle - .validate_transactions() - .await - .expect("Validation should start"); - - let delay = sleep(Duration::from_secs(60)); - tokio::pin!(delay); - let mut mined = false; - let mut cancelled = false; - let mut completed = false; - loop { - tokio::select! { - event = alice_event_stream.recv() => { - match &*event.unwrap() { - TransactionEvent::TransactionMined { tx_id, is_valid: _ } => { - if tx_id == &tx_id2 { - mined = true; - } - }, - TransactionEvent::TransactionCancelled(tx_id, _) => { - if tx_id == &tx_id1 { - cancelled = true; - } - }, - TransactionEvent::TransactionValidationCompleted(id) => { - if id == &validation_id { - completed = true; - } - }, - _ => (), - } - - if mined && cancelled && completed { - break; - } - }, - () = &mut delay => { - break; - }, - } - } - assert!(mined, "Expected to received TransactionMined event"); - assert!(cancelled, "Expected to received TransactionCancelled event"); - assert!(completed, "Expected a TransactionValidationCompleted event"); -} - -#[tokio::test] -async fn test_coinbase_transaction_reused_for_same_height() { - let factories = CryptoFactories::default(); - let (connection, _temp_dir) = make_wallet_database_connection(None); - - let mut ts_interface = setup_transaction_service_no_comms(factories, connection, None).await; - - let blockheight1 = 10; - let fees1 = 2000 * uT; - let reward1 = 1_000_000 * uT; - - let blockheight2 = 11; - let fees2 = 3000 * uT; - let reward2 = 2_000_000 * uT; - - // a requested coinbase transaction for the same height and amount should be the same - let tx1 = ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward1, fees1, blockheight1, b"test".to_vec()) - .await - .unwrap(); - - let tx2 = ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward1, fees1, blockheight1, b"test".to_vec()) - .await - .unwrap(); - - assert_eq!(tx1, tx2); - let transactions = ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - - assert_eq!(transactions.len(), 1); - let mut amount = MicroMinotari::zero(); - for tx in transactions.values() { - amount += tx.amount; - } - assert_eq!(amount, fees1 + reward1); - assert_eq!( - ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - // a requested coinbase transaction for the same height but new amount should be different - let tx3 = ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward2, fees2, blockheight1, b"test".to_vec()) - .await - .unwrap(); - - assert_ne!(tx3, tx1); - let transactions = ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - assert_eq!(transactions.len(), 2); - let mut amount = MicroMinotari::zero(); - for tx in transactions.values() { - amount += tx.amount; - } - assert_eq!(amount, fees1 + reward1 + fees2 + reward2); - assert_eq!( - ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); - - // a requested coinbase transaction for a new height should be different - let tx_height2 = ts_interface - .transaction_service_handle - .generate_coinbase_transaction(reward2, fees2, blockheight2, b"test".to_vec()) - .await - .unwrap(); - - assert_ne!(tx1, tx_height2); - let transactions = ts_interface - .transaction_service_handle - .get_completed_transactions() - .await - .unwrap(); - assert_eq!(transactions.len(), 3); - let mut amount = MicroMinotari::zero(); - for tx in transactions.values() { - amount += tx.amount; - } - assert_eq!(amount, fees1 + reward1 + fees2 + reward2 + fees2 + reward2); - assert_eq!( - ts_interface - .output_manager_service_handle - .get_balance() - .await - .unwrap() - .pending_incoming_balance, - MicroMinotari::from(0) - ); -} - -#[tokio::test] -async fn test_transaction_resending() { - let factories = CryptoFactories::default(); - - let alice_node_identity = - NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE); - let bob_node_identity = - NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE); - // Setup Alice wallet with no comms stack - let (connection, _tempdir) = make_wallet_database_connection(None); - - let mut alice_ts_interface = setup_transaction_service_no_comms( - factories.clone(), - connection, - Some(TransactionServiceConfig { - transaction_resend_period: Duration::from_secs(20), - resend_response_cooldown: Duration::from_secs(10), - ..Default::default() - }), - ) - .await; - - // Send a transaction to Bob - let alice_total_available = 250000 * uT; - let uo = make_input( - &mut OsRng, - alice_total_available, - &OutputFeatures::default(), - &alice_ts_interface.key_manager_handle, - ) - .await; - alice_ts_interface - .output_manager_service_handle - .add_output(uo, None) - .await - .unwrap(); - - let amount_sent = 100000 * uT; - - let bob_address = TariAddress::new(bob_node_identity.public_key().clone(), Network::LocalNet); - let tx_id = alice_ts_interface - .transaction_service_handle - .send_transaction( - bob_address, - amount_sent, - UtxoSelectionCriteria::default(), - OutputFeatures::default(), - 100 * uT, - "Testing Message".to_string(), - ) - .await - .unwrap(); - - // Check that there were repeats - alice_ts_interface - .outbound_service_mock_state - .wait_call_count(2, Duration::from_secs(60)) - .await - .expect("Alice call wait 1"); - - let mut alice_sender_message = TransactionSenderMessage::None; - for _ in 0..2 { - let call = alice_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); - alice_sender_message = try_decode_sender_message(call.1.to_vec().clone()).unwrap(); - if let TransactionSenderMessage::Single(data) = alice_sender_message.clone() { - assert_eq!(data.tx_id, tx_id); - } else { - panic!("Should be a Single Transaction Sender Message") - } - } - - // Setup Bob's wallet with no comms stack - let (connection, _tempdir) = make_wallet_database_connection(None); - - let mut bob_ts_interface = setup_transaction_service_no_comms( - factories, - connection, - Some(TransactionServiceConfig { - transaction_resend_period: Duration::from_secs(20), - resend_response_cooldown: Duration::from_secs(10), - ..Default::default() - }), - ) - .await; - - // Pass sender message to Bob's wallet - bob_ts_interface - .transaction_send_message_channel - .send(create_dummy_message( - alice_sender_message.clone().try_into().unwrap(), - alice_node_identity.public_key(), - )) - .await - .unwrap(); - - // Check that the reply was repeated - bob_ts_interface - .outbound_service_mock_state - .wait_call_count(2, Duration::from_secs(60)) - .await - .expect("Bob call wait 1"); - - let mut bob_reply_message; - for _ in 0..2 { - let call = bob_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); - bob_reply_message = try_decode_transaction_reply_message(call.1.to_vec().clone()).unwrap(); - assert_eq!(bob_reply_message.tx_id, tx_id); - } - - sleep(Duration::from_secs(2)).await; - // See if sending a second message too soon is ignored - bob_ts_interface - .transaction_send_message_channel - .send(create_dummy_message( - alice_sender_message.clone().try_into().unwrap(), - alice_node_identity.public_key(), - )) - .await - .unwrap(); - - assert!(bob_ts_interface - .outbound_service_mock_state - .wait_call_count(1, Duration::from_secs(2)) - .await - .is_err()); - - // Wait for the cooldown to expire but before the resend period has elapsed see if a repeat illicits a response. - sleep(Duration::from_secs(8)).await; - bob_ts_interface - .transaction_send_message_channel - .send(create_dummy_message( - alice_sender_message.try_into().unwrap(), - alice_node_identity.public_key(), - )) - .await - .unwrap(); - bob_ts_interface - .outbound_service_mock_state - .wait_call_count(2, Duration::from_secs(60)) - .await - .expect("Bob call wait 2"); - let _result = bob_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); - let call = bob_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); - bob_reply_message = try_decode_transaction_reply_message(call.1.to_vec()).unwrap(); - assert_eq!(bob_reply_message.tx_id, tx_id); - - let _result = alice_ts_interface.outbound_service_mock_state.take_calls().await; - - // Send the reply to Alice - alice_ts_interface - .transaction_ack_message_channel - .send(create_dummy_message( - bob_reply_message.clone().try_into().unwrap(), - bob_node_identity.public_key(), - )) - .await - .unwrap(); - - alice_ts_interface - .outbound_service_mock_state - .wait_call_count(2, Duration::from_secs(60)) - .await - .expect("Alice call wait 2"); - - let _result = alice_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); - let call = alice_ts_interface.outbound_service_mock_state.pop_call().await.unwrap(); - let alice_finalize_message = try_decode_finalized_transaction_message(call.1.to_vec()).unwrap(); - assert_eq!(alice_finalize_message.tx_id, tx_id.as_u64()); - - // See if sending a second message before cooldown and see if it is ignored - alice_ts_interface - .transaction_ack_message_channel - .send(create_dummy_message( - bob_reply_message.clone().try_into().unwrap(), - bob_node_identity.public_key(), - )) - .await - .unwrap(); - - assert!(alice_ts_interface - .outbound_service_mock_state - .wait_call_count(1, Duration::from_secs(8)) - .await - .is_err()); - - // Wait for the cooldown to expire but before the resend period has elapsed see if a repeat illicts a response. - sleep(Duration::from_secs(6)).await; - - alice_ts_interface - .transaction_ack_message_channel - .send(create_dummy_message( - bob_reply_message.try_into().unwrap(), - bob_node_identity.public_key(), - )) - .await - .unwrap(); - - alice_ts_interface - .outbound_service_mock_state - .wait_call_count(1, Duration::from_secs(30)) + + alice_ts_interface + .outbound_service_mock_state + .wait_call_count(1, Duration::from_secs(30)) .await .expect("Alice call wait 3"); @@ -4911,7 +3954,7 @@ async fn test_resend_on_startup() { NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE); // First we will check the Send Tranasction message - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let input = create_wallet_output_with_data( script!(Nop), OutputFeatures::default(), @@ -4922,7 +3965,7 @@ async fn test_resend_on_startup() { .await .unwrap(); let constants = create_consensus_constants(0); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut builder = SenderTransactionProtocol::builder(constants.clone(), key_manager.clone()); let amount = MicroMinotari::from(10_000); let change = TestParams::new(&key_manager).await; @@ -5418,7 +4461,7 @@ async fn test_transaction_timeout_cancellation() { // Now to test if the timeout has elapsed during downtime and that it is honoured on startup // First we will check the Send Transction message - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let input = create_wallet_output_with_data( script!(Nop), OutputFeatures::default(), @@ -5429,7 +4472,7 @@ async fn test_transaction_timeout_cancellation() { .await .unwrap(); let constants = create_consensus_constants(0); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut builder = SenderTransactionProtocol::builder(constants, key_manager.clone()); let amount = MicroMinotari::from(10_000); let change = TestParams::new(&key_manager).await; @@ -6003,7 +5046,6 @@ async fn broadcast_all_completed_transactions_on_startup() { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Outbound, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, transaction_signature: tx.first_kernel_excess_sig().unwrap_or(&Signature::default()).clone(), @@ -6113,17 +5155,42 @@ async fn test_update_faux_tx_on_oms_validation() { alice_ts_interface.base_node_identity.public_key().clone(), Network::LocalNet, ); + + let uo_1 = make_input( + &mut OsRng.clone(), + MicroMinotari::from(10000), + &OutputFeatures::default(), + &alice_ts_interface.key_manager_handle, + ) + .await; + let uo_2 = make_input( + &mut OsRng.clone(), + MicroMinotari::from(20000), + &OutputFeatures::default(), + &alice_ts_interface.key_manager_handle, + ) + .await; + let uo_3 = make_input( + &mut OsRng.clone(), + MicroMinotari::from(30000), + &OutputFeatures::default(), + &alice_ts_interface.key_manager_handle, + ) + .await; + let tx_id_1 = alice_ts_interface .transaction_service_handle .import_utxo_with_status( MicroMinotari::from(10000), alice_address.clone(), "blah".to_string(), - None, ImportStatus::Imported, None, None, None, + uo_1.to_transaction_output(&alice_ts_interface.key_manager_handle) + .await + .unwrap(), ) .await .unwrap(); @@ -6133,51 +5200,33 @@ async fn test_update_faux_tx_on_oms_validation() { MicroMinotari::from(20000), alice_address.clone(), "one-sided 1".to_string(), - None, ImportStatus::FauxUnconfirmed, None, None, None, + uo_2.to_transaction_output(&alice_ts_interface.key_manager_handle) + .await + .unwrap(), ) .await .unwrap(); - let tx_id_3 = alice_ts_interface .transaction_service_handle .import_utxo_with_status( MicroMinotari::from(30000), alice_address, "one-sided 2".to_string(), - None, ImportStatus::FauxConfirmed, None, None, None, + uo_3.to_transaction_output(&alice_ts_interface.key_manager_handle) + .await + .unwrap(), ) .await .unwrap(); - let uo_1 = make_input( - &mut OsRng.clone(), - MicroMinotari::from(10000), - &OutputFeatures::default(), - &alice_ts_interface.key_manager_handle, - ) - .await; - let uo_2 = make_input( - &mut OsRng.clone(), - MicroMinotari::from(20000), - &OutputFeatures::default(), - &alice_ts_interface.key_manager_handle, - ) - .await; - let uo_3 = make_input( - &mut OsRng.clone(), - MicroMinotari::from(30000), - &OutputFeatures::default(), - &alice_ts_interface.key_manager_handle, - ) - .await; for (tx_id, uo) in [(tx_id_1, uo_1), (tx_id_2, uo_2), (tx_id_3, uo_3)] { alice_ts_interface .output_manager_service_handle diff --git a/base_layer/wallet/tests/transaction_service_tests/storage.rs b/base_layer/wallet/tests/transaction_service_tests/storage.rs index fb4044c888..b84475d8ee 100644 --- a/base_layer/wallet/tests/transaction_service_tests/storage.rs +++ b/base_layer/wallet/tests/transaction_service_tests/storage.rs @@ -50,9 +50,9 @@ use tari_common_types::{ use tari_core::{ covenants::Covenant, transactions::{ - key_manager::{TransactionKeyManagerBranch, TransactionKeyManagerInterface}, + key_manager::{create_memory_db_key_manager, TransactionKeyManagerBranch, TransactionKeyManagerInterface}, tari_amount::{uT, MicroMinotari}, - test_helpers::{create_test_core_key_manager_with_memory_db, create_wallet_output_with_data, TestParams}, + test_helpers::{create_wallet_output_with_data, TestParams}, transaction_components::{ OutputFeatures, RangeProofType, @@ -74,7 +74,7 @@ use tempfile::tempdir; pub async fn test_db_backend(backend: T) { let mut db = TransactionDatabase::new(backend); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let input = create_wallet_output_with_data( script!(Nop), OutputFeatures::default(), @@ -85,7 +85,7 @@ pub async fn test_db_backend(backend: T) { .await .unwrap(); let constants = create_consensus_constants(0); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut builder = SenderTransactionProtocol::builder(constants.clone(), key_manager.clone()); let amount = MicroMinotari::from(10_000); builder @@ -323,7 +323,6 @@ pub async fn test_db_backend(backend: T) { timestamp: Utc::now().naive_utc(), cancelled: None, direction: TransactionDirection::Outbound, - coinbase_block_height: None, send_count: 0, last_send_timestamp: None, @@ -517,14 +516,10 @@ pub async fn test_db_backend(backend: T) { panic!("Should have found cancelled outbound tx"); } + // Transactions with empty kernel signatures should not be returned with this method, as those will be considered + // as faux transactions (imported or one-sided) let unmined_txs = db.fetch_unconfirmed_transactions_info().unwrap(); - - assert_eq!(unmined_txs.len(), 4); - - db.set_transaction_as_unmined(completed_txs[0].tx_id).unwrap(); - - let unmined_txs = db.fetch_unconfirmed_transactions_info().unwrap(); - assert_eq!(unmined_txs.len(), 5); + assert_eq!(unmined_txs.len(), 0); } #[tokio::test] @@ -574,10 +569,10 @@ async fn import_tx_and_read_it_from_db() { "message".to_string(), Utc::now().naive_utc(), TransactionDirection::Inbound, - Some(0), Some(5), Some(NaiveDateTime::from_timestamp_opt(0, 0).unwrap()), - ); + ) + .unwrap(); sqlite_db .write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( @@ -603,10 +598,10 @@ async fn import_tx_and_read_it_from_db() { "message".to_string(), Utc::now().naive_utc(), TransactionDirection::Inbound, - Some(0), Some(6), Some(NaiveDateTime::from_timestamp_opt(0, 0).unwrap()), - ); + ) + .unwrap(); sqlite_db .write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( @@ -632,10 +627,10 @@ async fn import_tx_and_read_it_from_db() { "message".to_string(), Utc::now().naive_utc(), TransactionDirection::Inbound, - Some(0), Some(7), Some(NaiveDateTime::from_timestamp_opt(0, 0).unwrap()), - ); + ) + .unwrap(); sqlite_db .write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( diff --git a/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs b/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs index 9bad075823..23cc579dde 100644 --- a/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs +++ b/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs @@ -43,7 +43,7 @@ use minotari_wallet::{ service::TransactionServiceResources, storage::{ database::TransactionDatabase, - models::{CompletedTransaction, TxCancellationReason}, + models::CompletedTransaction, sqlite_db::TransactionServiceSqliteDatabase, }, }, @@ -78,8 +78,9 @@ use tari_core::{ types::Signature as SignatureProto, }, transactions::{ + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, tari_amount::{uT, MicroMinotari, T}, - test_helpers::{create_test_core_key_manager_with_memory_db, schema_to_transaction, TestKeyManager}, + test_helpers::schema_to_transaction, transaction_components::OutputFeatures, CryptoFactories, }, @@ -98,7 +99,7 @@ use crate::support::{ }; pub async fn setup() -> ( - TransactionServiceResources, + TransactionServiceResources, OutboundServiceMockState, MockRpcServer>, Arc, @@ -145,7 +146,7 @@ pub async fn setup() -> ( let (oms_event_publisher, _) = broadcast::channel(200); let output_manager_service_handle = OutputManagerHandle::new(oms_request_sender, oms_event_publisher); - let core_key_manager_service_handle = create_test_core_key_manager_with_memory_db(); + let core_key_manager_service_handle = create_memory_db_key_manager(); let (outbound_message_requester, mock_outbound_service) = create_outbound_service_mock(100); let outbound_mock_state = mock_outbound_service.get_state(); @@ -193,13 +194,15 @@ pub async fn add_transaction_to_database( tx_id: TxId, amount: MicroMinotari, status: Option, - coinbase_block_height: Option, db: TransactionDatabase, ) { - let key_manager_handle = create_test_core_key_manager_with_memory_db(); + let key_manager_handle = create_memory_db_key_manager(); let uo0 = make_input(&mut OsRng, 10 * amount, &OutputFeatures::default(), &key_manager_handle).await; - let (txs1, _uou1) = - schema_to_transaction(&[txn_schema!(from: vec![uo0], to: vec![amount])], &key_manager_handle).await; + let (txs1, _uou1) = schema_to_transaction( + &[txn_schema!(from: vec![uo0.clone()], to: vec![amount])], + &key_manager_handle, + ) + .await; let tx1 = (*txs1[0]).clone(); let completed_tx1 = CompletedTransaction::new( tx_id, @@ -212,10 +215,10 @@ pub async fn add_transaction_to_database( "Test".to_string(), Utc::now().naive_local(), TransactionDirection::Outbound, - coinbase_block_height, None, None, - ); + ) + .unwrap(); db.insert_completed_transaction(tx_id, completed_tx1).unwrap(); } @@ -228,7 +231,6 @@ pub async fn oms_reply_channel_task( let (request, reply_tx) = request_context.split(); let response = match request { OutputManagerRequest::CancelTransaction(_) => Ok(OutputManagerResponse::TransactionCancelled), - OutputManagerRequest::SetCoinbaseAbandoned(_, _) => Ok(OutputManagerResponse::CoinbaseAbandonedSet), _ => Err(OutputManagerError::InvalidResponseError( "Unhandled request type".to_string(), )), @@ -270,7 +272,7 @@ async fn tx_broadcast_protocol_submit_success() { // Fails because there is no transaction in the database to be broadcast assert!(join_handle.await.unwrap().is_err()); - add_transaction_to_database(1u64.into(), 1 * T, None, None, resources.db.clone()).await; + add_transaction_to_database(1u64.into(), 1 * T, None, resources.db.clone()).await; let db_completed_tx = resources.db.get_completed_transaction(1u64.into()).unwrap(); assert!(db_completed_tx.confirmations.is_none()); @@ -339,7 +341,7 @@ async fn tx_broadcast_protocol_submit_rejection() { ) = setup().await; let mut event_stream = resources.event_publisher.subscribe(); - add_transaction_to_database(1u64.into(), 1 * T, None, None, resources.db.clone()).await; + add_transaction_to_database(1u64.into(), 1 * T, None, resources.db.clone()).await; let timeout_update_watch = Watch::new(Duration::from_secs(1)); wallet_connectivity.notify_base_node_set(server_node_identity.to_peer()); // Now we add the connection @@ -410,7 +412,7 @@ async fn tx_broadcast_protocol_restart_protocol_as_query() { wallet_connectivity, ) = setup().await; - add_transaction_to_database(1u64.into(), 1 * T, None, None, resources.db.clone()).await; + add_transaction_to_database(1u64.into(), 1 * T, None, resources.db.clone()).await; // Set Base Node query response to be not stored, as if the base node does not have the tx in its pool rpc_service_state.set_transaction_query_response(TxQueryResponse { @@ -502,7 +504,7 @@ async fn tx_broadcast_protocol_submit_success_followed_by_rejection() { ) = setup().await; let mut event_stream = resources.event_publisher.subscribe(); - add_transaction_to_database(1u64.into(), 1 * T, None, None, resources.db.clone()).await; + add_transaction_to_database(1u64.into(), 1 * T, None, resources.db.clone()).await; resources.config.transaction_mempool_resubmission_window = Duration::from_secs(3); resources.config.broadcast_monitoring_timeout = Duration::from_secs(60); @@ -592,7 +594,7 @@ async fn tx_broadcast_protocol_submit_already_mined() { _transaction_event_receiver, wallet_connectivity, ) = setup().await; - add_transaction_to_database(1u64.into(), 1 * T, None, None, resources.db.clone()).await; + add_transaction_to_database(1u64.into(), 1 * T, None, resources.db.clone()).await; // Set Base Node to respond with AlreadyMined rpc_service_state.set_submit_transaction_response(TxSubmissionResponse { @@ -659,7 +661,7 @@ async fn tx_broadcast_protocol_submit_and_base_node_gets_changed() { wallet_connectivity, ) = setup().await; - add_transaction_to_database(1u64.into(), 1 * T, None, None, resources.db.clone()).await; + add_transaction_to_database(1u64.into(), 1 * T, None, resources.db.clone()).await; resources.config.broadcast_monitoring_timeout = Duration::from_secs(60); @@ -766,7 +768,6 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { 1u64.into(), 1 * T, Some(TransactionStatus::Broadcast), - None, resources.db.clone(), ) .await; @@ -774,7 +775,6 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { 2u64.into(), 2 * T, Some(TransactionStatus::Completed), - None, resources.db.clone(), ) .await; @@ -809,7 +809,6 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { wallet_connectivity.clone(), resources.config.clone(), resources.event_publisher.clone(), - resources.output_manager_service.clone(), ); let join_handle = task::spawn(protocol.execute()); @@ -837,7 +836,6 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { wallet_connectivity.clone(), resources.config.clone(), resources.event_publisher.clone(), - resources.output_manager_service.clone(), ); let join_handle = task::spawn(protocol.execute()); @@ -883,7 +881,6 @@ async fn tx_validation_protocol_tx_becomes_mined_unconfirmed_then_confirmed() { wallet_connectivity.clone(), resources.config.clone(), resources.event_publisher.clone(), - resources.output_manager_service.clone(), ); let join_handle = task::spawn(protocol.execute()); @@ -923,7 +920,6 @@ async fn tx_revalidation() { 1u64.into(), 1 * T, Some(TransactionStatus::Completed), - None, resources.db.clone(), ) .await; @@ -931,7 +927,6 @@ async fn tx_revalidation() { 2u64.into(), 2 * T, Some(TransactionStatus::Completed), - None, resources.db.clone(), ) .await; @@ -967,7 +962,6 @@ async fn tx_revalidation() { wallet_connectivity.clone(), resources.config.clone(), resources.event_publisher.clone(), - resources.output_manager_service.clone(), ); let join_handle = task::spawn(protocol.execute()); @@ -1018,7 +1012,6 @@ async fn tx_revalidation() { wallet_connectivity.clone(), resources.config.clone(), resources.event_publisher.clone(), - resources.output_manager_service.clone(), ); let join_handle = task::spawn(protocol.execute()); @@ -1061,7 +1054,6 @@ async fn tx_validation_protocol_reorg() { i.into(), i * T, Some(TransactionStatus::Broadcast), - None, resources.db.clone(), ) .await; @@ -1070,8 +1062,7 @@ async fn tx_validation_protocol_reorg() { add_transaction_to_database( 6u64.into(), 6 * T, - Some(TransactionStatus::Coinbase), - Some(8), + Some(TransactionStatus::Broadcast), resources.db.clone(), ) .await; @@ -1079,8 +1070,7 @@ async fn tx_validation_protocol_reorg() { add_transaction_to_database( 7u64.into(), 7 * T, - Some(TransactionStatus::Coinbase), - Some(9), + Some(TransactionStatus::Broadcast), resources.db.clone(), ) .await; @@ -1098,8 +1088,8 @@ async fn tx_validation_protocol_reorg() { let tx3 = resources.db.get_completed_transaction(3u64.into()).unwrap(); let tx4 = resources.db.get_completed_transaction(4u64.into()).unwrap(); let tx5 = resources.db.get_completed_transaction(5u64.into()).unwrap(); - let coinbase_tx1 = resources.db.get_completed_transaction(6u64.into()).unwrap(); - let coinbase_tx2 = resources.db.get_completed_transaction(7u64.into()).unwrap(); + let tx6 = resources.db.get_completed_transaction(6u64.into()).unwrap(); + let tx7 = resources.db.get_completed_transaction(7u64.into()).unwrap(); let timestamp = EpochTime::now().as_u64(); let transaction_query_batch_responses = vec![ @@ -1145,27 +1135,27 @@ async fn tx_validation_protocol_reorg() { }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( - coinbase_tx1.transaction.first_kernel_excess_sig().unwrap().clone(), + tx5.transaction.first_kernel_excess_sig().unwrap().clone(), )), location: TxLocationProto::from(TxLocation::Mined) as i32, - block_hash: block_headers.get(&8).unwrap().hash().to_vec(), - confirmations: 2, - block_height: 8, + block_hash: block_headers.get(&9).unwrap().hash().to_vec(), + confirmations: 1, + block_height: 9, mined_timestamp: timestamp, }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( - tx5.transaction.first_kernel_excess_sig().unwrap().clone(), + tx6.transaction.first_kernel_excess_sig().unwrap().clone(), )), location: TxLocationProto::from(TxLocation::Mined) as i32, - block_hash: block_headers.get(&9).unwrap().hash().to_vec(), - confirmations: 1, - block_height: 9, + block_hash: block_headers.get(&8).unwrap().hash().to_vec(), + confirmations: 2, + block_height: 8, mined_timestamp: timestamp, }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( - coinbase_tx2.transaction.first_kernel_excess_sig().unwrap().clone(), + tx7.transaction.first_kernel_excess_sig().unwrap().clone(), )), location: TxLocationProto::from(TxLocation::Mined) as i32, block_hash: block_headers.get(&9).unwrap().hash().to_vec(), @@ -1191,7 +1181,6 @@ async fn tx_validation_protocol_reorg() { wallet_connectivity.clone(), resources.config.clone(), resources.event_publisher.clone(), - resources.output_manager_service.clone(), ); let join_handle = task::spawn(protocol.execute()); @@ -1212,8 +1201,7 @@ async fn tx_validation_protocol_reorg() { assert_eq!(confirmed_count, 3); assert_eq!(unconfirmed_count, 4); - // Now we will reorg to new blocks 8 and 9, tx 4 will disappear and tx5 will appear in block 9, coinbase_tx2 should - // become invalid and coinbase_tx1 should return to coinbase status + // Now we will reorg to new blocks 8 and 9; tx5 will appear in block 8; tx4, tx6 and tx7 will become invalid let _block_header = block_headers.remove(&9); let _block_header = block_headers.remove(&10); @@ -1254,16 +1242,6 @@ async fn tx_validation_protocol_reorg() { block_height: 7, mined_timestamp: timestamp, }, - TxQueryBatchResponseProto { - signature: Some(SignatureProto::from( - coinbase_tx1.transaction.first_kernel_excess_sig().unwrap().clone(), - )), - location: TxLocationProto::from(TxLocation::NotStored) as i32, - block_hash: vec![], - confirmations: 0, - block_height: 0, - mined_timestamp: 0, - }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( tx5.transaction.first_kernel_excess_sig().unwrap().clone(), @@ -1276,7 +1254,17 @@ async fn tx_validation_protocol_reorg() { }, TxQueryBatchResponseProto { signature: Some(SignatureProto::from( - coinbase_tx2.transaction.first_kernel_excess_sig().unwrap().clone(), + tx6.transaction.first_kernel_excess_sig().unwrap().clone(), + )), + location: TxLocationProto::from(TxLocation::NotStored) as i32, + block_hash: vec![], + confirmations: 0, + block_height: 0, + mined_timestamp: 0, + }, + TxQueryBatchResponseProto { + signature: Some(SignatureProto::from( + tx7.transaction.first_kernel_excess_sig().unwrap().clone(), )), location: TxLocationProto::from(TxLocation::NotStored) as i32, block_hash: vec![], @@ -1303,7 +1291,6 @@ async fn tx_validation_protocol_reorg() { wallet_connectivity.clone(), resources.config.clone(), resources.event_publisher.clone(), - resources.output_manager_service.clone(), ); let join_handle = task::spawn(protocol.execute()); @@ -1318,30 +1305,24 @@ async fn tx_validation_protocol_reorg() { assert_eq!(rpc_service_state.take_get_header_by_height_calls().len(), 0); let completed_txs = resources.db.get_completed_transactions().unwrap(); - assert_eq!( - completed_txs.get(&4u64.into()).unwrap().status, - TransactionStatus::Completed - ); - assert_eq!( - completed_txs.get(&5u64.into()).unwrap().status, - TransactionStatus::MinedUnconfirmed - ); - assert_eq!( - completed_txs.get(&5u64.into()).cloned().unwrap().mined_height.unwrap(), - 8 - ); - assert_eq!( - completed_txs.get(&5u64.into()).cloned().unwrap().confirmations.unwrap(), - 1 - ); - assert_eq!( - completed_txs.get(&7u64.into()).unwrap().status, - TransactionStatus::Coinbase - ); - let cancelled_completed_txs = resources.db.get_cancelled_completed_transactions().unwrap(); + // Tx 1 + assert!(completed_txs.get(&1u64.into()).unwrap().mined_in_block.is_some()); + + // Tx 2 + assert!(completed_txs.get(&2u64.into()).unwrap().mined_in_block.is_some()); + + // Tx 3 + assert!(completed_txs.get(&3u64.into()).unwrap().mined_in_block.is_some()); + + // Tx 4 (reorged out) + assert!(completed_txs.get(&4u64.into()).unwrap().mined_in_block.is_none()); + + // Tx 5 + assert!(completed_txs.get(&5u64.into()).unwrap().mined_in_block.is_some()); + + // Tx 6 (reorged out) + assert!(completed_txs.get(&6u64.into()).unwrap().mined_in_block.is_none()); - assert!(matches!( - cancelled_completed_txs.get(&6u64.into()).unwrap().cancelled, - Some(TxCancellationReason::AbandonedCoinbase) - )); + // Tx 7 (reorged out) + assert!(completed_txs.get(&7u64.into()).unwrap().mined_in_block.is_none()); } diff --git a/base_layer/wallet/tests/utxo_scanner/mod.rs b/base_layer/wallet/tests/utxo_scanner/mod.rs index 0ad4d3ccca..a7b79a6951 100644 --- a/base_layer/wallet/tests/utxo_scanner/mod.rs +++ b/base_layer/wallet/tests/utxo_scanner/mod.rs @@ -61,8 +61,8 @@ use tari_core::{ blocks::BlockHeader, proto::base_node::{ChainMetadata, TipInfoResponse}, transactions::{ + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager}, tari_amount::MicroMinotari, - test_helpers::{create_test_core_key_manager_with_memory_db, TestKeyManager}, transaction_components::{OutputFeatures, WalletOutput}, CryptoFactories, }, @@ -241,7 +241,7 @@ async fn generate_block_headers_and_utxos( birthday_epoch_time: u64, birthday_offset: u64, only_coinbase: bool, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> TestBlockData { let mut block_headers = HashMap::new(); let mut utxos_by_block = Vec::new(); @@ -299,7 +299,7 @@ async fn test_utxo_scanner_recovery() { const NUM_BLOCKS: u64 = 11; const BIRTHDAY_OFFSET: u64 = 5; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let TestBlockData { block_headers, wallet_outputs, @@ -334,7 +334,7 @@ async fn test_utxo_scanner_recovery() { output.clone(), &key_manager, None, - OutputSource::Unknown, + OutputSource::Standard, None, None, ) @@ -398,7 +398,7 @@ async fn test_utxo_scanner_recovery_with_restart() { const BIRTHDAY_OFFSET: u64 = 5; const SYNC_INTERRUPT: u64 = 6; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let TestBlockData { block_headers, wallet_outputs, @@ -433,7 +433,7 @@ async fn test_utxo_scanner_recovery_with_restart() { output.clone(), &key_manager, None, - OutputSource::Unknown, + OutputSource::Standard, None, None, ) @@ -472,11 +472,11 @@ async fn test_utxo_scanner_recovery_with_restart() { amount: _, source_address, message, - maturity: _, import_status: _, tx_id: _, current_height: _, mined_timestamp: _, + scanned_output: _, } = req { assert_eq!(message, "Output found on blockchain during Wallet Recovery".to_string()); @@ -539,11 +539,11 @@ async fn test_utxo_scanner_recovery_with_restart() { amount: _, source_address: _, message, - maturity: _, import_status: _, tx_id: _, current_height: _, mined_timestamp: _, + scanned_output: _, } = req { assert_eq!(message, "recovery".to_string()); @@ -564,7 +564,7 @@ async fn test_utxo_scanner_recovery_with_restart_and_reorg() { const NUM_BLOCKS: u64 = 11; const BIRTHDAY_OFFSET: u64 = 5; const SYNC_INTERRUPT: u64 = 6; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let TestBlockData { mut block_headers, mut wallet_outputs, @@ -597,7 +597,7 @@ async fn test_utxo_scanner_recovery_with_restart_and_reorg() { output.clone(), &key_manager, None, - OutputSource::Unknown, + OutputSource::Standard, None, None, ) @@ -634,7 +634,7 @@ async fn test_utxo_scanner_recovery_with_restart_and_reorg() { .filter(|u| u.height <= 4) .collect::>(); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let TestBlockData { block_headers: new_block_headers, wallet_outputs: new_wallet_outputs, @@ -675,7 +675,7 @@ async fn test_utxo_scanner_recovery_with_restart_and_reorg() { output.clone(), &key_manager, None, - OutputSource::Unknown, + OutputSource::Standard, None, None, ) @@ -755,7 +755,7 @@ async fn test_utxo_scanner_scanned_block_cache_clearing() { const NUM_BLOCKS: u64 = 11; const BIRTHDAY_OFFSET: u64 = 5; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let TestBlockData { block_headers, wallet_outputs: _wallet_outputs, @@ -864,7 +864,7 @@ async fn test_utxo_scanner_one_sided_payments() { const NUM_BLOCKS: u64 = 11; const BIRTHDAY_OFFSET: u64 = 5; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let TestBlockData { mut block_headers, wallet_outputs, @@ -899,7 +899,7 @@ async fn test_utxo_scanner_one_sided_payments() { output.clone(), &key_manager, None, - OutputSource::Unknown, + OutputSource::Standard, None, None, ) @@ -950,11 +950,11 @@ async fn test_utxo_scanner_one_sided_payments() { amount: _, source_address: _, message, - maturity: _, import_status: _, tx_id: _, current_height: _, mined_timestamp: _, + scanned_output: _, } = req { assert_eq!(message, "one-sided non-default".to_string()); @@ -984,7 +984,7 @@ async fn test_utxo_scanner_one_sided_payments() { block_headers.insert(NUM_BLOCKS, block_header11); db_wallet_outputs.push( - DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Unknown, None, None) + DbWalletOutput::from_wallet_output(uo, &key_manager, None, OutputSource::Standard, None, None) .await .unwrap(), ); @@ -1048,11 +1048,11 @@ async fn test_utxo_scanner_one_sided_payments() { amount: _, source_address: _, message, - maturity: _, import_status: _, tx_id: _, current_height: h, mined_timestamp: _, + scanned_output: _, } = req { println!("{:?}", h); @@ -1073,7 +1073,7 @@ async fn test_birthday_timestamp_over_chain() { const NUM_BLOCKS: u64 = 10; const BIRTHDAY_OFFSET: u64 = 5; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let TestBlockData { block_headers, utxos_by_block, diff --git a/base_layer/wallet_ffi/src/callback_handler_tests.rs b/base_layer/wallet_ffi/src/callback_handler_tests.rs index 94e52ffd7c..9d5456dcc5 100644 --- a/base_layer/wallet_ffi/src/callback_handler_tests.rs +++ b/base_layer/wallet_ffi/src/callback_handler_tests.rs @@ -323,8 +323,8 @@ mod test { TransactionDirection::Inbound, None, None, - None, - ); + ) + .unwrap(); db.insert_completed_transaction(2u64.into(), completed_tx.clone()) .unwrap(); @@ -389,10 +389,10 @@ mod test { "6".to_string(), Utc::now().naive_utc(), TransactionDirection::Inbound, - None, Some(2), Some(NaiveDateTime::from_timestamp_opt(0, 0).unwrap_or(NaiveDateTime::MIN)), - ); + ) + .unwrap(); db.insert_completed_transaction(6u64.into(), faux_unconfirmed_tx.clone()) .unwrap(); @@ -421,10 +421,10 @@ mod test { "7".to_string(), Utc::now().naive_utc(), TransactionDirection::Inbound, - None, Some(5), Some(NaiveDateTime::from_timestamp_opt(0, 0).unwrap()), - ); + ) + .unwrap(); db.insert_completed_transaction(7u64.into(), faux_confirmed_tx.clone()) .unwrap(); diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index ea3e57314f..b26c6ee3b6 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -317,8 +317,7 @@ impl From for TariUtxo { OutputStatus::ShortTermEncumberedToBeReceived => 7, OutputStatus::ShortTermEncumberedToBeSpent => 8, OutputStatus::SpentMinedUnconfirmed => 9, - OutputStatus::AbandonedCoinbase => 10, - OutputStatus::NotStored => 11, + OutputStatus::NotStored => 10, }, } } @@ -8582,13 +8581,8 @@ mod test { use tari_core::{ covenant, transactions::{ - key_manager::SecretTransactionKeyManagerInterface, - test_helpers::{ - create_test_core_key_manager_with_memory_db, - create_test_input, - create_wallet_output_with_data, - TestParams, - }, + key_manager::{create_memory_db_key_manager, SecretTransactionKeyManagerInterface}, + test_helpers::{create_test_input, create_wallet_output_with_data, TestParams}, }, }; use tari_key_manager::{mnemonic::MnemonicLanguage, mnemonic_wordlists}; @@ -9899,7 +9893,7 @@ mod test { #[allow(clippy::too_many_lines)] fn test_wallet_get_utxos() { unsafe { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut error = 0; let error_ptr = &mut error as *mut c_int; let mut recovery_in_progress = true; @@ -10109,7 +10103,7 @@ mod test { ); assert_eq!(error, 0); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); for i in 0..10 { let uout = (*alice_wallet) .runtime @@ -10164,7 +10158,7 @@ mod test { #[allow(clippy::too_many_lines, clippy::needless_collect)] fn test_wallet_coin_join() { unsafe { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut error = 0; let error_ptr = &mut error as *mut c_int; let mut recovery_in_progress = true; @@ -10365,7 +10359,7 @@ mod test { #[allow(clippy::too_many_lines, clippy::needless_collect)] fn test_wallet_coin_split() { unsafe { - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let mut error = 0; let error_ptr = &mut error as *mut c_int; let mut recovery_in_progress = true; @@ -10633,7 +10627,7 @@ mod test { ); assert_eq!(error, 0); - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); for i in 1..=5 { (*alice_wallet) .runtime @@ -10755,7 +10749,7 @@ mod test { let mut error = 0; let error_ptr = &mut error as *mut c_int; // Test the consistent features case - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let utxo_1 = runtime .block_on(create_wallet_output_with_data( script!(Nop), @@ -10896,7 +10890,7 @@ mod test { ); // Test the consistent features case - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let utxo_1 = runtime .block_on(create_wallet_output_with_data( script!(Nop), @@ -10992,7 +10986,7 @@ mod test { let mut error = 0; let error_ptr = &mut error as *mut c_int; - let key_manager = create_test_core_key_manager_with_memory_db(); + let key_manager = create_memory_db_key_manager(); let utxo_1 = runtime .block_on(create_wallet_output_with_data( script!(Nop), diff --git a/common/config/presets/f_merge_mining_proxy.toml b/common/config/presets/f_merge_mining_proxy.toml index ea9d59cec0..ea5b4ad2ff 100644 --- a/common/config/presets/f_merge_mining_proxy.toml +++ b/common/config/presets/f_merge_mining_proxy.toml @@ -42,12 +42,6 @@ monerod_url = [# stagenet # GRPC authentication for the base node (default = "none") #base_node_grpc_authentication = { username = "miner", password = "xxxx" } -# The Minotari wallet's GRPC address. (default = "/ip4/127.0.0.1/tcp/18143") -#console_wallet_grpc_address = "/ip4/127.0.0.1/tcp/18143" - -# GRPC authentication for the Minotari wallet (default = "none") -#wallet_grpc_authentication = { username = "miner", password = "xxxx" } - # Address of the minotari_merge_mining_proxy application. (default = "/ip4/127.0.0.1/tcp/18081") #listener_address = "/ip4/127.0.0.1/tcp/18081" @@ -69,3 +63,9 @@ monerod_url = [# stagenet # The maximum amount of VMs that RandomX will be use (default = 5) #max_randomx_vms = 5 + +# The Tari wallet address (valid address in hex) where the mining funds will be sent to - must be assigned +# e.g. "78e724f466d202abdee0f23c261289074e4a2fc9eb61e83e0179eead76ce2d3f17" +#wallet_payment_address = "xxx" +# Stealth payment yes or no (default: true) +#stealth_payment = true diff --git a/common/config/presets/g_miner.toml b/common/config/presets/g_miner.toml index f0d3d63b16..ad8ade469d 100644 --- a/common/config/presets/g_miner.toml +++ b/common/config/presets/g_miner.toml @@ -12,11 +12,6 @@ # GRPC authentication for the base node (default = "none") #base_node_grpc_authentication = { username = "miner", password = "xxxx" } -# GRPC address of console wallet (default = "/ip4/127.0.0.1/tcp/18143") -#wallet_grpc_address = "/ip4/127.0.0.1/tcp/18143" -# GRPC authentication for the console wallet (default = "none") -#wallet_grpc_authentication = { username = "miner", password = "xxxx" } - # Number of mining threads (default: number of logical CPU cores) #num_mining_threads = 8 @@ -28,14 +23,25 @@ #validate_tip_timeout_sec = 30 # Stratum Mode configuration - mining pool address (e.g. "miningcore.tari.com:3052") -# mining_pool_address = "miningcore.tari.com:3052" +#mining_pool_address = "miningcore.tari.com:3052" # Stratum Mode configuration - mining wallet address/public key # (e.g. "20B19870ABEE8ABC6ACC77AE4E6CA169057645B27C35334B74446B4D3EE52150") -# mining_wallet_address = "YOUR_WALLET_PUBLIC_KEY" +#stratum_mining_wallet_address = "YOUR_WALLET_TARI_ADDRESS" + +# Stratum Mode configuration - mining worker name (e.g. "worker1") (default = "") +#stratum_mining_worker_name = "worker1" -# Stratum Mode configuration - mining worker name (e.g. "worker1") -# mining_worker_name = "worker1" +# The extra data to store in the coinbase, usually some data about the mining pool. +# Note that this data is publicly readable, but it is suggested you populate it so that +# pool dominance can be seen before any one party has more than 51%. +#coinbase_extra = "minotari_miner" # Base node reconnect timeout after any GRPC or miner error (default: 10 s) -# wait_timeout_on_error = 10 +#wait_timeout_on_error = 10 + +# The Tari wallet address (valid address in hex) where the mining funds will be sent to - must be assigned +# e.g. "78e724f466d202abdee0f23c261289074e4a2fc9eb61e83e0179eead76ce2d3f17" +#wallet_payment_address = "YOUR_WALLET_TARI_ADDRESS" +# Stealth payment yes or no (default: true) +#stealth_payment = true diff --git a/common/src/exit_codes.rs b/common/src/exit_codes.rs index 84e3900f5b..6d90d49d5b 100644 --- a/common/src/exit_codes.rs +++ b/common/src/exit_codes.rs @@ -120,6 +120,14 @@ pub enum ExitCode { TorAuthConfiguration = 118, #[error("Unable to read Tor cookie file")] TorAuthUnreadableCookie = 119, + #[error("Tokio runtime error")] + TokioRuntimeError = 120, + #[error("Key manager service error")] + KeyManagerServiceError = 121, + #[error("Consensus manager builder error")] + ConsensusManagerBuilderError = 122, + #[error("Payment wallet address error")] + WalletPaymentAddress = 123, } impl From for ExitError { diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index a0d6104a95..ecfa79e0df 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -30,6 +30,7 @@ tari_utilities = { version = "0.6" } minotari_wallet = { path = "../base_layer/wallet" } minotari_wallet_ffi = { path = "../base_layer/wallet_ffi" } minotari_wallet_grpc_client = { path = "../clients/rust/wallet_grpc_client" } +tari_key_manager = { path = "../base_layer/key_manager" } anyhow = "1.0.53" async-trait = "0.1.50" diff --git a/integration_tests/log4rs/cucumber.yml b/integration_tests/log4rs/cucumber.yml index 9d03e91388..db40232410 100644 --- a/integration_tests/log4rs/cucumber.yml +++ b/integration_tests/log4rs/cucumber.yml @@ -94,6 +94,22 @@ appenders: pattern: "{{log_dir}}/log/other.{}.log" encoder: pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{X(grpc)}] {f}.{L} {i} [{t}] {l:5} {m}{n}" + # An appender named "base_layer" that writes to a file with a custom pattern encoder + miner: + kind: rolling_file + path: "{{log_dir}}/log/miner.log" + policy: + kind: compound + trigger: + kind: size + limit: 10mb + roller: + kind: fixed_window + base: 1 + count: 5 + pattern: "{{log_dir}}/log/miner/miner.{}.log" + encoder: + pattern: "{d(%Y-%m-%d %H:%M:%S.%f)} [{t}] [Thread:{I}] {l:5} {m}{n}" # We don't want prints during cucumber test, everything useful will in logs. # root: @@ -108,6 +124,7 @@ loggers: - network - base_layer_wallet - base_layer_base_node + - miner additive: true stdout: level: info # we have only single print, and it's info @@ -135,6 +152,22 @@ loggers: level: debug appenders: - base_layer_wallet + # miner + minotari::application: + level: debug + appenders: + - miner + additive: false + minotari::miner: + level: debug + appenders: + - miner + additive: false + minotari_miner: + level: debug + appenders: + - miner + additive: false # Route log events sent to the "comms" logger to the "network" appender comms: level: debug diff --git a/integration_tests/src/merge_mining_proxy.rs b/integration_tests/src/merge_mining_proxy.rs index 1a6d1fc5d2..90ec00b434 100644 --- a/integration_tests/src/merge_mining_proxy.rs +++ b/integration_tests/src/merge_mining_proxy.rs @@ -22,11 +22,17 @@ use std::{convert::TryInto, thread}; +use minotari_app_grpc::tari_rpc::GetIdentityRequest; use minotari_app_utilities::common_cli_args::CommonCliArgs; use minotari_merge_mining_proxy::{merge_miner, Cli}; +use minotari_wallet_grpc_client::WalletGrpcClient; use serde_json::{json, Value}; +use tari_common::configuration::Network; +use tari_common_types::{tari_address::TariAddress, types::PublicKey}; +use tari_utilities::ByteArray; use tempfile::tempdir; use tokio::runtime; +use tonic::transport::Channel; use super::get_port; use crate::TariWorld; @@ -39,6 +45,7 @@ pub struct MergeMiningProxyProcess { pub port: u64, pub origin_submission: bool, id: u64, + pub stealth: bool, } pub async fn register_merge_mining_proxy_process( @@ -47,6 +54,7 @@ pub async fn register_merge_mining_proxy_process( base_node_name: String, wallet_name: String, origin_submission: bool, + stealth: bool, ) { let merge_mining_proxy = MergeMiningProxyProcess { name: merge_mining_proxy_name.clone(), @@ -55,6 +63,7 @@ pub async fn register_merge_mining_proxy_process( port: get_port(18000..18499).unwrap(), origin_submission, id: 0, + stealth, }; merge_mining_proxy.start(world).await; @@ -70,10 +79,23 @@ impl MergeMiningProxyProcess { let data_dir_str = data_dir.clone().into_os_string().into_string().unwrap(); let mut config_path = data_dir; config_path.push("config.toml"); - let wallet_grpc_port = world.get_wallet(&self.wallet_name).unwrap().grpc_port; let base_node_grpc_port = world.get_node(&self.base_node_name).unwrap().grpc_port; let proxy_full_address = format! {"/ip4/127.0.0.1/tcp/{}", self.port}; let origin_submission = self.origin_submission; + let mut wallet_client = create_wallet_client(world, self.wallet_name.clone()) + .await + .expect("wallet grpc client"); + let wallet_public_key = PublicKey::from_vec( + &wallet_client + .identify(GetIdentityRequest {}) + .await + .unwrap() + .into_inner() + .public_key, + ) + .unwrap(); + let wallet_payment_address = TariAddress::new(wallet_public_key, Network::LocalNet); + let stealth = self.stealth; thread::spawn(move || { let cli = Cli { common: CommonCliArgs { @@ -84,10 +106,6 @@ impl MergeMiningProxyProcess { network: Some("localnet".to_string().try_into().unwrap()), config_property_overrides: vec![ ("merge_mining_proxy.listener_address".to_string(), proxy_full_address), - ( - "merge_mining_proxy.console_wallet_grpc_address".to_string(), - format!("/ip4/127.0.0.1/tcp/{}", wallet_grpc_port), - ), ( "merge_mining_proxy.base_node_grpc_address".to_string(), format!("/ip4/127.0.0.1/tcp/{}", base_node_grpc_port), @@ -114,6 +132,11 @@ impl MergeMiningProxyProcess { "merge_mining_proxy.submit_to_origin".to_string(), origin_submission.to_string(), ), + ( + "miner.wallet_payment_address".to_string(), + wallet_payment_address.to_hex(), + ), + ("miner.stealth_payment".to_string(), stealth.to_string()), ], }, }; @@ -192,3 +215,12 @@ impl MergeMiningProxyProcess { self.submit_block(block).await } } + +pub async fn create_wallet_client(world: &TariWorld, wallet_name: String) -> anyhow::Result> { + let wallet_grpc_port = world.wallets.get(&wallet_name).unwrap().grpc_port; + let wallet_addr = format!("http://127.0.0.1:{}", wallet_grpc_port); + + eprintln!("Wallet GRPC at {}", wallet_addr); + + Ok(WalletGrpcClient::connect(wallet_addr.as_str()).await?) +} diff --git a/integration_tests/src/miner.rs b/integration_tests/src/miner.rs index 73494a7b0c..65f86dd81c 100644 --- a/integration_tests/src/miner.rs +++ b/integration_tests/src/miner.rs @@ -20,47 +20,38 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{convert::TryInto, str::FromStr, time::Duration}; - -use minotari_app_grpc::{ - authentication::ClientAuthenticationInterceptor, - tari_rpc::{ - pow_algo::PowAlgos, - wallet_client::WalletClient, - Block, - GetCoinbaseRequest, - GetCoinbaseResponse, - NewBlockTemplate, - NewBlockTemplateRequest, - NewBlockTemplateResponse, - PowAlgo, - TransactionKernel, - TransactionOutput, - }, +use std::{convert::TryFrom, time::Duration}; + +use minotari_app_grpc::tari_rpc::{ + pow_algo::PowAlgos, + Block, + GetIdentityRequest, + NewBlockTemplate, + NewBlockTemplateRequest, + PowAlgo, + TransactionOutput as GrpcTransactionOutput, }; use minotari_app_utilities::common_cli_args::CommonCliArgs; use minotari_miner::{run_miner, Cli}; use minotari_node_grpc_client::BaseNodeGrpcClient; +use minotari_wallet_grpc_client::WalletGrpcClient; use tari_common::configuration::Network; -use tari_common_types::grpc_authentication::GrpcAuthentication; +use tari_common_types::{tari_address::TariAddress, types::PublicKey}; use tari_core::{ consensus::ConsensusManager, transactions::{ - key_manager::TransactionKeyManagerInterface, - test_helpers::TestKeyManager, + generate_coinbase_with_wallet_output, + key_manager::{MemoryDbKeyManager, TariKeyId}, + tari_amount::MicroMinotari, transaction_components::WalletOutput, - CoinbaseBuilder, }, }; -use tonic::{ - codegen::InterceptedService, - transport::{Channel, Endpoint}, -}; +use tari_utilities::ByteArray; +use tonic::transport::Channel; use crate::TariWorld; type BaseNodeClient = BaseNodeGrpcClient; -type WalletGrpcClient = WalletClient>; #[derive(Clone, Debug)] pub struct MinerProcess { @@ -68,14 +59,22 @@ pub struct MinerProcess { pub base_node_name: String, pub wallet_name: String, pub mine_until_height: u64, + pub stealth: bool, } -pub fn register_miner_process(world: &mut TariWorld, miner_name: String, base_node_name: String, wallet_name: String) { +pub fn register_miner_process( + world: &mut TariWorld, + miner_name: String, + base_node_name: String, + wallet_name: String, + stealth: bool, +) { let miner = MinerProcess { name: miner_name.clone(), base_node_name, wallet_name, mine_until_height: 100_000, + stealth, }; world.miners.insert(miner_name, miner); @@ -89,8 +88,21 @@ impl MinerProcess { miner_min_diff: Option, miner_max_diff: Option, ) { + let mut wallet_client = create_wallet_client(world, self.wallet_name.clone()) + .await + .expect("wallet grpc client"); + let wallet_public_key = PublicKey::from_vec( + &wallet_client + .identify(GetIdentityRequest {}) + .await + .unwrap() + .into_inner() + .public_key, + ) + .unwrap(); + let wallet_payment_address = TariAddress::new(wallet_public_key, Network::LocalNet); + let node = world.get_node(&self.base_node_name).unwrap().grpc_port; - let wallet = world.get_wallet(&self.wallet_name).unwrap().grpc_port; let temp_dir = world .current_base_dir .as_ref() @@ -112,12 +124,13 @@ impl MinerProcess { "miner.base_node_grpc_address".to_string(), format!("/ip4/127.0.0.1/tcp/{}", node), ), - ( - "miner.wallet_grpc_address".to_string(), - format!("/ip4/127.0.0.1/tcp/{}", wallet), - ), ("miner.num_mining_threads".to_string(), "1".to_string()), ("miner.mine_on_tip_only".to_string(), "false".to_string()), + ( + "miner.wallet_payment_address".to_string(), + wallet_payment_address.to_hex(), + ), + ("miner.stealth_payment".to_string(), self.stealth.to_string()), ], network: Some(Network::LocalNet), }, @@ -130,28 +143,36 @@ impl MinerProcess { } } -#[allow(dead_code)] -pub async fn mine_blocks(world: &mut TariWorld, miner_name: String, num_blocks: u64) { - let mut base_client = create_base_node_client(world, &miner_name).await; - let mut wallet_client = create_wallet_client(world, &miner_name).await; +pub async fn create_wallet_client(world: &TariWorld, wallet_name: String) -> anyhow::Result> { + let wallet_grpc_port = world.wallets.get(&wallet_name).unwrap().grpc_port; + let wallet_addr = format!("http://127.0.0.1:{}", wallet_grpc_port); - for _ in 0..num_blocks { - mine_block(&mut base_client, &mut wallet_client).await; - tokio::time::sleep(Duration::from_millis(100)).await; - } + eprintln!("Wallet GRPC at {}", wallet_addr); - // Give some time for the base node and wallet to sync the new blocks - tokio::time::sleep(Duration::from_secs(5)).await; + Ok(WalletGrpcClient::connect(wallet_addr.as_str()).await?) } pub async fn mine_blocks_without_wallet( base_client: &mut BaseNodeClient, num_blocks: u64, weight: u64, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, + stealth_payment: bool, + consensus_manager: &ConsensusManager, ) { for _ in 0..num_blocks { - mine_block_without_wallet(base_client, weight, key_manager).await; + mine_block_without_wallet( + base_client, + weight, + key_manager, + script_key_id, + wallet_payment_address, + stealth_payment, + consensus_manager, + ) + .await; tokio::time::sleep(Duration::from_millis(100)).await; } @@ -159,28 +180,24 @@ pub async fn mine_blocks_without_wallet( tokio::time::sleep(Duration::from_secs(5)).await; } -async fn create_base_node_client(world: &TariWorld, miner_name: &String) -> BaseNodeClient { - let miner = world.miners.get(miner_name).unwrap(); - let base_node_grpc_port = world.base_nodes.get(&miner.base_node_name).unwrap().grpc_port; - let base_node_grpc_url = format!("http://127.0.0.1:{}", base_node_grpc_port); - eprintln!("Base node GRPC at {}", base_node_grpc_url); - BaseNodeClient::connect(base_node_grpc_url).await.unwrap() -} - -async fn create_wallet_client(world: &TariWorld, miner_name: &String) -> WalletGrpcClient { - let miner = world.miners.get(miner_name).unwrap(); - let wallet_grpc_port = world.wallets.get(&miner.wallet_name).unwrap().grpc_port; - let wallet_addr = format!("http://127.0.0.1:{}", wallet_grpc_port); - eprintln!("Wallet GRPC at {}", wallet_addr); - let channel = Endpoint::from_str(&wallet_addr).unwrap().connect().await.unwrap(); - WalletClient::with_interceptor( - channel, - ClientAuthenticationInterceptor::create(&GrpcAuthentication::default()).unwrap(), +pub async fn mine_block( + base_client: &mut BaseNodeClient, + key_manager: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, + stealth_payment: bool, + consensus_manager: &ConsensusManager, +) { + let (block_template, _wallet_output) = create_block_template_with_coinbase( + base_client, + 0, + key_manager, + script_key_id, + wallet_payment_address, + stealth_payment, + consensus_manager, ) -} - -pub async fn mine_block(base_client: &mut BaseNodeClient, wallet_client: &mut WalletGrpcClient) { - let block_template = create_block_template_with_coinbase(base_client, wallet_client).await; + .await; // Ask the base node for a valid block using the template let block_result = base_client @@ -198,10 +215,26 @@ pub async fn mine_block(base_client: &mut BaseNodeClient, wallet_client: &mut Wa ); } -async fn mine_block_without_wallet(base_client: &mut BaseNodeClient, weight: u64, key_manager: &TestKeyManager) { - let (block_template, _wallet_output) = - create_block_template_with_coinbase_without_wallet(base_client, weight, key_manager).await; - mine_block_without_wallet_with_template(base_client, block_template.new_block_template.unwrap()).await; +async fn mine_block_without_wallet( + base_client: &mut BaseNodeClient, + weight: u64, + key_manager: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, + stealth_payment: bool, + consensus_manager: &ConsensusManager, +) { + let (block_template, _wallet_output) = create_block_template_with_coinbase( + base_client, + weight, + key_manager, + script_key_id, + wallet_payment_address, + stealth_payment, + consensus_manager, + ) + .await; + mine_block_without_wallet_with_template(base_client, block_template).await; } async fn mine_block_without_wallet_with_template(base_client: &mut BaseNodeClient, block_template: NewBlockTemplate) { @@ -222,40 +255,14 @@ async fn mine_block_without_wallet_with_template(base_client: &mut BaseNodeClien } async fn create_block_template_with_coinbase( - base_client: &mut BaseNodeClient, - wallet_client: &mut WalletGrpcClient, -) -> NewBlockTemplate { - // get the block template from the base node - let template_req = NewBlockTemplateRequest { - algo: Some(PowAlgo { - pow_algo: PowAlgos::Sha3x.into(), - }), - max_weight: 0, - }; - - let template_res = base_client - .get_new_block_template(template_req) - .await - .unwrap() - .into_inner(); - - let mut block_template = template_res.new_block_template.clone().unwrap(); - - // add the coinbase outputs and kernels to the block template - let (output, kernel) = get_coinbase_outputs_and_kernels(wallet_client, template_res).await; - let body = block_template.body.as_mut().unwrap(); - - body.outputs.push(output); - body.kernels.push(kernel); - - block_template -} - -async fn create_block_template_with_coinbase_without_wallet( base_client: &mut BaseNodeClient, weight: u64, - key_manager: &TestKeyManager, -) -> (NewBlockTemplateResponse, WalletOutput) { + key_manager: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, + stealth_payment: bool, + consensus_manager: &ConsensusManager, +) -> (NewBlockTemplate, WalletOutput) { // get the block template from the base node let template_req = NewBlockTemplateRequest { algo: Some(PowAlgo { @@ -264,106 +271,41 @@ async fn create_block_template_with_coinbase_without_wallet( max_weight: weight, }; - let mut template_res = base_client + let template_response = base_client .get_new_block_template(template_req) .await .unwrap() .into_inner(); - // let mut block_template = template_res.new_block_template.clone().unwrap(); - - // add the coinbase outputs and kernels to the block template - let (output, kernel, wallet_output) = get_coinbase_without_wallet_client(template_res.clone(), key_manager).await; - // let body = block_template.body.as_mut().unwrap(); - - template_res - .new_block_template - .as_mut() - .unwrap() - .body - .as_mut() - .unwrap() - .outputs - .push(output); - template_res - .new_block_template - .as_mut() - .unwrap() - .body - .as_mut() - .unwrap() - .kernels - .push(kernel); - - (template_res, wallet_output) -} - -async fn get_coinbase_outputs_and_kernels( - wallet_client: &mut WalletGrpcClient, - template_res: NewBlockTemplateResponse, -) -> (TransactionOutput, TransactionKernel) { - let coinbase_req = coinbase_request(&template_res); - let coinbase_res = wallet_client.get_coinbase(coinbase_req).await.unwrap().into_inner(); - extract_outputs_and_kernels(coinbase_res) -} - -async fn get_coinbase_without_wallet_client( - template_res: NewBlockTemplateResponse, - key_manager: &TestKeyManager, -) -> (TransactionOutput, TransactionKernel, WalletOutput) { - let coinbase_req = coinbase_request(&template_res); - generate_coinbase(coinbase_req, key_manager).await -} - -async fn generate_coinbase( - coinbase_req: GetCoinbaseRequest, - key_manager: &TestKeyManager, -) -> (TransactionOutput, TransactionKernel, WalletOutput) { - let reward = coinbase_req.reward; - let height = coinbase_req.height; - let fee = coinbase_req.fee; - let extra = coinbase_req.extra; + let mut block_template = template_response.new_block_template.clone().unwrap(); - let (spending_key_id, _, script_key_id, _) = key_manager.get_next_spend_and_script_key_ids().await.unwrap(); - - let consensus_manager = ConsensusManager::builder(Network::LocalNet).build().unwrap(); - let consensus_constants = consensus_manager.consensus_constants(height); - - let (tx, ubutxo) = CoinbaseBuilder::new(key_manager.clone()) - .with_block_height(height) - .with_fees(fee.into()) - .with_spend_key_id(spending_key_id) - .with_script_key_id(script_key_id) - .with_extra(extra) - .build_with_reward(consensus_constants, reward.into()) - .await - .unwrap(); - - let tx_out = tx.body().outputs().first().unwrap().clone(); - let tx_krnl = tx.body().kernels().first().unwrap().clone(); - - (tx_out.try_into().unwrap(), tx_krnl.into(), ubutxo) -} - -fn coinbase_request(template_response: &NewBlockTemplateResponse) -> GetCoinbaseRequest { let template = template_response.new_block_template.as_ref().unwrap(); let miner_data = template_response.miner_data.as_ref().unwrap(); let fee = miner_data.total_fees; let reward = miner_data.reward; let height = template.header.as_ref().unwrap().height; - GetCoinbaseRequest { - reward, - fee, + + // add the coinbase outputs and kernels to the block template + let (_, coinbase_output, coinbase_kernel, coinbase_wallet_output) = generate_coinbase_with_wallet_output( + MicroMinotari::from(fee), + MicroMinotari::from(reward), height, - extra: vec![], - } -} + &[], + key_manager, + script_key_id, + wallet_payment_address, + stealth_payment, + consensus_manager.consensus_constants(height), + ) + .await + .unwrap(); + let body = block_template.body.as_mut().unwrap(); + + let grpc_output = GrpcTransactionOutput::try_from(coinbase_output).unwrap(); + body.outputs.push(grpc_output); + body.kernels.push(coinbase_kernel.into()); -fn extract_outputs_and_kernels(coinbase: GetCoinbaseResponse) -> (TransactionOutput, TransactionKernel) { - let transaction_body = coinbase.transaction.unwrap().body.unwrap(); - let output = transaction_body.outputs.get(0).cloned().unwrap(); - let kernel = transaction_body.kernels.get(0).cloned().unwrap(); - (output, kernel) + (block_template, coinbase_wallet_output) } pub async fn mine_block_with_coinbase_on_node(world: &mut TariWorld, base_node: String, coinbase_name: String) { @@ -374,20 +316,41 @@ pub async fn mine_block_with_coinbase_on_node(world: &mut TariWorld, base_node: .get_grpc_client() .await .unwrap(); - let (template, wallet_output) = - create_block_template_with_coinbase_without_wallet(&mut client, 0, &world.key_manager).await; + let script_key_id = &world.script_key_id().await; + let (template, wallet_output) = create_block_template_with_coinbase( + &mut client, + 0, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await; world.utxos.insert(coinbase_name, wallet_output); - mine_block_without_wallet_with_template(&mut client, template.new_block_template.unwrap()).await; + mine_block_without_wallet_with_template(&mut client, template).await; } -pub async fn mine_block_before_submit(client: &mut BaseNodeClient, key_manager: &TestKeyManager) -> Block { - let (template, _wallet_output) = create_block_template_with_coinbase_without_wallet(client, 0, key_manager).await; +pub async fn mine_block_before_submit( + client: &mut BaseNodeClient, + key_manager: &MemoryDbKeyManager, + script_key_id: &TariKeyId, + wallet_payment_address: &TariAddress, + stealth_payment: bool, + consensus_manager: &ConsensusManager, +) -> Block { + let (template, _wallet_output) = create_block_template_with_coinbase( + client, + 0, + key_manager, + script_key_id, + wallet_payment_address, + stealth_payment, + consensus_manager, + ) + .await; - let new_block = client - .get_new_block(template.new_block_template.unwrap()) - .await - .unwrap() - .into_inner(); + let new_block = client.get_new_block(template).await.unwrap().into_inner(); new_block.block.unwrap() } diff --git a/integration_tests/src/transaction.rs b/integration_tests/src/transaction.rs index ad1b2e18ec..d358d9f63b 100644 --- a/integration_tests/src/transaction.rs +++ b/integration_tests/src/transaction.rs @@ -26,9 +26,9 @@ use tari_core::{ borsh::SerializedSize, covenants::Covenant, transactions::{ - key_manager::TariKeyId, + key_manager::{MemoryDbKeyManager, TariKeyId}, tari_amount::MicroMinotari, - test_helpers::{create_transaction_with, TestKeyManager, TestParams}, + test_helpers::{create_transaction_with, TestParams}, transaction_components::{ OutputFeatures, Transaction, @@ -54,7 +54,7 @@ struct TestTransactionBuilder { } impl TestTransactionBuilder { - pub async fn new(key_manager: &TestKeyManager) -> Self { + pub async fn new(key_manager: &MemoryDbKeyManager) -> Self { Self { amount: MicroMinotari(0), fee_per_gram: MicroMinotari(1), @@ -80,7 +80,7 @@ impl TestTransactionBuilder { self.amount += amount } - pub async fn add_input(&mut self, u: WalletOutput, key_manager: &TestKeyManager) -> &mut Self { + pub async fn add_input(&mut self, u: WalletOutput, key_manager: &MemoryDbKeyManager) -> &mut Self { self.update_amount(u.value); if u.features.maturity > self.inputs_max_height { @@ -97,7 +97,7 @@ impl TestTransactionBuilder { self } - pub async fn build(mut self, key_manager: &TestKeyManager) -> (Transaction, WalletOutput) { + pub async fn build(mut self, key_manager: &MemoryDbKeyManager) -> (Transaction, WalletOutput) { self.create_utxo(key_manager, self.inputs.len()).await; let inputs = self.inputs.iter().map(|f| f.1.clone()).collect(); @@ -107,7 +107,7 @@ impl TestTransactionBuilder { (tx, self.output.clone().unwrap().1) } - async fn create_utxo(&mut self, key_manager: &TestKeyManager, num_inputs: usize) { + async fn create_utxo(&mut self, key_manager: &MemoryDbKeyManager, num_inputs: usize) { let script = script!(Nop); let features = OutputFeatures::default(); let covenant = Covenant::default(); @@ -153,7 +153,7 @@ impl TestTransactionBuilder { pub async fn build_transaction_with_output_and_fee_per_gram( utxos: Vec, fee_per_gram: u64, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (Transaction, WalletOutput) { let mut builder = TestTransactionBuilder::new(key_manager).await; for wallet_output in utxos { @@ -167,7 +167,7 @@ pub async fn build_transaction_with_output_and_fee_per_gram( pub async fn build_transaction_with_output_and_lockheight( utxos: Vec, lockheight: u64, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (Transaction, WalletOutput) { let mut builder = TestTransactionBuilder::new(key_manager).await; for wallet_output in utxos { @@ -180,7 +180,7 @@ pub async fn build_transaction_with_output_and_lockheight( pub async fn build_transaction_with_output( utxos: Vec, - key_manager: &TestKeyManager, + key_manager: &MemoryDbKeyManager, ) -> (Transaction, WalletOutput) { let mut builder = TestTransactionBuilder::new(key_manager).await; for wallet_output in utxos { diff --git a/integration_tests/src/wallet_process.rs b/integration_tests/src/wallet_process.rs index 586f29b306..555d31fb4d 100644 --- a/integration_tests/src/wallet_process.rs +++ b/integration_tests/src/wallet_process.rs @@ -189,7 +189,7 @@ pub async fn spawn_wallet( wait_for_service(grpc_port).await; if let Some((_, _, hacky_request)) = base_node { - let mut wallet_client = create_wallet_client(world, wallet_name) + let mut wallet_client = create_wallet_client(world, wallet_name.clone()) .await .expect("wallet grpc client"); diff --git a/integration_tests/src/world.rs b/integration_tests/src/world.rs index 1ff9058588..18aeadd894 100644 --- a/integration_tests/src/world.rs +++ b/integration_tests/src/world.rs @@ -28,15 +28,24 @@ use std::{ use cucumber::gherkin::{Feature, Scenario}; use indexmap::IndexMap; +use rand::rngs::OsRng; use serde_json::Value; use tari_chat_client::ChatClient; +use tari_common::configuration::Network; +use tari_common_types::{ + tari_address::TariAddress, + types::{PrivateKey, PublicKey}, +}; use tari_core::{ blocks::Block, + consensus::ConsensusManager, transactions::{ - test_helpers::{create_test_core_key_manager_with_memory_db, TestKeyManager}, + key_manager::{create_memory_db_key_manager, MemoryDbKeyManager, TariKeyId}, transaction_components::{Transaction, WalletOutput}, }, }; +use tari_crypto::keys::{PublicKey as PK, SecretKey}; +use tari_key_manager::key_manager_service::{KeyId, KeyManagerInterface}; use tari_utilities::hex::Hex; use thiserror::Error; @@ -91,12 +100,20 @@ pub struct TariWorld { pub last_imported_tx_ids: Vec, // We need to store this for the merge mining proxy steps. The checks are get and check are done on separate steps. pub last_merge_miner_response: Value, - pub key_manager: TestKeyManager, + pub key_manager: MemoryDbKeyManager, + // This will be used for all one-sided coinbase payments + pub wallet_private_key: PrivateKey, + // This receiver wallet address will be used for default one-sided coinbase payments + pub default_payment_address: TariAddress, + pub consensus_manager: ConsensusManager, } impl Default for TariWorld { fn default() -> Self { println!("\nWorld initialized - remove this line when called!\n"); + let wallet_private_key = PrivateKey::random(&mut OsRng); + let default_payment_address = + TariAddress::new(PublicKey::from_secret_key(&wallet_private_key), Network::LocalNet); Self { current_scenario_name: None, current_feature_name: None, @@ -119,7 +136,10 @@ impl Default for TariWorld { errors: Default::default(), last_imported_tx_ids: vec![], last_merge_miner_response: Default::default(), - key_manager: create_test_core_key_manager_with_memory_db(), + key_manager: create_memory_db_key_manager(), + wallet_private_key, + default_payment_address, + consensus_manager: ConsensusManager::builder(Network::LocalNet).build().unwrap(), } } } @@ -284,4 +304,13 @@ impl TariWorld { p.kill_signal.trigger(); } } + + pub async fn script_key_id(&mut self) -> TariKeyId { + match self.key_manager.import_key(self.wallet_private_key.clone()).await { + Ok(key_id) => key_id, + Err(_) => KeyId::Imported { + key: PublicKey::from_secret_key(&self.wallet_private_key), + }, + } + } } diff --git a/integration_tests/tests/features/StressTest.feature b/integration_tests/tests/features/StressTest.feature index 8ad9f6bd3d..8f57c391f5 100644 --- a/integration_tests/tests/features/StressTest.feature +++ b/integration_tests/tests/features/StressTest.feature @@ -22,7 +22,7 @@ Feature: Stress Test # When mining node MINER mines 3 blocks # When mining node MINER mines blocks # Then all nodes are on the same chain tip - # Then wallet WALLET_A detects all transactions as Mined_Confirmed + # Then wallet WALLET_A detects all transactions as Mined_or_Faux_Confirmed # When I send transactions of 1111 uT each from wallet WALLET_A to wallet WALLET_B at fee_per_gram 4 # # Mine enough blocks for the first block of transactions to be confirmed. # When mining node MINER mines 4 blocks @@ -30,9 +30,9 @@ Feature: Stress Test # # Now wait until all transactions are detected as confirmed in WALLET_A, continue to mine blocks if transactions # # are not found to be confirmed as sometimes the previous mining occurs faster than transactions are submitted # # to the mempool - # Then while mining via SHA3 miner MINER all transactions in wallet WALLET_A are found to be Mined_Confirmed - # # Then wallet WALLET_B detects all transactions as Mined_Confirmed - # Then while mining via node NODE1 all transactions in wallet WALLET_B are found to be Mined_Confirmed + # Then while mining via SHA3 miner MINER all transactions in wallet WALLET_A are found to be Mined_or_Faux_Confirmed + # # Then wallet WALLET_B detects all transactions as Mined_or_Faux_Confirmed + # Then while mining via node NODE1 all transactions in wallet WALLET_B are found to be Mined_or_Faux_Confirmed # @flaky # Examples: @@ -71,7 +71,7 @@ Feature: Stress Test # When mining node MINER mines 8 blocks # Then all nodes are on the same chain tip - # Then wallet WALLET_A detects all transactions as Mined_Confirmed + # Then wallet WALLET_A detects all transactions as Mined_or_Faux_Confirmed # When I send 2000 transactions of 1111 uT each from wallet WALLET_A to wallet WALLET_B at fee_per_gram 4 # # Mine enough blocks for the first block of transactions to be confirmed. # When mining node MINER mines 4 blocks @@ -79,6 +79,6 @@ Feature: Stress Test # # Now wait until all transactions are detected as confirmed in WALLET_A, continue to mine blocks if transactions # # are not found to be confirmed as sometimes the previous mining occurs faster than transactions are submitted # # to the mempool - # Then while mining via SHA3 miner MINER all transactions in wallet WALLET_A are found to be Mined_Confirmed - # # Then wallet WALLET_B detects all transactions as Mined_Confirmed - # Then while mining via node NODE1 all transactions in wallet WALLET_B are found to be Mined_Confirmed + # Then while mining via SHA3 miner MINER all transactions in wallet WALLET_A are found to be Mined_or_Faux_Confirmed + # # Then wallet WALLET_B detects all transactions as Mined_or_Faux_Confirmed + # Then while mining via node NODE1 all transactions in wallet WALLET_B are found to be Mined_or_Faux_Confirmed diff --git a/integration_tests/tests/features/Sync.feature b/integration_tests/tests/features/Sync.feature index 7714374add..74daea7df2 100644 --- a/integration_tests/tests/features/Sync.feature +++ b/integration_tests/tests/features/Sync.feature @@ -25,7 +25,7 @@ Feature: Block Sync @critical Scenario: Simple block sync Given I have 1 seed nodes - When I have a SHA3 miner MINER connected to all seed nodes + When I have a stealth SHA3 miner MINER connected to all seed nodes When mining node MINER mines 20 blocks When I have 2 base nodes connected to all seed nodes Then all nodes are at height 20 diff --git a/integration_tests/tests/features/TransactionInfo.feature b/integration_tests/tests/features/TransactionInfo.feature index 5c35c1ee9a..0093e6e000 100644 --- a/integration_tests/tests/features/TransactionInfo.feature +++ b/integration_tests/tests/features/TransactionInfo.feature @@ -4,13 +4,14 @@ @transaction-info @wallet Feature: Transaction Info -@long-running +@critical @long-running Scenario: Get Transaction Info Given I have a seed node NODE - When I have a SHA3 miner MINER connected to all seed nodes + When I have a stealth SHA3 miner MINER connected to all seed nodes When I have wallet WALLET_A connected to all seed nodes When I have wallet WALLET_B connected to all seed nodes When I have mining node MINER connected to base node NODE and wallet WALLET_A + When I have a stealth SHA3 miner MINER2 connected to all seed nodes # We need to ensure the coinbase lock heights are gone; mine enough blocks When mining node MINER mines 4 blocks Then all nodes are at height 4 @@ -23,17 +24,13 @@ Scenario: Get Transaction Info Then wallet WALLET_B detects all transactions are at least Completed Then wallet WALLET_A detects all transactions are at least Broadcast Then wallet WALLET_B detects all transactions are at least Broadcast - # This wait is needed to stop next merge mining task from continuing - When I wait 1 seconds - When mining node MINER mines 1 blocks + When mining node MINER2 mines 1 blocks Then all nodes are at height 5 - Then wallet WALLET_A detects all transactions are at least Mined_Unconfirmed - Then wallet WALLET_B detects all transactions are at least Mined_Unconfirmed - # This wait is needed to stop base nodes from shutting down - When I wait 1 seconds - When mining node MINER mines 10 blocks + Then wallet WALLET_A detects all transactions are at least Mined_or_Faux_Unconfirmed + Then wallet WALLET_B detects all transactions are at least Mined_or_Faux_Unconfirmed + When mining node MINER2 mines 10 blocks Then all nodes are at height 15 - Then wallet WALLET_A detects all transactions as Mined_Confirmed - Then wallet WALLET_B detects all transactions as Mined_Confirmed + Then wallet WALLET_A detects all transactions as Mined_or_Faux_Confirmed + Then wallet WALLET_B detects all transactions as Mined_or_Faux_Confirmed # This wait is needed to stop base nodes from shutting down When I wait 1 seconds diff --git a/integration_tests/tests/features/WalletMonitoring.feature b/integration_tests/tests/features/WalletMonitoring.feature index 26103b22f7..6a967dfbea 100644 --- a/integration_tests/tests/features/WalletMonitoring.feature +++ b/integration_tests/tests/features/WalletMonitoring.feature @@ -21,7 +21,7 @@ Feature: Wallet Monitoring # And I list all COINBASE transactions for wallet WALLET_A1 # Then wallet WALLET_A1 has 10 coinbase transactions # Then all COINBASE transactions for wallet WALLET_A1 are valid - # Then wallet WALLET_A1 detects at least 7 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_A1 detects at least 7 coinbase transactions as Mined_or_Faux_Confirmed # # # # Chain 2: # # Collects 10 coinbases into one wallet @@ -36,7 +36,7 @@ Feature: Wallet Monitoring # And I list all COINBASE transactions for wallet WALLET_B1 # Then wallet WALLET_B1 has 10 coinbase transactions # Then all COINBASE transactions for wallet WALLET_B1 are valid - # Then wallet WALLET_B1 detects at least 7 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_B1 detects at least 7 coinbase transactions as Mined_or_Faux_Confirmed # # # # Connect Chain 1 and 2 # # @@ -66,15 +66,15 @@ Feature: Wallet Monitoring # When mining node MINING_A mines 10 blocks with min difficulty 20 and max difficulty 9999999999 # Then node SEED_A is at height 10 # Then node NODE_A1 is at height 10 - # Then wallet WALLET_A1 detects exactly 7 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_A1 detects exactly 7 coinbase transactions as Mined_or_Faux_Confirmed # # Use 7 of the 10 coinbase UTXOs in transactions (others require 3 confirmations) # And I multi-send 7 transactions of 1000000 uT from wallet WALLET_A1 to wallet WALLET_A2 at fee 100 # When mining node MINING_A mines 10 blocks with min difficulty 20 and max difficulty 9999999999 # Then node SEED_A is at height 20 # Then node NODE_A1 is at height 20 - # Then wallet WALLET_A2 detects all transactions as Mined_Confirmed + # Then wallet WALLET_A2 detects all transactions as Mined_or_Faux_Confirmed # Then all NORMAL transactions for wallet WALLET_A1 are valid - # Then wallet WALLET_A1 detects exactly 17 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_A1 detects exactly 17 coinbase transactions as Mined_or_Faux_Confirmed # # # # Chain 2: # # Collects 10 coinbases into one wallet, send 7 transactions @@ -88,15 +88,15 @@ Feature: Wallet Monitoring # When mining node MINING_B mines 10 blocks with min difficulty 1 and max difficulty 2 # Then node SEED_B is at height 10 # Then node NODE_B1 is at height 10 - # Then wallet WALLET_B1 detects exactly 7 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_B1 detects exactly 7 coinbase transactions as Mined_or_Faux_Confirmed # # Use 7 of the 10 coinbase UTXOs in transactions (others require 3 confirmations) # And I multi-send 7 transactions of 1000000 uT from wallet WALLET_B1 to wallet WALLET_B2 at fee 100 # When mining node MINING_B mines 10 blocks with min difficulty 1 and max difficulty 2 # Then node SEED_B is at height 20 # Then node NODE_B1 is at height 20 - # Then wallet WALLET_B2 detects all transactions as Mined_Confirmed + # Then wallet WALLET_B2 detects all transactions as Mined_or_Faux_Confirmed # Then all NORMAL transactions for wallet WALLET_B1 are valid - # Then wallet WALLET_B1 detects exactly 17 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_B1 detects exactly 17 coinbase transactions as Mined_or_Faux_Confirmed # # # # Connect Chain 1 and 2 # # @@ -105,8 +105,8 @@ Feature: Wallet Monitoring # # When tip advances past required confirmations, invalid coinbases still being monitored will be cancelled. # And mining node NODE_C mines 6 blocks # Then all nodes are at height 26 - # Then wallet WALLET_A1 detects exactly 20 coinbase transactions as Mined_Confirmed - # Then wallet WALLET_B1 detects exactly 17 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_A1 detects exactly 20 coinbase transactions as Mined_or_Faux_Confirmed + # Then wallet WALLET_B1 detects exactly 17 coinbase transactions as Mined_or_Faux_Confirmed # And I list all NORMAL transactions for wallet WALLET_A1 # And I list all NORMAL transactions for wallet WALLET_B1 # # Uncomment this step when wallets can handle reorg diff --git a/integration_tests/tests/features/WalletQuery.feature b/integration_tests/tests/features/WalletQuery.feature index c55296cb8a..09e6fe575d 100644 --- a/integration_tests/tests/features/WalletQuery.feature +++ b/integration_tests/tests/features/WalletQuery.feature @@ -12,7 +12,7 @@ Feature: Wallet Querying When mining node MINER mines 5 blocks Then all nodes are at height 5 When I mine 5 blocks on NODE - Then all wallets detect all transactions as Mined_Confirmed + Then all wallets detect all transactions as Mined_or_Faux_Confirmed @critical Scenario: As a wallet I want to submit a transaction @@ -23,5 +23,5 @@ Feature: Wallet Querying When I wait 5 seconds When I transfer 5T from WALLET_A to WALLET_B When I mine 5 blocks on NODE - Then all wallets detect all transactions as Mined_Confirmed + Then all wallets detect all transactions as Mined_or_Faux_Confirmed diff --git a/integration_tests/tests/features/WalletRoutingMechanism.feature b/integration_tests/tests/features/WalletRoutingMechanism.feature index 0879bb4870..3f7a006aaa 100644 --- a/integration_tests/tests/features/WalletRoutingMechanism.feature +++ b/integration_tests/tests/features/WalletRoutingMechanism.feature @@ -23,12 +23,12 @@ Feature: Wallet Routing Mechanism When I wait 1 seconds # And mining node MINER mines 1 blocks # Then all nodes are at height 21 - # Then all wallets detect all transactions as Mined_Unconfirmed + # Then all wallets detect all transactions as Mined_or_Faux_Unconfirmed # # This wait is needed to stop next merge mining task from continuing When I wait 1 seconds # And mining node MINER mines 11 blocks # Then all nodes are at height 32 - # Then all wallets detect all transactions as Mined_Confirmed + # Then all wallets detect all transactions as Mined_or_Faux_Confirmed # This wait is needed to stop base nodes from shutting down When I wait 1 seconds # @long-running diff --git a/integration_tests/tests/features/WalletTransactions.feature b/integration_tests/tests/features/WalletTransactions.feature index d967b85f65..1df409c268 100644 --- a/integration_tests/tests/features/WalletTransactions.feature +++ b/integration_tests/tests/features/WalletTransactions.feature @@ -226,7 +226,7 @@ Feature: Wallet Transactions # Then node SEED_A is at height 7 # Then node NODE_A1 is at height 7 # When I mine 3 blocks on SEED_A - # Then wallet WALLET_A1 detects at least 7 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_A1 detects at least 7 coinbase transactions as Mined_or_Faux_Confirmed # Then node SEED_A is at height 10 # Then node NODE_A1 is at height 10 # When I multi-send 7 transactions of 1000000 uT from wallet WALLET_A1 to wallet WALLET_A2 at fee 100 @@ -244,7 +244,7 @@ Feature: Wallet Transactions # Then node SEED_B is at height 7 # Then node NODE_B1 is at height 7 # When I mine 5 blocks on SEED_B - # Then wallet WALLET_B1 detects at least 7 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_B1 detects at least 7 coinbase transactions as Mined_or_Faux_Confirmed # Then node SEED_B is at height 12 # Then node NODE_B1 is at height 12 # When I multi-send 7 transactions of 1000000 uT from wallet WALLET_B1 to wallet WALLET_B2 at fee 100 @@ -306,7 +306,7 @@ Feature: Wallet Transactions # Then node SEED_A is at height 1 # Then node NODE_A1 is at height 1 # When I mine 3 blocks on SEED_A - # Then wallet WALLET_A1 detects at least 1 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_A1 detects at least 1 coinbase transactions as Mined_or_Faux_Confirmed # Then node SEED_A is at height 4 # Then node NODE_A1 is at height 4 # When I multi-send 1 transactions of 10000 uT from wallet WALLET_A1 to wallet WALLET_A2 at fee 20 @@ -324,7 +324,7 @@ Feature: Wallet Transactions # Then node SEED_B is at height 2 # Then node NODE_B1 is at height 2 # When I mine 3 blocks on SEED_B - # Then wallet WALLET_B1 detects at least 2 coinbase transactions as Mined_Confirmed + # Then wallet WALLET_B1 detects at least 2 coinbase transactions as Mined_or_Faux_Confirmed # Then node SEED_B is at height 5 # Then node NODE_B1 is at height 5 # When I multi-send 2 transactions of 10000 uT from wallet WALLET_B1 to wallet WALLET_B2 at fee 20 @@ -380,7 +380,7 @@ Feature: Wallet Transactions # When I wait 30 seconds # When mining node MINER mines 5 blocks # Then all nodes are at height 15 - # When wallet WALLET_SENDER detects all transactions as Mined_Confirmed + # When wallet WALLET_SENDER detects all transactions as Mined_or_Faux_Confirmed # When I start wallet WALLET_RECV # When I wait 5 seconds # Then I restart wallet WALLET_RECV @@ -422,5 +422,5 @@ Feature: Wallet Transactions When I create a burn transaction of 201552500000 uT from WALLET_A at fee 100 When mining node MINER_B mines 5 blocks Then all nodes are at height 20 - Then wallet WALLET_A detects all transactions as Mined_Confirmed + Then wallet WALLET_A detects all transactions as Mined_or_Faux_Confirmed When I wait for wallet WALLET_A to have at least 20000000000 uT diff --git a/integration_tests/tests/features/WalletTransfer.feature b/integration_tests/tests/features/WalletTransfer.feature index 904b675cd9..df45336c93 100644 --- a/integration_tests/tests/features/WalletTransfer.feature +++ b/integration_tests/tests/features/WalletTransfer.feature @@ -40,7 +40,7 @@ Feature: Wallet Transfer When I transfer 50000 uT from WALLET_A to WALLET_B and WALLET_C at fee 20 When mining node MINER mines 10 blocks Then all nodes are at height 20 - Then all wallets detect all transactions as Mined_Confirmed + Then all wallets detect all transactions as Mined_or_Faux_Confirmed Scenario: As a wallet I want to submit transfers to myself @@ -55,7 +55,7 @@ Feature: Wallet Transfer When I transfer 50000 uT to self from wallet WALLET_A at fee 25 When I mine 5 blocks on NODE Then all nodes are at height 15 - Then all wallets detect all transactions as Mined_Confirmed + Then all wallets detect all transactions as Mined_or_Faux_Confirmed Scenario: As a wallet I want to create a HTLC transaction Given I have a seed node NODE diff --git a/integration_tests/tests/steps/merge_mining_steps.rs b/integration_tests/tests/steps/merge_mining_steps.rs index 8155c76ea0..723463d68c 100644 --- a/integration_tests/tests/steps/merge_mining_steps.rs +++ b/integration_tests/tests/steps/merge_mining_steps.rs @@ -38,7 +38,7 @@ async fn merge_mining_proxy_with_submission( "disabled" => false, _ => panic!("This should be a boolean"), }; - register_merge_mining_proxy_process(world, mining_proxy_name, base_node_name, wallet_name, enabled).await; + register_merge_mining_proxy_process(world, mining_proxy_name, base_node_name, wallet_name, enabled, true).await; } #[when(expr = "I have a merge mining proxy {word} connected to {word} and {word} with default config")] @@ -48,7 +48,7 @@ async fn merge_mining_proxy_with_default_config( base_node_name: String, wallet_name: String, ) { - register_merge_mining_proxy_process(world, mining_proxy_name, base_node_name, wallet_name, true).await; + register_merge_mining_proxy_process(world, mining_proxy_name, base_node_name, wallet_name, true, false).await; } #[when(expr = "I ask for a block height from proxy {word}")] diff --git a/integration_tests/tests/steps/mining_steps.rs b/integration_tests/tests/steps/mining_steps.rs index bbc5d12f71..a9dab35920 100644 --- a/integration_tests/tests/steps/mining_steps.rs +++ b/integration_tests/tests/steps/mining_steps.rs @@ -45,7 +45,7 @@ use crate::steps::{node_steps::submit_transaction_to, wallet_steps::create_tx_sp #[when(expr = "I have mine-before-tip mining node {word} connected to base node {word} and wallet {word}")] #[when(expr = "I have mining node {word} connected to base node {word} and wallet {word}")] pub async fn create_miner(world: &mut TariWorld, miner_name: String, bn_name: String, wallet_name: String) { - register_miner_process(world, miner_name, bn_name, wallet_name); + register_miner_process(world, miner_name, bn_name, wallet_name, false); } #[when(expr = "mining node {word} mines {int} blocks")] @@ -65,7 +65,18 @@ async fn mine_blocks_on(world: &mut TariWorld, blocks: u64, base_node: String) { .get_node_client(&base_node) .await .expect("Couldn't get the node client to mine with"); - mine_blocks_without_wallet(&mut client, blocks, 0, &world.key_manager).await; + let script_key_id = &world.script_key_id().await; + mine_blocks_without_wallet( + &mut client, + blocks, + 0, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await; } #[when(expr = "mining node {word} mines {int} blocks with min difficulty {int} and max difficulty {int}")] @@ -94,7 +105,18 @@ async fn mine_custom_weight_blocks_with_height(world: &mut TariWorld, num_blocks .get_node_client(&node_name) .await .expect("Couldn't get the node client to mine with"); - mine_blocks_without_wallet(&mut client, num_blocks, weight, &world.key_manager).await; + let script_key_id = &world.script_key_id().await; + mine_blocks_without_wallet( + &mut client, + num_blocks, + weight, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await; } #[then(expr = "I have a SHA3 miner {word} connected to node {word}")] @@ -105,10 +127,13 @@ async fn sha3_miner_connected_to_base_node(world: &mut TariWorld, miner: String, let peers = base_node.seed_nodes.clone(); world.wallet_connected_to_base_node.insert(miner.clone(), miner.clone()); spawn_wallet(world, miner.clone(), Some(miner.clone()), peers, None, None).await; - register_miner_process(world, miner.clone(), miner.clone(), miner); + register_miner_process(world, miner.clone(), miner.clone(), miner, false); } -#[then(expr = "while mining via SHA3 miner {word} all transactions in wallet {word} are found to be Mined_Confirmed")] +#[then( + expr = "while mining via SHA3 miner {word} all transactions in wallet {word} are found to be \ + Mined_or_Faux_Confirmed" +)] async fn while_mining_all_txs_in_wallet_are_mined_confirmed(world: &mut TariWorld, miner: String, wallet: String) { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); let wallet_address = world.get_wallet_address(&wallet).await.unwrap(); @@ -121,7 +146,7 @@ async fn while_mining_all_txs_in_wallet_are_mined_confirmed(world: &mut TariWorl let miner_ps = world.miners.get(&miner).unwrap(); let num_retries = 100; println!( - "Detecting {} Mined_Confirmed transactions for wallet {}", + "Detecting {} Mined_or_Faux_Confirmed transactions for wallet {}", wallet_tx_ids.len(), wallet ); @@ -136,7 +161,7 @@ async fn while_mining_all_txs_in_wallet_are_mined_confirmed(world: &mut TariWorl // TRANSACTION_STATUS_MINED_CONFIRMED code is currently 6 if tx_status == 6 { println!( - "Wallet transaction with id {} has been detected with status Mined_Confirmed", + "Wallet transaction with id {} has been detected with status Mined_or_Faux_Confirmed", tx_id ); break 'inner; @@ -144,12 +169,15 @@ async fn while_mining_all_txs_in_wallet_are_mined_confirmed(world: &mut TariWorl if retry == num_retries { panic!( - "Unable to have wallet transaction with tx_id = {} with status Mined_Confirmed", + "Unable to have wallet transaction with tx_id = {} with status Mined_or_Faux_Confirmed", tx_id ); } - println!("Mine a block for tx_id {} to have status Mined_Confirmed", tx_id); + println!( + "Mine a block for tx_id {} to have status Mined_or_Faux_Confirmed", + tx_id + ); miner_ps.mine(world, Some(1), None, None).await; tokio::time::sleep(Duration::from_secs(5)).await; @@ -157,7 +185,7 @@ async fn while_mining_all_txs_in_wallet_are_mined_confirmed(world: &mut TariWorl } } -#[then(expr = "while mining via node {word} all transactions in wallet {word} are found to be Mined_Confirmed")] +#[then(expr = "while mining via node {word} all transactions in wallet {word} are found to be Mined_or_Faux_Confirmed")] async fn while_mining_in_node_all_txs_in_wallet_are_mined_confirmed( world: &mut TariWorld, node: String, @@ -165,6 +193,7 @@ async fn while_mining_in_node_all_txs_in_wallet_are_mined_confirmed( ) { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); let wallet_address = world.get_wallet_address(&wallet).await.unwrap(); + let script_key_id = &world.script_key_id().await; let wallet_tx_ids = world.wallet_tx_ids.get(&wallet_address).unwrap(); if wallet_tx_ids.is_empty() { @@ -176,13 +205,13 @@ async fn while_mining_in_node_all_txs_in_wallet_are_mined_confirmed( let mut mined_status_flag = false; println!( - "Detecting transactions on wallet {}, while mining on node {}, to be Mined_Confirmed", + "Detecting transactions on wallet {}, while mining on node {}, to be Mined_or_Faux_Confirmed", &wallet, &node ); for tx_id in wallet_tx_ids { println!( - "Waiting for transaction with id {} to have status Mined_Confirmed, while mining on node {}", + "Waiting for transaction with id {} to have status Mined_or_Faux_Confirmed, while mining on node {}", tx_id, &node ); @@ -194,27 +223,39 @@ async fn while_mining_in_node_all_txs_in_wallet_are_mined_confirmed( let tx_status = res.transactions.first().unwrap().status; // TRANSACTION_STATUS_MINED_CONFIRMED code is currently 6 if tx_status == 6 { - println!("Transaction with id {} has been Mined_Confirmed", tx_id); + println!("Transaction with id {} has been Mined_or_Faux_Confirmed", tx_id); mined_status_flag = true; break 'inner; } - println!("Mine a block for tx_id {} to have status Mined_Confirmed", tx_id); - mine_block(&mut node_client, &mut wallet_client).await; + println!( + "Mine a block for tx_id {} to have status Mined_or_Faux_Confirmed", + tx_id + ); + mine_block( + &mut node_client, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await; tokio::time::sleep(Duration::from_secs(5)).await; } if !mined_status_flag { panic!( - "Failed to have transaction with id {} on wallet {}, while mining on node {}, to be Mined_Confirmed", + "Failed to have transaction with id {} on wallet {}, while mining on node {}, to be \ + Mined_or_Faux_Confirmed", tx_id, &wallet, &node ); } } println!( - "Wallet {} has all transactions Mined_Confirmed, while mining on node {}", + "Wallet {} has all transactions Mined_or_Faux_Confirmed, while mining on node {}", &wallet, &node ); } @@ -233,7 +274,24 @@ async fn sha3_miner_connected_to_all_seed_nodes(world: &mut TariWorld, sha3_mine ) .await; - register_miner_process(world, sha3_miner.clone(), sha3_miner.clone(), sha3_miner); + register_miner_process(world, sha3_miner.clone(), sha3_miner.clone(), sha3_miner, false); +} + +#[when(expr = "I have a stealth SHA3 miner {word} connected to all seed nodes")] +async fn stealth_sha3_miner_connected_to_all_seed_nodes(world: &mut TariWorld, sha3_miner: String) { + spawn_base_node(world, false, sha3_miner.clone(), world.seed_nodes.clone()).await; + + spawn_wallet( + world, + sha3_miner.clone(), + Some(sha3_miner.clone()), + world.seed_nodes.clone(), + None, + None, + ) + .await; + + register_miner_process(world, sha3_miner.clone(), sha3_miner.clone(), sha3_miner, true); } #[given(expr = "I have a SHA3 miner {word} connected to seed node {word}")] @@ -254,7 +312,7 @@ async fn sha3_miner_connected_to_seed_node(world: &mut TariWorld, sha3_miner: St .await; println!("Register SHA3 miner {}", &sha3_miner); - register_miner_process(world, sha3_miner.clone(), sha3_miner.clone(), sha3_miner); + register_miner_process(world, sha3_miner.clone(), sha3_miner.clone(), sha3_miner, false); } #[when(expr = "I have individual mining nodes connected to each wallet and base node {word}")] @@ -263,7 +321,7 @@ async fn mining_nodes_connected_to_each_wallet_and_base_node(world: &mut TariWor for (ind, wallet_name) in wallets.keys().enumerate() { let miner = format!("Miner_{}", ind); - register_miner_process(world, miner, base_node.clone(), wallet_name.clone()); + register_miner_process(world, miner, base_node.clone(), wallet_name.clone(), false); } } @@ -281,8 +339,19 @@ async fn mining_node_mine_blocks(world: &mut TariWorld, blocks: u64) { async fn mine_without_submit(world: &mut TariWorld, block: String, node: String) { let mut client = world.get_node_client(&node).await.unwrap(); - let unmined_block: Block = - Block::try_from(mine_block_before_submit(&mut client, &world.key_manager).await).unwrap(); + let script_key_id = &world.script_key_id().await; + let unmined_block: Block = Block::try_from( + mine_block_before_submit( + &mut client, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await, + ) + .unwrap(); world.blocks.insert(block, unmined_block); } @@ -332,7 +401,7 @@ async fn num_blocks_with_difficulty(world: &mut TariWorld, num_blocks: u64, diff let miner_name = format!("miner-{}", &node); if world.miners.get(&miner_name).is_none() { - register_miner_process(world, miner_name.clone(), node.clone(), wallet_name.clone()); + register_miner_process(world, miner_name.clone(), node.clone(), wallet_name.clone(), false); } let miner = world.miners.get(&miner_name).unwrap(); diff --git a/integration_tests/tests/steps/node_steps.rs b/integration_tests/tests/steps/node_steps.rs index a99b8c0b20..53d6b6a395 100644 --- a/integration_tests/tests/steps/node_steps.rs +++ b/integration_tests/tests/steps/node_steps.rs @@ -645,7 +645,16 @@ async fn no_meddling_with_data(world: &mut TariWorld, node: String) { // No meddling let chain_tip = client.get_tip_info(Empty {}).await.unwrap().into_inner(); let current_height = chain_tip.metadata.unwrap().height_of_longest_chain; - let block = mine_block_before_submit(&mut client, &world.key_manager).await; + let script_key_id = &world.script_key_id().await; + let block = mine_block_before_submit( + &mut client, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await; let _sumbmit_res = client.submit_block(block).await.unwrap(); let chain_tip = client.get_tip_info(Empty {}).await.unwrap().into_inner(); @@ -660,7 +669,19 @@ async fn no_meddling_with_data(world: &mut TariWorld, node: String) { ); // Meddle with kernal_mmr_size - let mut block: Block = Block::try_from(mine_block_before_submit(&mut client, &world.key_manager).await).unwrap(); + let script_key_id = &world.script_key_id().await; + let mut block: Block = Block::try_from( + mine_block_before_submit( + &mut client, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await, + ) + .unwrap(); block.header.kernel_mmr_size += 1; match client.submit_block(grpc::Block::try_from(block).unwrap()).await { Ok(_) => panic!("The block should not have been valid"), @@ -673,7 +694,19 @@ async fn no_meddling_with_data(world: &mut TariWorld, node: String) { } // Meddle with output_mmr_size - let mut block: Block = Block::try_from(mine_block_before_submit(&mut client, &world.key_manager).await).unwrap(); + let script_key_id = &world.script_key_id().await; + let mut block: Block = Block::try_from( + mine_block_before_submit( + &mut client, + &world.key_manager, + script_key_id, + &world.default_payment_address.clone(), + false, + &world.consensus_manager.clone(), + ) + .await, + ) + .unwrap(); block.header.output_smt_size += 1; match client.submit_block(grpc::Block::try_from(block).unwrap()).await { Ok(_) => panic!("The block should not have been valid"), diff --git a/integration_tests/tests/steps/wallet_steps.rs b/integration_tests/tests/steps/wallet_steps.rs index f3577bc8ae..b60642d3d6 100644 --- a/integration_tests/tests/steps/wallet_steps.rs +++ b/integration_tests/tests/steps/wallet_steps.rs @@ -41,10 +41,8 @@ use grpc::{ use minotari_app_grpc::tari_rpc::{self as grpc}; use minotari_console_wallet::{CliCommands, ExportUtxosArgs}; use minotari_wallet::transaction_service::config::TransactionRoutingMechanism; -use tari_common::configuration::Network; use tari_common_types::types::{ComAndPubSignature, Commitment, PrivateKey, PublicKey}; use tari_core::{ - consensus::ConsensusManager, covenants::Covenant, transactions::{ tari_amount::MicroMinotari, @@ -191,7 +189,9 @@ async fn wallet_detects_all_txs_as_mined_confirmed(world: &mut TariWorld, wallet grpc::TransactionStatus::Completed | grpc::TransactionStatus::Broadcast | grpc::TransactionStatus::MinedUnconfirmed | - grpc::TransactionStatus::MinedConfirmed => { + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), @@ -200,7 +200,9 @@ async fn wallet_detects_all_txs_as_mined_confirmed(world: &mut TariWorld, wallet grpc::TransactionStatus::Completed | grpc::TransactionStatus::Broadcast | grpc::TransactionStatus::MinedUnconfirmed | - grpc::TransactionStatus::MinedConfirmed => { + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), @@ -208,19 +210,24 @@ async fn wallet_detects_all_txs_as_mined_confirmed(world: &mut TariWorld, wallet "Broadcast" => match tx_info.status() { grpc::TransactionStatus::Broadcast | grpc::TransactionStatus::MinedUnconfirmed | - grpc::TransactionStatus::MinedConfirmed => { + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), }, - "Mined_Unconfirmed" => match tx_info.status() { - grpc::TransactionStatus::MinedUnconfirmed | grpc::TransactionStatus::MinedConfirmed => { + "Mined_or_Faux_Unconfirmed" => match tx_info.status() { + grpc::TransactionStatus::MinedUnconfirmed | + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), }, - "Mined_Confirmed" => match tx_info.status() { - grpc::TransactionStatus::MinedConfirmed | grpc::TransactionStatus::Broadcast => { + "Mined_or_Faux_Confirmed" => match tx_info.status() { + grpc::TransactionStatus::MinedConfirmed | grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), @@ -238,6 +245,8 @@ async fn wallet_detects_all_txs_as_mined_confirmed(world: &mut TariWorld, wallet }, _ => panic!("Unknown status {}, don't know what to expect", status), } + // tokio sleep 100ms + tokio::time::sleep(Duration::from_millis(100)).await; } } } @@ -284,7 +293,9 @@ async fn wallet_detects_all_txs_are_at_least_in_some_status( grpc::TransactionStatus::Completed | grpc::TransactionStatus::Broadcast | grpc::TransactionStatus::MinedUnconfirmed | - grpc::TransactionStatus::MinedConfirmed => { + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), @@ -293,7 +304,9 @@ async fn wallet_detects_all_txs_are_at_least_in_some_status( grpc::TransactionStatus::Completed | grpc::TransactionStatus::Broadcast | grpc::TransactionStatus::MinedUnconfirmed | - grpc::TransactionStatus::MinedConfirmed => { + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), @@ -301,13 +314,18 @@ async fn wallet_detects_all_txs_are_at_least_in_some_status( "Broadcast" => match tx_info.status() { grpc::TransactionStatus::Broadcast | grpc::TransactionStatus::MinedUnconfirmed | - grpc::TransactionStatus::MinedConfirmed => { + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), }, - "Mined_Unconfirmed" => match tx_info.status() { - grpc::TransactionStatus::MinedUnconfirmed | grpc::TransactionStatus::MinedConfirmed => { + "Mined_or_Faux_Unconfirmed" => match tx_info.status() { + grpc::TransactionStatus::MinedUnconfirmed | + grpc::TransactionStatus::MinedConfirmed | + grpc::TransactionStatus::FauxUnconfirmed | + grpc::TransactionStatus::FauxConfirmed => { break; }, _ => (), @@ -503,11 +521,11 @@ async fn wallet_has_at_least_num_txs(world: &mut TariWorld, wallet: String, num_ "TRANSACTION_STATUS_PENDING" => 4, "TRANSACTION_STATUS_COINBASE" => 5, "TRANSACTION_STATUS_MINED_CONFIRMED" => 6, - "TRANSACTION_STATUS_NOT_FOUND" => 7, - "TRANSACTION_STATUS_REJECTED" => 8, - "TRANSACTION_STATUS_FAUX_UNCONFIRMED" => 9, - "TRANSACTION_STATUS_FAUX_CONFIRMED" => 10, - "TRANSACTION_STATUS_QUEUED" => 11, + "TRANSACTION_STATUS_REJECTED" => 7, + "TRANSACTION_STATUS_FAUX_UNCONFIRMED" => 8, + "TRANSACTION_STATUS_FAUX_CONFIRMED" => 9, + "TRANSACTION_STATUS_QUEUED" => 10, + "TRANSACTION_STATUS_NOT_FOUND" => 11, _ => panic!("Invalid transaction status {}", transaction_status), }; @@ -916,7 +934,7 @@ async fn send_amount_from_wallet_to_wallet_at_fee( ); } -#[then(expr = "wallet {word} detects at least {int} coinbase transactions as Mined_Confirmed")] +#[then(expr = "wallet {word} detects at least {int} coinbase transactions as Mined_or_Faux_Confirmed")] async fn wallet_detects_at_least_coinbase_transactions(world: &mut TariWorld, wallet_name: String, coinbases: u64) { let mut client = create_wallet_client(world, wallet_name.clone()).await.unwrap(); let mut completed_tx_res = client @@ -957,18 +975,18 @@ async fn wallet_detects_at_least_coinbase_transactions(world: &mut TariWorld, wa if total_mined_confirmed_coinbases >= coinbases { println!( - "Wallet {} detected at least {} coinbase transactions as Mined_Confirmed", + "Wallet {} detected at least {} coinbase transactions as Mined_or_Faux_Confirmed", &wallet_name, coinbases ); } else { panic!( - "Wallet {} failed to detect at least {} coinbase transactions as Mined_Confirmed", + "Wallet {} failed to detect at least {} coinbase transactions as Mined_or_Faux_Confirmed", wallet_name, coinbases ); } } -#[then(expr = "wallet {word} detects at least {int} coinbase transactions as Mined_Unconfirmed")] +#[then(expr = "wallet {word} detects at least {int} coinbase transactions as Mined_or_Faux_Unconfirmed")] async fn wallet_detects_at_least_unmined_transactions(world: &mut TariWorld, wallet_name: String, coinbases: u64) { let mut client = create_wallet_client(world, wallet_name.clone()).await.unwrap(); let mut completed_tx_res = client @@ -1009,18 +1027,18 @@ async fn wallet_detects_at_least_unmined_transactions(world: &mut TariWorld, wal if total_mined_unconfirmed_coinbases >= coinbases { println!( - "Wallet {} detected at least {} coinbase transactions as Mined_Unconfirmed", + "Wallet {} detected at least {} coinbase transactions as Mined_or_Faux_Unconfirmed", &wallet_name, coinbases ); } else { panic!( - "Wallet {} failed to detect at least {} coinbase transactions as Mined_Unconfirmed", + "Wallet {} failed to detect at least {} coinbase transactions as Mined_or_Faux_Unconfirmed", wallet_name, coinbases ); } } -#[then(expr = "wallet {word} detects exactly {int} coinbase transactions as Mined_Confirmed")] +#[then(expr = "wallet {word} detects exactly {int} coinbase transactions as Mined_or_Faux_Confirmed")] async fn wallet_detects_exactly_coinbase_transactions(world: &mut TariWorld, wallet_name: String, coinbases: u64) { let mut client = create_wallet_client(world, wallet_name.clone()).await.unwrap(); let wallet_address = world.get_wallet_address(&wallet_name).await.unwrap(); @@ -1054,12 +1072,12 @@ async fn wallet_detects_exactly_coinbase_transactions(world: &mut TariWorld, wal if total_mined_confirmed_coinbases == coinbases { println!( - "Wallet {} detected exactly {} coinbase transactions as Mined_Confirmed", + "Wallet {} detected exactly {} coinbase transactions as Mined_or_Faux_Confirmed", &wallet_name, coinbases ); } else { panic!( - "Wallet {} failed to detect exactly {} coinbase transactions as Mined_Confirmed", + "Wallet {} failed to detect exactly {} coinbase transactions as Mined_or_Faux_Confirmed", wallet_name, coinbases ); } @@ -1106,7 +1124,7 @@ async fn start_wallet_without_node(world: &mut TariWorld, wallet: String) { } } -#[then(expr = "all wallets detect all transactions as Mined_Confirmed")] +#[then(expr = "all wallets detect all transactions as Mined_or_Faux_Confirmed")] async fn all_wallets_detect_all_txs_as_mined_confirmed(world: &mut TariWorld) { for wallet in world.wallets.keys() { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); @@ -1137,7 +1155,7 @@ async fn all_wallets_detect_all_txs_as_mined_confirmed(world: &mut TariWorld) { // TRANSACTION_STATUS_MINED_CONFIRMED code is currently 6 if tx_status == 6 { println!( - "Wallet {} has detected transaction with id {} as Mined_Confirmed", + "Wallet {} has detected transaction with id {} as Mined_or_Faux_Confirmed", &wallet, tx_id ); break 'inner; @@ -1145,7 +1163,7 @@ async fn all_wallets_detect_all_txs_as_mined_confirmed(world: &mut TariWorld) { if retry == num_retries { panic!( - "Transaction with id {} does not have status as Mined_Confirmed, on wallet {}", + "Transaction with id {} does not have status as Mined_or_Faux_Confirmed, on wallet {}", tx_id, &wallet ); } @@ -1202,7 +1220,7 @@ async fn wallets_should_have_at_least_num_spendable_coinbase_outs( // MINED_CONFIRMED status = 6 if tx_info.status == 6 { println!( - "Coinbase transaction with id {} for wallet {} is Mined_Confirmed", + "Coinbase transaction with id {} for wallet {} is Mined_or_Faux_Confirmed", tx_id, &wallet ); spendable_coinbase_count += 1; @@ -1530,12 +1548,11 @@ async fn wallet_with_tari_connected_to_base_node( let mut num_blocks = 0; let mut reward = 0; - let consensus_manager = ConsensusManager::builder(Network::LocalNet).build().unwrap(); - while reward < amount { current_height += 1; num_blocks += 1; - reward += consensus_manager.get_block_reward_at(current_height).as_u64() / 1_000_000; // 1 T = 1_000_000 uT + reward += world.consensus_manager.get_block_reward_at(current_height).as_u64() / 1_000_000; // 1 T = 1_000_000 + // uT } println!("Creating miner...");