Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add atomic swap htlc sending and claiming #3552

Merged
merged 1 commit into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

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

28 changes: 27 additions & 1 deletion applications/tari_app_grpc/proto/wallet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ service Wallet {
rpc CancelTransaction (CancelTransactionRequest) returns (CancelTransactionResponse);
// Will triggger a complete revalidation of all wallet outputs.
rpc RevalidateAllTransactions (RevalidateRequest) returns (RevalidateResponse);
// This will send a XTR SHA Atomic swap transaction
rpc SendShaAtomicSwapTransaction(SendShaAtomicSwapRequest) returns (SendShaAtomicSwapResponse);
// This will claim a XTR SHA Atomic swap transaction
rpc ClaimShaAtomicSwapTransaction(ClaimShaAtomicSwapRequest) returns (ClaimShaAtomicSwapResponse);
}

message GetVersionRequest { }
Expand All @@ -76,6 +80,10 @@ message TransferRequest {
repeated PaymentRecipient recipients = 1;
}

message SendShaAtomicSwapRequest {
PaymentRecipient recipient = 1;
}

message PaymentRecipient {
string address = 1;
uint64 amount = 2;
Expand All @@ -92,13 +100,31 @@ message TransferResponse {
repeated TransferResult results = 1;
}

message SendShaAtomicSwapResponse {
uint64 transaction_id = 1;
string pre_image = 2;
string output_hash = 3;
bool is_success = 4;
string failure_message = 5;
}

message TransferResult {
string address = 1;
uint64 transaction_id = 2;
bool is_success = 3;
string failure_message = 4;
}

message ClaimShaAtomicSwapRequest{
string output = 1;
string pre_image = 2;
uint64 fee_per_gram = 3;
}

message ClaimShaAtomicSwapResponse {
TransferResult results = 1;
}

message GetTransactionInfoRequest {
repeated uint64 transaction_ids = 1;
}
Expand Down Expand Up @@ -204,4 +230,4 @@ message CancelTransactionResponse {

message RevalidateRequest{}

message RevalidateResponse{}
message RevalidateResponse{}
9 changes: 7 additions & 2 deletions applications/tari_app_utilities/src/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@
use futures::future::Either;
use log::*;
use std::sync::Arc;
use tokio::{runtime, runtime::Runtime};

use tari_common::{CommsTransport, GlobalConfig, SocksAuthentication, TorControlAuthentication};
use tari_common_types::types::BlockHash;
use tari_comms::{
peer_manager::NodeId,
socks,
Expand All @@ -37,6 +36,7 @@ use tari_comms::{
};
use tari_core::tari_utilities::hex::Hex;
use tari_p2p::transport::{TorConfig, TransportType};
use tokio::{runtime, runtime::Runtime};

use crate::identity_management::load_from_json;
use tari_common_types::emoji::EmojiId;
Expand Down Expand Up @@ -175,6 +175,11 @@ pub fn parse_emoji_id_or_public_key(key: &str) -> Option<CommsPublicKey> {
.ok()
}

/// Returns a hash from a hex string
pub fn parse_hash(hash_string: &str) -> Option<BlockHash> {
BlockHash::from_hex(hash_string).ok()
}

/// Returns a CommsPublicKey from either a emoji id, a public key or node id
pub fn parse_emoji_id_or_public_key_or_node_id(key: &str) -> Option<Either<CommsPublicKey, NodeId>> {
parse_emoji_id_or_public_key(key)
Expand Down
2 changes: 2 additions & 0 deletions applications/tari_console_wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ tari_app_grpc = { path = "../tari_app_grpc" }
tari_shutdown = { path = "../../infrastructure/shutdown" }
tari_key_manager = { path = "../../base_layer/key_manager" }

sha2 = "0.9.5"
digest = "0.9.0"
chrono = { version = "0.4.19", default-features = false }
bitflags = "1.2.1"
futures = { version = "^0.3.16", default-features = false, features = ["alloc"] }
Expand Down
52 changes: 48 additions & 4 deletions applications/tari_console_wallet/src/automation/command_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crate::automation::{commands::WalletCommand, error::ParseError};

use chrono::{DateTime, Utc};
use core::str::SplitWhitespace;
use std::{
fmt::{Display, Formatter},
str::FromStr,
};
use tari_app_utilities::utilities::parse_emoji_id_or_public_key;
use tari_comms::multiaddr::Multiaddr;

use tari_app_utilities::utilities::{parse_emoji_id_or_public_key, parse_hash};
use tari_common_types::types::PublicKey;
use tari_comms::multiaddr::Multiaddr;
use tari_core::transactions::tari_amount::MicroTari;
use tari_crypto::tari_utilities::hex::Hex;

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ParsedCommand {
pub command: WalletCommand,
pub args: Vec<ParsedArgument>,
Expand All @@ -57,6 +57,8 @@ impl Display for ParsedCommand {
SetBaseNode => "set-base-node",
SetCustomBaseNode => "set-custom-base-node",
ClearCustomBaseNode => "clear-custom-base-node",
InitShaAtomicSwap => "init-sha-atomic-swap",
FinaliseShaAtomicSwap => "finalise-sha-atomic-swap",
};

let args = self
Expand All @@ -82,6 +84,7 @@ pub enum ParsedArgument {
CSVFileName(String),
Address(Multiaddr),
Negotiated(bool),
Hash(Vec<u8>),
}

impl Display for ParsedArgument {
Expand All @@ -98,6 +101,7 @@ impl Display for ParsedArgument {
CSVFileName(v) => write!(f, "{}", v.to_string()),
Address(v) => write!(f, "{}", v.to_string()),
Negotiated(v) => write!(f, "{}", v.to_string()),
Hash(v) => write!(f, "{}", v.to_hex()),
}
}
}
Expand All @@ -124,6 +128,8 @@ pub fn parse_command(command: &str) -> Result<ParsedCommand, ParseError> {
SetBaseNode => parse_public_key_and_address(args)?,
SetCustomBaseNode => parse_public_key_and_address(args)?,
ClearCustomBaseNode => Vec::new(),
InitShaAtomicSwap => parse_init_sha_atomic_swap(args)?,
FinaliseShaAtomicSwap => parse_finalise_sha_atomic_swap(args)?,
};

Ok(ParsedCommand { command, args })
Expand Down Expand Up @@ -175,6 +181,44 @@ fn parse_public_key_and_address(mut args: SplitWhitespace) -> Result<Vec<ParsedA
Ok(parsed_args)
}

fn parse_init_sha_atomic_swap(mut args: SplitWhitespace) -> Result<Vec<ParsedArgument>, ParseError> {
let mut parsed_args = Vec::new();

// amount
let amount = args.next().ok_or_else(|| ParseError::Empty("amount".to_string()))?;
let amount = MicroTari::from_str(amount)?;
parsed_args.push(ParsedArgument::Amount(amount));

// public key/emoji id
let pubkey = args
.next()
.ok_or_else(|| ParseError::Empty("public key or emoji id".to_string()))?;
let pubkey = parse_emoji_id_or_public_key(pubkey).ok_or(ParseError::PublicKey)?;
parsed_args.push(ParsedArgument::PublicKey(pubkey));
// message
let message = args.collect::<Vec<&str>>().join(" ");
parsed_args.push(ParsedArgument::Text(message));

Ok(parsed_args)
}

fn parse_finalise_sha_atomic_swap(mut args: SplitWhitespace) -> Result<Vec<ParsedArgument>, ParseError> {
let mut parsed_args = Vec::new();
// hash
let hash = args
.next()
.ok_or_else(|| ParseError::Empty("Output hash".to_string()))?;
let hash = parse_hash(hash).ok_or(ParseError::Hash)?;
parsed_args.push(ParsedArgument::Hash(hash));

// public key
let pre_image = args.next().ok_or_else(|| ParseError::Empty("public key".to_string()))?;
let pre_image = parse_emoji_id_or_public_key(pre_image).ok_or(ParseError::PublicKey)?;
parsed_args.push(ParsedArgument::PublicKey(pre_image));

Ok(parsed_args)
}

fn parse_make_it_rain(mut args: SplitWhitespace) -> Result<Vec<ParsedArgument>, ParseError> {
let mut parsed_args = Vec::new();

Expand Down
87 changes: 86 additions & 1 deletion applications/tari_console_wallet/src/automation/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@

use super::error::CommandError;
use chrono::Utc;
use digest::Digest;
use log::*;
use sha2::Sha256;
use std::{
convert::TryFrom,
fs::File,
io::{LineWriter, Write},
str::FromStr,
time::{Duration, Instant},
};
use tari_crypto::tari_utilities::{ByteArray, Hashable};

use futures::FutureExt;
use strum_macros::{Display, EnumIter, EnumString};
Expand All @@ -51,7 +54,7 @@ use tari_core::{
tari_utilities::hex::Hex,
transactions::{
tari_amount::{uT, MicroTari, Tari},
transaction::UnblindedOutput,
transaction::{TransactionOutput, UnblindedOutput},
},
};
use tari_wallet::{
Expand Down Expand Up @@ -83,6 +86,8 @@ pub enum WalletCommand {
SetBaseNode,
SetCustomBaseNode,
ClearCustomBaseNode,
InitShaAtomicSwap,
FinaliseShaAtomicSwap,
}

#[derive(Debug, EnumString, PartialEq, Clone)]
Expand Down Expand Up @@ -124,6 +129,31 @@ fn get_transaction_parameters(
Ok((fee_per_gram, amount, dest_pubkey, message))
}

fn get_init_sha_atomic_swap_parameters(
args: Vec<ParsedArgument>,
) -> Result<(MicroTari, MicroTari, PublicKey, String), CommandError> {
// TODO: Consolidate "fee per gram" in codebase
let fee_per_gram = 25 * uT;

use ParsedArgument::*;
let amount = match args[0].clone() {
Amount(mtari) => Ok(mtari),
_ => Err(CommandError::Argument),
}?;

let dest_pubkey = match args[1].clone() {
PublicKey(key) => Ok(key),
_ => Err(CommandError::Argument),
}?;

let message = match args[2].clone() {
Text(msg) => Ok(msg),
_ => Err(CommandError::Argument),
}?;

Ok((fee_per_gram, amount, dest_pubkey, message))
}

/// Send a normal negotiated transaction to a recipient
pub async fn send_tari(
mut wallet_transaction_service: TransactionServiceHandle,
Expand All @@ -136,6 +166,45 @@ pub async fn send_tari(
.map_err(CommandError::TransactionServiceError)
}

/// publishes a tari-SHA atomic swap HTLC transaction
pub async fn init_sha_atomic_swap(
mut wallet_transaction_service: TransactionServiceHandle,
args: Vec<ParsedArgument>,
) -> Result<(TxId, PublicKey, TransactionOutput), CommandError> {
let (fee_per_gram, amount, dest_pubkey, message) = get_init_sha_atomic_swap_parameters(args)?;

let (tx_id, pre_image, output) = wallet_transaction_service
.send_sha_atomic_swap_transaction(dest_pubkey, amount, fee_per_gram, message)
.await
.map_err(CommandError::TransactionServiceError)?;
Ok((tx_id, pre_image, output))
}

/// claims a tari-SHA atomic swap HTLC transaction
pub async fn finalise_sha_atomic_swap(
mut output_service: OutputManagerHandle,
mut transaction_service: TransactionServiceHandle,
args: Vec<ParsedArgument>,
) -> Result<TxId, CommandError> {
use ParsedArgument::*;
let output = match args[0].clone() {
Hash(output) => Ok(output),
_ => Err(CommandError::Argument),
}?;

let pre_image = match args[1].clone() {
PublicKey(key) => Ok(key),
_ => Err(CommandError::Argument),
}?;
let (tx_id, fee, amount, tx) = output_service
.create_claim_sha_atomic_swap_transaction(output, pre_image, MicroTari(25))
.await?;
transaction_service
.submit_transaction(tx_id, tx, fee, amount, "Claimed HTLC atomic swap".into())
.await?;
Ok(tx_id)
}

/// Send a one-sided transaction to a recipient
pub async fn send_one_sided(
mut wallet_transaction_service: TransactionServiceHandle,
Expand Down Expand Up @@ -674,6 +743,22 @@ pub async fn command_runner(
.await?;
println!("Custom base node peer cleared from wallet database.");
},
InitShaAtomicSwap => {
let (tx_id, pre_image, output) =
init_sha_atomic_swap(transaction_service.clone(), parsed.clone().args).await?;
debug!(target: LOG_TARGET, "tari HTLC tx_id {}", tx_id);
let hash: [u8; 32] = Sha256::digest(pre_image.as_bytes()).into();
println!("pre_image hex: {}", pre_image.to_hex());
println!("pre_image hash: {}", hash.to_hex());
println!("Output hash: {}", output.hash().to_hex());
tx_ids.push(tx_id);
},
FinaliseShaAtomicSwap => {
let tx_id =
finalise_sha_atomic_swap(output_service.clone(), transaction_service.clone(), parsed.args).await?;
debug!(target: LOG_TARGET, "claiming tari HTLC tx_id {}", tx_id);
tx_ids.push(tx_id);
},
}
}

Expand Down
Loading