#[cfg(feature = "resolver-electrum")]
mod electrum;
#[cfg(any(feature = "resolver-esplora", feature = "resolver-esplora-async",))]
mod esplora;
use core::iter;
#[cfg(feature = "std")]
use std::process::exit;
use amplify::IoError;
use bpstd::psbt::Utxo;
use bpstd::{ScriptPubkey, Terminal, Tx, Txid, UnsignedTx};
use rgb::WitnessStatus;
#[cfg(feature = "resolver-electrum")]
pub use self::electrum::ElectrumResolver;
#[cfg(feature = "resolver-esplora-async")]
pub use self::esplora::EsploraAsyncResolver;
#[cfg(feature = "resolver-esplora")]
pub use self::esplora::EsploraResolver;
#[cfg(not(feature = "async"))]
pub trait Resolver {
fn resolve_tx(&self, txid: Txid) -> Result<Option<UnsignedTx>, ResolverError>;
fn resolve_tx_status(&self, txid: Txid) -> Result<WitnessStatus, ResolverError>;
fn resolve_utxos(
&self,
iter: impl IntoIterator<Item = (Terminal, ScriptPubkey)>,
) -> impl Iterator<Item = Result<Utxo, ResolverError>>;
fn last_block_height(&self) -> Result<u64, ResolverError>;
fn broadcast(&self, tx: &Tx) -> Result<(), ResolverError>;
}
#[cfg(feature = "async")]
pub trait Resolver {
async fn resolve_tx_async(&self, txid: Txid) -> Result<Option<UnsignedTx>, ResolverError>;
async fn resolve_tx_status_async(&self, txid: Txid) -> Result<WitnessStatus, ResolverError>;
async fn resolve_utxos_async(
&self,
iter: impl IntoIterator<Item = (Terminal, ScriptPubkey)>,
) -> impl Iterator<Item = Result<Utxo, ResolverError>>;
async fn last_block_height_async(&self) -> Result<u64, ResolverError>;
async fn broadcast_async(&self, tx: &Tx) -> Result<(), ResolverError>;
}
#[derive(Default)]
pub struct MultiResolver {
#[cfg(feature = "resolver-electrum")]
electrum: Option<ElectrumResolver>,
#[cfg(feature = "resolver-esplora")]
esplora: Option<EsploraResolver>,
#[cfg(feature = "resolver-esplora-async")]
esplora: Option<EsploraAsyncResolver>,
}
#[derive(Copy, Clone)]
pub struct NoResolver;
impl NoResolver {
fn call(&self) -> ! {
#[cfg(feature = "std")]
{
eprintln!(
"Error: no blockchain indexer specified; use either --esplora or --electrum \
argument"
);
exit(1);
}
#[cfg(not(feature = "std"))]
panic!(
"Error: no blockchain indexer, you need to use one of resolver-* features during the \
compilation"
);
}
}
#[cfg(not(feature = "async"))]
impl Resolver for NoResolver {
fn resolve_tx(&self, _txid: Txid) -> Result<Option<UnsignedTx>, ResolverError> { self.call() }
fn resolve_tx_status(&self, _txid: Txid) -> Result<WitnessStatus, ResolverError> { self.call() }
fn resolve_utxos(
&self,
_iter: impl IntoIterator<Item = (Terminal, ScriptPubkey)>,
) -> impl Iterator<Item = Result<Utxo, ResolverError>> {
self.call();
#[allow(unreachable_code)]
iter::empty()
}
fn last_block_height(&self) -> Result<u64, ResolverError> { self.call() }
fn broadcast(&self, _tx: &Tx) -> Result<(), ResolverError> { self.call() }
}
#[cfg(feature = "async")]
impl Resolver for NoResolver {
async fn resolve_tx_async(&self, _txid: Txid) -> Result<Option<UnsignedTx>, ResolverError> {
self.call()
}
async fn resolve_tx_status_async(&self, _txid: Txid) -> Result<WitnessStatus, ResolverError> {
self.call()
}
async fn resolve_utxos_async(
&self,
_iter: impl IntoIterator<Item = (Terminal, ScriptPubkey)>,
) -> impl Iterator<Item = Result<Utxo, ResolverError>> {
self.call();
#[allow(unreachable_code)]
iter::empty()
}
async fn last_block_height_async(&self) -> Result<u64, ResolverError> { self.call() }
async fn broadcast_async(&self, _tx: &Tx) -> Result<(), ResolverError> { self.call() }
}
impl MultiResolver {
#[cfg(feature = "resolver-electrum")]
pub fn new_electrum(url: &str) -> Result<Self, ResolverError> {
Ok(Self { electrum: Some(ElectrumResolver::new(url)?), ..default!() })
}
#[cfg(feature = "resolver-esplora")]
pub fn new_esplora(url: &str) -> Result<Self, ResolverError> {
Ok(Self { esplora: Some(EsploraResolver::new(url)?), ..default!() })
}
#[allow(clippy::needless_update)]
#[cfg(feature = "resolver-esplora-async")]
pub fn new_esplora(url: &str) -> Result<Self, ResolverError> {
Ok(Self { esplora: Some(EsploraAsyncResolver::new(url)?), ..default!() })
}
pub fn new_absent() -> Result<Self, ResolverError> { Ok(Self::default()) }
}
#[cfg(not(feature = "async"))]
impl Resolver for MultiResolver {
fn resolve_tx(&self, txid: Txid) -> Result<Option<UnsignedTx>, ResolverError> {
#[cfg(feature = "resolver-esplora")]
if let Some(resolver) = &self.esplora {
return resolver.resolve_tx(txid);
}
#[cfg(feature = "resolver-electrum")]
if let Some(resolver) = &self.electrum {
return resolver.resolve_tx(txid);
}
NoResolver.call()
}
fn resolve_tx_status(&self, txid: Txid) -> Result<WitnessStatus, ResolverError> {
#[cfg(feature = "resolver-esplora")]
if let Some(resolver) = &self.esplora {
return resolver.resolve_tx_status(txid);
}
#[cfg(feature = "resolver-electrum")]
if let Some(resolver) = &self.electrum {
return resolver.resolve_tx_status(txid);
}
NoResolver.call()
}
#[allow(unreachable_code)]
fn resolve_utxos(
&self,
iter: impl IntoIterator<Item = (Terminal, ScriptPubkey)>,
) -> impl Iterator<Item = Result<Utxo, ResolverError>> {
#[cfg(feature = "resolver-esplora")]
if let Some(resolver) = &self.esplora {
return resolver.resolve_utxos(iter).collect::<Vec<_>>().into_iter();
}
#[cfg(feature = "resolver-electrum")]
if let Some(resolver) = &self.electrum {
return resolver.resolve_utxos(iter).collect::<Vec<_>>().into_iter();
}
NoResolver.call();
vec![].into_iter()
}
fn last_block_height(&self) -> Result<u64, ResolverError> {
#[cfg(feature = "resolver-esplora")]
if let Some(resolver) = &self.esplora {
return resolver.last_block_height();
}
#[cfg(feature = "resolver-electrum")]
if let Some(resolver) = &self.electrum {
return resolver.last_block_height();
}
NoResolver.call()
}
fn broadcast(&self, tx: &Tx) -> Result<(), ResolverError> {
#[cfg(feature = "resolver-esplora")]
if let Some(resolver) = &self.esplora {
return resolver.broadcast(tx);
}
#[cfg(feature = "resolver-electrum")]
if let Some(resolver) = &self.electrum {
return resolver.broadcast(tx);
}
NoResolver.call()
}
}
#[cfg(feature = "async")]
impl Resolver for MultiResolver {
async fn resolve_tx_async(&self, txid: Txid) -> Result<Option<UnsignedTx>, ResolverError> {
#[cfg(feature = "resolver-esplora-async")]
if let Some(resolver) = &self.esplora {
return resolver.resolve_tx_async(txid).await;
}
NoResolver.call()
}
async fn resolve_tx_status_async(&self, txid: Txid) -> Result<WitnessStatus, ResolverError> {
#[cfg(feature = "resolver-esplora-async")]
if let Some(resolver) = &self.esplora {
return resolver.resolve_tx_status_async(txid).await;
}
NoResolver.call()
}
#[allow(unreachable_code)]
async fn resolve_utxos_async(
&self,
iter: impl IntoIterator<Item = (Terminal, ScriptPubkey)>,
) -> impl Iterator<Item = Result<Utxo, ResolverError>> {
#[cfg(feature = "resolver-esplora-async")]
if let Some(resolver) = &self.esplora {
return resolver
.resolve_utxos_async(iter)
.await
.collect::<Vec<_>>()
.into_iter();
}
NoResolver.call();
vec![].into_iter()
}
async fn last_block_height_async(&self) -> Result<u64, ResolverError> {
#[cfg(feature = "resolver-esplora-async")]
if let Some(resolver) = &self.esplora {
return resolver.last_block_height_async().await;
}
NoResolver.call()
}
async fn broadcast_async(&self, tx: &Tx) -> Result<(), ResolverError> {
#[cfg(feature = "resolver-esplora-async")]
if let Some(resolver) = &self.esplora {
return resolver.broadcast_async(tx).await;
}
NoResolver.call()
}
}
#[derive(Debug, Display, Error, From)]
#[display(inner)]
pub enum ResolverError {
Io(IoError),
Connectivity,
Local,
Protocol,
Logic,
ServerSide(String),
}