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, Copy, Deserialize)]
50pub enum Network {
51    Testnet = 0b0000,
52    Mainnet = 0b0001,
53}
54
55impl Network {
56    pub fn to_network_id(&self) -> u8 {
57        *self as u8
58    }
59}
60
61impl FromStr for Network {
62    type Err = String;
63
64    fn from_str(str: &str) -> Result<Network, Self::Err> {
65        match str {
66            "mainnet" => Ok(Network::Mainnet),
67            "testnet" => Ok(Network::Testnet),
68            _ => Err(format!("Invalid network variant: {}", str)),
69        }
70    }
71}
72
73#[derive(Error, Debug)]
74#[error(transparent)]
75pub struct ChainQueryError(pub anyhow::Error);
76
77#[derive(Debug, Clone)]
78pub struct EraSummary {
79    pub start: EraTime,
80    pub end: Option<EraTime>,
81    pub parameters: EraParameters,
82}
83
84#[derive(Debug, Clone)]
85pub struct EraTime {
86    pub time: Duration,
87    pub slot: u64,
88    pub epoch: u64,
89}
90
91#[derive(Debug, Clone)]
92pub struct EraParameters {
93    pub epoch_length: u64,
94    pub slot_length: u64,
95    pub safe_zone: Option<u64>,
96}
97
98/// A subset of Cardano protocol parameters, only handling values that we use for transaction
99/// building
100#[derive(Debug, Clone)]
101pub struct ProtocolParameters {
102    pub min_fee_coefficient: csl::Coin,
103    pub min_fee_constant: csl::Coin,
104    pub min_fee_reference_scripts: Option<csl::UnitInterval>,
105    pub min_utxo_deposit_coefficient: csl::Coin,
106    pub min_utxo_deposit_constant: csl::Coin,
107    // pub max_block_body_size: Bytes,
108    // pub max_block_header_size: Bytes,
109    pub max_transaction_size: Option<u32>,
110    pub max_value_size: Option<u32>,
111    // pub extra_entropy: Option<Nonce>,
112    pub stake_credential_deposit: csl::Coin,
113    pub stake_pool_deposit: csl::Coin,
114    // pub stake_pool_retirement_epoch_bound: u64,
115    // pub stake_pool_pledge_influence: Ratio,
116    // pub min_stake_pool_cost: LovelaceOnly,
117    // pub desired_number_of_stake_pools: u64,
118    // pub federated_block_production_ratio: Option<Ratio>,
119    // pub monetary_expansion: Ratio,
120    // pub treasury_expansion: Ratio,
121    // pub collateral_percentage: Option<u64>,
122    // pub max_collateral_inputs: Option<u64>,
123    pub plutus_cost_models: Option<csl::Costmdls>,
124    pub script_execution_prices: Option<csl::ExUnitPrices>,
125    // pub max_execution_units_per_transaction: Option<ExecutionUnits>,
126    // pub max_execution_units_per_block: Option<ExecutionUnits>,
127    // pub max_reference_scripts_size: Bytes,
128    // pub stake_pool_voting_thresholds: Option<StakePoolVotingThresholds>,
129    // pub constitutional_committee_min_size: Option<u64>,
130    // pub constitutional_committee_max_term_length: Option<u64>,
131    // pub governance_action_lifetime: Option<Epoch>,
132    // pub governance_action_deposit: Option<LovelaceOnly>,
133    // pub delegate_representative_voting_thresholds: Option<DelegateRepresentativeVotingThresholds>,
134    // pub delegate_representative_deposit: Option<LovelaceOnly>,
135    // pub delegate_representative_max_idle_time: Option<Epoch>,
136    // pub version: ProtocolVersion,
137    //
138}
139
140#[derive(Debug, Clone)]
141pub enum ChainTip {
142    Origin,
143    Point { slot: u64, id: String },
144}
145
146impl ChainTip {
147    pub fn slot(&self) -> u64 {
148        match self {
149            ChainTip::Origin => 0,
150            ChainTip::Point { slot, id: _ } => *slot,
151        }
152    }
153}
154
155#[derive(Debug, Clone)]
156pub struct FullTransactionOutput {
157    pub address: Address,
158    pub value: Value,
159    pub datum: OutputDatum,
160    pub reference_script: Option<Script>,
161}
162
163impl From<FullTransactionOutput> for TransactionOutput {
164    fn from(full_tx_out: FullTransactionOutput) -> TransactionOutput {
165        TransactionOutput {
166            address: full_tx_out.address,
167            value: full_tx_out.value,
168            datum: full_tx_out.datum,
169            reference_script: full_tx_out.reference_script.map(|script| match script {
170                Script::PlutusScript(script) => script.hash().to_pla(),
171                Script::NativeScript(script) => script.hash().to_pla(),
172            }),
173        }
174    }
175}
176
177impl From<&FullTransactionOutput> for TransactionOutput {
178    fn from(full_tx_out: &FullTransactionOutput) -> TransactionOutput {
179        full_tx_out.clone().into()
180    }
181}