use std::{
error::Error,
ops::{Bound, RangeBounds},
};
use futures::{future::BoxFuture, stream::BoxStream};
pub use hopr_types::{
internal::prelude::{ChannelDirection, ChannelEntry, ChannelId, ChannelStatusDiscriminants},
primitive::prelude::HoprBalance,
};
use hopr_types::{
internal::prelude::{ChannelStatus, generate_channel_id},
primitive::prelude::Address,
};
use strum::IntoDiscriminant;
use crate::chain::ChainReceipt;
pub type DateTime = chrono::DateTime<chrono::Utc>;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ChannelSelector {
pub source: Option<Address>,
pub destination: Option<Address>,
pub id: Option<ChannelId>,
pub allowed_states: Vec<ChannelStatusDiscriminants>,
pub closure_time_range: (Bound<DateTime>, Bound<DateTime>),
}
impl Default for ChannelSelector {
fn default() -> Self {
Self {
source: None,
destination: None,
id: None,
allowed_states: vec![],
closure_time_range: (Bound::Unbounded, Bound::Unbounded),
}
}
}
impl ChannelSelector {
#[must_use]
pub fn with_source<A: Into<Address>>(mut self, address: A) -> Self {
self.source = Some(address.into());
self.id = None;
self
}
#[must_use]
pub fn with_destination<A: Into<Address>>(mut self, address: A) -> Self {
self.destination = Some(address.into());
self.id = None;
self
}
#[must_use]
pub fn with_id(mut self, id: ChannelId) -> Self {
self.id = Some(id);
self.source = None;
self.destination = None;
self
}
#[must_use]
pub fn with_allowed_states(mut self, allowed_states: &[ChannelStatusDiscriminants]) -> Self {
self.allowed_states.extend_from_slice(allowed_states);
self
}
#[must_use]
pub fn with_closure_time_range<T: RangeBounds<DateTime>>(mut self, range: T) -> Self {
self.closure_time_range = (range.start_bound().cloned(), range.end_bound().cloned());
self
}
#[must_use]
pub fn with_redeemable_channels(self, min_grace_period: Option<std::time::Duration>) -> Self {
self.with_allowed_states(&[
ChannelStatusDiscriminants::Open,
ChannelStatusDiscriminants::PendingToClose,
])
.with_closure_time_range(chrono::Utc::now() + min_grace_period.unwrap_or_default()..)
}
pub fn satisfies(&self, channel: &ChannelEntry) -> bool {
if let Some(id) = &self.id {
if channel.get_id() != id {
return false;
}
} else {
if let Some(source) = &self.source
&& channel.source != *source
{
return false;
}
if let Some(dst) = &self.destination
&& channel.destination != *dst
{
return false;
}
}
if !self.allowed_states.is_empty() && !self.allowed_states.contains(&channel.status.discriminant()) {
return false;
}
if self
.allowed_states
.contains(&ChannelStatusDiscriminants::PendingToClose)
&& let ChannelStatus::PendingToClose(time) = &channel.status
{
let time = DateTime::from(*time);
if !self.closure_time_range.contains(&time) {
return false;
}
}
true
}
}
#[auto_impl::auto_impl(&, Box, Arc)]
pub trait ChainReadChannelOperations {
type Error: Error + Send + Sync + 'static;
fn me(&self) -> &Address;
fn channel_by_parties(&self, src: &Address, dst: &Address) -> Result<Option<ChannelEntry>, Self::Error> {
self.channel_by_id(&generate_channel_id(src, dst))
}
fn channel_by_id(&self, channel_id: &ChannelId) -> Result<Option<ChannelEntry>, Self::Error>;
fn stream_channels<'a>(&'a self, selector: ChannelSelector) -> Result<BoxStream<'a, ChannelEntry>, Self::Error>;
}
#[async_trait::async_trait]
#[auto_impl::auto_impl(&, Box, Arc)]
pub trait ChainWriteChannelOperations {
type Error: Error + Send + Sync + 'static;
async fn open_channel<'a>(
&'a self,
dst: &'a Address,
amount: HoprBalance,
) -> Result<BoxFuture<'a, Result<ChainReceipt, Self::Error>>, Self::Error>;
async fn fund_channel<'a>(
&'a self,
channel_id: &'a ChannelId,
amount: HoprBalance,
) -> Result<BoxFuture<'a, Result<ChainReceipt, Self::Error>>, Self::Error>;
async fn close_channel<'a>(
&'a self,
channel_id: &'a ChannelId,
) -> Result<BoxFuture<'a, Result<ChainReceipt, Self::Error>>, Self::Error>;
}