Skip to main content

tempo_node/rpc/consensus/
types.rs

1//! RPC types for the consensus namespace.
2
3use alloy_primitives::B256;
4use futures::Future;
5use serde::{Deserialize, Serialize};
6use tempo_alloy::rpc::TempoHeaderResponse;
7use tempo_primitives::Block;
8use tokio::sync::broadcast;
9
10/// A block with a threshold BLS certificate (notarization or finalization).
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
12#[serde(rename_all = "camelCase")]
13pub struct CertifiedBlock {
14    pub epoch: u64,
15    pub view: u64,
16    pub digest: B256,
17
18    /// Hex-encoded full notarization or finalization.
19    pub certificate: String,
20
21    /// The Tempo block.
22    pub block: Block,
23}
24
25/// Consensus event emitted.
26#[derive(Clone, Debug, Serialize, Deserialize)]
27#[serde(tag = "type", rename_all = "camelCase")]
28pub enum Event {
29    /// A block was notarized.
30    Notarized {
31        #[serde(flatten)]
32        block: CertifiedBlock,
33        /// Unix timestamp in milliseconds when this event was observed.
34        seen: u64,
35    },
36    /// A block was finalized.
37    Finalized {
38        #[serde(flatten)]
39        block: CertifiedBlock,
40        /// Unix timestamp in milliseconds when this event was observed.
41        seen: u64,
42    },
43    /// A view was nullified.
44    Nullified {
45        epoch: u64,
46        view: u64,
47        /// Unix timestamp in milliseconds when this event was observed.
48        seen: u64,
49    },
50}
51
52/// Query for consensus data.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub enum Query {
56    /// Get the latest item.
57    Latest,
58    /// Get by block height.
59    Height(u64),
60}
61
62/// Response for get_latest - current consensus state snapshot.
63#[derive(Clone, Debug, Default, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct ConsensusState {
66    /// The latest finalized block (if any).
67    pub finalized: Option<CertifiedBlock>,
68    /// The latest notarized block (if any, and not yet finalized).
69    pub notarized: Option<CertifiedBlock>,
70}
71
72/// Error type for identity transition proof requests.
73#[derive(Clone, Debug, thiserror::Error)]
74pub enum IdentityProofError {
75    /// Node is not ready - consensus state not yet initialized.
76    #[error("node not ready")]
77    NotReady,
78    /// Block data has been pruned.
79    #[error("block data pruned at height {0}")]
80    PrunedData(u64),
81    /// Failed to decode DKG outcome from block.
82    #[error("malformed DKG outcome at height {0}")]
83    MalformedData(u64),
84}
85
86/// Response containing identity transition proofs.
87///
88/// Each transition represents a full DKG ceremony where the network's
89/// BLS public key changed. The proof demonstrates that the old network
90/// identity endorsed the new identity.
91#[derive(Clone, Debug, Serialize, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub struct IdentityTransitionResponse {
94    /// Network identity of the requested epoch.
95    pub identity: String,
96    /// List of identity transitions, ordered newest to oldest.
97    /// Empty if no full DKG ceremonies have occurred.
98    pub transitions: Vec<IdentityTransition>,
99}
100
101/// A single identity transition (full DKG event).
102///
103/// This proves that the network transitioned from `old_identity` to
104/// `new_identity` at the given epoch, with a certificate signed by
105/// the old network identity.
106///
107/// For genesis (epoch 0), `proof` will be `None` since there is no
108/// finalization certificate for the genesis block.
109#[derive(Clone, Debug, Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct IdentityTransition {
112    /// Epoch where the full DKG ceremony occurred.
113    pub transition_epoch: u64,
114    /// Hex-encoded BLS public key before the transition.
115    pub old_identity: String,
116    /// Hex-encoded BLS public key after the transition.
117    pub new_identity: String,
118    /// Proof of the transition. `None` for genesis identity (epoch 0).
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub proof: Option<TransitionProofData>,
121}
122
123/// Cryptographic proof data for an identity transition.
124#[derive(Clone, Debug, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct TransitionProofData {
127    /// The block header containing the new DKG outcome in extra_data.
128    pub header: TempoHeaderResponse,
129    /// Hex-encoded finalization certificate.
130    pub finalization_certificate: String,
131}
132
133/// Trait for accessing consensus feed data.
134pub trait ConsensusFeed: Send + Sync + 'static {
135    /// Get a finalization by query (supports `Latest` or `Height`).
136    fn get_finalization(&self, query: Query)
137    -> impl Future<Output = Option<CertifiedBlock>> + Send;
138
139    /// Get the current consensus state (latest finalized + latest notarized).
140    fn get_latest(&self) -> impl Future<Output = ConsensusState> + Send;
141
142    /// Subscribe to consensus events.
143    fn subscribe(&self) -> impl Future<Output = Option<broadcast::Receiver<Event>>> + Send;
144
145    /// Get identity transition proofs (full DKG events where network public key changed).
146    ///
147    /// - `from_epoch`: Optional epoch to start searching from (defaults to latest finalized)
148    /// - `full`: If true, return all transitions back to genesis; if false, return only the most recent
149    fn get_identity_transition_proof(
150        &self,
151        from_epoch: Option<u64>,
152        full: bool,
153    ) -> impl Future<Output = Result<IdentityTransitionResponse, IdentityProofError>> + Send;
154}