Skip to content

Commit

Permalink
ffi: added 2 functions, wallet_preview_coin_split and wallet_preview_…
Browse files Browse the repository at this point in the history
…coin_join (#4264)

Description
---
Added 2 preview functions for coin split and join

Motivation and Context
---
To let mobile users appraise the fee and expected outputs without performing the actual split/join operation.

How Has This Been Tested?
---
unit tests
  • Loading branch information
agubarev authored Jul 5, 2022
1 parent 8772185 commit 5f580e6
Show file tree
Hide file tree
Showing 5 changed files with 544 additions and 27 deletions.
48 changes: 48 additions & 0 deletions base_layer/wallet/src/output_manager_service/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ pub enum OutputManagerRequest {
RevalidateTxos,
CreateCoinSplit((Vec<Commitment>, MicroTari, usize, MicroTari)),
CreateCoinSplitEven((Vec<Commitment>, usize, MicroTari)),
PreviewCoinJoin((Vec<Commitment>, MicroTari)),
PreviewCoinSplitEven((Vec<Commitment>, usize, MicroTari)),
CreateCoinJoin {
commitments: Vec<Commitment>,
fee_per_gram: MicroTari,
Expand Down Expand Up @@ -177,6 +179,16 @@ impl fmt::Display for OutputManagerRequest {
GetInvalidOutputs => write!(f, "GetInvalidOutputs"),
ValidateUtxos => write!(f, "ValidateUtxos"),
RevalidateTxos => write!(f, "RevalidateTxos"),
PreviewCoinJoin((commitments, fee_per_gram)) => write!(
f,
"PreviewCoinJoin(commitments={:#?}, fee_per_gram={})",
commitments, fee_per_gram
),
PreviewCoinSplitEven((commitments, number_of_splits, fee_per_gram)) => write!(
f,
"PreviewCoinSplitEven(commitments={:#?}, number_of_splits={}, fee_per_gram={})",
commitments, number_of_splits, fee_per_gram
),
CreateCoinSplit(v) => write!(f, "CreateCoinSplit ({:?})", v.0),
CreateCoinSplitEven(v) => write!(f, "CreateCoinSplitEven ({:?})", v.0),
CreateCoinJoin {
Expand Down Expand Up @@ -272,6 +284,7 @@ pub enum OutputManagerResponse {
CoinbaseAbandonedSet,
ClaimHtlcTransaction((TxId, MicroTari, MicroTari, Transaction)),
OutputStatusesByTxId(OutputStatusesByTxId),
CoinPreview((Vec<MicroTari>, MicroTari)),
}

pub type OutputManagerEventSender = broadcast::Sender<Arc<OutputManagerEvent>>;
Expand Down Expand Up @@ -673,6 +686,41 @@ impl OutputManagerHandle {
}
}

pub async fn preview_coin_join_with_commitments(
&mut self,
commitments: Vec<Commitment>,
fee_per_gram: MicroTari,
) -> Result<(Vec<MicroTari>, MicroTari), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::PreviewCoinJoin((commitments, fee_per_gram)))
.await??
{
OutputManagerResponse::CoinPreview((expected_outputs, fee)) => Ok((expected_outputs, fee)),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}

pub async fn preview_coin_split_with_commitments_no_amount(
&mut self,
commitments: Vec<Commitment>,
split_count: usize,
fee_per_gram: MicroTari,
) -> Result<(Vec<MicroTari>, MicroTari), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::PreviewCoinSplitEven((
commitments,
split_count,
fee_per_gram,
)))
.await??
{
OutputManagerResponse::CoinPreview((expected_outputs, fee)) => Ok((expected_outputs, fee)),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}

/// Create a coin split transaction.
/// Returns (tx_id, tx, utxos_total_value).
pub async fn create_coin_split(
Expand Down
84 changes: 84 additions & 0 deletions base_layer/wallet/src/output_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,18 @@ where
let outputs = self.fetch_invalid_outputs()?.into_iter().map(|v| v.into()).collect();
Ok(OutputManagerResponse::InvalidOutputs(outputs))
},
OutputManagerRequest::PreviewCoinJoin((commitments, fee_per_gram)) => {
Ok(OutputManagerResponse::CoinPreview(
self.preview_coin_join_with_commitments(commitments, fee_per_gram)
.await?,
))
},
OutputManagerRequest::PreviewCoinSplitEven((commitments, number_of_splits, fee_per_gram)) => {
Ok(OutputManagerResponse::CoinPreview(
self.preview_coin_split_with_commitments_no_amount(commitments, number_of_splits, fee_per_gram)
.await?,
))
},
OutputManagerRequest::CreateCoinSplit((commitments, amount_per_split, split_count, fee_per_gram)) => {
if commitments.is_empty() {
self.create_coin_split_auto(Some(amount_per_split), split_count, fee_per_gram)
Expand Down Expand Up @@ -1716,6 +1728,78 @@ where
)
}

pub async fn preview_coin_join_with_commitments(
&self,
commitments: Vec<Commitment>,
fee_per_gram: MicroTari,
) -> Result<(Vec<MicroTari>, MicroTari), OutputManagerError> {
let src_outputs = self.resources.db.fetch_unspent_outputs_for_spending(
UtxoSelectionCriteria::specific(commitments),
MicroTari::zero(),
None,
)?;

let accumulated_amount = src_outputs
.iter()
.fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value);

let fee = self
.get_fee_calc()
.calculate(fee_per_gram, 1, src_outputs.len(), 1, self.default_metadata_size());

Ok((vec![accumulated_amount.saturating_sub(fee)], fee))
}

pub async fn preview_coin_split_with_commitments_no_amount(
&mut self,
commitments: Vec<Commitment>,
number_of_splits: usize,
fee_per_gram: MicroTari,
) -> Result<(Vec<MicroTari>, MicroTari), OutputManagerError> {
if commitments.is_empty() {
return Err(OutputManagerError::NoCommitmentsProvided);
}

if number_of_splits == 0 {
return Err(OutputManagerError::InvalidArgument(
"number_of_splits must be greater than 0".to_string(),
));
}

let src_outputs = self.resources.db.fetch_unspent_outputs_for_spending(
UtxoSelectionCriteria::specific(commitments),
MicroTari::zero(),
None,
)?;

let fee = self.get_fee_calc().calculate(
fee_per_gram,
1,
src_outputs.len(),
number_of_splits,
self.default_metadata_size() * number_of_splits,
);

let accumulated_amount = src_outputs
.iter()
.fold(MicroTari::zero(), |acc, x| acc + x.unblinded_output.value);

let aftertax_amount = accumulated_amount.saturating_sub(fee);
let amount_per_split = MicroTari(aftertax_amount.as_u64() / number_of_splits as u64);
let unspent_remainder = MicroTari(aftertax_amount.as_u64() % amount_per_split.as_u64());
let mut expected_outputs = vec![];

for i in 1..=number_of_splits {
expected_outputs.push(if i == number_of_splits {
amount_per_split + unspent_remainder
} else {
amount_per_split
});
}

Ok((expected_outputs, fee))
}

async fn create_coin_split_with_commitments(
&mut self,
commitments: Vec<Commitment>,
Expand Down
53 changes: 53 additions & 0 deletions base_layer/wallet/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,31 @@ where
signature.verify_challenge(&public_key, challenge.clone().as_slice())
}

/// Appraise the expected outputs and a fee
pub async fn preview_coin_split_with_commitments_no_amount(
&mut self,
commitments: Vec<Commitment>,
split_count: usize,
fee_per_gram: MicroTari,
) -> Result<(Vec<MicroTari>, MicroTari), WalletError> {
self.output_manager_service
.preview_coin_split_with_commitments_no_amount(commitments, split_count, fee_per_gram)
.await
.map_err(WalletError::OutputManagerError)
}

/// Appraise the expected outputs and a fee
pub async fn preview_coin_join_with_commitments(
&mut self,
commitments: Vec<Commitment>,
fee_per_gram: MicroTari,
) -> Result<(Vec<MicroTari>, MicroTari), WalletError> {
self.output_manager_service
.preview_coin_join_with_commitments(commitments, fee_per_gram)
.await
.map_err(WalletError::OutputManagerError)
}

/// Do a coin split
pub async fn coin_split(
&mut self,
Expand Down Expand Up @@ -593,6 +618,34 @@ where
}
}

/// Do a coin split
pub async fn coin_split_even_with_commitments(
&mut self,
commitments: Vec<Commitment>,
split_count: usize,
fee_per_gram: MicroTari,
message: String,
) -> Result<TxId, WalletError> {
let coin_split_tx = self
.output_manager_service
.create_coin_split_even(commitments, split_count, fee_per_gram)
.await;

match coin_split_tx {
Ok((tx_id, split_tx, amount)) => {
let coin_tx = self
.transaction_service
.submit_transaction(tx_id, split_tx, amount, message)
.await;
match coin_tx {
Ok(_) => Ok(tx_id),
Err(e) => Err(WalletError::TransactionServiceError(e)),
}
},
Err(e) => Err(WalletError::OutputManagerError(e)),
}
}

pub async fn coin_join(
&mut self,
commitments: Vec<Commitment>,
Expand Down
Loading

0 comments on commit 5f580e6

Please sign in to comment.