Skip to main content

linera_rpc/
message.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use linera_base::{
6    crypto::CryptoHash,
7    data_types::{BlobContent, BlockHeight, NetworkDescription},
8    identifiers::{BlobId, ChainId, EventId},
9};
10use linera_chain::{
11    data_types::{BlockProposal, LiteVote},
12    types::{ConfirmedBlock, ConfirmedBlockCertificate},
13};
14use linera_core::{
15    data_types::{ChainInfoQuery, ChainInfoResponse, CrossChainRequest},
16    node::NodeError,
17    worker::Notification,
18};
19use linera_version::VersionInfo;
20use serde::{Deserialize, Serialize};
21
22use crate::{
23    config::ShardId, HandleConfirmedCertificateRequest, HandleLiteCertRequest,
24    HandleTimeoutCertificateRequest, HandleValidatedCertificateRequest,
25};
26
27/// Information about shard configuration for a specific chain.
28#[derive(Clone, Serialize, Deserialize, Debug)]
29#[cfg_attr(with_testing, derive(Eq, PartialEq))]
30pub struct ShardInfo {
31    /// The ID of the shard assigned to the chain.
32    pub shard_id: ShardId,
33    /// The total number of shards in the validator network.
34    pub total_shards: usize,
35}
36
37/// An RPC message exchanged between clients, proxies and validators.
38#[derive(Clone, Serialize, Deserialize, Debug)]
39#[cfg_attr(with_testing, derive(Eq, PartialEq))]
40#[allow(missing_docs)]
41pub enum RpcMessage {
42    // Inbound
43    BlockProposal(Box<BlockProposal>),
44    TimeoutCertificate(Box<HandleTimeoutCertificateRequest>),
45    ValidatedCertificate(Box<HandleValidatedCertificateRequest>),
46    ConfirmedCertificate(Box<HandleConfirmedCertificateRequest>),
47    LiteCertificate(Box<HandleLiteCertRequest<'static>>),
48    ChainInfoQuery(Box<ChainInfoQuery>),
49    UploadBlob(Box<BlobContent>),
50    DownloadBlob(Box<BlobId>),
51    DownloadBlobs(Vec<BlobId>),
52    DownloadPendingBlob(Box<(ChainId, BlobId)>),
53    HandlePendingBlob(Box<(ChainId, BlobContent)>),
54    DownloadConfirmedBlock(Box<CryptoHash>),
55    DownloadCertificates(Vec<CryptoHash>),
56    DownloadCertificatesByHeights(ChainId, Vec<BlockHeight>),
57    BlobLastUsedBy(Box<BlobId>),
58    MissingBlobIds(Vec<BlobId>),
59    EventBlockHeights(Vec<EventId>),
60    VersionInfoQuery,
61    NetworkDescriptionQuery,
62
63    // Outbound
64    Vote(Box<LiteVote>),
65    ChainInfoResponse(Box<ChainInfoResponse>),
66    Error(Box<NodeError>),
67    VersionInfoResponse(Box<VersionInfo>),
68    NetworkDescriptionResponse(Box<NetworkDescription>),
69    UploadBlobResponse(Box<BlobId>),
70    DownloadBlobResponse(Box<BlobContent>),
71    DownloadPendingBlobResponse(Box<BlobContent>),
72    DownloadConfirmedBlockResponse(Box<ConfirmedBlock>),
73    DownloadCertificatesResponse(Vec<ConfirmedBlockCertificate>),
74    DownloadCertificatesByHeightsResponse(Vec<ConfirmedBlockCertificate>),
75    BlobLastUsedByResponse(Box<CryptoHash>),
76    MissingBlobIdsResponse(Vec<BlobId>),
77    EventBlockHeightsResponse(Vec<Option<BlockHeight>>),
78
79    // Internal to a validator
80    CrossChainRequest(Box<CrossChainRequest>),
81
82    BlobLastUsedByCertificate(Box<BlobId>),
83    BlobLastUsedByCertificateResponse(Box<ConfirmedBlockCertificate>),
84    ShardInfoQuery(ChainId),
85    ShardInfoResponse(ShardInfo),
86
87    // Notification subscription
88    SubscribeNotifications(Vec<ChainId>),
89    Notification(Box<Notification>),
90}
91
92impl RpcMessage {
93    /// Obtains the [`ChainId`] of the chain targeted by this message, if there is one.
94    ///
95    /// Only inbound messages have target chains.
96    pub fn target_chain_id(&self) -> Option<ChainId> {
97        use RpcMessage::*;
98
99        let chain_id = match self {
100            BlockProposal(proposal) => proposal.content.block.chain_id,
101            LiteCertificate(request) => request.certificate.value.chain_id,
102            TimeoutCertificate(request) => request.certificate.inner().chain_id(),
103            ValidatedCertificate(request) => request.certificate.inner().chain_id(),
104            ConfirmedCertificate(request) => request.certificate.inner().chain_id(),
105            ChainInfoQuery(query) => query.chain_id,
106            CrossChainRequest(request) => request.target_chain_id(),
107            DownloadPendingBlob(request) => request.0,
108            DownloadCertificatesByHeights(chain_id, _) => *chain_id,
109            HandlePendingBlob(request) => request.0,
110            ShardInfoQuery(chain_id) => *chain_id,
111            Vote(_)
112            | Error(_)
113            | ChainInfoResponse(_)
114            | VersionInfoQuery
115            | VersionInfoResponse(_)
116            | NetworkDescriptionQuery
117            | NetworkDescriptionResponse(_)
118            | UploadBlob(_)
119            | UploadBlobResponse(_)
120            | DownloadBlob(_)
121            | DownloadBlobs(_)
122            | DownloadBlobResponse(_)
123            | DownloadPendingBlobResponse(_)
124            | DownloadConfirmedBlock(_)
125            | DownloadConfirmedBlockResponse(_)
126            | DownloadCertificatesByHeightsResponse(_)
127            | DownloadCertificates(_)
128            | BlobLastUsedBy(_)
129            | BlobLastUsedByResponse(_)
130            | BlobLastUsedByCertificate(_)
131            | BlobLastUsedByCertificateResponse(_)
132            | MissingBlobIds(_)
133            | MissingBlobIdsResponse(_)
134            | EventBlockHeights(_)
135            | EventBlockHeightsResponse(_)
136            | ShardInfoResponse(_)
137            | DownloadCertificatesResponse(_)
138            | SubscribeNotifications(_)
139            | Notification(_) => {
140                return None;
141            }
142        };
143
144        Some(chain_id)
145    }
146
147    /// Whether this message is "local" i.e. will be executed locally on the proxy
148    /// or if it'll be proxied to the server.
149    pub fn is_local_message(&self) -> bool {
150        use RpcMessage::*;
151
152        match self {
153            VersionInfoQuery
154            | NetworkDescriptionQuery
155            | ShardInfoQuery(_)
156            | UploadBlob(_)
157            | DownloadBlob(_)
158            | DownloadBlobs(_)
159            | DownloadConfirmedBlock(_)
160            | BlobLastUsedBy(_)
161            | BlobLastUsedByCertificate(_)
162            | MissingBlobIds(_)
163            | EventBlockHeights(_)
164            | DownloadCertificates(_)
165            | DownloadCertificatesByHeights(_, _) => true,
166            BlockProposal(_)
167            | LiteCertificate(_)
168            | TimeoutCertificate(_)
169            | ValidatedCertificate(_)
170            | ConfirmedCertificate(_)
171            | ChainInfoQuery(_)
172            | CrossChainRequest(_)
173            | Vote(_)
174            | Error(_)
175            | ChainInfoResponse(_)
176            | VersionInfoResponse(_)
177            | NetworkDescriptionResponse(_)
178            | ShardInfoResponse(_)
179            | UploadBlobResponse(_)
180            | DownloadPendingBlob(_)
181            | DownloadPendingBlobResponse(_)
182            | HandlePendingBlob(_)
183            | DownloadBlobResponse(_)
184            | DownloadConfirmedBlockResponse(_)
185            | BlobLastUsedByResponse(_)
186            | BlobLastUsedByCertificateResponse(_)
187            | MissingBlobIdsResponse(_)
188            | EventBlockHeightsResponse(_)
189            | DownloadCertificatesResponse(_)
190            | DownloadCertificatesByHeightsResponse(_)
191            | SubscribeNotifications(_)
192            | Notification(_) => false,
193        }
194    }
195}
196
197impl TryFrom<RpcMessage> for ChainInfoResponse {
198    type Error = NodeError;
199    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
200        match message {
201            RpcMessage::ChainInfoResponse(response) => Ok(*response),
202            RpcMessage::Error(error) => Err(*error),
203            _ => Err(NodeError::UnexpectedMessage),
204        }
205    }
206}
207
208impl TryFrom<RpcMessage> for VersionInfo {
209    type Error = NodeError;
210    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
211        match message {
212            RpcMessage::VersionInfoResponse(version_info) => Ok(*version_info),
213            RpcMessage::Error(error) => Err(*error),
214            _ => Err(NodeError::UnexpectedMessage),
215        }
216    }
217}
218
219impl TryFrom<RpcMessage> for BlobContent {
220    type Error = NodeError;
221    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
222        match message {
223            RpcMessage::DownloadBlobResponse(blob)
224            | RpcMessage::DownloadPendingBlobResponse(blob) => Ok(*blob),
225            RpcMessage::Error(error) => Err(*error),
226            _ => Err(NodeError::UnexpectedMessage),
227        }
228    }
229}
230
231impl TryFrom<RpcMessage> for ConfirmedBlock {
232    type Error = NodeError;
233    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
234        match message {
235            RpcMessage::DownloadConfirmedBlockResponse(certificate) => Ok(*certificate),
236            RpcMessage::Error(error) => Err(*error),
237            _ => Err(NodeError::UnexpectedMessage),
238        }
239    }
240}
241
242impl TryFrom<RpcMessage> for ConfirmedBlockCertificate {
243    type Error = NodeError;
244    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
245        match message {
246            RpcMessage::BlobLastUsedByCertificateResponse(certificate) => Ok(*certificate),
247            RpcMessage::Error(error) => Err(*error),
248            _ => Err(NodeError::UnexpectedMessage),
249        }
250    }
251}
252
253impl TryFrom<RpcMessage> for Vec<ConfirmedBlockCertificate> {
254    type Error = NodeError;
255    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
256        match message {
257            RpcMessage::DownloadCertificatesResponse(certificates) => Ok(certificates),
258            RpcMessage::DownloadCertificatesByHeightsResponse(certificates) => Ok(certificates),
259            RpcMessage::Error(error) => Err(*error),
260            _ => Err(NodeError::UnexpectedMessage),
261        }
262    }
263}
264
265impl TryFrom<RpcMessage> for CryptoHash {
266    type Error = NodeError;
267    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
268        match message {
269            RpcMessage::BlobLastUsedByResponse(hash) => Ok(*hash),
270            RpcMessage::Error(error) => Err(*error),
271            _ => Err(NodeError::UnexpectedMessage),
272        }
273    }
274}
275
276impl TryFrom<RpcMessage> for NetworkDescription {
277    type Error = NodeError;
278    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
279        match message {
280            RpcMessage::NetworkDescriptionResponse(description) => Ok(*description),
281            _ => Err(NodeError::UnexpectedMessage),
282        }
283    }
284}
285
286impl TryFrom<RpcMessage> for Vec<Option<BlockHeight>> {
287    type Error = NodeError;
288    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
289        match message {
290            RpcMessage::EventBlockHeightsResponse(heights) => Ok(heights),
291            RpcMessage::Error(error) => Err(*error),
292            _ => Err(NodeError::UnexpectedMessage),
293        }
294    }
295}
296
297impl TryFrom<RpcMessage> for Vec<BlobId> {
298    type Error = NodeError;
299    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
300        match message {
301            RpcMessage::MissingBlobIdsResponse(blob_ids) => Ok(blob_ids),
302            RpcMessage::Error(error) => Err(*error),
303            _ => Err(NodeError::UnexpectedMessage),
304        }
305    }
306}
307
308impl TryFrom<RpcMessage> for BlobId {
309    type Error = NodeError;
310    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
311        match message {
312            RpcMessage::UploadBlobResponse(blob_id) => Ok(*blob_id),
313            RpcMessage::Error(error) => Err(*error),
314            _ => Err(NodeError::UnexpectedMessage),
315        }
316    }
317}
318
319impl TryFrom<RpcMessage> for ShardInfo {
320    type Error = NodeError;
321    fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
322        match message {
323            RpcMessage::ShardInfoResponse(shard_info) => Ok(shard_info),
324            RpcMessage::Error(error) => Err(*error),
325            _ => Err(NodeError::UnexpectedMessage),
326        }
327    }
328}
329
330impl From<NodeError> for RpcMessage {
331    fn from(error: NodeError) -> Self {
332        RpcMessage::Error(Box::new(error))
333    }
334}