tx_indexer/
config.rs

1use crate::{
2    filter::Filter,
3    handler::{callback::EventHandler, retry::RetryPolicy},
4};
5use anyhow::anyhow;
6use core::str::FromStr;
7use oura::{sources::MagicArg, utils::ChainWellKnownInfo};
8use std::fmt;
9use std::fs::File;
10use std::io::BufReader;
11use std::{error::Error, path::PathBuf};
12use strum_macros::Display;
13
14pub struct TxIndexerConfig<H: EventHandler> {
15    pub handler: H,
16    /// Event source
17    pub source: TxIndexerSource,
18    /// Retry policy - how much to retry for each event callback failure
19    /// This only takes effect on ErrorPolicy for a particular error is `Retry`.
20    /// Once retries are exhausted, the handler will error (same treatment as ErrorPolicy::Exit)
21    pub retry_policy: RetryPolicy,
22}
23
24pub enum TxIndexerSource {
25    CardanoNode {
26        node_address: NodeAddress,
27        network: NetworkConfig,
28        /// Slot number and hash as hex string (optional).
29        /// If not provided, sync will begin from the tip of the chain.
30        since_slot: Option<(u64, String)>,
31        /// Minimum depth a block has to be from the tip for it to be considered "confirmed"
32        /// See: https://oura.txpipe.io/v1/advanced/rollback_buffer
33        safe_block_depth: usize,
34        /// Filter transaction events by specific component(s).
35        event_filter: Filter,
36    },
37    FixtureFiles {
38        dir_path: PathBuf,
39    },
40}
41
42impl<H: EventHandler> TxIndexerConfig<H> {
43    #[allow(clippy::too_many_arguments)]
44    pub fn cardano_node(
45        handler: H,
46        node_address: NodeAddress,
47        network: NetworkConfig,
48        since_slot: Option<(u64, String)>,
49        safe_block_depth: usize,
50        event_filter: Filter,
51        retry_policy: RetryPolicy,
52    ) -> Self {
53        Self {
54            handler,
55            source: TxIndexerSource::CardanoNode {
56                node_address,
57                network,
58                since_slot,
59                safe_block_depth,
60                event_filter,
61            },
62            retry_policy,
63        }
64    }
65
66    pub fn source_from_fixtures(handler: H, dir_path: PathBuf, retry_policy: RetryPolicy) -> Self {
67        Self {
68            handler,
69            source: TxIndexerSource::FixtureFiles { dir_path },
70            retry_policy,
71        }
72    }
73}
74
75/// Simple description on how to connect to a local or remote node.
76/// Used to build Oura source config.
77pub enum NodeAddress {
78    /// Path to Unix node.socket
79    UnixSocket(String),
80    /// Hostname and port number for TCP connection to remote node
81    TcpAddress(String, u16),
82}
83
84/// Typed network magic restricted to specific networks fully supported by Oura.
85#[derive(Clone, Debug, Display)]
86pub enum NetworkName {
87    PREPROD,
88    PREVIEW,
89    MAINNET,
90}
91
92#[derive(Clone, Debug)]
93pub enum NetworkConfig {
94    ConfigPath {
95        node_config_path: String,
96        magic: u64,
97    },
98    WellKnown(NetworkName),
99    Config {
100        chain_info: ChainWellKnownInfo,
101        magic: u64,
102    },
103}
104
105#[derive(Clone, Debug, PartialEq, Eq)]
106pub struct NetworkNameParseErr;
107
108impl fmt::Display for NetworkNameParseErr {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        "provided string was not `preprod` or `preview` or `mainnet`".fmt(f)
111    }
112}
113impl Error for NetworkNameParseErr {}
114
115impl FromStr for NetworkName {
116    type Err = NetworkNameParseErr;
117    fn from_str(s: &str) -> Result<NetworkName, Self::Err> {
118        match &s.to_lowercase()[..] {
119            "preprod" => Ok(NetworkName::PREPROD),
120            "preview" => Ok(NetworkName::PREVIEW),
121            "mainnet" => Ok(NetworkName::MAINNET),
122            _ => Err(NetworkNameParseErr),
123        }
124    }
125}
126
127impl NetworkConfig {
128    pub fn to_magic_arg(&self) -> MagicArg {
129        MagicArg(match self {
130            NetworkConfig::WellKnown(network_name) => match network_name {
131                NetworkName::PREPROD => pallas::network::miniprotocols::PRE_PRODUCTION_MAGIC,
132                NetworkName::PREVIEW => pallas::network::miniprotocols::PREVIEW_MAGIC,
133                NetworkName::MAINNET => pallas::network::miniprotocols::MAINNET_MAGIC,
134            },
135            NetworkConfig::ConfigPath { magic, .. } => *magic,
136            NetworkConfig::Config { magic, .. } => *magic,
137        })
138    }
139
140    pub fn to_chain_info(&self) -> Result<ChainWellKnownInfo, anyhow::Error> {
141        Ok(match self {
142            NetworkConfig::WellKnown(network_name) => match network_name {
143                NetworkName::PREPROD => ChainWellKnownInfo::preprod(),
144                NetworkName::PREVIEW => ChainWellKnownInfo::preview(),
145                NetworkName::MAINNET => ChainWellKnownInfo::mainnet(),
146            },
147            NetworkConfig::Config { chain_info, .. } => chain_info.clone(),
148            NetworkConfig::ConfigPath {
149                node_config_path, ..
150            } => {
151                let file = File::open(node_config_path.clone())
152                    .map_err(|err| anyhow!("Chain Info not found at given path: {}", err))?;
153                let reader = BufReader::new(file);
154                serde_json::from_reader(reader).expect("Invalid JSON format for ChainWellKnownInfo")
155            }
156        })
157    }
158}
159
160// Encapsulating usage of deprecated stuff (impossible to construct struct without it).
161// This avoids having to put "#![allow(deprecated)]" on the top of this file.
162pub mod deprecation_usage {
163    #![allow(deprecated)]
164
165    use oura::mapper::Config as MapperConfig;
166    use oura::sources::n2c::Config as N2CConfig;
167    use oura::sources::n2n::Config as N2NConfig;
168    use oura::sources::{AddressArg, IntersectArg, MagicArg, PointArg};
169
170    pub fn n2c_config(
171        addr: AddressArg,
172        magic: MagicArg,
173        since_slot: Option<(u64, String)>,
174        safe_block_depth: usize,
175    ) -> N2CConfig {
176        N2CConfig {
177            address: addr,
178            magic: Some(magic),
179            intersect: since_slot
180                .map(|since_slot| IntersectArg::Point(PointArg(since_slot.0, since_slot.1))),
181            mapper: MapperConfig {
182                include_transaction_details: true,
183                ..Default::default()
184            },
185            min_depth: safe_block_depth,
186            retry_policy: None,
187            finalize: None,
188            // Deprecated fields
189            since: None,
190            well_known: None,
191        }
192    }
193
194    pub fn n2n_config(
195        addr: AddressArg,
196        magic: MagicArg,
197        since_slot: Option<(u64, String)>,
198        safe_block_depth: usize,
199    ) -> N2NConfig {
200        N2NConfig {
201            address: addr,
202            magic: Some(magic),
203            intersect: since_slot
204                .map(|since_slot| IntersectArg::Point(PointArg(since_slot.0, since_slot.1))),
205            mapper: MapperConfig {
206                include_transaction_details: true,
207                ..Default::default()
208            },
209            min_depth: safe_block_depth,
210            retry_policy: None,
211            finalize: None,
212            // Deprecated fields
213            since: None,
214            well_known: None,
215        }
216    }
217}
218
219pub use self::deprecation_usage::*;