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 scanned transaction handling for one-sided payments with callbacks #3794

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
14 changes: 14 additions & 0 deletions applications/ffi_client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ try {
console.log("txMinedUnconfirmed: ", ptr, confirmations);
}
);
// callback_faux_transaction_confirmed: unsafe extern "C" fn(*mut TariCompletedTransaction),
const txFauxConfirmed = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("txFauxConfirmed: ", ptr);
});
// callback_faux_transaction_unconfirmed: unsafe extern "C" fn(*mut TariCompletedTransaction, u64),
const txFauxUnconfirmed = ffi.Callback(
"void",
["pointer"],
function (ptr, confirmations) {
console.log("txFauxUnconfirmed: ", ptr, confirmations);
}
);
// callback_direct_send_result: unsafe extern "C" fn(c_ulonglong, bool),
const directSendResult = ffi.Callback("void", [u64, bool], function (i, j) {
console.log("directSendResult: ", i, j);
Expand Down Expand Up @@ -112,6 +124,8 @@ try {
txBroadcast,
txMined,
txMinedUnconfirmed,
txFauxConfirmed,
txFauxUnconfirmed,
directSendResult,
safResult,
txCancelled,
Expand Down
3 changes: 3 additions & 0 deletions applications/ffi_client/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ const libWallet = ffi.Library("./libtari_wallet_ffi.dylib", {
fn,
fn,
fn,
fn,
fn,
bool,
errPtr,
],
],
Expand Down
12 changes: 12 additions & 0 deletions applications/ffi_client/recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ try {
console.log("txMinedUnconfirmed: ", ptr, confirmations);
}
);
// callback_faux_transaction_confirmed: unsafe extern "C" fn(*mut TariCompletedTransaction),
const txFauxConfirmed = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("txFauxConfirmed: ", ptr);
});
// callback_faux_transaction_unconfirmed: unsafe extern "C" fn(*mut TariCompletedTransaction, u64),
const txFauxUnconfirmed = ffi.Callback(
"void",
["pointer"],
function (ptr, confirmations) {
console.log("txFauxUnconfirmed: ", ptr, confirmations);
}
);
// callback_direct_send_result: unsafe extern "C" fn(c_ulonglong, bool),
const directSendResult = ffi.Callback("void", [u64, bool], function (i, j) {
console.log("directSendResult: ", i, j);
Expand Down
4 changes: 4 additions & 0 deletions applications/tari_app_grpc/proto/wallet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ enum TransactionStatus {
TRANSACTION_STATUS_NOT_FOUND = 7;
// The transaction was rejected by the mempool
TRANSACTION_STATUS_REJECTED = 8;
// This is faux transaction mainly for one-sided transaction outputs or wallet recovery outputs have been found
TRANSACTION_STATUS_FAUX_UNCONFIRMED = 9;
// All Imported and FauxUnconfirmed transactions will end up with this status when the outputs have been confirmed
TRANSACTION_STATUS_FAUX_CONFIRMED = 10;
}

message GetCompletedTransactionsRequest { }
Expand Down
2 changes: 2 additions & 0 deletions applications/tari_app_grpc/src/conversions/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ impl From<TransactionStatus> for grpc::TransactionStatus {
Pending => grpc::TransactionStatus::Pending,
Coinbase => grpc::TransactionStatus::Coinbase,
Rejected => grpc::TransactionStatus::Rejected,
FauxUnconfirmed => grpc::TransactionStatus::FauxUnconfirmed,
FauxConfirmed => grpc::TransactionStatus::FauxConfirmed,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,15 @@ impl WalletEventMonitor {
self.trigger_balance_refresh();
notifier.transaction_received(tx_id);
},
TransactionEvent::TransactionMinedUnconfirmed{tx_id, num_confirmations, is_valid: _} => {
TransactionEvent::TransactionMinedUnconfirmed{tx_id, num_confirmations, is_valid: _} |
TransactionEvent::FauxTransactionUnconfirmed{tx_id, num_confirmations, is_valid: _}=> {
self.trigger_confirmations_refresh(tx_id, num_confirmations).await;
self.trigger_tx_state_refresh(tx_id).await;
self.trigger_balance_refresh();
notifier.transaction_mined_unconfirmed(tx_id, num_confirmations);
},
TransactionEvent::TransactionMined{tx_id, is_valid: _} => {
TransactionEvent::TransactionMined{tx_id, is_valid: _} |
TransactionEvent::FauxTransactionConfirmed{tx_id, is_valid: _}=> {
self.trigger_confirmations_cleanup(tx_id).await;
self.trigger_tx_state_refresh(tx_id).await;
self.trigger_balance_refresh();
Expand All @@ -114,7 +116,8 @@ impl WalletEventMonitor {
TransactionEvent::ReceivedTransaction(tx_id) |
TransactionEvent::ReceivedTransactionReply(tx_id) |
TransactionEvent::TransactionBroadcast(tx_id) |
TransactionEvent::TransactionMinedRequestTimedOut(tx_id) | TransactionEvent::TransactionImported(tx_id) => {
TransactionEvent::TransactionMinedRequestTimedOut(tx_id) |
TransactionEvent::TransactionImported(tx_id) => {
self.trigger_tx_state_refresh(tx_id).await;
self.trigger_balance_refresh();
},
Expand Down
62 changes: 61 additions & 1 deletion base_layer/common_types/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub enum TransactionStatus {
Broadcast,
/// This transaction has been mined and included in a block.
MinedUnconfirmed,
/// This transaction was generated as part of importing a spendable UTXO
/// This transaction was generated as part of importing a spendable unblinded UTXO
Imported,
/// This transaction is still being negotiated by the parties
Pending,
Expand All @@ -27,6 +27,27 @@ pub enum TransactionStatus {
MinedConfirmed,
/// This transaction was Rejected by the mempool
Rejected,
/// This is faux transaction mainly for one-sided transaction outputs or wallet recovery outputs have been found
FauxUnconfirmed,
/// All Imported and FauxUnconfirmed transactions will end up with this status when the outputs have been confirmed
FauxConfirmed,
}

impl TransactionStatus {
pub fn is_faux(&self) -> bool {
match self {
TransactionStatus::Completed => false,
TransactionStatus::Broadcast => false,
TransactionStatus::MinedUnconfirmed => false,
TransactionStatus::Imported => true,
TransactionStatus::Pending => false,
TransactionStatus::Coinbase => false,
TransactionStatus::MinedConfirmed => false,
TransactionStatus::Rejected => false,
TransactionStatus::FauxUnconfirmed => true,
TransactionStatus::FauxConfirmed => true,
}
}
Comment on lines +36 to +50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
impl TransactionStatus {
pub fn is_faux(&self) -> bool {
match self {
TransactionStatus::Completed => false,
TransactionStatus::Broadcast => false,
TransactionStatus::MinedUnconfirmed => false,
TransactionStatus::Imported => true,
TransactionStatus::Pending => false,
TransactionStatus::Coinbase => false,
TransactionStatus::MinedConfirmed => false,
TransactionStatus::Rejected => false,
TransactionStatus::FauxUnconfirmed => true,
TransactionStatus::FauxConfirmed => true,
}
}
impl TransactionStatus {
pub fn is_faux(&self) -> bool {
match self {
TransactionStatus::Imported |
TransactionStatus::FauxUnconfirmed |
TransactionStatus::FauxConfirmed => true,
_ => false,
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will fix that as a side issue when I submit a new PR based on this merge.

}

#[derive(Debug, Error)]
Expand All @@ -48,6 +69,8 @@ impl TryFrom<i32> for TransactionStatus {
5 => Ok(TransactionStatus::Coinbase),
6 => Ok(TransactionStatus::MinedConfirmed),
7 => Ok(TransactionStatus::Rejected),
8 => Ok(TransactionStatus::FauxUnconfirmed),
9 => Ok(TransactionStatus::FauxConfirmed),
code => Err(TransactionConversionError { code }),
}
}
Expand All @@ -71,6 +94,43 @@ impl Display for TransactionStatus {
TransactionStatus::Pending => write!(f, "Pending"),
TransactionStatus::Coinbase => write!(f, "Coinbase"),
TransactionStatus::Rejected => write!(f, "Rejected"),
TransactionStatus::FauxUnconfirmed => write!(f, "FauxUnconfirmed"),
TransactionStatus::FauxConfirmed => write!(f, "FauxConfirmed"),
}
}
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ImportStatus {
/// This transaction import status is used when importing a spendable UTXO
Imported,
/// This transaction import status is used when a one-sided transaction has been scanned but is unconfirmed
FauxUnconfirmed,
/// This transaction import status is used when a one-sided transaction has been scanned and confirmed
FauxConfirmed,
}

impl TryFrom<ImportStatus> for TransactionStatus {
type Error = TransactionConversionError;

fn try_from(value: ImportStatus) -> Result<Self, Self::Error> {
match value {
ImportStatus::Imported => Ok(TransactionStatus::Imported),
ImportStatus::FauxUnconfirmed => Ok(TransactionStatus::FauxUnconfirmed),
ImportStatus::FauxConfirmed => Ok(TransactionStatus::FauxConfirmed),
}
}
}

impl TryFrom<TransactionStatus> for ImportStatus {
type Error = TransactionConversionError;

fn try_from(value: TransactionStatus) -> Result<Self, Self::Error> {
match value {
TransactionStatus::Imported => Ok(ImportStatus::Imported),
TransactionStatus::FauxUnconfirmed => Ok(ImportStatus::FauxUnconfirmed),
TransactionStatus::FauxConfirmed => Ok(ImportStatus::FauxConfirmed),
_ => Err(TransactionConversionError { code: i32::MAX }),
}
}
}
Expand Down
52 changes: 40 additions & 12 deletions base_layer/wallet/src/output_manager_service/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::{fmt, fmt::Formatter, sync::Arc};
use aes_gcm::Aes256Gcm;
use tari_common_types::{
transaction::TxId,
types::{HashOutput, PublicKey},
types::{BlockHash, HashOutput, PublicKey},
};
use tari_core::{
covenants::Covenant,
Expand Down Expand Up @@ -106,8 +106,14 @@ pub enum OutputManagerRequest {
num_kernels: usize,
num_outputs: usize,
},
ScanForRecoverableOutputs(Vec<TransactionOutput>),
ScanOutputs(Vec<TransactionOutput>),
ScanForRecoverableOutputs {
outputs: Vec<TransactionOutput>,
tx_id: TxId,
},
ScanOutputs {
outputs: Vec<TransactionOutput>,
tx_id: TxId,
},
AddKnownOneSidedPaymentScript(KnownOneSidedPaymentScript),
CreateOutputWithFeatures {
value: MicroTari,
Expand Down Expand Up @@ -165,8 +171,8 @@ impl fmt::Display for OutputManagerRequest {
"FeeEstimate(amount: {}, fee_per_gram: {}, num_kernels: {}, num_outputs: {})",
amount, fee_per_gram, num_kernels, num_outputs
),
ScanForRecoverableOutputs(_) => write!(f, "ScanForRecoverableOutputs"),
ScanOutputs(_) => write!(f, "ScanOutputs"),
ScanForRecoverableOutputs { .. } => write!(f, "ScanForRecoverableOutputs"),
ScanOutputs { .. } => write!(f, "ScanOutputs"),
AddKnownOneSidedPaymentScript(_) => write!(f, "AddKnownOneSidedPaymentScript"),
CreateOutputWithFeatures { value, features } => {
write!(f, "CreateOutputWithFeatures({}, {})", value, features,)
Expand Down Expand Up @@ -220,12 +226,21 @@ pub enum OutputManagerResponse {
RewoundOutputs(Vec<UnblindedOutput>),
ScanOutputs(Vec<UnblindedOutput>),
AddKnownOneSidedPaymentScript,
CreateOutputWithFeatures { output: Box<UnblindedOutputBuilder> },
CreatePayToSelfWithOutputs { transaction: Box<Transaction>, tx_id: TxId },
CreateOutputWithFeatures {
output: Box<UnblindedOutputBuilder>,
},
CreatePayToSelfWithOutputs {
transaction: Box<Transaction>,
tx_id: TxId,
},
ReinstatedCancelledInboundTx,
CoinbaseAbandonedSet,
ClaimHtlcTransaction((TxId, MicroTari, MicroTari, Transaction)),
OutputStatusesByTxId(Vec<OutputStatus>),
OutputStatusesByTxId {
statuses: Vec<OutputStatus>,
mined_height: Option<u64>,
block_hash: Option<BlockHash>,
},
}

pub type OutputManagerEventSender = broadcast::Sender<Arc<OutputManagerEvent>>;
Expand Down Expand Up @@ -642,10 +657,11 @@ impl OutputManagerHandle {
pub async fn scan_for_recoverable_outputs(
&mut self,
outputs: Vec<TransactionOutput>,
tx_id: TxId,
) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::ScanForRecoverableOutputs(outputs))
.call(OutputManagerRequest::ScanForRecoverableOutputs { outputs, tx_id })
.await??
{
OutputManagerResponse::RewoundOutputs(outputs) => Ok(outputs),
Expand All @@ -656,8 +672,13 @@ impl OutputManagerHandle {
pub async fn scan_outputs_for_one_sided_payments(
&mut self,
outputs: Vec<TransactionOutput>,
tx_id: TxId,
) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
match self.handle.call(OutputManagerRequest::ScanOutputs(outputs)).await?? {
match self
.handle
.call(OutputManagerRequest::ScanOutputs { outputs, tx_id })
.await??
{
OutputManagerResponse::ScanOutputs(outputs) => Ok(outputs),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
Expand Down Expand Up @@ -749,13 +770,20 @@ impl OutputManagerHandle {
}
}

pub async fn get_output_statuses_by_tx_id(&mut self, tx_id: TxId) -> Result<Vec<OutputStatus>, OutputManagerError> {
pub async fn get_output_statuses_by_tx_id(
&mut self,
tx_id: TxId,
) -> Result<(Vec<OutputStatus>, Option<u64>, Option<BlockHash>), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::GetOutputStatusesByTxId(tx_id))
.await??
{
OutputManagerResponse::OutputStatusesByTxId(s) => Ok(s),
OutputManagerResponse::OutputStatusesByTxId {
statuses,
mined_height,
block_hash,
} => Ok((statuses, mined_height, block_hash)),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ use std::{sync::Arc, time::Instant};

use log::*;
use rand::rngs::OsRng;
use tari_common_types::types::{PrivateKey, PublicKey, RangeProof};
use tari_common_types::{
transaction::TxId,
types::{PrivateKey, PublicKey, RangeProof},
};
use tari_core::transactions::{
transaction::{TransactionOutput, UnblindedOutput},
CryptoFactories,
Expand Down Expand Up @@ -73,6 +76,7 @@ where TBackend: OutputManagerBackend + 'static
pub async fn scan_and_recover_outputs(
&mut self,
outputs: Vec<TransactionOutput>,
tx_id: TxId,
) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
let start = Instant::now();
let outputs_length = outputs.len();
Expand Down Expand Up @@ -133,7 +137,7 @@ where TBackend: OutputManagerBackend + 'static
Some(proof),
)?;
let output_hex = db_output.commitment.to_hex();
if let Err(e) = self.db.add_unspent_output(db_output).await {
if let Err(e) = self.db.add_unspent_output_with_tx_id(tx_id, db_output).await {
match e {
OutputManagerStorageError::DuplicateOutput => {
info!(
Expand Down
Loading