use core::future::Future;
use alloc::vec::Vec;
use std_shims::io;
use zeroize::Zeroize;
use monero_oxide::io::read_u64;
use crate::InterfaceError;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub enum FeePriority {
Unimportant,
Normal,
Elevated,
Priority,
Custom {
priority: u32,
},
}
impl FeePriority {
pub fn to_u32(&self) -> u32 {
match self {
FeePriority::Unimportant => 1,
FeePriority::Normal => 2,
FeePriority::Elevated => 3,
FeePriority::Priority => 4,
FeePriority::Custom { priority, .. } => *priority,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct FeeRate {
per_weight: u64,
mask: u64,
}
impl FeeRate {
pub fn new(per_weight: u64, mask: u64) -> Option<FeeRate> {
if (per_weight == 0) || (mask == 0) {
None?;
}
Some(FeeRate { per_weight, mask })
}
pub fn write(&self, w: &mut impl io::Write) -> io::Result<()> {
w.write_all(&self.per_weight.to_le_bytes())?;
w.write_all(&self.mask.to_le_bytes())
}
pub fn serialize(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(16);
self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
res
}
pub fn per_weight(&self) -> u64 {
self.per_weight
}
pub fn read(r: &mut impl io::Read) -> io::Result<FeeRate> {
let per_weight = read_u64(r)?;
let mask = read_u64(r)?;
FeeRate::new(per_weight, mask).ok_or_else(|| io::Error::other("fee rate was invalid"))
}
pub fn calculate_fee_from_weight(&self, weight: usize) -> u64 {
let fee =
self.per_weight * u64::try_from(weight).expect("couldn't convert weight (usize) to u64");
fee.div_ceil(self.mask) * self.mask
}
pub fn calculate_weight_from_fee(&self, fee: u64) -> Option<usize> {
usize::try_from(fee / self.per_weight).ok()
}
}
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
pub enum FeeError {
#[error("interface error ({0})")]
InterfaceError(InterfaceError),
#[error("invalid fee")]
InvalidFee,
#[error("invalid fee priority")]
InvalidFeePriority,
}
impl From<InterfaceError> for FeeError {
fn from(err: InterfaceError) -> Self {
Self::InterfaceError(err)
}
}
pub trait ProvidesUnvalidatedFeeRates: Sync {
fn fee_rate(
&self,
priority: FeePriority,
) -> impl Send + Future<Output = Result<FeeRate, FeeError>>;
}
pub trait ProvidesFeeRates: Sync {
fn fee_rate(
&self,
priority: FeePriority,
max_per_weight: u64,
) -> impl Send + Future<Output = Result<FeeRate, FeeError>>;
}
impl<P: ProvidesUnvalidatedFeeRates> ProvidesFeeRates for P {
fn fee_rate(
&self,
priority: FeePriority,
max_per_weight: u64,
) -> impl Send + Future<Output = Result<FeeRate, FeeError>> {
async move {
let fee_rate = <P as ProvidesUnvalidatedFeeRates>::fee_rate(self, priority).await?;
if fee_rate.per_weight > max_per_weight {
Err(FeeError::InvalidFee)?;
}
Ok(fee_rate)
}
}
}