use crate::output_manager_service::{
error::OutputManagerError,
protocols::txo_validation_protocol::{TxoValidationRetry, TxoValidationType},
service::Balance,
storage::database::PendingTransactionOutputs,
TxId,
};
use aes_gcm::Aes256Gcm;
use futures::{stream::Fuse, StreamExt};
use std::{collections::HashMap, fmt, time::Duration};
use tari_comms::types::CommsPublicKey;
use tari_core::transactions::{
tari_amount::MicroTari,
transaction::{Transaction, TransactionInput, TransactionOutput, UnblindedOutput},
transaction_protocol::sender::TransactionSenderMessage,
types::PublicKey,
ReceiverTransactionProtocol,
SenderTransactionProtocol,
};
use tari_service_framework::reply_channel::SenderService;
use tokio::sync::broadcast;
use tower::Service;
pub enum OutputManagerRequest {
GetBalance,
AddOutput(UnblindedOutput),
GetRecipientTransaction(TransactionSenderMessage),
GetCoinbaseTransaction((u64, MicroTari, MicroTari, u64)),
ConfirmPendingTransaction(u64),
ConfirmTransaction((u64, Vec<TransactionInput>, Vec<TransactionOutput>)),
PrepareToSendTransaction((MicroTari, MicroTari, Option<u64>, String)),
CancelTransaction(u64),
TimeoutTransactions(Duration),
GetPendingTransactions,
GetSpentOutputs,
GetUnspentOutputs,
GetInvalidOutputs,
GetSeedWords,
SetBaseNodePublicKey(CommsPublicKey),
ValidateUtxos(TxoValidationType, TxoValidationRetry),
CreateCoinSplit((MicroTari, usize, MicroTari, Option<u64>)),
ApplyEncryption(Box<Aes256Gcm>),
RemoveEncryption,
GetPublicRewindKeys,
FeeEstimate((MicroTari, MicroTari, u64, u64)),
RewindOutputs(Vec<TransactionOutput>),
}
impl fmt::Display for OutputManagerRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::GetBalance => f.write_str("GetBalance"),
Self::AddOutput(v) => f.write_str(&format!("AddOutput ({})", v.value)),
Self::GetRecipientTransaction(_) => f.write_str("GetRecipientTransaction"),
Self::ConfirmTransaction(v) => f.write_str(&format!("ConfirmTransaction ({})", v.0)),
Self::ConfirmPendingTransaction(v) => f.write_str(&format!("ConfirmPendingTransaction ({})", v)),
Self::PrepareToSendTransaction((_, _, _, msg)) => {
f.write_str(&format!("PrepareToSendTransaction ({})", msg))
},
Self::CancelTransaction(v) => f.write_str(&format!("CancelTransaction ({})", v)),
Self::TimeoutTransactions(d) => f.write_str(&format!("TimeoutTransactions ({}s)", d.as_secs())),
Self::GetPendingTransactions => f.write_str("GetPendingTransactions"),
Self::GetSpentOutputs => f.write_str("GetSpentOutputs"),
Self::GetUnspentOutputs => f.write_str("GetUnspentOutputs"),
Self::GetInvalidOutputs => f.write_str("GetInvalidOutputs"),
Self::GetSeedWords => f.write_str("GetSeedWords"),
Self::SetBaseNodePublicKey(k) => f.write_str(&format!("SetBaseNodePublicKey ({})", k)),
Self::ValidateUtxos(validation_type, retry) => f.write_str(&format!("{} ({:?})", validation_type, retry)),
Self::CreateCoinSplit(v) => f.write_str(&format!("CreateCoinSplit ({})", v.0)),
Self::ApplyEncryption(_) => f.write_str("ApplyEncryption"),
Self::RemoveEncryption => f.write_str("RemoveEncryption"),
Self::GetCoinbaseTransaction(_) => f.write_str("GetCoinbaseTransaction"),
Self::GetPublicRewindKeys => f.write_str("GetPublicRewindKeys"),
Self::FeeEstimate(_) => f.write_str("FeeEstimate"),
Self::RewindOutputs(_) => f.write_str("RewindAndImportOutputs"),
}
}
}
#[derive(Debug, Clone)]
pub enum OutputManagerResponse {
Balance(Balance),
OutputAdded,
RecipientTransactionGenerated(ReceiverTransactionProtocol),
CoinbaseTransaction(Transaction),
OutputConfirmed,
PendingTransactionConfirmed,
TransactionConfirmed,
TransactionToSend(SenderTransactionProtocol),
TransactionCancelled,
TransactionsTimedOut,
PendingTransactions(HashMap<u64, PendingTransactionOutputs>),
SpentOutputs(Vec<UnblindedOutput>),
UnspentOutputs(Vec<UnblindedOutput>),
InvalidOutputs(Vec<UnblindedOutput>),
SeedWords(Vec<String>),
BaseNodePublicKeySet,
UtxoValidationStarted(u64),
Transaction((u64, Transaction, MicroTari, MicroTari)),
EncryptionApplied,
EncryptionRemoved,
PublicRewindKeys(Box<PublicRewindKeys>),
FeeEstimate(MicroTari),
RewindOutputs(Vec<UnblindedOutput>),
}
pub type OutputManagerEventSender = broadcast::Sender<OutputManagerEvent>;
pub type OutputManagerEventReceiver = broadcast::Receiver<OutputManagerEvent>;
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum OutputManagerEvent {
TxoValidationTimedOut(u64),
TxoValidationSuccess(u64),
TxoValidationFailure(u64),
TxoValidationAborted(u64),
Error(String),
}
#[derive(Debug, Clone)]
pub struct PublicRewindKeys {
pub rewind_public_key: PublicKey,
pub rewind_blinding_public_key: PublicKey,
}
#[derive(Clone)]
pub struct OutputManagerHandle {
handle: SenderService<OutputManagerRequest, Result<OutputManagerResponse, OutputManagerError>>,
event_stream_sender: OutputManagerEventSender,
}
impl OutputManagerHandle {
pub fn new(
handle: SenderService<OutputManagerRequest, Result<OutputManagerResponse, OutputManagerError>>,
event_stream_sender: OutputManagerEventSender,
) -> Self
{
OutputManagerHandle {
handle,
event_stream_sender,
}
}
pub fn get_event_stream_fused(&self) -> Fuse<OutputManagerEventReceiver> {
self.event_stream_sender.subscribe().fuse()
}
pub async fn add_output(&mut self, output: UnblindedOutput) -> Result<(), OutputManagerError> {
match self.handle.call(OutputManagerRequest::AddOutput(output)).await?? {
OutputManagerResponse::OutputAdded => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_balance(&mut self) -> Result<Balance, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetBalance).await?? {
OutputManagerResponse::Balance(b) => Ok(b),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_recipient_transaction(
&mut self,
sender_message: TransactionSenderMessage,
) -> Result<ReceiverTransactionProtocol, OutputManagerError>
{
match self
.handle
.call(OutputManagerRequest::GetRecipientTransaction(sender_message))
.await??
{
OutputManagerResponse::RecipientTransactionGenerated(rtp) => Ok(rtp),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_coinbase_transaction(
&mut self,
tx_id: TxId,
reward: MicroTari,
fees: MicroTari,
block_height: u64,
) -> Result<Transaction, OutputManagerError>
{
match self
.handle
.call(OutputManagerRequest::GetCoinbaseTransaction((
tx_id,
reward,
fees,
block_height,
)))
.await??
{
OutputManagerResponse::CoinbaseTransaction(tx) => Ok(tx),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn prepare_transaction_to_send(
&mut self,
amount: MicroTari,
fee_per_gram: MicroTari,
lock_height: Option<u64>,
message: String,
) -> Result<SenderTransactionProtocol, OutputManagerError>
{
match self
.handle
.call(OutputManagerRequest::PrepareToSendTransaction((
amount,
fee_per_gram,
lock_height,
message,
)))
.await??
{
OutputManagerResponse::TransactionToSend(stp) => Ok(stp),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn fee_estimate(
&mut self,
amount: MicroTari,
fee_per_gram: MicroTari,
num_kernels: u64,
num_outputs: u64,
) -> Result<MicroTari, OutputManagerError>
{
match self
.handle
.call(OutputManagerRequest::FeeEstimate((
amount,
fee_per_gram,
num_kernels,
num_outputs,
)))
.await??
{
OutputManagerResponse::FeeEstimate(fee) => Ok(fee),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn confirm_pending_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::ConfirmPendingTransaction(tx_id))
.await??
{
OutputManagerResponse::PendingTransactionConfirmed => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn confirm_transaction(
&mut self,
tx_id: u64,
spent_outputs: Vec<TransactionInput>,
received_outputs: Vec<TransactionOutput>,
) -> Result<(), OutputManagerError>
{
match self
.handle
.call(OutputManagerRequest::ConfirmTransaction((
tx_id,
spent_outputs,
received_outputs,
)))
.await??
{
OutputManagerResponse::TransactionConfirmed => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn cancel_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::CancelTransaction(tx_id))
.await??
{
OutputManagerResponse::TransactionCancelled => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn timeout_transactions(&mut self, period: Duration) -> Result<(), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::TimeoutTransactions(period))
.await??
{
OutputManagerResponse::TransactionsTimedOut => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_pending_transactions(
&mut self,
) -> Result<HashMap<u64, PendingTransactionOutputs>, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetPendingTransactions).await?? {
OutputManagerResponse::PendingTransactions(p) => Ok(p),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_spent_outputs(&mut self) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetSpentOutputs).await?? {
OutputManagerResponse::SpentOutputs(s) => Ok(s),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_unspent_outputs(&mut self) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetUnspentOutputs).await?? {
OutputManagerResponse::UnspentOutputs(s) => Ok(s),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_invalid_outputs(&mut self) -> Result<Vec<UnblindedOutput>, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetInvalidOutputs).await?? {
OutputManagerResponse::InvalidOutputs(s) => Ok(s),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_seed_words(&mut self) -> Result<Vec<String>, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetSeedWords).await?? {
OutputManagerResponse::SeedWords(s) => Ok(s),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn get_rewind_public_keys(&mut self) -> Result<PublicRewindKeys, OutputManagerError> {
match self.handle.call(OutputManagerRequest::GetPublicRewindKeys).await?? {
OutputManagerResponse::PublicRewindKeys(rk) => Ok(*rk),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn set_base_node_public_key(&mut self, public_key: CommsPublicKey) -> Result<(), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::SetBaseNodePublicKey(public_key))
.await??
{
OutputManagerResponse::BaseNodePublicKeySet => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn validate_txos(
&mut self,
validation_type: TxoValidationType,
retries: TxoValidationRetry,
) -> Result<u64, OutputManagerError>
{
match self
.handle
.call(OutputManagerRequest::ValidateUtxos(validation_type, retries))
.await??
{
OutputManagerResponse::UtxoValidationStarted(request_key) => Ok(request_key),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn create_coin_split(
&mut self,
amount_per_split: MicroTari,
split_count: usize,
fee_per_gram: MicroTari,
lock_height: Option<u64>,
) -> Result<(u64, Transaction, MicroTari, MicroTari), OutputManagerError>
{
match self
.handle
.call(OutputManagerRequest::CreateCoinSplit((
amount_per_split,
split_count,
fee_per_gram,
lock_height,
)))
.await??
{
OutputManagerResponse::Transaction(ct) => Ok(ct),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn apply_encryption(&mut self, cipher: Aes256Gcm) -> Result<(), OutputManagerError> {
match self
.handle
.call(OutputManagerRequest::ApplyEncryption(Box::new(cipher)))
.await??
{
OutputManagerResponse::EncryptionApplied => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn remove_encryption(&mut self) -> Result<(), OutputManagerError> {
match self.handle.call(OutputManagerRequest::RemoveEncryption).await?? {
OutputManagerResponse::EncryptionRemoved => Ok(()),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
pub async fn rewind_outputs(
&mut self,
outputs: Vec<TransactionOutput>,
) -> Result<Vec<UnblindedOutput>, OutputManagerError>
{
match self.handle.call(OutputManagerRequest::RewindOutputs(outputs)).await?? {
OutputManagerResponse::RewindOutputs(outputs) => Ok(outputs),
_ => Err(OutputManagerError::UnexpectedApiResponse),
}
}
}