Skip to content

Commit

Permalink
feat!: add one-sided coinbase payments (#5967)
Browse files Browse the repository at this point in the history
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`.


<!-- Checklist -->
<!-- 1. Is the title of your PR in the form that would make nice release
notes? The title, excluding the conventional commit
tag, will be included exactly as is in the CHANGELOG, so please think
about it carefully. -->


Breaking Changes
---

- [ ] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [x] Other - Please specify

<!-- Does this include a breaking change? If so, include this line as a
footer -->
- Coinbases can now only be paid to a nominated wallet address directly.
- Existing wallets need to be recovered into new wallets.
  • Loading branch information
hansieodendaal authored Nov 23, 2023
1 parent 64e375b commit 89b19f6
Show file tree
Hide file tree
Showing 139 changed files with 2,364 additions and 3,694 deletions.
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 6 additions & 20 deletions applications/minotari_app_grpc/proto/wallet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 { }
Expand All @@ -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;
Expand Down Expand Up @@ -329,7 +316,6 @@ message TransactionEvent {
string direction = 6;
uint64 amount = 7;
string message = 8;
bool is_coinbase = 9;
}

message TransactionEventResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ impl From<TransactionStatus> 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,
Expand Down
1 change: 0 additions & 1 deletion applications/minotari_console_wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 0 additions & 6 deletions applications/minotari_console_wallet/src/grpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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"),
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ use minotari_app_grpc::tari_rpc::{
GetAddressResponse,
GetBalanceRequest,
GetBalanceResponse,
GetCoinbaseRequest,
GetCoinbaseResponse,
GetCompletedTransactionsRequest,
GetCompletedTransactionsResponse,
GetConnectivityRequest,
Expand Down Expand Up @@ -309,24 +307,6 @@ impl wallet_server::Wallet for WalletGrpcServer {
Ok(Response::new(RevalidateResponse {}))
}

async fn get_coinbase(
&self,
request: Request<GetCoinbaseRequest>,
) -> Result<Response<GetCoinbaseResponse>, 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<SendShaAtomicSwapRequest>,
Expand Down Expand Up @@ -1090,7 +1070,6 @@ fn simple_event(event: &str) -> TransactionEvent {
direction: event.to_string(),
amount: 0,
message: String::default(),
is_coinbase: false,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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: {})",
Expand Down Expand Up @@ -602,7 +598,6 @@ impl<B: Backend> Component<B> 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 => {
Expand Down
32 changes: 1 addition & 31 deletions applications/minotari_console_wallet/src/ui/state/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use std::{
time::{Duration, Instant},
};

use bitflags::bitflags;
use chrono::{DateTime, Local, NaiveDateTime};
use log::*;
use minotari_wallet::{
Expand Down Expand Up @@ -97,7 +96,6 @@ pub struct AppState {
inner: Arc<RwLock<AppStateInner>>,
cached_data: AppStateData,
cache_update_cooldown: Option<Instant>,
completed_tx_filter: TransactionFilter,
config: AppStateConfig,
wallet_config: WalletConfig,
wallet_connectivity: WalletConnectivityHandle,
Expand All @@ -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(
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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<Local>, String)] {
&self.cached_data.notifications
}
Expand Down Expand Up @@ -1166,7 +1147,6 @@ pub struct CompletedTransactionInfo {
pub cancelled: Option<TxCancellationReason>,
pub direction: TransactionDirection,
pub mined_height: Option<u64>,
pub is_coinbase: bool,
pub weight: u64,
pub inputs_count: usize,
pub outputs_count: usize,
Expand All @@ -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();
Expand All @@ -1208,7 +1187,6 @@ impl CompletedTransactionInfo {
cancelled: tx.cancelled,
direction: tx.direction,
mined_height: tx.mined_height,
is_coinbase,
weight,
inputs_count,
outputs_count,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions applications/minotari_merge_mining_proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ 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" }
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" }
Expand Down
Loading

0 comments on commit 89b19f6

Please sign in to comment.