rust-eigenda-client 0.1.6

EigenDA Client
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
// This file is @generated by prost-build.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AuthenticatedRequest {
    #[prost(oneof = "authenticated_request::Payload", tags = "1, 2")]
    pub payload: ::core::option::Option<authenticated_request::Payload>,
}
/// Nested message and enum types in `AuthenticatedRequest`.
pub mod authenticated_request {
    #[allow(clippy::derive_partial_eq_without_eq)]
    #[derive(Clone, PartialEq, ::prost::Oneof)]
    pub enum Payload {
        #[prost(message, tag = "1")]
        DisperseRequest(super::DisperseBlobRequest),
        #[prost(message, tag = "2")]
        AuthenticationData(super::AuthenticationData),
    }
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AuthenticatedReply {
    #[prost(oneof = "authenticated_reply::Payload", tags = "1, 2")]
    pub payload: ::core::option::Option<authenticated_reply::Payload>,
}
/// Nested message and enum types in `AuthenticatedReply`.
pub mod authenticated_reply {
    #[allow(clippy::derive_partial_eq_without_eq)]
    #[derive(Clone, PartialEq, ::prost::Oneof)]
    pub enum Payload {
        #[prost(message, tag = "1")]
        BlobAuthHeader(super::BlobAuthHeader),
        #[prost(message, tag = "2")]
        DisperseReply(super::DisperseBlobReply),
    }
}
/// BlobAuthHeader contains information about the blob for the client to verify and sign.
/// - Once payments are enabled, the BlobAuthHeader will contain the KZG commitment to the blob, which the client
/// will verify and sign. Having the client verify the KZG commitment instead of calculating it avoids
/// the need for the client to have the KZG structured reference string (SRS), which can be large.
/// The signed KZG commitment prevents the disperser from sending a different blob to the DA Nodes
/// than the one the client sent.
/// - In the meantime, the BlobAuthHeader contains a simple challenge parameter is used to prevent
/// replay attacks in the event that a signature is leaked.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlobAuthHeader {
    #[prost(uint32, tag = "1")]
    pub challenge_parameter: u32,
}
/// AuthenticationData contains the signature of the BlobAuthHeader.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AuthenticationData {
    #[prost(bytes = "vec", tag = "1")]
    pub authentication_data: ::prost::alloc::vec::Vec<u8>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DisperseBlobRequest {
    /// The data to be dispersed.
    /// The size of data must be <= 2MiB. Every 32 bytes of data chunk is interpreted as an integer in big endian format
    /// where the lower address has more significant bits. The integer must stay in the valid range to be interpreted
    /// as a field element on the bn254 curve. The valid range is
    /// 0 <= x < 21888242871839275222246405745257275088548364400416034343698204186575808495617
    /// containing slightly less than 254 bits and more than 253 bits. If any one of the 32 bytes chunk is outside the range,
    /// the whole request is deemed as invalid, and rejected.
    #[prost(bytes = "vec", tag = "1")]
    pub data: ::prost::alloc::vec::Vec<u8>,
    /// The quorums to which the blob will be sent, in addition to the required quorums which are configured
    /// on the EigenDA smart contract. If required quorums are included here, an error will be returned.
    /// The disperser will ensure that the encoded blobs for each quorum are all processed
    /// within the same batch.
    #[prost(uint32, repeated, tag = "2")]
    pub custom_quorum_numbers: ::prost::alloc::vec::Vec<u32>,
    /// The account ID of the client. This should be a hex-encoded string of the ECSDA public key
    /// corresponding to the key used by the client to sign the BlobAuthHeader.
    #[prost(string, tag = "3")]
    pub account_id: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DisperseBlobReply {
    /// The status of the blob associated with the request_id.
    #[prost(enumeration = "BlobStatus", tag = "1")]
    pub result: i32,
    /// The request ID generated by the disperser.
    /// Once a request is accepted (although not processed), a unique request ID will be
    /// generated.
    /// Two different DisperseBlobRequests (determined by the hash of the DisperseBlobRequest)
    /// will have different IDs, and the same DisperseBlobRequest sent repeatedly at different
    /// times will also have different IDs.
    /// The client should use this ID to query the processing status of the request (via
    /// the GetBlobStatus API).
    #[prost(bytes = "vec", tag = "2")]
    pub request_id: ::prost::alloc::vec::Vec<u8>,
}
/// BlobStatusRequest is used to query the status of a blob.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlobStatusRequest {
    #[prost(bytes = "vec", tag = "1")]
    pub request_id: ::prost::alloc::vec::Vec<u8>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlobStatusReply {
    /// The status of the blob.
    #[prost(enumeration = "BlobStatus", tag = "1")]
    pub status: i32,
    /// The blob info needed for clients to confirm the blob against the EigenDA contracts.
    #[prost(message, optional, tag = "2")]
    pub info: ::core::option::Option<BlobInfo>,
}
/// RetrieveBlobRequest contains parameters to retrieve the blob.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RetrieveBlobRequest {
    #[prost(bytes = "vec", tag = "1")]
    pub batch_header_hash: ::prost::alloc::vec::Vec<u8>,
    #[prost(uint32, tag = "2")]
    pub blob_index: u32,
}
/// RetrieveBlobReply contains the retrieved blob data
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RetrieveBlobReply {
    #[prost(bytes = "vec", tag = "1")]
    pub data: ::prost::alloc::vec::Vec<u8>,
}
/// BlobInfo contains information needed to confirm the blob against the EigenDA contracts
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlobInfo {
    #[prost(message, optional, tag = "1")]
    pub blob_header: ::core::option::Option<BlobHeader>,
    #[prost(message, optional, tag = "2")]
    pub blob_verification_proof: ::core::option::Option<BlobVerificationProof>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlobHeader {
    /// KZG commitment of the blob.
    #[prost(message, optional, tag = "1")]
    pub commitment: ::core::option::Option<super::common::G1Commitment>,
    /// The length of the blob in symbols (each symbol is 32 bytes).
    #[prost(uint32, tag = "2")]
    pub data_length: u32,
    /// The params of the quorums that this blob participates in.
    #[prost(message, repeated, tag = "3")]
    pub blob_quorum_params: ::prost::alloc::vec::Vec<BlobQuorumParam>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlobQuorumParam {
    /// The ID of the quorum.
    #[prost(uint32, tag = "1")]
    pub quorum_number: u32,
    /// The max percentage of stake within the quorum that can be held by or delegated
    /// to adversarial operators. Currently, this and the next parameter are standardized
    /// across the quorum using values read from the EigenDA contracts.
    #[prost(uint32, tag = "2")]
    pub adversary_threshold_percentage: u32,
    /// The min percentage of stake that must attest in order to consider
    /// the dispersal is successful.
    #[prost(uint32, tag = "3")]
    pub confirmation_threshold_percentage: u32,
    /// The length of each chunk.
    #[prost(uint32, tag = "4")]
    pub chunk_length: u32,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlobVerificationProof {
    /// batch_id is an incremental ID assigned to a batch by EigenDAServiceManager
    #[prost(uint32, tag = "1")]
    pub batch_id: u32,
    /// The index of the blob in the batch (which is logically an ordered list of blobs).
    #[prost(uint32, tag = "2")]
    pub blob_index: u32,
    #[prost(message, optional, tag = "3")]
    pub batch_metadata: ::core::option::Option<BatchMetadata>,
    /// inclusion_proof is a merkle proof for a blob header's inclusion in a batch
    #[prost(bytes = "vec", tag = "4")]
    pub inclusion_proof: ::prost::alloc::vec::Vec<u8>,
    /// indexes of quorums in BatchHeader.quorum_numbers that match the quorums in BlobHeader.blob_quorum_params
    /// Ex. BlobHeader.blob_quorum_params = [
    /// 	{
    /// 		quorum_number = 0,
    /// 		...
    /// 	},
    /// 	{
    /// 		quorum_number = 3,
    /// 		...
    /// 	},
    /// 	{
    /// 		quorum_number = 5,
    /// 		...
    /// 	},
    /// ]
    /// BatchHeader.quorum_numbers = \[0, 5, 3\] => 0x000503
    /// Then, quorum_indexes = \[0, 2, 1\] => 0x000201
    #[prost(bytes = "vec", tag = "5")]
    pub quorum_indexes: ::prost::alloc::vec::Vec<u8>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BatchMetadata {
    #[prost(message, optional, tag = "1")]
    pub batch_header: ::core::option::Option<BatchHeader>,
    /// The hash of all public keys of the operators that did not sign the batch.
    #[prost(bytes = "vec", tag = "2")]
    pub signatory_record_hash: ::prost::alloc::vec::Vec<u8>,
    /// The fee payment paid by users for dispersing this batch. It's the bytes
    /// representation of a big.Int value.
    #[prost(bytes = "vec", tag = "3")]
    pub fee: ::prost::alloc::vec::Vec<u8>,
    /// The Ethereum block number at which the batch is confirmed onchain.
    #[prost(uint32, tag = "4")]
    pub confirmation_block_number: u32,
    /// This is the hash of the ReducedBatchHeader defined onchain, see:
    /// <https://github.com/Layr-Labs/eigenda/blob/master/contracts/src/interfaces/IEigenDAServiceManager.sol#L43>
    /// The is the message that the operators will sign their signatures on.
    #[prost(bytes = "vec", tag = "5")]
    pub batch_header_hash: ::prost::alloc::vec::Vec<u8>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BatchHeader {
    /// The root of the merkle tree with the hashes of blob headers as leaves.
    #[prost(bytes = "vec", tag = "1")]
    pub batch_root: ::prost::alloc::vec::Vec<u8>,
    /// All quorums associated with blobs in this batch. Sorted in ascending order.
    /// Ex. \[0, 2, 1\] => 0x000102
    #[prost(bytes = "vec", tag = "2")]
    pub quorum_numbers: ::prost::alloc::vec::Vec<u8>,
    /// The percentage of stake that has signed for this batch.
    /// The quorum_signed_percentages\[i\] is percentage for the quorum_numbers\[i\].
    #[prost(bytes = "vec", tag = "3")]
    pub quorum_signed_percentages: ::prost::alloc::vec::Vec<u8>,
    /// The Ethereum block number at which the batch was created.
    /// The Disperser will encode and disperse the blobs based on the onchain info
    /// (e.g. operator stakes) at this block number.
    #[prost(uint32, tag = "4")]
    pub reference_block_number: u32,
}
/// BlobStatus represents the status of a blob.
/// The status of a blob is updated as the blob is processed by the disperser.
/// The status of a blob can be queried by the client using the GetBlobStatus API.
/// Intermediate states are states that the blob can be in while being processed, and it can be updated to a differet state:
/// - PROCESSING
/// - DISPERSING
/// - CONFIRMED
/// Terminal states are states that will not be updated to a different state:
/// - FAILED
/// - FINALIZED
/// - INSUFFICIENT_SIGNATURES
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum BlobStatus {
    Unknown = 0,
    /// PROCESSING means that the blob is currently being processed by the disperser
    Processing = 1,
    /// CONFIRMED means that the blob has been dispersed to DA Nodes and the dispersed
    /// batch containing the blob has been confirmed onchain
    Confirmed = 2,
    /// FAILED means that the blob has failed permanently (for reasons other than insufficient
    /// signatures, which is a separate state)
    Failed = 3,
    /// FINALIZED means that the block containing the blob's confirmation transaction has been finalized on Ethereum
    Finalized = 4,
    /// INSUFFICIENT_SIGNATURES means that the confirmation threshold for the blob was not met
    /// for at least one quorum.
    InsufficientSignatures = 5,
    /// DISPERSING means that the blob is currently being dispersed to DA Nodes and being confirmed onchain
    Dispersing = 6,
}
impl BlobStatus {
    /// String value of the enum field names used in the ProtoBuf definition.
    ///
    /// The values are not transformed in any way and thus are considered stable
    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
    pub fn as_str_name(&self) -> &'static str {
        match self {
            BlobStatus::Unknown => "UNKNOWN",
            BlobStatus::Processing => "PROCESSING",
            BlobStatus::Confirmed => "CONFIRMED",
            BlobStatus::Failed => "FAILED",
            BlobStatus::Finalized => "FINALIZED",
            BlobStatus::InsufficientSignatures => "INSUFFICIENT_SIGNATURES",
            BlobStatus::Dispersing => "DISPERSING",
        }
    }
    /// Creates an enum from field names used in the ProtoBuf definition.
    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
        match value {
            "UNKNOWN" => Some(Self::Unknown),
            "PROCESSING" => Some(Self::Processing),
            "CONFIRMED" => Some(Self::Confirmed),
            "FAILED" => Some(Self::Failed),
            "FINALIZED" => Some(Self::Finalized),
            "INSUFFICIENT_SIGNATURES" => Some(Self::InsufficientSignatures),
            "DISPERSING" => Some(Self::Dispersing),
            _ => None,
        }
    }
}
/// Generated client implementations.
pub mod disperser_client {
    #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
    use tonic::codegen::*;
    use tonic::codegen::http::Uri;
    /// Disperser defines the public APIs for dispersing blobs.
    #[derive(Debug, Clone)]
    pub struct DisperserClient<T> {
        inner: tonic::client::Grpc<T>,
    }
    impl DisperserClient<tonic::transport::Channel> {
        /// Attempt to create a new client by connecting to a given endpoint.
        pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
        where
            D: TryInto<tonic::transport::Endpoint>,
            D::Error: Into<StdError>,
        {
            let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
            Ok(Self::new(conn))
        }
    }
    impl<T> DisperserClient<T>
    where
        T: tonic::client::GrpcService<tonic::body::BoxBody>,
        T::Error: Into<StdError>,
        T::ResponseBody: Body<Data = Bytes> + Send + 'static,
        <T::ResponseBody as Body>::Error: Into<StdError> + Send,
    {
        pub fn new(inner: T) -> Self {
            let inner = tonic::client::Grpc::new(inner);
            Self { inner }
        }
        pub fn with_origin(inner: T, origin: Uri) -> Self {
            let inner = tonic::client::Grpc::with_origin(inner, origin);
            Self { inner }
        }
        pub fn with_interceptor<F>(
            inner: T,
            interceptor: F,
        ) -> DisperserClient<InterceptedService<T, F>>
        where
            F: tonic::service::Interceptor,
            T::ResponseBody: Default,
            T: tonic::codegen::Service<
                http::Request<tonic::body::BoxBody>,
                Response = http::Response<
                    <T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
                >,
            >,
            <T as tonic::codegen::Service<
                http::Request<tonic::body::BoxBody>,
            >>::Error: Into<StdError> + Send + Sync,
        {
            DisperserClient::new(InterceptedService::new(inner, interceptor))
        }
        /// Compress requests with the given encoding.
        ///
        /// This requires the server to support it otherwise it might respond with an
        /// error.
        #[must_use]
        pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
            self.inner = self.inner.send_compressed(encoding);
            self
        }
        /// Enable decompressing responses.
        #[must_use]
        pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
            self.inner = self.inner.accept_compressed(encoding);
            self
        }
        /// Limits the maximum size of a decoded message.
        ///
        /// Default: `4MB`
        #[must_use]
        pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
            self.inner = self.inner.max_decoding_message_size(limit);
            self
        }
        /// Limits the maximum size of an encoded message.
        ///
        /// Default: `usize::MAX`
        #[must_use]
        pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
            self.inner = self.inner.max_encoding_message_size(limit);
            self
        }
        /// This API accepts blob to disperse from clients.
        /// This executes the dispersal async, i.e. it returns once the request
        /// is accepted. The client could use GetBlobStatus() API to poll the the
        /// processing status of the blob.
        pub async fn disperse_blob(
            &mut self,
            request: impl tonic::IntoRequest<super::DisperseBlobRequest>,
        ) -> std::result::Result<
            tonic::Response<super::DisperseBlobReply>,
            tonic::Status,
        > {
            self.inner
                .ready()
                .await
                .map_err(|e| {
                    tonic::Status::new(
                        tonic::Code::Unknown,
                        format!("Service was not ready: {}", e.into()),
                    )
                })?;
            let codec = tonic::codec::ProstCodec::default();
            let path = http::uri::PathAndQuery::from_static(
                "/disperser.Disperser/DisperseBlob",
            );
            let mut req = request.into_request();
            req.extensions_mut()
                .insert(GrpcMethod::new("disperser.Disperser", "DisperseBlob"));
            self.inner.unary(req, path, codec).await
        }
        /// DisperseBlobAuthenticated is similar to DisperseBlob, except that it requires the
        /// client to authenticate itself via the AuthenticationData message. The protoco is as follows:
        /// 1. The client sends a DisperseBlobAuthenticated request with the DisperseBlobRequest message
        /// 2. The Disperser sends back a BlobAuthHeader message containing information for the client to
        ///    verify and sign.
        /// 3. The client verifies the BlobAuthHeader and sends back the signed BlobAuthHeader in an
        /// 	  AuthenticationData message.
        /// 4. The Disperser verifies the signature and returns a DisperseBlobReply message.
        pub async fn disperse_blob_authenticated(
            &mut self,
            request: impl tonic::IntoStreamingRequest<
                Message = super::AuthenticatedRequest,
            >,
        ) -> std::result::Result<
            tonic::Response<tonic::codec::Streaming<super::AuthenticatedReply>>,
            tonic::Status,
        > {
            self.inner
                .ready()
                .await
                .map_err(|e| {
                    tonic::Status::new(
                        tonic::Code::Unknown,
                        format!("Service was not ready: {}", e.into()),
                    )
                })?;
            let codec = tonic::codec::ProstCodec::default();
            let path = http::uri::PathAndQuery::from_static(
                "/disperser.Disperser/DisperseBlobAuthenticated",
            );
            let mut req = request.into_streaming_request();
            req.extensions_mut()
                .insert(
                    GrpcMethod::new("disperser.Disperser", "DisperseBlobAuthenticated"),
                );
            self.inner.streaming(req, path, codec).await
        }
        /// This API is meant to be polled for the blob status.
        pub async fn get_blob_status(
            &mut self,
            request: impl tonic::IntoRequest<super::BlobStatusRequest>,
        ) -> std::result::Result<
            tonic::Response<super::BlobStatusReply>,
            tonic::Status,
        > {
            self.inner
                .ready()
                .await
                .map_err(|e| {
                    tonic::Status::new(
                        tonic::Code::Unknown,
                        format!("Service was not ready: {}", e.into()),
                    )
                })?;
            let codec = tonic::codec::ProstCodec::default();
            let path = http::uri::PathAndQuery::from_static(
                "/disperser.Disperser/GetBlobStatus",
            );
            let mut req = request.into_request();
            req.extensions_mut()
                .insert(GrpcMethod::new("disperser.Disperser", "GetBlobStatus"));
            self.inner.unary(req, path, codec).await
        }
        /// This retrieves the requested blob from the Disperser's backend.
        /// This is a more efficient way to retrieve blobs than directly retrieving
        /// from the DA Nodes (see detail about this approach in
        /// api/proto/retriever/retriever.proto).
        /// The blob should have been initially dispersed via this Disperser service
        /// for this API to work.
        pub async fn retrieve_blob(
            &mut self,
            request: impl tonic::IntoRequest<super::RetrieveBlobRequest>,
        ) -> std::result::Result<
            tonic::Response<super::RetrieveBlobReply>,
            tonic::Status,
        > {
            self.inner
                .ready()
                .await
                .map_err(|e| {
                    tonic::Status::new(
                        tonic::Code::Unknown,
                        format!("Service was not ready: {}", e.into()),
                    )
                })?;
            let codec = tonic::codec::ProstCodec::default();
            let path = http::uri::PathAndQuery::from_static(
                "/disperser.Disperser/RetrieveBlob",
            );
            let mut req = request.into_request();
            req.extensions_mut()
                .insert(GrpcMethod::new("disperser.Disperser", "RetrieveBlob"));
            self.inner.unary(req, path, codec).await
        }
    }
}