Skip to content

Commit

Permalink
fixed implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ksrichard committed Nov 28, 2024
1 parent 251bda2 commit e7e5c9f
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,9 @@ impl wallet_server::Wallet for WalletGrpcServer {
Ok(Response::new(response))
}

/// Returns the fee to register a template.
/// This method is needed by Tari CLI now, so it provides a better UX and tells the user instantly
/// how much a new template registration will cost.
async fn get_template_registration_fee(
&self,
request: Request<CreateTemplateRegistrationRequest>,
Expand Down
31 changes: 31 additions & 0 deletions base_layer/wallet/src/output_manager_service/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ pub enum OutputManagerRequest {
fee_per_gram: MicroMinotari,
lock_height: Option<u64>,
},
GetPayToSelfTransactionFee {
amount: MicroMinotari,
selection_criteria: UtxoSelectionCriteria,
output_features: Box<OutputFeatures>,
fee_per_gram: MicroMinotari,
},
CreatePayToSelfWithOutputs {
outputs: Vec<WalletOutputBuilder>,
fee_per_gram: MicroMinotari,
Expand Down Expand Up @@ -263,6 +269,7 @@ impl fmt::Display for OutputManagerRequest {
),

GetOutputInfoByTxId(t) => write!(f, "GetOutputInfoByTxId: {}", t),
GetPayToSelfTransactionFee { .. } => write!(f, "GetPayToSelfTransactionFee"),
}
}
}
Expand Down Expand Up @@ -290,6 +297,7 @@ pub enum OutputManagerResponse {
OutputConfirmed,
PendingTransactionConfirmed,
PayToSelfTransaction((MicroMinotari, Transaction)),
PayToSelfTransactionFee(MicroMinotari),
TransactionToSend(SenderTransactionProtocol),
TransactionCancelled,
SpentOutputs(Vec<DbWalletOutput>),
Expand Down Expand Up @@ -924,6 +932,29 @@ impl OutputManagerHandle {
}
}

/// Get pay to self transaction fee without locking any UTXOs.
pub async fn pay_to_self_transaction_fee(
&mut self,
amount: MicroMinotari,
utxo_selection: UtxoSelectionCriteria,
output_features: OutputFeatures,
fee_per_gram: MicroMinotari,
) -> Result<MicroMinotari, OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::GetPayToSelfTransactionFee {
amount,
selection_criteria: utxo_selection,
output_features: Box::new(output_features),
fee_per_gram,
})
.await??
{
OutputManagerResponse::PayToSelfTransactionFee(fee) => Ok(fee),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}

pub async fn reinstate_cancelled_inbound_transaction_outputs(
&mut self,
tx_id: TxId,
Expand Down
112 changes: 108 additions & 4 deletions base_layer/wallet/src/output_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,15 @@ where
)
.await
.map(OutputManagerResponse::PayToSelfTransaction),
OutputManagerRequest::GetPayToSelfTransactionFee {
amount,
selection_criteria,
output_features,
fee_per_gram,
} => self
.pay_to_self_transaction_fee(amount, selection_criteria, *output_features, fee_per_gram)
.await
.map(OutputManagerResponse::PayToSelfTransactionFee),
OutputManagerRequest::FeeEstimate {
amount,
selection_criteria,
Expand Down Expand Up @@ -1533,10 +1542,10 @@ where
&aggregated_metadata_ephemeral_public_key_shares,
)
.await
.map_err(|e|service_error_with_id(tx_id, e.to_string(), true))?
.map_err(|e| service_error_with_id(tx_id, e.to_string(), true))?
.try_build(&self.resources.key_manager)
.await
.map_err(|e|service_error_with_id(tx_id, e.to_string(), true))?;
.map_err(|e| service_error_with_id(tx_id, e.to_string(), true))?;
let total_metadata_ephemeral_public_key =
aggregated_metadata_ephemeral_public_key_shares + output.metadata_signature.ephemeral_pubkey();
trace!(target: LOG_TARGET, "encumber_aggregate_utxo: created output with partial metadata signature");
Expand Down Expand Up @@ -1849,10 +1858,10 @@ where
&recipient_address,
)
.await
.map_err(|e|service_error_with_id(tx_id, e.to_string(), true))?
.map_err(|e| service_error_with_id(tx_id, e.to_string(), true))?
.try_build(&self.resources.key_manager)
.await
.map_err(|e|service_error_with_id(tx_id, e.to_string(), true))?;
.map_err(|e| service_error_with_id(tx_id, e.to_string(), true))?;

