Skip to main content

hopr_chain_connector/
lib.rs

1mod backend;
2mod connector;
3pub mod errors;
4mod reader;
5#[cfg(any(test, feature = "testing"))]
6pub mod testing;
7
8pub(crate) mod utils;
9
10#[cfg(any(test, feature = "testing"))]
11pub use backend::InMemoryBackend;
12pub use backend::{Backend, TempDbBackend, TempDbError};
13pub use connector::{BlockchainConnectorConfig, HoprBlockchainConnector};
14pub use hopr_api::{
15    chain as api,
16    types::chain::payload::{BasicPayloadGenerator, SafePayloadGenerator},
17};
18pub use reader::HoprBlockchainReader;
19
20/// Re-exports of the `blokli_client` crate.
21pub mod blokli_client {
22    pub use blokli_client::{
23        BlokliClient, BlokliClientConfig, BlokliDnsOverride,
24        api::{BlokliQueryClient, BlokliSubscriptionClient, BlokliTransactionClient, types},
25        exports::Url,
26    };
27}
28
29/// Configuration for creating a [`blokli_client::BlokliClient`] via [`create_blokli_client`].
30///
31/// The connector applies its own opinionated defaults for timeouts and reconnection behaviour;
32/// only `url` and `dns_override` are caller-controlled.
33///
34/// # Examples
35///
36/// Basic usage — no DNS override:
37/// ```ignore
38/// let client = create_blokli_client(HoprBlokliClientConfig::new(url));
39/// ```
40///
41/// With DNS override (useful when system DNS is unavailable):
42/// ```ignore
43/// let client = create_blokli_client(HoprBlokliClientConfig {
44///     url,
45///     dns_override: Some((ip, Some(8545))),
46/// });
47/// ```
48#[derive(Clone, Debug, validator::Validate)]
49pub struct HoprBlokliClientConfig {
50    /// Blokli service URL.
51    pub url: blokli_client::Url,
52    /// Optional DNS override: an IP address (and optional port) to use instead of resolving
53    /// [`Self::url`]'s host via DNS.
54    ///
55    /// When `None` (the default) system DNS is used. When set, the connection goes directly to
56    /// the given IP, while the original host is kept for the `Host` header and TLS SNI.
57    /// `port` defaults to the URL's port when `None`.
58    pub dns_override: Option<(std::net::IpAddr, Option<u16>)>,
59}
60
61impl HoprBlokliClientConfig {
62    /// Creates a config with the given URL and no DNS override.
63    pub fn new(url: blokli_client::Url) -> Self {
64        Self {
65            url,
66            dns_override: None,
67        }
68    }
69}
70
71/// Creates a [`blokli_client::BlokliClient`] with the connector's opinionated defaults.
72///
73/// Applies a 3 s general timeout and 30 s SSE reconnect timeout. Callers that need DNS
74/// pinning set [`HoprBlokliClientConfig::dns_override`]; all other settings are fixed.
75pub fn create_blokli_client(cfg: HoprBlokliClientConfig) -> blokli_client::BlokliClient {
76    blokli_client::BlokliClient::new(
77        cfg.url,
78        blokli_client::BlokliClientConfig {
79            timeout: std::time::Duration::from_secs(3),
80            stream_reconnect_timeout: std::time::Duration::from_secs(30),
81            dns_override: cfg
82                .dns_override
83                .map(|(ip, port)| ::blokli_client::BlokliDnsOverride { ip, port }),
84            ..Default::default()
85        },
86    )
87}
88
89#[doc(hidden)]
90pub mod reexports {
91    pub use hopr_api::types::chain;
92}
93
94use hopr_api::types::crypto::prelude::Keypair;
95pub use hopr_api::types::{
96    chain::prelude::{ContractAddresses, PayloadGenerator},
97    crypto::prelude::ChainKeypair,
98    primitive::prelude::Address,
99};
100
101/// Connector to HOPR on-chain contracts that uses multisig Safe as a signer and [`TempDbBackend`].
102pub type HoprBlockchainSafeConnector<C> = HoprBlockchainConnector<
103    C,
104    TempDbBackend,
105    SafePayloadGenerator,
106    <SafePayloadGenerator as PayloadGenerator>::TxRequest,
107>;
108
109/// Connector to HOPR on-chain contracts that uses standard EOA as a signer and [`TempDbBackend`].
110pub type HoprBlockchainBasicConnector<C> = HoprBlockchainConnector<
111    C,
112    TempDbBackend,
113    BasicPayloadGenerator,
114    <BasicPayloadGenerator as PayloadGenerator>::TxRequest,
115>;
116
117/// Convenience function to create [`HoprBlockchainConnector`] with own contract addresses.
118///
119/// The returned instance uses [`TempDbBackend`] and
120/// `SafePayloadGenerator`
121pub fn create_trustless_hopr_blokli_connector<C>(
122    chain_key: &ChainKeypair,
123    cfg: BlockchainConnectorConfig,
124    client: C,
125    module_address: Address,
126    contracts: ContractAddresses,
127) -> Result<HoprBlockchainSafeConnector<C>, errors::ConnectorError>
128where
129    C: blokli_client::BlokliSubscriptionClient
130        + blokli_client::BlokliQueryClient
131        + blokli_client::BlokliTransactionClient
132        + Send
133        + Sync
134        + 'static,
135{
136    let payload_gen = SafePayloadGenerator::new(chain_key, contracts, module_address);
137
138    Ok(HoprBlockchainConnector::new(
139        chain_key.clone(),
140        cfg,
141        client,
142        TempDbBackend::new().map_err(errors::ConnectorError::backend)?,
143        payload_gen,
144    ))
145}
146
147/// Convenience function to create [`HoprBlockchainConnector`] with own contract addresses.
148///
149/// The transactions generated using this Connector are simply signed using the `chain_key` EOA.
150///
151/// The returned instance uses [`TempDbBackend`] and [`BasicPayloadGenerator`]
152pub fn create_trustless_safeless_hopr_blokli_connector<C>(
153    chain_key: &ChainKeypair,
154    cfg: BlockchainConnectorConfig,
155    client: C,
156    contracts: ContractAddresses,
157) -> Result<HoprBlockchainBasicConnector<C>, errors::ConnectorError>
158where
159    C: blokli_client::BlokliSubscriptionClient
160        + blokli_client::BlokliQueryClient
161        + blokli_client::BlokliTransactionClient
162        + Send
163        + Sync
164        + 'static,
165{
166    let payload_gen = BasicPayloadGenerator::new(chain_key.public().to_address(), contracts);
167
168    Ok(HoprBlockchainConnector::new(
169        chain_key.clone(),
170        cfg,
171        client,
172        TempDbBackend::new().map_err(errors::ConnectorError::backend)?,
173        payload_gen,
174    ))
175}
176
177/// Convenience function to create [`HoprBlockchainConnector`] with contract addresses retrieved from the given
178/// `client`.
179///
180/// This instantiation explicitly trusts the contract address information retrieved from the
181/// [`blokli_client::BlokliClient`].
182/// If you wish to provide your own deployment information, use the [`create_trustless_hopr_blokli_connector`] function.
183///
184/// The returned instance uses [`TempDbBackend`] and [`SafePayloadGenerator`].
185pub async fn create_trustful_hopr_blokli_connector<C>(
186    chain_key: &ChainKeypair,
187    cfg: BlockchainConnectorConfig,
188    client: C,
189    module_address: Address,
190) -> Result<HoprBlockchainSafeConnector<C>, errors::ConnectorError>
191where
192    C: blokli_client::BlokliSubscriptionClient
193        + blokli_client::BlokliQueryClient
194        + blokli_client::BlokliTransactionClient
195        + Send
196        + Sync
197        + 'static,
198{
199    let info = client.query_chain_info().await?;
200    let contract_addrs = serde_json::from_str(&info.contract_addresses.0)
201        .map_err(|e| errors::ConnectorError::TypeConversion(format!("contract addresses not a valid JSON: {e}")))?;
202
203    let payload_gen = SafePayloadGenerator::new(chain_key, contract_addrs, module_address);
204
205    Ok(HoprBlockchainConnector::new(
206        chain_key.clone(),
207        cfg,
208        client,
209        TempDbBackend::new().map_err(errors::ConnectorError::backend)?,
210        payload_gen,
211    ))
212}
213
214/// Convenience function to create [`HoprBlockchainConnector`] with contract addresses retrieved from the given
215/// `client`.
216///
217/// The transactions generated using this Connector are simply signed using the `chain_key` EOA.
218///
219/// This instantiation explicitly trusts the contract address information retrieved from the
220/// [`blokli_client::BlokliClient`].
221/// If you wish to provide your own deployment information, use the [`create_trustless_safeless_hopr_blokli_connector`]
222/// function.
223///
224/// The returned instance uses [`TempDbBackend`] and [`BasicPayloadGenerator`].
225pub async fn create_trustful_safeless_hopr_blokli_connector<C>(
226    chain_key: &ChainKeypair,
227    cfg: BlockchainConnectorConfig,
228    client: C,
229) -> Result<HoprBlockchainBasicConnector<C>, errors::ConnectorError>
230where
231    C: blokli_client::BlokliSubscriptionClient
232        + blokli_client::BlokliQueryClient
233        + blokli_client::BlokliTransactionClient
234        + Send
235        + Sync
236        + 'static,
237{
238    let info = client.query_chain_info().await?;
239    let contract_addrs = serde_json::from_str(&info.contract_addresses.0)
240        .map_err(|e| errors::ConnectorError::TypeConversion(format!("contract addresses not a valid JSON: {e}")))?;
241
242    let payload_gen = BasicPayloadGenerator::new(chain_key.public().to_address(), contract_addrs);
243
244    Ok(HoprBlockchainConnector::new(
245        chain_key.clone(),
246        cfg,
247        client,
248        TempDbBackend::new().map_err(errors::ConnectorError::backend)?,
249        payload_gen,
250    ))
251}