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}