// Magical Bitcoin Library // Written in 2020 by // Alekos Filini // // Copyright (c) 2020 Magical Bitcoin // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //! Runtime-checked blockchain types //! //! This module provides the implementation of [`AnyBlockchain`] which allows switching the //! inner [`Blockchain`] type at runtime. use super::*; macro_rules! impl_from { ( $from:ty, $to:ty, $variant:ident, $( $cfg:tt )* ) => { $( $cfg )* impl From<$from> for $to { fn from(inner: $from) -> Self { <$to>::$variant(inner) } } }; } macro_rules! impl_inner_method { ( $self:expr, $name:ident $(, $args:expr)* ) => { match $self { #[cfg(feature = "electrum")] AnyBlockchain::Electrum(inner) => inner.$name( $($args, )* ), #[cfg(feature = "esplora")] AnyBlockchain::Esplora(inner) => inner.$name( $($args, )* ), #[cfg(feature = "compact_filters")] AnyBlockchain::CompactFilters(inner) => inner.$name( $($args, )* ), } } } /// Type that can contain any of the [`Blockchain`] types defined by the library /// /// It allows switching backend at runtime pub enum AnyBlockchain { #[cfg(feature = "electrum")] #[cfg_attr(docsrs, doc(cfg(feature = "electrum")))] Electrum(electrum::ElectrumBlockchain), #[cfg(feature = "esplora")] #[cfg_attr(docsrs, doc(cfg(feature = "esplora")))] Esplora(esplora::EsploraBlockchain), #[cfg(feature = "compact_filters")] #[cfg_attr(docsrs, doc(cfg(feature = "compact_filters")))] CompactFilters(compact_filters::CompactFiltersBlockchain), } #[maybe_async] impl Blockchain for AnyBlockchain { fn get_capabilities(&self) -> HashSet { maybe_await!(impl_inner_method!(self, get_capabilities)) } fn setup( &self, stop_gap: Option, database: &mut D, progress_update: P, ) -> Result<(), Error> { maybe_await!(impl_inner_method!( self, setup, stop_gap, database, progress_update )) } fn sync( &self, stop_gap: Option, database: &mut D, progress_update: P, ) -> Result<(), Error> { maybe_await!(impl_inner_method!( self, sync, stop_gap, database, progress_update )) } fn get_tx(&self, txid: &Txid) -> Result, Error> { maybe_await!(impl_inner_method!(self, get_tx, txid)) } fn broadcast(&self, tx: &Transaction) -> Result<(), Error> { maybe_await!(impl_inner_method!(self, broadcast, tx)) } fn get_height(&self) -> Result { maybe_await!(impl_inner_method!(self, get_height)) } fn estimate_fee(&self, target: usize) -> Result { maybe_await!(impl_inner_method!(self, estimate_fee, target)) } } impl_from!(electrum::ElectrumBlockchain, AnyBlockchain, Electrum, #[cfg(feature = "electrum")]); impl_from!(esplora::EsploraBlockchain, AnyBlockchain, Esplora, #[cfg(feature = "esplora")]); impl_from!(compact_filters::CompactFiltersBlockchain, AnyBlockchain, CompactFilters, #[cfg(feature = "compact_filters")]); /// Type that can contain any of the blockchain configurations defined by the library /// /// This allows storing a single configuration that can be loaded into an [`AnyBlockchain`] /// instance. Wallets that plan to offer users the ability to switch blockchain backend at runtime /// will find this particularly useful. #[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum AnyBlockchainConfig { #[cfg(feature = "electrum")] #[cfg_attr(docsrs, doc(cfg(feature = "electrum")))] Electrum(electrum::ElectrumBlockchainConfig), #[cfg(feature = "esplora")] #[cfg_attr(docsrs, doc(cfg(feature = "esplora")))] Esplora(esplora::EsploraBlockchainConfig), #[cfg(feature = "compact_filters")] #[cfg_attr(docsrs, doc(cfg(feature = "compact_filters")))] CompactFilters(compact_filters::CompactFiltersBlockchainConfig), } impl ConfigurableBlockchain for AnyBlockchain { type Config = AnyBlockchainConfig; fn from_config(config: &Self::Config) -> Result { Ok(match config { #[cfg(feature = "electrum")] AnyBlockchainConfig::Electrum(inner) => { AnyBlockchain::Electrum(electrum::ElectrumBlockchain::from_config(inner)?) } #[cfg(feature = "esplora")] AnyBlockchainConfig::Esplora(inner) => { AnyBlockchain::Esplora(esplora::EsploraBlockchain::from_config(inner)?) } #[cfg(feature = "compact_filters")] AnyBlockchainConfig::CompactFilters(inner) => AnyBlockchain::CompactFilters( compact_filters::CompactFiltersBlockchain::from_config(inner)?, ), }) } } impl_from!(electrum::ElectrumBlockchainConfig, AnyBlockchainConfig, Electrum, #[cfg(feature = "electrum")]); impl_from!(esplora::EsploraBlockchainConfig, AnyBlockchainConfig, Esplora, #[cfg(feature = "esplora")]); impl_from!(compact_filters::CompactFiltersBlockchainConfig, AnyBlockchainConfig, CompactFilters, #[cfg(feature = "compact_filters")]);