Skip to content

Commit

Permalink
feat: add sending to stealth address (command, grpc, gui) (#4307)
Browse files Browse the repository at this point in the history
* Add sending to stealth address (command, GRPC, gui).

* Extract one sided logic to separate function.

* Fix clippy issues.

* change to reverse dns notation

Co-authored-by: aviator-app[bot] <48659329+aviator-app[bot]@users.noreply.github.com>
  • Loading branch information
Cifko and aviator-app[bot] committed Jul 20, 2022
1 parent 8854ca1 commit a897278
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 87 deletions.
1 change: 1 addition & 0 deletions applications/tari_app_grpc/proto/wallet.proto
Expand Up @@ -110,6 +110,7 @@ message PaymentRecipient {
enum PaymentType {
STANDARD_MIMBLEWIMBLE = 0;
ONE_SIDED = 1;
ONE_SIDED_TO_STEALTH_ADDRESS = 2;
}
PaymentType payment_type = 5;
}
Expand Down
31 changes: 31 additions & 0 deletions applications/tari_console_wallet/src/automation/commands.rs
Expand Up @@ -224,6 +224,25 @@ pub async fn send_one_sided(
.map_err(CommandError::TransactionServiceError)
}

pub async fn send_one_sided_to_stealth_address(
mut wallet_transaction_service: TransactionServiceHandle,
fee_per_gram: u64,
amount: MicroTari,
dest_pubkey: PublicKey,
message: String,
) -> Result<TxId, CommandError> {
wallet_transaction_service
.send_one_sided_to_stealth_address_transaction(
dest_pubkey,
amount,
OutputFeatures::default(),
fee_per_gram * uT,
message,
)
.await
.map_err(CommandError::TransactionServiceError)
}

pub async fn coin_split(
amount_per_split: MicroTari,
num_splits: usize,
Expand Down Expand Up @@ -605,6 +624,18 @@ pub async fn command_runner(
debug!(target: LOG_TARGET, "send-one-sided tx_id {}", tx_id);
tx_ids.push(tx_id);
},
SendOneSidedToStealthAddress(args) => {
let tx_id = send_one_sided_to_stealth_address(
transaction_service.clone(),
config.fee_per_gram,
args.amount,
args.destination.into(),
args.message,
)
.await?;
debug!(target: LOG_TARGET, "send-one-sided-to-stealth-address tx_id {}", tx_id);
tx_ids.push(tx_id);
},
MakeItRain(args) => {
make_it_rain(
transaction_service.clone(),
Expand Down
1 change: 1 addition & 0 deletions applications/tari_console_wallet/src/cli.rs
Expand Up @@ -95,6 +95,7 @@ pub enum CliCommands {
GetBalance,
SendTari(SendTariArgs),
SendOneSided(SendTariArgs),
SendOneSidedToStealthAddress(SendTariArgs),
MakeItRain(MakeItRainArgs),
CoinSplit(CoinSplitArgs),
DiscoverPeer(DiscoverPeerArgs),
Expand Down
45 changes: 23 additions & 22 deletions applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs
Expand Up @@ -449,14 +449,13 @@ impl wallet_server::Wallet for WalletGrpcServer {
.collect::<Result<Vec<_>, _>>()
.map_err(Status::invalid_argument)?;

let mut standard_transfers = Vec::new();
let mut one_sided_transfers = Vec::new();
let mut transfers = Vec::new();
for (address, pk, amount, fee_per_gram, message, payment_type) in recipients {
let mut transaction_service = self.get_transaction_service();
if payment_type == PaymentType::StandardMimblewimble as i32 {
standard_transfers.push(async move {
(
address,
transfers.push(async move {
(
address,
if payment_type == PaymentType::StandardMimblewimble as i32 {
transaction_service
.send_transaction(
pk,
Expand All @@ -465,13 +464,8 @@ impl wallet_server::Wallet for WalletGrpcServer {
fee_per_gram.into(),
message,
)
.await,
)
});
} else if payment_type == PaymentType::OneSided as i32 {
one_sided_transfers.push(async move {
(
address,
.await
} else if payment_type == PaymentType::OneSided as i32 {
transaction_service
.send_one_sided_transaction(
pk,
Expand All @@ -480,19 +474,26 @@ impl wallet_server::Wallet for WalletGrpcServer {
fee_per_gram.into(),
message,
)
.await,
)
});
} else {
}
.await
} else {
transaction_service
.send_one_sided_to_stealth_address_transaction(
pk,
amount.into(),
OutputFeatures::default(),
fee_per_gram.into(),
message,
)
.await
},
)
});
}

let standard_results = future::join_all(standard_transfers).await;
let one_sided_results = future::join_all(one_sided_transfers).await;
let transfers_results = future::join_all(transfers).await;

let results = standard_results
let results = transfers_results
.into_iter()
.chain(one_sided_results.into_iter())
.map(|(address, result)| match result {
Ok(tx_id) => TransferResult {
address,
Expand Down
142 changes: 92 additions & 50 deletions applications/tari_console_wallet/src/ui/components/send_tab.rs
Expand Up @@ -106,7 +106,9 @@ impl SendTab {
Span::styled("S", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to send a normal transaction, "),
Span::styled("O", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to send a one-sided transaction."),
Span::raw(" to send a one-sided transaction, "),
Span::styled("X", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to send a one-sided transaction to a stealth address."),
]),
])
.wrap(Wrap { trim: false })
Expand Down Expand Up @@ -270,17 +272,18 @@ impl SendTab {
f.render_stateful_widget(table, area, &mut self.table_state)
}

#[allow(clippy::too_many_lines)]
fn on_key_confirmation_dialog(&mut self, c: char, app_state: &mut AppState) -> KeyHandled {
if self.confirmation_dialog.is_some() {
if 'n' == c {
self.confirmation_dialog = None;
return KeyHandled::Handled;
} else if 'y' == c {
let one_sided_transaction =
matches!(self.confirmation_dialog, Some(ConfirmationDialogType::OneSidedSend));
match self.confirmation_dialog {
None => (),
Some(ConfirmationDialogType::NormalSend) | Some(ConfirmationDialogType::OneSidedSend) => {
Some(ConfirmationDialogType::Normal) |
Some(ConfirmationDialogType::OneSided) |
Some(ConfirmationDialogType::StealthAddress) => {
if 'y' == c {
let amount = if let Ok(v) = self.amount_field.parse::<MicroTari>() {
v
Expand All @@ -304,42 +307,67 @@ impl SendTab {
let (tx, rx) = watch::channel(UiTransactionSendStatus::Initiated);

let mut reset_fields = false;
if one_sided_transaction {
match Handle::current().block_on(app_state.send_one_sided_transaction(
self.to_field.clone(),
amount.into(),
self.selected_unique_id.clone(),
None,
fee_per_gram,
self.message_field.clone(),
tx,
)) {
Err(e) => {
self.error_message = Some(format!(
"Error sending one-sided transaction:\n{}\nPress Enter to continue.",
e
))
},
Ok(_) => reset_fields = true,
}
} else {
match Handle::current().block_on(app_state.send_transaction(
self.to_field.clone(),
amount.into(),
self.selected_unique_id.clone(),
None,
fee_per_gram,
self.message_field.clone(),
tx,
)) {
Err(e) => {
self.error_message = Some(format!(
"Error sending normal transaction:\n{}\nPress Enter to continue.",
e
))
},
Ok(_) => reset_fields = true,
}
match self.confirmation_dialog {
Some(ConfirmationDialogType::OneSided) => {
match Handle::current().block_on(app_state.send_one_sided_transaction(
self.to_field.clone(),
amount.into(),
self.selected_unique_id.clone(),
None,
fee_per_gram,
self.message_field.clone(),
tx,
)) {
Err(e) => {
self.error_message = Some(format!(
"Error sending one-sided transaction:\n{}\nPress Enter to continue.",
e
))
},
Ok(_) => reset_fields = true,
}
},
Some(ConfirmationDialogType::StealthAddress) => {
match Handle::current().block_on(
app_state.send_one_sided_to_stealth_address_transaction(
self.to_field.clone(),
amount.into(),
self.selected_unique_id.clone(),
None,
fee_per_gram,
self.message_field.clone(),
tx,
),
) {
Err(e) => {
self.error_message = Some(format!(
"Error sending one-sided transaction to stealth address:\n{}\nPress \
Enter to continue.",
e
))
},
Ok(_) => reset_fields = true,
}
},
_ => {
match Handle::current().block_on(app_state.send_transaction(
self.to_field.clone(),
amount.into(),
self.selected_unique_id.clone(),
None,
fee_per_gram,
self.message_field.clone(),
tx,
)) {
Err(e) => {
self.error_message = Some(format!(
"Error sending normal transaction:\n{}\nPress Enter to continue.",
e
))
},
Ok(_) => reset_fields = true,
}
},
}
if reset_fields {
self.to_field = "".to_string();
Expand Down Expand Up @@ -432,6 +460,7 @@ impl SendTab {
}

impl<B: Backend> Component<B> for SendTab {
#[allow(clippy::too_many_lines)]
fn draw(&mut self, f: &mut Frame<B>, area: Rect, app_state: &AppState) {
let areas = Layout::default()
.constraints(
Expand Down Expand Up @@ -511,7 +540,7 @@ impl<B: Backend> Component<B> for SendTab {

match self.confirmation_dialog {
None => (),
Some(ConfirmationDialogType::NormalSend) => {
Some(ConfirmationDialogType::Normal) => {
draw_dialog(
f,
area,
Expand All @@ -522,7 +551,7 @@ impl<B: Backend> Component<B> for SendTab {
9,
);
},
Some(ConfirmationDialogType::OneSidedSend) => {
Some(ConfirmationDialogType::OneSided) => {
draw_dialog(
f,
area,
Expand All @@ -533,6 +562,18 @@ impl<B: Backend> Component<B> for SendTab {
9,
);
},
Some(ConfirmationDialogType::StealthAddress) => {
draw_dialog(
f,
area,
"Confirm Sending Transaction".to_string(),
"Are you sure you want to send this one-sided transaction to a stealth address?\n(Y)es / (N)o"
.to_string(),
Color::Red,
120,
9,
);
},
}
}

Expand Down Expand Up @@ -584,7 +625,7 @@ impl<B: Backend> Component<B> for SendTab {
},
'f' => self.send_input_mode = SendInputMode::Fee,
'm' => self.send_input_mode = SendInputMode::Message,
's' | 'o' => {
's' | 'o' | 'x' => {
if self.to_field.is_empty() {
self.error_message = Some("Destination Public Key/Emoji ID\nPress Enter to continue.".to_string());
return;
Expand All @@ -599,11 +640,11 @@ impl<B: Backend> Component<B> for SendTab {
return;
}

if matches!(c, 'o') {
self.confirmation_dialog = Some(ConfirmationDialogType::OneSidedSend);
} else {
self.confirmation_dialog = Some(ConfirmationDialogType::NormalSend);
}
self.confirmation_dialog = Some(match c {
'o' => ConfirmationDialogType::OneSided,
'x' => ConfirmationDialogType::StealthAddress,
_ => ConfirmationDialogType::Normal,
});
},
_ => {},
}
Expand Down Expand Up @@ -690,6 +731,7 @@ pub enum SendInputMode {

#[derive(PartialEq, Debug)]
pub enum ConfirmationDialogType {
NormalSend,
OneSidedSend,
Normal,
OneSided,
StealthAddress,
}

0 comments on commit a897278

Please sign in to comment.