Skip to content

Commit 57014c1

Browse files
authored
Fetch certificate last used the blob (#4420)
## Motivation Continuation of the work on improving performance of clients. Trying to decrease # of roundtrips between clients and validators. ## Proposal It was noted that we often make two requests to download a certificate for blob: 1. `BlobLastUsedBy` – returning a certificate hash 2. `DownloadCertificate` – returning the certificate. Here we introduce a new endpoint – `BlobLastUsedByCertificate(blob_id) -> ConfirmedCertifiate` – which does both things in one query. Note that the new endpoint is introduced in proxy only. This decreases number of necessary network roundtrips from 9 to 6: <img width="314" height="264" alt="Screenshot 2025-08-27 at 12 07 08" src="https://github.com/user-attachments/assets/1e6bc42c-276b-45a1-bf77-b6a304c57b17" /> ## Test Plan CI ## Release Plan - Nothing to do / These changes follow the usual release cycle. **OR** - These changes should be backported to the latest `devnet` branch, then - be released in a new SDK, - be released in a validator hotfix. - These changes should be backported to the latest `testnet` branch, then - be released in a new SDK, - be released in a validator hotfix. ## Links <!-- Optional section for related PRs, related issues, and other references. If needed, please create issues to track future improvements and link them here. --> - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 668646d commit 57014c1

File tree

14 files changed

+135
-2
lines changed

14 files changed

+135
-2
lines changed

linera-core/src/node.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ pub trait ValidatorNode {
155155
/// Returns the hash of the `Certificate` that last used a blob.
156156
async fn blob_last_used_by(&self, blob_id: BlobId) -> Result<CryptoHash, NodeError>;
157157

158+
/// Returns the certificate that last used the blob.
159+
async fn blob_last_used_by_certificate(
160+
&self,
161+
blob_id: BlobId,
162+
) -> Result<ConfirmedBlockCertificate, NodeError>;
163+
158164
/// Returns the missing `Blob`s by their IDs.
159165
async fn missing_blob_ids(&self, blob_ids: Vec<BlobId>) -> Result<Vec<BlobId>, NodeError>;
160166
}

linera-core/src/remote_node.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,7 @@ impl<N: ValidatorNode> RemoteNode<N> {
184184
&self,
185185
blob_id: BlobId,
186186
) -> Result<ConfirmedBlockCertificate, NodeError> {
187-
let last_used_hash = self.node.blob_last_used_by(blob_id).await?;
188-
let certificate = self.node.download_certificate(last_used_hash).await?;
187+
let certificate = self.node.blob_last_used_by_certificate(blob_id).await?;
189188
if !certificate.block().requires_or_creates_blob(&blob_id) {
190189
warn!(
191190
"Got invalid last used by certificate for blob {} from validator {}",

linera-core/src/unit_tests/test_utils.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,16 @@ where
256256
.await
257257
}
258258

259+
async fn blob_last_used_by_certificate(
260+
&self,
261+
blob_id: BlobId,
262+
) -> Result<ConfirmedBlockCertificate, NodeError> {
263+
self.spawn_and_receive(move |validator, sender| {
264+
validator.do_blob_last_used_by_certificate(blob_id, sender)
265+
})
266+
.await
267+
}
268+
259269
async fn missing_blob_ids(&self, blob_ids: Vec<BlobId>) -> Result<Vec<BlobId>, NodeError> {
260270
self.spawn_and_receive(move |validator, sender| {
261271
validator.do_missing_blob_ids(blob_ids, sender)
@@ -677,6 +687,20 @@ where
677687
sender.send(certificate_hash)
678688
}
679689

690+
async fn do_blob_last_used_by_certificate(
691+
self,
692+
blob_id: BlobId,
693+
sender: oneshot::Sender<Result<ConfirmedBlockCertificate, NodeError>>,
694+
) -> Result<(), Result<ConfirmedBlockCertificate, NodeError>> {
695+
match self.blob_last_used_by(blob_id).await {
696+
Ok(cert_hash) => {
697+
let cert = self.download_certificate(cert_hash).await;
698+
sender.send(cert)
699+
}
700+
Err(err) => sender.send(Err(err)),
701+
}
702+
}
703+
680704
async fn do_missing_blob_ids(
681705
self,
682706
blob_ids: Vec<BlobId>,

linera-rpc/proto/rpc.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ service ValidatorNode {
9292
// Return the hash of the `Certificate` that last used a blob.
9393
rpc BlobLastUsedBy(BlobId) returns (CryptoHash);
9494

95+
// Returns the certificate that last used the blob.
96+
rpc BlobLastUsedByCertificate(BlobId) returns (Certificate);
97+
98+
9599
// Return the `BlobId`s that are not contained as `Blob`.
96100
rpc MissingBlobIds(BlobIds) returns (BlobIds);
97101
}

linera-rpc/src/client.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,20 @@ impl ValidatorNode for Client {
276276
})
277277
}
278278

279+
async fn blob_last_used_by_certificate(
280+
&self,
281+
blob_id: BlobId,
282+
) -> Result<ConfirmedBlockCertificate, NodeError> {
283+
Ok(match self {
284+
Client::Grpc(grpc_client) => grpc_client.blob_last_used_by_certificate(blob_id).await?,
285+
286+
#[cfg(with_simple_network)]
287+
Client::Simple(simple_client) => {
288+
simple_client.blob_last_used_by_certificate(blob_id).await?
289+
}
290+
})
291+
}
292+
279293
async fn missing_blob_ids(&self, blob_ids: Vec<BlobId>) -> Result<Vec<BlobId>, NodeError> {
280294
Ok(match self {
281295
Client::Grpc(grpc_client) => grpc_client.missing_blob_ids(blob_ids).await?,

linera-rpc/src/grpc/client.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ impl ValidatorNode for GrpcClient {
474474
Ok(client_delegate!(self, blob_last_used_by, blob_id)?.try_into()?)
475475
}
476476

477+
#[instrument(target = "grpc_client", skip(self), err(level = Level::WARN), fields(address = self.address))]
478+
async fn blob_last_used_by_certificate(
479+
&self,
480+
blob_id: BlobId,
481+
) -> Result<ConfirmedBlockCertificate, NodeError> {
482+
Ok(client_delegate!(self, blob_last_used_by_certificate, blob_id)?.try_into()?)
483+
}
484+
477485
#[instrument(target = "grpc_client", skip(self), err(level = Level::WARN), fields(address = self.address))]
478486
async fn missing_blob_ids(&self, blob_ids: Vec<BlobId>) -> Result<Vec<BlobId>, NodeError> {
479487
Ok(client_delegate!(self, missing_blob_ids, blob_ids)?.try_into()?)

linera-rpc/src/message.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ pub enum RpcMessage {
6262

6363
// Internal to a validator
6464
CrossChainRequest(Box<CrossChainRequest>),
65+
66+
BlobLastUsedByCertificate(Box<BlobId>),
67+
BlobLastUsedByCertificateResponse(Box<ConfirmedBlockCertificate>),
6568
}
6669

6770
impl RpcMessage {
@@ -100,6 +103,8 @@ impl RpcMessage {
100103
| DownloadCertificates(_)
101104
| BlobLastUsedBy(_)
102105
| BlobLastUsedByResponse(_)
106+
| BlobLastUsedByCertificate(_)
107+
| BlobLastUsedByCertificateResponse(_)
103108
| MissingBlobIds(_)
104109
| MissingBlobIdsResponse(_)
105110
| DownloadCertificatesResponse(_) => {
@@ -122,6 +127,7 @@ impl RpcMessage {
122127
| DownloadBlob(_)
123128
| DownloadConfirmedBlock(_)
124129
| BlobLastUsedBy(_)
130+
| BlobLastUsedByCertificate(_)
125131
| MissingBlobIds(_)
126132
| DownloadCertificates(_)
127133
| DownloadCertificatesByHeights(_, _) => true,
@@ -144,6 +150,7 @@ impl RpcMessage {
144150
| DownloadBlobResponse(_)
145151
| DownloadConfirmedBlockResponse(_)
146152
| BlobLastUsedByResponse(_)
153+
| BlobLastUsedByCertificateResponse(_)
147154
| MissingBlobIdsResponse(_)
148155
| DownloadCertificatesResponse(_)
149156
| DownloadCertificatesByHeightsResponse(_) => false,
@@ -196,6 +203,17 @@ impl TryFrom<RpcMessage> for ConfirmedBlock {
196203
}
197204
}
198205

206+
impl TryFrom<RpcMessage> for ConfirmedBlockCertificate {
207+
type Error = NodeError;
208+
fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {
209+
match message {
210+
RpcMessage::BlobLastUsedByCertificateResponse(certificate) => Ok(*certificate),
211+
RpcMessage::Error(error) => Err(*error),
212+
_ => Err(NodeError::UnexpectedMessage),
213+
}
214+
}
215+
}
216+
199217
impl TryFrom<RpcMessage> for Vec<ConfirmedBlockCertificate> {
200218
type Error = NodeError;
201219
fn try_from(message: RpcMessage) -> Result<Self, Self::Error> {

linera-rpc/src/simple/client.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,16 @@ impl ValidatorNode for SimpleClient {
246246
.await
247247
}
248248

249+
async fn blob_last_used_by_certificate(
250+
&self,
251+
blob_id: BlobId,
252+
) -> Result<ConfirmedBlockCertificate, NodeError> {
253+
self.query::<ConfirmedBlockCertificate>(RpcMessage::BlobLastUsedByCertificate(Box::new(
254+
blob_id,
255+
)))
256+
.await
257+
}
258+
249259
async fn missing_blob_ids(&self, blob_ids: Vec<BlobId>) -> Result<Vec<BlobId>, NodeError> {
250260
self.query(RpcMessage::MissingBlobIds(blob_ids)).await
251261
}

linera-rpc/src/simple/server.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ where
379379
| RpcMessage::DownloadConfirmedBlockResponse(_)
380380
| RpcMessage::BlobLastUsedBy(_)
381381
| RpcMessage::BlobLastUsedByResponse(_)
382+
| RpcMessage::BlobLastUsedByCertificate(_)
383+
| RpcMessage::BlobLastUsedByCertificateResponse(_)
382384
| RpcMessage::MissingBlobIds(_)
383385
| RpcMessage::MissingBlobIdsResponse(_)
384386
| RpcMessage::DownloadCertificates(_)

linera-rpc/tests/snapshots/format__format.yaml.snap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,14 @@ RpcMessage:
11331133
CrossChainRequest:
11341134
NEWTYPE:
11351135
TYPENAME: CrossChainRequest
1136+
31:
1137+
BlobLastUsedByCertificate:
1138+
NEWTYPE:
1139+
TYPENAME: BlobId
1140+
32:
1141+
BlobLastUsedByCertificateResponse:
1142+
NEWTYPE:
1143+
TYPENAME: ConfirmedBlockCertificate
11361144
Secp256k1PublicKey:
11371145
NEWTYPESTRUCT:
11381146
TUPLEARRAY:

0 commit comments

Comments
 (0)