Skip to main content

hyli_model/
api.rs

1use std::collections::BTreeMap;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use serde::{Deserialize, Serialize};
5use serde_with::serde_as;
6use strum::IntoDiscriminant;
7use utoipa::ToSchema;
8
9use crate::{
10    utils::TimestampMs, BlockHash, BlockHeight, ConsensusProposalHash, ContractName,
11    DataProposalHash, Identity, LaneBytesSize, LaneId, ProgramId, StateCommitment, TimeoutWindow,
12    Transaction, TransactionKind, TxHash, ValidatorPublicKey, Verifier,
13};
14
15#[derive(Clone, Serialize, Deserialize, Debug, ToSchema)]
16pub struct NodeInfo {
17    pub id: String,
18    pub pubkey: Option<ValidatorPublicKey>,
19    pub da_address: String,
20}
21
22#[derive(Clone, Serialize, Deserialize, Debug, ToSchema)]
23pub struct NetworkStats {
24    pub total_transactions: i64,           // Total number of transactions
25    pub txs_last_day: i64,                 // Number of transactions in the last day
26    pub total_contracts: i64,              // Total number of contracts
27    pub contracts_last_day: i64,           // Number of contracts in the last day
28    pub graph_tx_volume: Vec<(i64, i64)>,  // Graph data for transactions volume
29    pub graph_block_time: Vec<(i64, f64)>, // Graph data for block time
30    pub peak_txs: (i64, i64),              // Peak transactions per minutes in the last 24 hours
31}
32
33#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
34#[derive(Clone, Serialize, Deserialize, Debug, ToSchema)]
35pub struct ProofStat {
36    pub verifier: String,
37    pub proof_count: i64,
38}
39
40#[derive(Clone, Debug, Default, Serialize, Deserialize, ToSchema)]
41pub struct APIRegisterContract {
42    pub verifier: Verifier,
43    pub program_id: ProgramId,
44    pub state_commitment: StateCommitment,
45    pub contract_name: ContractName,
46    pub timeout_window: Option<(u64, u64)>,
47    pub constructor_metadata: Option<Vec<u8>>,
48}
49
50/// Copy from Staking contract
51#[derive(Debug, Default, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
52pub struct APIStaking {
53    pub stakes: BTreeMap<Identity, u128>,
54    pub delegations: BTreeMap<ValidatorPublicKey, Vec<Identity>>,
55    /// When a validator distribute rewards, it is added in this list to
56    /// avoid distributing twice the rewards for a same block
57    pub rewarded: BTreeMap<ValidatorPublicKey, Vec<BlockHeight>>,
58
59    /// List of validators that are part of consensus
60    pub bonded: Vec<ValidatorPublicKey>,
61    pub total_bond: u128,
62
63    /// Struct to handle fees
64    pub fees: APIFees,
65}
66
67#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
68pub struct APIFeesBalance {
69    pub balance: i128,
70    pub cumul_sizes: BTreeMap<LaneId, LaneBytesSize>,
71}
72
73#[derive(Debug, Default, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
74pub struct APIFees {
75    /// Balance of each validator
76    pub balances: BTreeMap<ValidatorPublicKey, APIFeesBalance>,
77}
78
79#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
80pub struct APIBlock {
81    // Struct for the blocks table
82    pub hash: ConsensusProposalHash,
83    pub parent_hash: ConsensusProposalHash,
84    pub height: u64,    // Corresponds to BlockHeight
85    pub timestamp: i64, // UNIX timestamp
86    pub total_txs: u64, // Total number of transactions in the block
87}
88
89#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
90#[cfg_attr(
91    feature = "sqlx",
92    sqlx(type_name = "transaction_type", rename_all = "snake_case")
93)]
94#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
95pub enum TransactionTypeDb {
96    BlobTransaction,
97    ProofTransaction,
98    RegisterContractTransaction,
99    Stake,
100}
101
102#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
103#[derive(
104    Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize, ToSchema, Clone, PartialEq, Eq,
105)]
106#[cfg_attr(
107    feature = "sqlx",
108    sqlx(type_name = "transaction_status", rename_all = "snake_case")
109)]
110pub enum TransactionStatusDb {
111    WaitingDissemination,
112    DataProposalCreated,
113    Success,
114    Failure,
115    Sequenced,
116    TimedOut,
117}
118
119impl std::str::FromStr for TransactionStatusDb {
120    type Err = String;
121
122    fn from_str(s: &str) -> Result<Self, Self::Err> {
123        match s {
124            "waiting_dissemination" => Ok(TransactionStatusDb::WaitingDissemination),
125            "data_proposal_created" => Ok(TransactionStatusDb::DataProposalCreated),
126            "success" => Ok(TransactionStatusDb::Success),
127            "failure" => Ok(TransactionStatusDb::Failure),
128            "sequenced" => Ok(TransactionStatusDb::Sequenced),
129            "timed_out" => Ok(TransactionStatusDb::TimedOut),
130            _ => Err(format!("Invalid transaction status: {}", s)),
131        }
132    }
133}
134
135impl std::fmt::Display for TransactionStatusDb {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        let str = match self {
138            TransactionStatusDb::WaitingDissemination => "waiting_dissemination",
139            TransactionStatusDb::DataProposalCreated => "data_proposal_created",
140            TransactionStatusDb::Success => "success",
141            TransactionStatusDb::Failure => "failure",
142            TransactionStatusDb::Sequenced => "sequenced",
143            TransactionStatusDb::TimedOut => "timed_out",
144        };
145        write!(f, "{}", str)
146    }
147}
148
149impl TransactionTypeDb {
150    pub fn from(transaction: &Transaction) -> Self {
151        transaction.transaction_data.discriminant().into()
152    }
153}
154
155impl From<TransactionKind> for TransactionTypeDb {
156    fn from(value: TransactionKind) -> Self {
157        match value {
158            TransactionKind::Blob => TransactionTypeDb::BlobTransaction,
159            TransactionKind::Proof => TransactionTypeDb::ProofTransaction,
160            TransactionKind::VerifiedProof => TransactionTypeDb::ProofTransaction,
161        }
162    }
163}
164
165#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
166pub struct APITransaction {
167    // Struct for the transactions table
168    pub tx_hash: TxHash,                           // Transaction hash
169    pub parent_dp_hash: DataProposalHash,          // Data proposal hash
170    pub block_hash: Option<ConsensusProposalHash>, // Corresponds to the block hash
171    pub block_height: Option<BlockHeight>,         // Corresponds to the block height
172    pub index: Option<u32>,                        // Index of the transaction within the block
173    pub version: u32,                              // Transaction version
174    pub transaction_type: TransactionTypeDb,       // Type of transaction
175    pub transaction_status: TransactionStatusDb,   // Status of the transaction
176    pub timestamp: Option<TimestampMs>,            // Timestamp of the transaction (block timestamp)
177    pub lane_id: Option<LaneId>,                   // Lane ID where the transaction got disseminated
178    pub identity: Option<String>,
179}
180
181#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
182pub struct APITransactionEvents {
183    pub block_hash: BlockHash,
184    pub block_height: BlockHeight,
185    pub events: Vec<serde_json::Value>,
186}
187
188#[derive(Serialize, Deserialize, ToSchema, Debug, Clone, PartialEq)]
189pub struct TransactionWithBlobs {
190    // Should match APITransaction
191    pub tx_hash: TxHash,
192    pub parent_dp_hash: DataProposalHash,
193    pub block_hash: ConsensusProposalHash,
194    pub block_height: BlockHeight,
195    pub index: u32,
196    pub version: u32,
197    pub transaction_type: TransactionTypeDb,
198    pub transaction_status: TransactionStatusDb,
199    pub timestamp: Option<TimestampMs>,
200    pub lane_id: Option<LaneId>,
201    pub identity: String,
202    // Additional field for blobs
203    pub blobs: Vec<BlobWithStatus>,
204}
205
206#[derive(Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq)]
207pub struct APIProofDetails {
208    // Should match APITransaction (but without identity)
209    pub tx_hash: TxHash,                           // Transaction hash
210    pub parent_dp_hash: DataProposalHash,          // Data proposal hash
211    pub block_hash: Option<ConsensusProposalHash>, // Corresponds to the block hash
212    pub block_height: Option<BlockHeight>,         // Corresponds to the block height
213    pub index: Option<u32>,                        // Index of the transaction within the block
214    pub version: u32,                              // Transaction version
215    pub transaction_type: TransactionTypeDb,       // Type of transaction
216    pub transaction_status: TransactionStatusDb,   // Status of the transaction
217    pub timestamp: Option<TimestampMs>,            // Timestamp of the transaction (block timestamp)
218    pub lane_id: Option<LaneId>,                   // Lane ID where the transaction got disseminated
219    pub proof_outputs: Vec<(TxHash, u32, u32, serde_json::Value)>,
220}
221
222#[serde_as]
223#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
224pub struct BlobWithStatus {
225    pub contract_name: String, // Contract name associated with the blob
226    #[serde_as(as = "serde_with::hex::Hex")]
227    pub data: Vec<u8>, // Actual blob data
228    pub proof_outputs: Vec<serde_json::Value>, // outputs of proofs
229}
230
231#[serde_as]
232#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
233pub struct APIContract {
234    // Struct for the contracts table
235    pub tx_hash: TxHash,  // Corresponds to the registration transaction hash
236    pub verifier: String, // Verifier of the contract
237    #[serde_as(as = "serde_with::hex::Hex")]
238    pub program_id: Vec<u8>, // Program ID
239    #[serde_as(as = "serde_with::hex::Hex")]
240    pub state_commitment: Vec<u8>, // State commitment of the contract
241    pub contract_name: String, // Contract name
242    pub total_tx: u64,    // Total number of transactions associated with the contract
243    pub unsettled_tx: u64, // Total number of unsettled transactions
244    pub earliest_unsettled: Option<BlockHeight>, // Earliest unsettled transaction block height
245}
246
247#[derive(Debug, Serialize, ToSchema)]
248pub struct APINodeContract {
249    pub contract_name: ContractName,        // Name of the contract
250    pub state_block_height: BlockHeight,    // Block height where the state is captured
251    pub state_commitment: StateCommitment,  // The contract state stored in JSON format
252    pub program_id: ProgramId,              // Program ID of the contract
253    pub verifier: Verifier,                 // Verifier of the contract
254    pub timeout_window: Option<(u64, u64)>, // Timeout window for the contract
255}
256
257impl<'de> Deserialize<'de> for APINodeContract {
258    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
259    where
260        D: serde::Deserializer<'de>,
261    {
262        #[derive(Deserialize)]
263        #[serde(untagged)]
264        enum Helper {
265            Old(crate::Contract),
266            New {
267                contract_name: ContractName,
268                state_block_height: BlockHeight,
269                state_commitment: StateCommitment,
270                program_id: ProgramId,
271                verifier: Verifier,
272                timeout_window: Option<(u64, u64)>,
273            },
274        }
275
276        match Helper::deserialize(deserializer)? {
277            Helper::Old(c) => Ok(APINodeContract {
278                contract_name: c.name,
279                state_block_height: BlockHeight(0),
280                state_commitment: c.state,
281                program_id: c.program_id,
282                verifier: c.verifier,
283                timeout_window: match c.timeout_window {
284                    TimeoutWindow::Timeout {
285                        hard_timeout,
286                        soft_timeout,
287                    } => Some((hard_timeout.0, soft_timeout.0)),
288                    TimeoutWindow::NoTimeout => None,
289                },
290            }),
291            Helper::New {
292                contract_name,
293                state_block_height,
294                state_commitment,
295                program_id,
296                verifier,
297                timeout_window,
298            } => Ok(APINodeContract {
299                contract_name,
300                state_block_height,
301                state_commitment,
302                program_id,
303                verifier,
304                timeout_window,
305            }),
306        }
307    }
308}
309
310#[test]
311fn test_deserialize_api_node_contract() {
312    // Test old format
313    let old_json = serde_json::to_value(&crate::Contract {
314        name: ContractName("my_contract".to_string()),
315        program_id: ProgramId(vec![4, 5, 6]),
316        state: StateCommitment(vec![1, 2, 3]),
317        verifier: Verifier("verifier1".to_string()),
318        timeout_window: TimeoutWindow::timeout(BlockHeight(32), BlockHeight(32)),
319    })
320    .unwrap();
321    let old_contract: APINodeContract = serde_json::from_value(old_json).unwrap();
322    assert_eq!(old_contract.contract_name.0, "my_contract");
323    assert_eq!(old_contract.state_block_height.0, 0); // Default value
324    assert_eq!(old_contract.state_commitment.0, vec![1, 2, 3]);
325    assert_eq!(old_contract.program_id.0, vec![4, 5, 6]);
326    assert_eq!(old_contract.verifier.0, "verifier1");
327    assert_eq!(old_contract.timeout_window, Some((32, 32)));
328    // Test new format
329    let new_json = serde_json::to_value(&APINodeContract {
330        contract_name: ContractName("new_contract".to_string()),
331        state_block_height: BlockHeight(42),
332        state_commitment: StateCommitment(vec![7, 8, 9]),
333        program_id: ProgramId(vec![10, 11, 12]),
334        verifier: Verifier("verifier2".to_string()),
335        timeout_window: Some((123, 123)),
336    })
337    .unwrap();
338    let new_contract: APINodeContract = serde_json::from_value(new_json).unwrap();
339    assert_eq!(new_contract.contract_name.0, "new_contract");
340    assert_eq!(new_contract.state_block_height.0, 42);
341    assert_eq!(new_contract.state_commitment.0, vec![7, 8, 9]);
342    assert_eq!(new_contract.program_id.0, vec![10, 11, 12]);
343    assert_eq!(new_contract.verifier.0, "verifier2");
344    assert_eq!(new_contract.timeout_window, Some((123, 123)));
345}
346
347#[derive(Debug, Serialize, Deserialize, ToSchema)]
348pub struct APIContractState {
349    // Struct for the contract_state table
350    pub contract_name: String,             // Name of the contract
351    pub block_hash: ConsensusProposalHash, // Hash of the block where the state is captured
352    pub state_commitment: Vec<u8>,         // The contract state stored in JSON format
353}
354
355#[serde_as]
356#[derive(Debug, Serialize, Deserialize, ToSchema)]
357pub struct APIBlob {
358    pub tx_hash: TxHash,       // Corresponds to the transaction hash
359    pub blob_index: u32,       // Index of the blob within the transaction
360    pub identity: String,      // Identity of the blob
361    pub contract_name: String, // Contract name associated with the blob
362    #[serde_as(as = "serde_with::hex::Hex")]
363    pub data: Vec<u8>, // Actual blob data
364    pub proof_outputs: Vec<serde_json::Value>, // outputs of proofs
365    pub verified: bool,        // Verification status
366}
367
368#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
369#[cfg_attr(
370    feature = "sqlx",
371    sqlx(type_name = "contract_change_type", rename_all = "snake_case")
372)]
373#[derive(
374    Debug, Serialize, Deserialize, ToSchema, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
375)]
376#[serde(rename_all = "snake_case")]
377pub enum ContractChangeType {
378    Registered,
379    ProgramIdUpdated,
380    StateUpdated,
381    TimeoutUpdated,
382    Deleted,
383}
384
385#[serde_as]
386#[derive(Debug, Serialize, Deserialize, ToSchema, Clone)]
387pub struct APIContractHistory {
388    pub contract_name: String,                // Contract name
389    pub block_height: u64,                    // Block height where the change occurred
390    pub tx_index: i32,                        // Transaction index within the block
391    pub change_type: Vec<ContractChangeType>, // Types of change
392    pub verifier: String,                     // Verifier at this point in time
393    #[serde_as(as = "serde_with::hex::Hex")]
394    pub program_id: Vec<u8>, // Program ID at this point in time
395    #[serde_as(as = "serde_with::hex::Hex")]
396    pub state_commitment: Vec<u8>, // State commitment at this point in time
397    pub soft_timeout: Option<i64>,            // Soft timeout value
398    pub hard_timeout: Option<i64>,            // Hard timeout value
399    pub tx_hash: TxHash,                      // Transaction that caused the change
400    pub parent_dp_hash: DataProposalHash,     // Parent data proposal hash
401}