use std::net;
use std::ops::{RangeBounds, RangeInclusive};
use crossbeam_channel as chan;
use thiserror::Error;
use nakamoto_common::bitcoin::network::constants::ServiceFlags;
use nakamoto_common::bitcoin::network::Address;
use nakamoto_common::bitcoin::Script;
use nakamoto_common::bitcoin::network::message::NetworkMessage;
use nakamoto_common::block::filter::BlockFilter;
use nakamoto_common::block::tree::{BlockReader, ImportResult};
use nakamoto_common::block::{self, Block, BlockHash, BlockHeader, Height, Transaction};
use nakamoto_common::nonempty::NonEmpty;
use nakamoto_p2p::fsm::Link;
use nakamoto_p2p::fsm::{self, Command, CommandError, GetFiltersError, Peer};
use crate::client::Event;
#[derive(Error, Debug)]
pub enum Error {
#[error("command channel disconnected")]
Disconnected,
#[error("command failed: {0}")]
Command(#[from] CommandError),
#[error("failed to get filters: {0}")]
GetFilters(#[from] GetFiltersError),
#[error("the operation timed out")]
Timeout,
#[error(transparent)]
Io(#[from] std::io::Error),
}
impl From<chan::RecvError> for Error {
fn from(_: chan::RecvError) -> Self {
Self::Disconnected
}
}
impl From<chan::RecvTimeoutError> for Error {
fn from(err: chan::RecvTimeoutError) -> Self {
match err {
chan::RecvTimeoutError::Timeout => Self::Timeout,
chan::RecvTimeoutError::Disconnected => Self::Disconnected,
}
}
}
impl<T> From<chan::SendError<T>> for Error {
fn from(_: chan::SendError<T>) -> Self {
Self::Disconnected
}
}
pub trait Handle: Sized + Send + Sync + Clone {
fn get_tip(&self) -> Result<(Height, BlockHeader), Error>;
fn get_block(&self, hash: &BlockHash) -> Result<(), Error>;
fn get_filters(&self, range: RangeInclusive<Height>) -> Result<(), Error>;
fn query_tree(
&self,
query: impl Fn(&dyn BlockReader) + Send + Sync + 'static,
) -> Result<(), Error>;
fn find_branch(&self, to: &BlockHash)
-> Result<Option<(Height, NonEmpty<BlockHeader>)>, Error>;
fn blocks(&self) -> chan::Receiver<(Block, Height)>;
fn filters(&self) -> chan::Receiver<(BlockFilter, BlockHash, Height)>;
fn events(&self) -> chan::Receiver<Event>;
fn command(&self, cmd: Command) -> Result<(), Error>;
fn rescan(
&self,
range: impl RangeBounds<Height>,
watch: impl Iterator<Item = Script>,
) -> Result<(), Error> {
let from = range.start_bound().cloned();
let to = range.end_bound().cloned();
self.command(Command::Rescan {
from,
to,
watch: watch.collect(),
})?;
Ok(())
}
fn watch(&self, watch: impl Iterator<Item = Script>) -> Result<(), Error> {
self.command(Command::Watch {
watch: watch.collect(),
})?;
Ok(())
}
fn broadcast(
&self,
msg: NetworkMessage,
predicate: fn(Peer) -> bool,
) -> Result<Vec<net::SocketAddr>, Error>;
fn query(&self, msg: NetworkMessage) -> Result<Option<net::SocketAddr>, Error>;
fn connect(&self, addr: net::SocketAddr) -> Result<Link, Error>;
fn disconnect(&self, addr: net::SocketAddr) -> Result<(), Error>;
fn submit_transaction(&self, tx: Transaction) -> Result<NonEmpty<net::SocketAddr>, Error>;
fn import_headers(
&self,
headers: Vec<BlockHeader>,
) -> Result<Result<ImportResult, block::tree::Error>, Error>;
fn import_addresses(&self, addrs: Vec<Address>) -> Result<(), Error>;
fn wait<F: FnMut(fsm::Event) -> Option<T>, T>(&self, f: F) -> Result<T, Error>;
fn wait_for_peers(
&self,
count: usize,
required_services: impl Into<ServiceFlags>,
) -> Result<Vec<(net::SocketAddr, Height, ServiceFlags)>, Error>;
fn wait_for_height(&self, h: Height) -> Result<BlockHash, Error>;
fn shutdown(self) -> Result<(), Error>;
}