use core::{ops::RangeBounds, future::Future};
use alloc::{format, vec::Vec, string::ToString};
use monero_oxide::ed25519::Point;
use crate::{InterfaceError, TransactionsError, ProvidesBlockchainMeta};
pub enum EvaluateUnlocked {
Normal,
FingerprintableDeterministic {
block_number: usize,
},
}
pub trait ProvidesUnvalidatedDecoys: ProvidesBlockchainMeta {
fn ringct_output_distribution(
&self,
range: impl Send + RangeBounds<usize>,
) -> impl Send + Future<Output = Result<Vec<u64>, InterfaceError>>;
fn unlocked_ringct_outputs(
&self,
indexes: &[u64],
evaluate_unlocked: EvaluateUnlocked,
) -> impl Send + Future<Output = Result<Vec<Option<[Point; 2]>>, TransactionsError>>;
}
pub trait ProvidesDecoys: ProvidesBlockchainMeta {
fn ringct_output_distribution(
&self,
range: impl Send + RangeBounds<usize>,
) -> impl Send + Future<Output = Result<Vec<u64>, InterfaceError>>;
fn unlocked_ringct_outputs(
&self,
indexes: &[u64],
evaluate_unlocked: EvaluateUnlocked,
) -> impl Send + Future<Output = Result<Vec<Option<[Point; 2]>>, TransactionsError>>;
}
impl<P: ProvidesUnvalidatedDecoys> ProvidesDecoys for P {
fn ringct_output_distribution(
&self,
range: impl Send + RangeBounds<usize>,
) -> impl Send + Future<Output = Result<Vec<u64>, InterfaceError>> {
async move {
let distribution =
<P as ProvidesUnvalidatedDecoys>::ringct_output_distribution(self, range).await?;
let mut monotonic = 0;
for d in &distribution {
if *d < monotonic {
Err(InterfaceError::InvalidInterface(
"received output distribution didn't increase monotonically".to_string(),
))?;
}
monotonic = *d;
}
Ok(distribution)
}
}
fn unlocked_ringct_outputs(
&self,
indexes: &[u64],
evaluate_unlocked: EvaluateUnlocked,
) -> impl Send + Future<Output = Result<Vec<Option<[Point; 2]>>, TransactionsError>> {
async move {
let outputs =
<P as ProvidesUnvalidatedDecoys>::unlocked_ringct_outputs(self, indexes, evaluate_unlocked)
.await?;
if outputs.len() != indexes.len() {
Err(InterfaceError::InternalError(format!(
"`{}` returned {} outputs, expected {}",
"ProvidesUnvalidatedDecoys::unlocked_ringct_outputs",
outputs.len(),
indexes.len(),
)))?;
}
Ok(outputs)
}
}
}