tx_bakery/
chain_query.rs

1//! Trait for a Cardano chain query client
2
3use std::collections::BTreeMap;
4use std::future::Future;
5use std::str::FromStr;
6
7use chrono::{DateTime, Duration, Utc};
8use plutus_ledger_api::csl::{csl_to_pla::ToPLA, lib as csl};
9use plutus_ledger_api::v3::{
10    address::Address,
11    datum::OutputDatum,
12    transaction::{TransactionInput, TransactionOutput},
13    value::Value,
14};
15use serde::Deserialize;
16use thiserror::Error;
17
18use crate::utils::script::Script;
19
20/// A chain query client responsible for all read actions from the blockchain (no write)
21pub trait ChainQuery {
22    /// Query the network id (not identical to network magic)
23    fn get_network(&self) -> Network;
24
25    fn query_system_start(&self) -> impl Future<Output = Result<DateTime<Utc>, ChainQueryError>>;
26
27    fn query_era_summaries(&self)
28        -> impl Future<Output = Result<Vec<EraSummary>, ChainQueryError>>;
29    /// Query protocol parameters
30    fn query_protocol_params(
31        &self,
32    ) -> impl Future<Output = Result<ProtocolParameters, ChainQueryError>>;
33
34    fn query_tip(&self) -> impl Future<Output = Result<ChainTip, ChainQueryError>>;
35
36    /// Query UTxOs at an address
37    fn query_utxos_by_addr(
38        &self,
39        address: &Address,
40    ) -> impl Future<Output = Result<BTreeMap<TransactionInput, FullTransactionOutput>, ChainQueryError>>;
41
42    fn query_utxos_by_ref(
43        &self,
44        references: Vec<&TransactionInput>,
45    ) -> impl Future<Output = Result<BTreeMap<TransactionInput, FullTransactionOutput>, ChainQueryError>>;
46}
47
48/// Cardano network discriminant
49#[derive(Debug, Clone, Deserialize)]
50pub enum Network {
51    Testnet = 0b0000,
52    Mainnet = 0b0001,
53}
54
55impl Network {
56    pub fn to_network_id(&self) -> u8 {
57        match self {
58            Network::Testnet => 0b0000,
59            Network::Mainnet => 0b0001,
60        }
61    }
62}
63
64impl FromStr for Network {
65    type Err = String;
66
67    fn from_str(str: &str) -> Result<Network, Self::Err> {
68        match str {
69            "mainnet" => Ok(Network::Mainnet),
70            "testnet" => Ok(Network::Testnet),
71            _ => Err(format!("Invalid network variant: {}", str)),
72        }
73    }
74}
75
76#[derive(Error, Debug)]
77#[error(transparent)]
78pub struct ChainQueryError(pub anyhow::Error);
79
80#[derive(Debug, Clone)]
81pub struct EraSummary {
82    pub start: EraTime,
83    pub end: Option<EraTime>,
84    pub parameters: EraParameters,
85}
86
87#[derive(Debug, Clone)]
88pub struct EraTime {
89    pub time: Duration,
90    pub slot: u64,
91    pub epoch: u64,
92}
93
94#[derive(Debug, Clone)]
95pub struct EraParameters {
96    pub epoch_length: u64,
97    pub slot_length: u64,
98    pub safe_zone: Option<u64>,
99}
100
101/// A subset of Cardano protocol parameters, only handling values that we use for transaction
102/// building
103#[derive(Debug, Clone)]
104pub struct ProtocolParameters {
105    pub min_fee_coefficient: csl::Coin,
106    pub min_fee_constant: csl::Coin,
107    pub min_fee_reference_scripts: Option<csl::UnitInterval>,
108    pub min_utxo_deposit_coefficient: csl::Coin,
109    pub min_utxo_deposit_constant: csl::Coin,
110    // pub max_block_body_size: Bytes,
111    // pub max_block_header_size: Bytes,
112    pub max_transaction_size: Option<u32>,
113    pub max_value_size: Option<u32>,
114    // pub extra_entropy: Option<Nonce>,
115    pub stake_credential_deposit: csl::Coin,
116    pub stake_pool_deposit: csl::Coin,
117    // pub stake_pool_retirement_epoch_bound: u64,
118    // pub stake_pool_pledge_influence: Ratio,
119    // pub min_stake_pool_cost: LovelaceOnly,
120    // pub desired_number_of_stake_pools: u64,
121    // pub federated_block_production_ratio: Option<Ratio>,
122    // pub monetary_expansion: Ratio,
123    // pub treasury_expansion: Ratio,
124    // pub collateral_percentage: Option<u64>,
125    // pub max_collateral_inputs: Option<u64>,
126    pub plutus_cost_models: Option<csl::Costmdls>,
127    pub script_execution_prices: Option<csl::ExUnitPrices>,
128    // pub max_execution_units_per_transaction: Option<ExecutionUnits>,
129    // pub max_execution_units_per_block: Option<ExecutionUnits>,
130    // pub max_reference_scripts_size: Bytes,
131    // pub stake_pool_voting_thresholds: Option<StakePoolVotingThresholds>,
132    // pub constitutional_committee_min_size: Option<u64>,
133    // pub constitutional_committee_max_term_length: Option<u64>,
134    // pub governance_action_lifetime: Option<Epoch>,
135    // pub governance_action_deposit: Option<LovelaceOnly>,
136    // pub delegate_representative_voting_thresholds: Option<DelegateRepresentativeVotingThresholds>,
137    // pub delegate_representative_deposit: Option<LovelaceOnly>,
138    // pub delegate_representative_max_idle_time: Option<Epoch>,
139    // pub version: ProtocolVersion,
140    //
141}
142
143#[derive(Debug, Clone)]
144pub enum ChainTip {
145    Origin,
146    Point { slot: u64, id: String },
147}
148
149impl ChainTip {
150    pub fn slot(&self) -> u64 {
151        match self {
152            ChainTip::Origin => 0,
153            ChainTip::Point { slot, id: _ } => *slot,
154        }
155    }
156}
157
158#[derive(Debug, Clone)]
159pub struct FullTransactionOutput {
160    pub address: Address,
161    pub value: Value,
162    pub datum: OutputDatum,
163    pub reference_script: Option<Script>,
164}
165
166impl From<FullTransactionOutput> for TransactionOutput {
167    fn from(full_tx_out: FullTransactionOutput) -> TransactionOutput {
168        TransactionOutput {
169            address: full_tx_out.address,
170            value: full_tx_out.value,
171            datum: full_tx_out.datum,
172            reference_script: full_tx_out.reference_script.map(|script| match script {
173                Script::PlutusScript(script) => script.hash().to_pla(),
174                Script::NativeScript(script) => script.hash().to_pla(),
175            }),
176        }
177    }
178}
179
180impl From<&FullTransactionOutput> for TransactionOutput {
181    fn from(full_tx_out: &FullTransactionOutput) -> TransactionOutput {
182        full_tx_out.clone().into()
183    }
184}