// Finalize the partial transaction - it will not be valid at this stage as the metadata and script
// signatures are not yet complete.
Expand All @@ -1877,6 +1886,101 @@ where
Ok((tx, amount, fee))
}

/// Returns the transaction fee for a pay to self transaction.
/// If there are not enough funds, we do an estimation with minimal input/output count.
/// This is needed to NOT lock up any UTXOs, just calculate fees without any data modification.
async fn pay_to_self_transaction_fee(
&mut self,
amount: MicroMinotari,
selection_criteria: UtxoSelectionCriteria,
output_features: OutputFeatures,
fee_per_gram: MicroMinotari,
) -> Result<MicroMinotari, OutputManagerError> {
let covenant = Covenant::default();

let features_and_scripts_byte_size = self
.resources
.consensus_constants
.transaction_weight_params()
.round_up_features_and_scripts_size(
output_features
.get_serialized_size()
.map_err(|e| OutputManagerError::ConversionError(e.to_string()))? +
TariScript::default()
.get_serialized_size()
.map_err(|e| OutputManagerError::ConversionError(e.to_string()))? +
covenant
.get_serialized_size()
.map_err(|e| OutputManagerError::ConversionError(e.to_string()))?,
);

let input_selection = match self
.select_utxos(
amount,
selection_criteria,
fee_per_gram,
1,
features_and_scripts_byte_size,
)
.await
{
Ok(v) => Ok(v),
Err(OutputManagerError::FundsPending | OutputManagerError::NotEnoughFunds) => {
debug!(
target: LOG_TARGET,
"We dont have enough funds available to make a fee estimate, so we estimate 1 input and 1 change"
);
let fee_calc = self.get_fee_calc();
// note that this is the minimal use case for estimation, so at least 1 input and 2 outputs
return Ok(fee_calc.calculate(fee_per_gram, 1, 1, 2, features_and_scripts_byte_size));
},
Err(e) => Err(e),
}?;

// Create builder with no recipients (other than ourselves)
let mut builder = SenderTransactionProtocol::builder(
self.resources.consensus_constants.clone(),
self.resources.key_manager.clone(),
);
builder
.with_lock_height(0)
.with_fee_per_gram(fee_per_gram)
.with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount)
.with_kernel_features(KernelFeatures::empty());

for kmo in input_selection.iter() {
builder.with_input(kmo.wallet_output.clone()).await?;
}

let (output, sender_offset_key_id) = self.output_to_self(output_features, amount, covenant).await?;

builder
.with_output(output.wallet_output.clone(), sender_offset_key_id.clone())
.await
.map_err(|e| OutputManagerError::BuildError(e.to_string()))?;

let (change_commitment_mask_key_id, change_script_public_key) = self
.resources
.key_manager
.get_next_commitment_mask_and_script_key()
.await?;
builder.with_change_data(
script!(PushPubKey(Box::new(change_script_public_key.pub_key.clone())))?,
ExecutionStack::default(),
change_script_public_key.key_id.clone(),
change_commitment_mask_key_id.key_id,
Covenant::default(),
self.resources.interactive_tari_address.clone(),
);

let stp = builder
.build()
.await
.map_err(|e| OutputManagerError::BuildError(e.message))?;

Ok(stp.get_fee_amount()?)
}

async fn create_pay_to_self_transaction(
&mut self,
tx_id: TxId,
Expand Down
6 changes: 3 additions & 3 deletions base_layer/wallet/src/transaction_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2525,12 +2525,12 @@ where
.map_err(|e| TransactionServiceError::SidechainSigningError(e.to_string()))?;

template_registration.author_signature = author_sig;
let tx_id = TxId::new_random();
let output_features = OutputFeatures::for_template_registration(template_registration);
let (fee, _) = self

let fee = self
.resources
.output_manager_service
.create_pay_to_self_transaction(tx_id, 0.into(), selection_criteria, output_features, fee_per_gram, None)
.pay_to_self_transaction_fee(0.into(), selection_criteria, output_features, fee_per_gram)
.await?;

Ok(fee)
Expand Down

0 comments on commit e7e5c9f

Please sign in to comment.