concordium_rust_sdk/v2/
mod.rs

1//! This module exposes [Client] which is a wrapper around the
2//! generated gRPC rust client, providing a more ergonomic interface than the
3//! generated client. See [Client] for documentation of how to use.
4use crate::{
5    endpoints,
6    id::{self, types::AccountCredentialMessage},
7    protocol_level_tokens,
8    types::{
9        self, block_certificates,
10        hashes::{self, BlockHash, TransactionHash, TransactionSignHash},
11        queries::ConsensusDetailedStatus,
12        smart_contracts::{
13            ContractContext, InstanceInfo, InvokeContractResult, ModuleReference, WasmModule,
14        },
15        transactions::{self, InitContractPayload, UpdateContractPayload, UpdateInstruction},
16        AbsoluteBlockHeight, AccountInfo, AccountPending, BlockItemSummary,
17        CredentialRegistrationID, Energy, Memo, Nonce, RegisteredData, SpecialTransactionOutcome,
18        TransactionStatus, UpdateSequenceNumber,
19    },
20};
21use anyhow::Context;
22use concordium_base::{
23    base::{
24        AccountIndex, BlockHeight, ChainParameterVersion0, ChainParameterVersion1,
25        CredentialsPerBlockLimit, ElectionDifficulty, Epoch, ExchangeRate, GenesisIndex,
26        MintDistributionV0, MintDistributionV1,
27    },
28    common::{
29        self,
30        types::{TransactionSignature, TransactionTime},
31    },
32    contracts_common::{
33        AccountAddress, AccountAddressParseError, Amount, ContractAddress, Duration,
34        OwnedContractName, OwnedParameter, OwnedReceiveName, ReceiveName,
35    },
36    hashes::HashFromStrError,
37    transactions::{BlockItem, EncodedPayload, PayloadLike},
38    updates::{
39        AuthorizationsV0, CooldownParameters, FinalizationCommitteeParameters, GASRewards,
40        GASRewardsV1, PoolParameters, TimeParameters, TimeoutParameters,
41        TransactionFeeDistribution, ValidatorScoreParameters,
42    },
43};
44pub use endpoints::{QueryError, QueryResult, RPCError, RPCResult};
45use futures::{Stream, StreamExt, TryStreamExt};
46pub use http::uri::Scheme;
47use num::{BigUint, ToPrimitive};
48use std::{collections::HashMap, num::ParseIntError, str::FromStr};
49use tonic::IntoRequest;
50pub use tonic::{
51    transport::{Endpoint, Error},
52    Code, Status,
53};
54
55use self::dry_run::WithRemainingQuota;
56
57mod conversions;
58pub mod dry_run;
59#[path = "generated/mod.rs"]
60#[allow(
61    clippy::large_enum_variant,
62    clippy::enum_variant_names,
63    clippy::derive_partial_eq_without_eq
64)]
65#[rustfmt::skip]
66mod gen;
67pub use gen::concordium::v2 as generated;
68pub mod proto_schema_version;
69
70/// A client for gRPC API v2 of the Concordium node. Can be used to control the
71/// node, send transactions and query information about the node and the state
72/// of the chain.
73///
74/// # Connecting to a Concordium node
75///
76/// Creates a new client connection to a Concordium node.
77/// Make sure to have access to the gRPC API v2 endpoint of a running node.
78///
79/// ```no_run
80/// # tokio_test::block_on(async {
81/// use concordium_rust_sdk::v2::{Client, Endpoint};
82/// use std::str::FromStr;
83///
84/// // Assumes the node is running locally and gRPC API v2 can be accessed on port 20001.
85/// let node_endpoint = Endpoint::from_str("http://localhost:20001")?;
86/// let mut client = Client::new(node_endpoint).await?;
87///
88/// // Verify the connection to the node by printing node information.
89/// let node_info = client.get_node_info().await?;
90/// println!("{:#?}", node_info);
91/// # Ok::<(), anyhow::Error>(())
92/// # });
93/// ```
94///
95/// # Concurrent use of the client
96///
97/// All endpoints take a `&mut self` as an argument which means that a single
98/// instance cannot be used concurrently. However instead of putting the Client
99/// behind a Mutex, the intended way to use it is to clone it. Cloning is very
100/// cheap and will reuse the underlying connection.
101#[derive(Clone, Debug)]
102pub struct Client {
103    client: generated::queries_client::QueriesClient<tonic::transport::Channel>,
104}
105
106/// A query response with the addition of the block hash used by the query.
107/// The block hash used for querying might be unknown when providing the block
108/// as [BlockIdentifier::Best] or [BlockIdentifier::LastFinal].
109#[derive(Clone, Copy, Debug)]
110pub struct QueryResponse<A> {
111    /// Block hash for which the query applies.
112    pub block_hash: BlockHash,
113    /// The result of the query.
114    pub response:   A,
115}
116
117impl<A> AsRef<A> for QueryResponse<A> {
118    fn as_ref(&self) -> &A { &self.response }
119}
120
121/// A block identifier used in queries.
122#[derive(Copy, Clone, Debug, derive_more::From, PartialEq, Eq)]
123pub enum BlockIdentifier {
124    /// Query in the context of the best block.
125    Best,
126    /// Query in the context of the last finalized block at the time of the
127    /// query.
128    LastFinal,
129    /// Query in the context of a specific block hash.
130    Given(BlockHash),
131    /// Query for a block at absolute height. If a unique
132    /// block can not be identified at that height the query will return
133    /// `NotFound`.
134    AbsoluteHeight(AbsoluteBlockHeight),
135    /// Query for a block at a height relative to genesis index. If a unique
136    /// block can not be identified at that height the query will return
137    /// `NotFound`.
138    RelativeHeight(RelativeBlockHeight),
139}
140
141#[derive(Debug, thiserror::Error)]
142pub enum BlockIdentifierFromStrError {
143    #[error("The input is not recognized.")]
144    InvalidFormat,
145    #[error("The input is not a valid hash: {0}.")]
146    InvalidHash(#[from] HashFromStrError),
147    #[error("The input is not a valid unsigned integer: {0}.")]
148    InvalidInteger(#[from] ParseIntError),
149}
150
151/// Display implementation to match the [`FromStr`] implementation defined just
152/// below.
153impl std::fmt::Display for BlockIdentifier {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        match self {
156            BlockIdentifier::Best => "best".fmt(f),
157            BlockIdentifier::LastFinal => "lastfinal".fmt(f),
158            BlockIdentifier::Given(bh) => bh.fmt(f),
159            BlockIdentifier::AbsoluteHeight(h) => write!(f, "@{h}"),
160            BlockIdentifier::RelativeHeight(rh) => {
161                write!(
162                    f,
163                    "@{}/{}{}",
164                    rh.height,
165                    rh.genesis_index,
166                    if rh.restrict { "!" } else { "" }
167                )
168            }
169        }
170    }
171}
172
173/// Parse a string as a [`BlockIdentifier`]. The format is one of the following
174///
175/// - the string `best` for [`Best`](BlockIdentifier::Best)
176/// - the string `lastFinal` or `lastfinal` for
177///   [`LastFinal`](BlockIdentifier::LastFinal)
178/// - a valid block hash for [`Given`](BlockIdentifier::Given)
179/// - a string starting with `@` followed by an integer and nothing else for
180///   [`AbsoluteHeight`](BlockIdentifier::AbsoluteHeight)
181/// - a string in the format `@123/3` optionally followed by `!` where `123` is
182///   the block height and `3` is the genesis index for
183///   [`RelativeHeight`](BlockIdentifier::RelativeHeight). If `!` is present
184///   then `restrict` is set to `true`.
185impl std::str::FromStr for BlockIdentifier {
186    type Err = BlockIdentifierFromStrError;
187
188    fn from_str(s: &str) -> Result<Self, Self::Err> {
189        match s {
190            "best" => Ok(Self::Best),
191            "lastFinal" => Ok(Self::LastFinal),
192            "lastfinal" => Ok(Self::LastFinal),
193            _ => {
194                if let Some(rest) = s.strip_prefix('@') {
195                    if let Some((height_str, gen_idx_str)) = rest.split_once('/') {
196                        let height = BlockHeight::from_str(height_str)?;
197                        if let Some(gen_idx) = gen_idx_str.strip_suffix('!') {
198                            let genesis_index = GenesisIndex::from_str(gen_idx)?;
199                            Ok(Self::RelativeHeight(RelativeBlockHeight {
200                                genesis_index,
201                                height,
202                                restrict: true,
203                            }))
204                        } else {
205                            let genesis_index = GenesisIndex::from_str(gen_idx_str)?;
206                            Ok(Self::RelativeHeight(RelativeBlockHeight {
207                                genesis_index,
208                                height,
209                                restrict: false,
210                            }))
211                        }
212                    } else {
213                        let h = AbsoluteBlockHeight::from_str(rest)?;
214                        Ok(Self::AbsoluteHeight(h))
215                    }
216                } else {
217                    let h = BlockHash::from_str(s)?;
218                    Ok(Self::Given(h))
219                }
220            }
221        }
222    }
223}
224
225/// Block height relative to an explicit genesis index.
226#[derive(Copy, Clone, Debug, PartialEq, Eq)]
227pub struct RelativeBlockHeight {
228    /// Genesis index to start from.
229    pub genesis_index: types::GenesisIndex,
230    /// Height starting from the genesis block at the genesis index.
231    pub height:        types::BlockHeight,
232    /// Whether to return results only from the specified genesis index
233    /// (`true`), or allow results from more recent genesis indices
234    /// as well (`false`).
235    pub restrict:      bool,
236}
237
238/// An account identifier used in queries.
239#[derive(Copy, Clone, Debug, derive_more::From, derive_more::Display)]
240pub enum AccountIdentifier {
241    /// Identify an account by an address.
242    #[display(fmt = "{_0}")]
243    Address(AccountAddress),
244    /// Identify an account by the credential registration id.
245    #[display(fmt = "{_0}")]
246    CredId(CredentialRegistrationID),
247    /// Identify an account by its account index.
248    #[display(fmt = "{_0}")]
249    Index(crate::types::AccountIndex),
250}
251
252impl FromStr for AccountIdentifier {
253    type Err = AccountAddressParseError;
254
255    fn from_str(s: &str) -> Result<Self, Self::Err> {
256        if let Ok(ai) = s.parse::<crate::types::AccountIndex>() {
257            return Ok(Self::Index(ai));
258        }
259        if let Ok(cid) = s.parse::<CredentialRegistrationID>() {
260            return Ok(Self::CredId(cid));
261        }
262        s.parse().map(Self::Address)
263    }
264}
265
266/// Identifier for an [`Epoch`] relative to the specified genesis index.
267#[derive(Debug, Copy, Clone)]
268pub struct SpecifiedEpoch {
269    /// Genesis index to query in.
270    pub genesis_index: types::GenesisIndex,
271    /// The epoch of the genesis to query.
272    pub epoch:         types::Epoch,
273}
274
275/// An identifier of an epoch used in queries.
276#[derive(Copy, Clone, Debug, derive_more::From)]
277pub enum EpochIdentifier {
278    /// A specified epoch to query.
279    Specified(SpecifiedEpoch),
280    /// Query the epoch of the block.
281    Block(BlockIdentifier),
282}
283
284/// Errors that may occur as a result of
285/// parsing a [`EpochIdentifier`] from a string via
286/// [from_str(&str)][std::str::FromStr].
287#[derive(Debug, thiserror::Error)]
288pub enum EpochIdentifierFromStrError {
289    #[error("The input is not recognized.")]
290    InvalidFormat,
291    #[error("The genesis index is not a valid unsigned integer")]
292    InvalidGenesis,
293    #[error("The epoch index is not a valid unsigned integer")]
294    InvalidEpoch,
295    #[error("The input is not a valid block identifier: {0}.")]
296    InvalidBlockIdentifier(#[from] BlockIdentifierFromStrError),
297}
298
299/// Parse a string as an [`EpochIdentifier`]. The format is one of the
300/// following:
301///
302/// - a string starting with `%` followed by two integers separated by `,` for
303///   [`Specified`](EpochIdentifier::Specified). First component is treated as
304///   the genesis index and the second component as the epoch.
305/// - a string starting with `@` followed by a [`BlockIdentifier`] for
306///   [`Block`](EpochIdentifier::Block).
307impl std::str::FromStr for EpochIdentifier {
308    type Err = EpochIdentifierFromStrError;
309
310    fn from_str(s: &str) -> Result<Self, Self::Err> {
311        if let Some(rest) = s.strip_prefix('%') {
312            if let Some((gen_idx_str, epoch_str)) = rest.split_once(',') {
313                let genesis_index = GenesisIndex::from_str(gen_idx_str)
314                    .map_err(|_| EpochIdentifierFromStrError::InvalidGenesis)?;
315                let epoch = Epoch::from_str(epoch_str)
316                    .map_err(|_| EpochIdentifierFromStrError::InvalidEpoch)?;
317                Ok(Self::Specified(SpecifiedEpoch {
318                    genesis_index,
319                    epoch,
320                }))
321            } else {
322                Err(EpochIdentifierFromStrError::InvalidFormat)
323            }
324        } else {
325            Ok(Self::Block(BlockIdentifier::from_str(s)?))
326        }
327    }
328}
329
330impl IntoRequest<generated::EpochRequest> for &EpochIdentifier {
331    fn into_request(self) -> tonic::Request<generated::EpochRequest> {
332        tonic::Request::new((*self).into())
333    }
334}
335
336impl From<EpochIdentifier> for generated::EpochRequest {
337    fn from(ei: EpochIdentifier) -> Self {
338        match ei {
339            EpochIdentifier::Specified(SpecifiedEpoch {
340                genesis_index,
341                epoch,
342            }) => generated::EpochRequest {
343                epoch_request_input: Some(
344                    generated::epoch_request::EpochRequestInput::RelativeEpoch(
345                        generated::epoch_request::RelativeEpoch {
346                            genesis_index: Some(generated::GenesisIndex {
347                                value: genesis_index.height,
348                            }),
349                            epoch:         Some(generated::Epoch { value: epoch.epoch }),
350                        },
351                    ),
352                ),
353            },
354            EpochIdentifier::Block(bi) => generated::EpochRequest {
355                epoch_request_input: Some(generated::epoch_request::EpochRequestInput::BlockHash(
356                    (&bi).into(),
357                )),
358            },
359        }
360    }
361}
362
363/// Information of a finalized block.
364#[derive(Copy, Clone, Debug)]
365pub struct FinalizedBlockInfo {
366    /// The block hash for the finalized block.
367    pub block_hash: BlockHash,
368    /// The absolute block height for the finalized block.
369    pub height:     AbsoluteBlockHeight,
370}
371
372#[derive(Debug, Clone)]
373/// Values of chain parameters that can be updated via chain updates.
374/// This applies to protocol version 1-3.
375pub struct ChainParametersV0 {
376    /// Election difficulty for consensus lottery.
377    pub election_difficulty: ElectionDifficulty,
378    /// Euro per energy exchange rate.
379    pub euro_per_energy: ExchangeRate,
380    /// Micro ccd per euro exchange rate.
381    pub micro_ccd_per_euro: ExchangeRate,
382    /// Extra number of epochs before reduction in stake, or baker
383    /// deregistration is completed.
384    pub baker_cooldown_epochs: Epoch,
385    /// The limit for the number of account creations in a block.
386    pub account_creation_limit: CredentialsPerBlockLimit,
387    /// Parameters related to the distribution of newly minted CCD.
388    pub mint_distribution: MintDistributionV0,
389    /// Parameters related to the distribution of transaction fees.
390    pub transaction_fee_distribution: TransactionFeeDistribution,
391    /// Parameters related to the distribution of the GAS account.
392    pub gas_rewards: GASRewards,
393    /// Address of the foundation account.
394    pub foundation_account: AccountAddress,
395    /// Minimum threshold for becoming a baker.
396    pub minimum_threshold_for_baking: Amount,
397    /// Keys allowed to do updates.
398    pub keys: types::UpdateKeysCollection<ChainParameterVersion0>,
399}
400
401#[derive(Debug, Clone)]
402/// Values of chain parameters that can be updated via chain updates.
403/// This applies to protocol version 4 and 5.
404pub struct ChainParametersV1 {
405    /// Election difficulty for consensus lottery.
406    pub election_difficulty: ElectionDifficulty,
407    /// Euro per energy exchange rate.
408    pub euro_per_energy: ExchangeRate,
409    /// Micro ccd per euro exchange rate.
410    pub micro_ccd_per_euro: ExchangeRate,
411    pub cooldown_parameters: CooldownParameters,
412    pub time_parameters: TimeParameters,
413    /// The limit for the number of account creations in a block.
414    pub account_creation_limit: CredentialsPerBlockLimit,
415    /// Parameters related to the distribution of newly minted CCD.
416    pub mint_distribution: MintDistributionV1,
417    /// Parameters related to the distribution of transaction fees.
418    pub transaction_fee_distribution: TransactionFeeDistribution,
419    /// Parameters related to the distribution of the GAS account.
420    pub gas_rewards: GASRewards,
421    /// Address of the foundation account.
422    pub foundation_account: AccountAddress,
423    /// Parameters for baker pools.
424    pub pool_parameters: PoolParameters,
425    /// Keys allowed to do updates.
426    pub keys: types::UpdateKeysCollection<ChainParameterVersion1>,
427}
428
429#[derive(Debug, Clone)]
430/// Values of chain parameters that can be updated via chain updates.
431/// This applies to protocol version 6 and 7.
432pub struct ChainParametersV2 {
433    /// Consensus protocol version 2 timeout parameters.
434    pub timeout_parameters: TimeoutParameters,
435    /// Minimum time interval between blocks.
436    pub min_block_time: Duration,
437    /// Maximum energy allowed per block.
438    pub block_energy_limit: Energy,
439    /// Euro per energy exchange rate.
440    pub euro_per_energy: ExchangeRate,
441    /// Micro ccd per euro exchange rate.
442    pub micro_ccd_per_euro: ExchangeRate,
443    /// Parameters related to cooldowns when staking.
444    pub cooldown_parameters: CooldownParameters,
445    /// Parameters related mint rate and reward period.
446    pub time_parameters: TimeParameters,
447    /// The limit for the number of account creations in a block.
448    pub account_creation_limit: CredentialsPerBlockLimit,
449    /// Parameters related to the distribution of newly minted CCD.
450    pub mint_distribution: MintDistributionV1,
451    /// Parameters related to the distribution of transaction fees.
452    pub transaction_fee_distribution: TransactionFeeDistribution,
453    /// Parameters related to the distribution from the GAS account.
454    pub gas_rewards: GASRewardsV1,
455    /// Address of the foundation account.
456    pub foundation_account: AccountAddress,
457    /// Parameters for baker pools.
458    pub pool_parameters: PoolParameters,
459    /// The finalization committee parameters.
460    pub finalization_committee_parameters: FinalizationCommitteeParameters,
461    /// Keys allowed to do updates.
462    pub keys: types::UpdateKeysCollection<ChainParameterVersion1>,
463}
464
465#[derive(Debug, Clone)]
466/// Values of chain parameters that can be updated via chain updates.
467/// This applies to protocol version 8 and up.
468pub struct ChainParametersV3 {
469    /// Consensus protocol version 2 timeout parameters.
470    pub timeout_parameters: TimeoutParameters,
471    /// Minimum time interval between blocks.
472    pub min_block_time: Duration,
473    /// Maximum energy allowed per block.
474    pub block_energy_limit: Energy,
475    /// Euro per energy exchange rate.
476    pub euro_per_energy: ExchangeRate,
477    /// Micro ccd per euro exchange rate.
478    pub micro_ccd_per_euro: ExchangeRate,
479    /// Parameters related to cooldowns when staking.
480    pub cooldown_parameters: CooldownParameters,
481    /// Parameters related mint rate and reward period.
482    pub time_parameters: TimeParameters,
483    /// The limit for the number of account creations in a block.
484    pub account_creation_limit: CredentialsPerBlockLimit,
485    /// Parameters related to the distribution of newly minted CCD.
486    pub mint_distribution: MintDistributionV1,
487    /// Parameters related to the distribution of transaction fees.
488    pub transaction_fee_distribution: TransactionFeeDistribution,
489    /// Parameters related to the distribution from the GAS account.
490    pub gas_rewards: GASRewardsV1,
491    /// Address of the foundation account.
492    pub foundation_account: AccountAddress,
493    /// Parameters for baker pools.
494    pub pool_parameters: PoolParameters,
495    /// The finalization committee parameters.
496    pub finalization_committee_parameters: FinalizationCommitteeParameters,
497    /// Validator score parameters.
498    pub validator_score_parameters: ValidatorScoreParameters,
499    /// Keys allowed to do updates.
500    pub keys: types::UpdateKeysCollection<ChainParameterVersion1>,
501}
502
503/// Chain parameters. See [`ChainParametersV0`] and [`ChainParametersV1`] for
504/// details. `V0` parameters apply to protocol version `1..=3`, and `V1`
505/// parameters apply to protocol versions `4` and up.
506#[derive(Debug, Clone)]
507pub enum ChainParameters {
508    V0(ChainParametersV0),
509    V1(ChainParametersV1),
510    V2(ChainParametersV2),
511    V3(ChainParametersV3),
512}
513
514impl ChainParameters {
515    /// Get the keys for parameter updates that are common to all versions.
516    pub fn common_update_keys(&self) -> &AuthorizationsV0 {
517        match self {
518            Self::V0(data) => &data.keys.level_2_keys,
519            Self::V1(data) => &data.keys.level_2_keys.v0,
520            Self::V2(data) => &data.keys.level_2_keys.v0,
521            Self::V3(data) => &data.keys.level_2_keys.v0,
522        }
523    }
524}
525
526impl ChainParameters {
527    /// Compute the exchange rate between `microCCD` and `NRG`.
528    pub fn micro_ccd_per_energy(&self) -> num::rational::Ratio<u128> {
529        let (num, denom) = match self {
530            ChainParameters::V0(v0) => {
531                let x = v0.micro_ccd_per_euro;
532                let y = v0.euro_per_energy;
533                (
534                    u128::from(x.numerator()) * u128::from(y.numerator()),
535                    u128::from(x.denominator()) * u128::from(y.denominator()),
536                )
537            }
538            ChainParameters::V1(v1) => {
539                let x = v1.micro_ccd_per_euro;
540                let y = v1.euro_per_energy;
541                (
542                    u128::from(x.numerator()) * u128::from(y.numerator()),
543                    u128::from(x.denominator()) * u128::from(y.denominator()),
544                )
545            }
546            ChainParameters::V2(v2) => {
547                let x = v2.micro_ccd_per_euro;
548                let y = v2.euro_per_energy;
549                (
550                    u128::from(x.numerator()) * u128::from(y.numerator()),
551                    u128::from(x.denominator()) * u128::from(y.denominator()),
552                )
553            }
554            ChainParameters::V3(v3) => {
555                let x = v3.micro_ccd_per_euro;
556                let y = v3.euro_per_energy;
557                (
558                    u128::from(x.numerator()) * u128::from(y.numerator()),
559                    u128::from(x.denominator()) * u128::from(y.denominator()),
560                )
561            }
562        };
563        num::rational::Ratio::new(num, denom)
564    }
565
566    /// Get the CCD cost of the given amount of energy for the current chain
567    /// parameters.
568    pub fn ccd_cost(&self, nrg: Energy) -> Amount {
569        let ratio = self.micro_ccd_per_energy();
570        let numer = BigUint::from(*ratio.numer()) * nrg.energy;
571        let denomer = BigUint::from(*ratio.denom());
572        let cost = num::rational::Ratio::new(numer, denomer);
573        let i = cost.ceil().to_integer();
574        // The next line should be a no-op when the values are coming from the chain
575        // since the amount should always fit a u64. With a 3_000_000 maximum
576        // NRG limit the exchange rate would have to be more than 6148914691236
577        // microCCD per NRG (6148914 CCD per NRG) for this to occur. But even in that
578        // case this behaviour here matches the node behaviour.
579        let micro = i % u64::MAX;
580        Amount::from_micro_ccd(micro.to_u64().expect("Value is known to be under u64::MAX"))
581    }
582
583    /// The foundation account that gets the foundation tax.
584    pub fn foundation_account(&self) -> AccountAddress {
585        match self {
586            ChainParameters::V0(v0) => v0.foundation_account,
587            ChainParameters::V1(v1) => v1.foundation_account,
588            ChainParameters::V2(v2) => v2.foundation_account,
589            ChainParameters::V3(v3) => v3.foundation_account,
590        }
591    }
592}
593
594impl From<&BlockIdentifier> for generated::BlockHashInput {
595    fn from(bi: &BlockIdentifier) -> Self {
596        let block_hash_input = match bi {
597            BlockIdentifier::Best => {
598                generated::block_hash_input::BlockHashInput::Best(Default::default())
599            }
600            BlockIdentifier::LastFinal => {
601                generated::block_hash_input::BlockHashInput::LastFinal(Default::default())
602            }
603            BlockIdentifier::Given(h) => {
604                generated::block_hash_input::BlockHashInput::Given(generated::BlockHash {
605                    value: h.as_ref().to_vec(),
606                })
607            }
608            &BlockIdentifier::AbsoluteHeight(h) => {
609                generated::block_hash_input::BlockHashInput::AbsoluteHeight(h.into())
610            }
611            &BlockIdentifier::RelativeHeight(h) => {
612                generated::block_hash_input::BlockHashInput::RelativeHeight(h.into())
613            }
614        };
615        generated::BlockHashInput {
616            block_hash_input: Some(block_hash_input),
617        }
618    }
619}
620
621impl IntoRequest<generated::BlockHashInput> for &BlockIdentifier {
622    fn into_request(self) -> tonic::Request<generated::BlockHashInput> {
623        tonic::Request::new(self.into())
624    }
625}
626
627impl From<&AccountAddress> for generated::AccountAddress {
628    fn from(addr: &AccountAddress) -> Self {
629        generated::AccountAddress {
630            value: concordium_base::common::to_bytes(addr),
631        }
632    }
633}
634
635impl From<AccountAddress> for generated::AccountAddress {
636    fn from(addr: AccountAddress) -> Self {
637        generated::AccountAddress {
638            value: common::to_bytes(&addr),
639        }
640    }
641}
642
643impl From<&super::types::Address> for generated::Address {
644    fn from(addr: &super::types::Address) -> Self {
645        let ty = match addr {
646            super::types::Address::Account(account) => {
647                generated::address::Type::Account(account.into())
648            }
649            super::types::Address::Contract(contract) => {
650                generated::address::Type::Contract(contract.into())
651            }
652        };
653        generated::Address { r#type: Some(ty) }
654    }
655}
656
657impl From<&Memo> for generated::Memo {
658    fn from(v: &Memo) -> Self {
659        Self {
660            value: v.as_ref().clone(),
661        }
662    }
663}
664
665impl<'a> From<ReceiveName<'a>> for generated::ReceiveName {
666    fn from(a: ReceiveName<'a>) -> Self {
667        generated::ReceiveName {
668            value: a.get_chain_name().to_string(),
669        }
670    }
671}
672
673impl From<&RegisteredData> for generated::RegisteredData {
674    fn from(v: &RegisteredData) -> Self {
675        Self {
676            value: v.as_ref().clone(),
677        }
678    }
679}
680impl From<&[u8]> for generated::Parameter {
681    fn from(a: &[u8]) -> Self { generated::Parameter { value: a.to_vec() } }
682}
683
684impl From<&TransactionHash> for generated::TransactionHash {
685    fn from(th: &TransactionHash) -> Self { generated::TransactionHash { value: th.to_vec() } }
686}
687
688impl From<&AccountIdentifier> for generated::AccountIdentifierInput {
689    fn from(ai: &AccountIdentifier) -> Self {
690        let account_identifier_input = match ai {
691            AccountIdentifier::Address(addr) => {
692                generated::account_identifier_input::AccountIdentifierInput::Address(addr.into())
693            }
694            AccountIdentifier::CredId(credid) => {
695                let credid = generated::CredentialRegistrationId {
696                    value: concordium_base::common::to_bytes(credid),
697                };
698                generated::account_identifier_input::AccountIdentifierInput::CredId(credid)
699            }
700            AccountIdentifier::Index(index) => {
701                generated::account_identifier_input::AccountIdentifierInput::AccountIndex(
702                    (*index).into(),
703                )
704            }
705        };
706        generated::AccountIdentifierInput {
707            account_identifier_input: Some(account_identifier_input),
708        }
709    }
710}
711
712impl From<&ModuleReference> for generated::ModuleRef {
713    fn from(mr: &ModuleReference) -> Self { Self { value: mr.to_vec() } }
714}
715
716impl From<ModuleReference> for generated::ModuleRef {
717    fn from(mr: ModuleReference) -> Self { Self { value: mr.to_vec() } }
718}
719
720impl From<&WasmModule> for generated::VersionedModuleSource {
721    fn from(v: &WasmModule) -> Self {
722        Self {
723            module: Some(match v.version {
724                types::smart_contracts::WasmVersion::V0 => {
725                    generated::versioned_module_source::Module::V0(
726                        generated::versioned_module_source::ModuleSourceV0 {
727                            value: v.source.as_ref().clone(),
728                        },
729                    )
730                }
731                types::smart_contracts::WasmVersion::V1 => {
732                    generated::versioned_module_source::Module::V1(
733                        generated::versioned_module_source::ModuleSourceV1 {
734                            value: v.source.as_ref().clone(),
735                        },
736                    )
737                }
738            }),
739        }
740    }
741}
742
743impl From<&OwnedContractName> for generated::InitName {
744    fn from(v: &OwnedContractName) -> Self {
745        Self {
746            value: v.as_contract_name().get_chain_name().to_string(),
747        }
748    }
749}
750
751impl From<&OwnedReceiveName> for generated::ReceiveName {
752    fn from(v: &OwnedReceiveName) -> Self {
753        Self {
754            value: v.as_receive_name().get_chain_name().to_string(),
755        }
756    }
757}
758
759impl From<&OwnedParameter> for generated::Parameter {
760    fn from(v: &OwnedParameter) -> Self {
761        Self {
762            value: v.as_ref().to_vec(),
763        }
764    }
765}
766
767impl From<&InitContractPayload> for generated::InitContractPayload {
768    fn from(v: &InitContractPayload) -> Self {
769        Self {
770            amount:     Some(v.amount.into()),
771            module_ref: Some(v.mod_ref.into()),
772            init_name:  Some((&v.init_name).into()),
773            parameter:  Some((&v.param).into()),
774        }
775    }
776}
777
778impl From<&UpdateContractPayload> for generated::UpdateContractPayload {
779    fn from(v: &UpdateContractPayload) -> Self {
780        Self {
781            amount:       Some(v.amount.into()),
782            address:      Some(v.address.into()),
783            receive_name: Some((&v.receive_name).into()),
784            parameter:    Some((&v.message).into()),
785        }
786    }
787}
788
789impl From<&ContractAddress> for generated::ContractAddress {
790    fn from(ca: &ContractAddress) -> Self {
791        Self {
792            index:    ca.index,
793            subindex: ca.subindex,
794        }
795    }
796}
797
798impl From<Nonce> for generated::SequenceNumber {
799    fn from(v: Nonce) -> Self { generated::SequenceNumber { value: v.nonce } }
800}
801
802impl From<UpdateSequenceNumber> for generated::UpdateSequenceNumber {
803    fn from(v: UpdateSequenceNumber) -> Self { generated::UpdateSequenceNumber { value: v.number } }
804}
805
806impl From<Energy> for generated::Energy {
807    fn from(v: Energy) -> Self { generated::Energy { value: v.energy } }
808}
809
810impl From<TransactionTime> for generated::TransactionTime {
811    fn from(v: TransactionTime) -> Self { generated::TransactionTime { value: v.seconds } }
812}
813
814impl From<&Amount> for generated::Amount {
815    fn from(v: &Amount) -> Self { Self { value: v.micro_ccd } }
816}
817
818impl From<Amount> for generated::Amount {
819    fn from(v: Amount) -> Self { Self { value: v.micro_ccd } }
820}
821
822impl
823    From<
824        &AccountCredentialMessage<
825            id::constants::IpPairing,
826            id::constants::ArCurve,
827            id::constants::AttributeKind,
828        >,
829    > for generated::CredentialDeployment
830{
831    fn from(
832        v: &AccountCredentialMessage<
833            id::constants::IpPairing,
834            id::constants::ArCurve,
835            id::constants::AttributeKind,
836        >,
837    ) -> Self {
838        Self {
839            message_expiry: Some(v.message_expiry.into()),
840            payload:        Some(generated::credential_deployment::Payload::RawPayload(
841                common::to_bytes(&v.credential),
842            )),
843        }
844    }
845}
846
847impl From<&UpdateInstruction> for generated::UpdateInstruction {
848    fn from(v: &UpdateInstruction) -> Self {
849        Self {
850            signatures: Some(generated::SignatureMap {
851                signatures: {
852                    let mut hm = HashMap::new();
853                    for (key_idx, sig) in v.signatures.signatures.iter() {
854                        hm.insert(key_idx.index.into(), generated::Signature {
855                            value: sig.sig.to_owned(),
856                        });
857                    }
858                    hm
859                },
860            }),
861            header:     Some(generated::UpdateInstructionHeader {
862                sequence_number: Some(v.header.seq_number.into()),
863                effective_time:  Some(v.header.effective_time.into()),
864                timeout:         Some(v.header.timeout.into()),
865            }),
866            payload:    Some(generated::UpdateInstructionPayload {
867                payload: Some(generated::update_instruction_payload::Payload::RawPayload(
868                    common::to_bytes(&v.payload),
869                )),
870            }),
871        }
872    }
873}
874
875impl IntoRequest<generated::AccountInfoRequest> for (&AccountIdentifier, &BlockIdentifier) {
876    fn into_request(self) -> tonic::Request<generated::AccountInfoRequest> {
877        let ai = generated::AccountInfoRequest {
878            block_hash:         Some(self.1.into()),
879            account_identifier: Some(self.0.into()),
880        };
881        tonic::Request::new(ai)
882    }
883}
884
885impl IntoRequest<generated::AncestorsRequest> for (&BlockIdentifier, u64) {
886    fn into_request(self) -> tonic::Request<generated::AncestorsRequest> {
887        let ar = generated::AncestorsRequest {
888            block_hash: Some(self.0.into()),
889            amount:     self.1,
890        };
891        tonic::Request::new(ar)
892    }
893}
894
895impl IntoRequest<generated::ModuleSourceRequest> for (&ModuleReference, &BlockIdentifier) {
896    fn into_request(self) -> tonic::Request<generated::ModuleSourceRequest> {
897        let r = generated::ModuleSourceRequest {
898            block_hash: Some(self.1.into()),
899            module_ref: Some(self.0.into()),
900        };
901        tonic::Request::new(r)
902    }
903}
904
905impl IntoRequest<generated::InstanceInfoRequest> for (ContractAddress, &BlockIdentifier) {
906    fn into_request(self) -> tonic::Request<generated::InstanceInfoRequest> {
907        let r = generated::InstanceInfoRequest {
908            block_hash: Some(self.1.into()),
909            address:    Some(self.0.into()),
910        };
911        tonic::Request::new(r)
912    }
913}
914
915impl<V: Into<Vec<u8>>> IntoRequest<generated::InstanceStateLookupRequest>
916    for (ContractAddress, &BlockIdentifier, V)
917{
918    fn into_request(self) -> tonic::Request<generated::InstanceStateLookupRequest> {
919        let r = generated::InstanceStateLookupRequest {
920            block_hash: Some(self.1.into()),
921            address:    Some(self.0.into()),
922            key:        self.2.into(),
923        };
924        tonic::Request::new(r)
925    }
926}
927
928impl IntoRequest<generated::TransactionHash> for &TransactionHash {
929    fn into_request(self) -> tonic::Request<generated::TransactionHash> {
930        tonic::Request::new(self.into())
931    }
932}
933
934impl IntoRequest<generated::AccountIdentifierInput> for &AccountIdentifier {
935    fn into_request(self) -> tonic::Request<generated::AccountIdentifierInput> {
936        tonic::Request::new(self.into())
937    }
938}
939
940impl IntoRequest<generated::AccountAddress> for &AccountAddress {
941    fn into_request(self) -> tonic::Request<generated::AccountAddress> {
942        tonic::Request::new(self.into())
943    }
944}
945
946impl From<transactions::TransactionHeader> for generated::AccountTransactionHeader {
947    fn from(v: transactions::TransactionHeader) -> Self { (&v).into() }
948}
949
950impl From<&transactions::TransactionHeader> for generated::AccountTransactionHeader {
951    fn from(v: &transactions::TransactionHeader) -> Self {
952        Self {
953            sender:          Some(generated::AccountAddress::from(v.sender)),
954            sequence_number: Some(v.nonce.into()),
955            energy_amount:   Some(v.energy_amount.into()),
956            expiry:          Some(v.expiry.into()),
957        }
958    }
959}
960
961impl From<TransactionSignature> for generated::AccountTransactionSignature {
962    fn from(v: TransactionSignature) -> Self { (&v).into() }
963}
964
965impl From<&TransactionSignature> for generated::AccountTransactionSignature {
966    fn from(v: &TransactionSignature) -> Self {
967        Self {
968            signatures: {
969                let mut cred_map: HashMap<u32, generated::AccountSignatureMap> = HashMap::new();
970                for (cred_idx, sig_map) in v.signatures.iter() {
971                    let mut acc_sig_map: HashMap<u32, generated::Signature> = HashMap::new();
972                    for (key_idx, sig) in sig_map.iter() {
973                        acc_sig_map.insert(key_idx.0.into(), generated::Signature {
974                            value: sig.sig.to_owned(),
975                        });
976                    }
977                    cred_map.insert(cred_idx.index.into(), generated::AccountSignatureMap {
978                        signatures: acc_sig_map,
979                    });
980                }
981                cred_map
982            },
983        }
984    }
985}
986
987impl IntoRequest<generated::PreAccountTransaction>
988    for (&transactions::TransactionHeader, &transactions::Payload)
989{
990    fn into_request(self) -> tonic::Request<generated::PreAccountTransaction> {
991        let request = generated::PreAccountTransaction {
992            header:  Some(self.0.into()),
993            payload: Some(generated::AccountTransactionPayload {
994                payload: Some(generated::account_transaction_payload::Payload::RawPayload(
995                    self.1.encode().into(),
996                )),
997            }),
998        };
999        tonic::Request::new(request)
1000    }
1001}
1002
1003impl<P: PayloadLike> IntoRequest<generated::SendBlockItemRequest> for &transactions::BlockItem<P> {
1004    fn into_request(self) -> tonic::Request<generated::SendBlockItemRequest> {
1005        let request = match self {
1006            transactions::BlockItem::AccountTransaction(v) => {
1007                generated::SendBlockItemRequest {
1008                    block_item: Some(
1009                        generated::send_block_item_request::BlockItem::AccountTransaction(
1010                            generated::AccountTransaction {
1011                                signature: Some((&v.signature).into()),
1012                                header:    Some((&v.header).into()),
1013                                payload:   {
1014                                    let atp = generated::AccountTransactionPayload{
1015                                    payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())),
1016                                };
1017                                    Some(atp)
1018                                },
1019                            },
1020                        ),
1021                    ),
1022                }
1023            }
1024            transactions::BlockItem::CredentialDeployment(v) => generated::SendBlockItemRequest {
1025                block_item: Some(
1026                    generated::send_block_item_request::BlockItem::CredentialDeployment(
1027                        v.as_ref().into(),
1028                    ),
1029                ),
1030            },
1031            transactions::BlockItem::UpdateInstruction(v) => generated::SendBlockItemRequest {
1032                block_item: Some(
1033                    generated::send_block_item_request::BlockItem::UpdateInstruction(v.into()),
1034                ),
1035            },
1036        };
1037        tonic::Request::new(request)
1038    }
1039}
1040
1041impl IntoRequest<generated::InvokeInstanceRequest> for (&BlockIdentifier, &ContractContext) {
1042    fn into_request(self) -> tonic::Request<generated::InvokeInstanceRequest> {
1043        let (block, context) = self;
1044        tonic::Request::new(generated::InvokeInstanceRequest {
1045            block_hash: Some(block.into()),
1046            invoker:    context.invoker.as_ref().map(|a| a.into()),
1047            instance:   Some((&context.contract).into()),
1048            amount:     Some(context.amount.into()),
1049            entrypoint: Some(context.method.as_receive_name().into()),
1050            parameter:  Some(context.parameter.as_ref().into()),
1051            energy:     context.energy.map(From::from),
1052        })
1053    }
1054}
1055
1056impl IntoRequest<generated::PoolInfoRequest> for (&BlockIdentifier, types::BakerId) {
1057    fn into_request(self) -> tonic::Request<generated::PoolInfoRequest> {
1058        let req = generated::PoolInfoRequest {
1059            block_hash: Some(self.0.into()),
1060            baker:      Some(self.1.into()),
1061        };
1062        tonic::Request::new(req)
1063    }
1064}
1065
1066impl IntoRequest<generated::BakerId> for types::BakerId {
1067    fn into_request(self) -> tonic::Request<generated::BakerId> {
1068        tonic::Request::new(generated::BakerId {
1069            value: self.id.index,
1070        })
1071    }
1072}
1073
1074impl IntoRequest<generated::BlocksAtHeightRequest> for &endpoints::BlocksAtHeightInput {
1075    fn into_request(self) -> tonic::Request<generated::BlocksAtHeightRequest> {
1076        tonic::Request::new(self.into())
1077    }
1078}
1079
1080impl IntoRequest<generated::GetPoolDelegatorsRequest> for (&BlockIdentifier, types::BakerId) {
1081    fn into_request(self) -> tonic::Request<generated::GetPoolDelegatorsRequest> {
1082        let req = generated::GetPoolDelegatorsRequest {
1083            block_hash: Some(self.0.into()),
1084            baker:      Some(self.1.into()),
1085        };
1086        tonic::Request::new(req)
1087    }
1088}
1089
1090impl TryFrom<crate::v2::generated::BannedPeer> for types::network::BannedPeer {
1091    type Error = anyhow::Error;
1092
1093    fn try_from(value: crate::v2::generated::BannedPeer) -> Result<Self, Self::Error> {
1094        Ok(types::network::BannedPeer(
1095            <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip_address.require()?.value)?,
1096        ))
1097    }
1098}
1099
1100impl TryFrom<generated::IpSocketAddress> for std::net::SocketAddr {
1101    type Error = anyhow::Error;
1102
1103    fn try_from(value: generated::IpSocketAddress) -> Result<Self, Self::Error> {
1104        Ok(std::net::SocketAddr::new(
1105            <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip.require()?.value)?,
1106            value.port.require()?.value as u16,
1107        ))
1108    }
1109}
1110
1111impl IntoRequest<crate::v2::generated::BannedPeer> for &types::network::BannedPeer {
1112    fn into_request(self) -> tonic::Request<crate::v2::generated::BannedPeer> {
1113        tonic::Request::new(crate::v2::generated::BannedPeer {
1114            ip_address: Some(crate::v2::generated::IpAddress {
1115                value: self.0.to_string(),
1116            }),
1117        })
1118    }
1119}
1120
1121impl From<generated::PeerId> for types::network::PeerId {
1122    fn from(value: generated::PeerId) -> Self { types::network::PeerId(value.value) }
1123}
1124
1125impl TryFrom<generated::PeersInfo> for types::network::PeersInfo {
1126    type Error = anyhow::Error;
1127
1128    fn try_from(peers_info: generated::PeersInfo) -> Result<Self, Self::Error> {
1129        // Get information of the peers that the node is connected to.
1130        // Note. If one peer contains malformed data then this function does not
1131        // return any information about the others.
1132        // This should only happen in cases where the sdk and node is not on the same
1133        // major version.
1134        let peers = peers_info
1135            .peers
1136            .into_iter()
1137            .map(|peer| {
1138                // Parse the catchup status of the peer.
1139                let peer_consensus_info = match peer.consensus_info.require()? {
1140                    generated::peers_info::peer::ConsensusInfo::Bootstrapper(_) => {
1141                        types::network::PeerConsensusInfo::Bootstrapper
1142                    }
1143                    generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(0) => {
1144                        types::network::PeerConsensusInfo::Node(
1145                            types::network::PeerCatchupStatus::UpToDate,
1146                        )
1147                    }
1148                    generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(1) => {
1149                        types::network::PeerConsensusInfo::Node(
1150                            types::network::PeerCatchupStatus::Pending,
1151                        )
1152                    }
1153                    generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(2) => {
1154                        types::network::PeerConsensusInfo::Node(
1155                            types::network::PeerCatchupStatus::CatchingUp,
1156                        )
1157                    }
1158                    _ => anyhow::bail!("Malformed catchup status from peer."),
1159                };
1160                // Parse the network statistics for the peer.
1161                let stats = peer.network_stats.require()?;
1162                let network_stats = types::network::NetworkStats {
1163                    packets_sent:     stats.packets_sent,
1164                    packets_received: stats.packets_received,
1165                    latency:          stats.latency,
1166                };
1167                Ok(types::network::Peer {
1168                    peer_id: peer.peer_id.require()?.into(),
1169                    consensus_info: peer_consensus_info,
1170                    network_stats,
1171                    addr: peer.socket_address.require()?.try_into()?,
1172                })
1173            })
1174            .collect::<anyhow::Result<Vec<types::network::Peer>>>()?;
1175        Ok(types::network::PeersInfo { peers })
1176    }
1177}
1178
1179impl TryFrom<generated::node_info::NetworkInfo> for types::NetworkInfo {
1180    type Error = anyhow::Error;
1181
1182    fn try_from(network_info: generated::node_info::NetworkInfo) -> Result<Self, Self::Error> {
1183        Ok(types::NetworkInfo {
1184            node_id:             network_info.node_id.require()?.value,
1185            peer_total_sent:     network_info.peer_total_sent,
1186            peer_total_received: network_info.peer_total_received,
1187            avg_bps_in:          network_info.avg_bps_in,
1188            avg_bps_out:         network_info.avg_bps_out,
1189        })
1190    }
1191}
1192
1193impl IntoRequest<crate::v2::generated::PeerToBan> for types::network::PeerToBan {
1194    fn into_request(self) -> tonic::Request<crate::v2::generated::PeerToBan> {
1195        tonic::Request::new(match self {
1196            types::network::PeerToBan::IpAddr(ip_addr) => crate::v2::generated::PeerToBan {
1197                ip_address: Some(crate::v2::generated::IpAddress {
1198                    value: ip_addr.to_string(),
1199                }),
1200            },
1201        })
1202    }
1203}
1204
1205impl TryFrom<generated::NodeInfo> for types::NodeInfo {
1206    type Error = anyhow::Error;
1207
1208    fn try_from(node_info: generated::NodeInfo) -> Result<Self, Self::Error> {
1209        let version = semver::Version::parse(&node_info.peer_version)?;
1210        let local_time = chrono::DateTime::<chrono::Utc>::from(std::time::UNIX_EPOCH)
1211            + chrono::TimeDelta::try_milliseconds(node_info.local_time.require()?.value as i64)
1212                .context("Node local time out of bounds!")?;
1213        let uptime = chrono::Duration::try_from(types::DurationSeconds::from(
1214            node_info.peer_uptime.require()?.value,
1215        ))?;
1216        let network_info = node_info.network_info.require()?.try_into()?;
1217        let details = match node_info.details.require()? {
1218            generated::node_info::Details::Bootstrapper(_) => types::NodeDetails::Bootstrapper,
1219            generated::node_info::Details::Node(status) => {
1220                let consensus_status = match status.consensus_status.require()? {
1221                    generated::node_info::node::ConsensusStatus::NotRunning(_) => {
1222                        types::NodeConsensusStatus::ConsensusNotRunning
1223                    }
1224                    generated::node_info::node::ConsensusStatus::Passive(_) => {
1225                        types::NodeConsensusStatus::ConsensusPassive
1226                    }
1227                    generated::node_info::node::ConsensusStatus::Active(baker) => {
1228                        let baker_id = baker.baker_id.require()?.into();
1229                        match baker.status.require()? {
1230                            generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(0) => types::NodeConsensusStatus::NotInCommittee(baker_id),
1231                            generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(1) => types::NodeConsensusStatus::AddedButNotActiveInCommittee(baker_id),
1232                            generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(2) => types::NodeConsensusStatus::AddedButWrongKeys(baker_id),
1233                            generated::node_info::baker_consensus_info::Status::ActiveBakerCommitteeInfo(_) => types::NodeConsensusStatus::Baker(baker_id),
1234                            generated::node_info::baker_consensus_info::Status::ActiveFinalizerCommitteeInfo(_) => types::NodeConsensusStatus::Finalizer(baker_id),
1235                            _ => anyhow::bail!("Malformed baker status")
1236                        }
1237                    }
1238                };
1239                types::NodeDetails::Node(consensus_status)
1240            }
1241        };
1242        Ok(types::NodeInfo {
1243            version,
1244            local_time,
1245            uptime,
1246            network_info,
1247            details,
1248        })
1249    }
1250}
1251
1252/// A helper trait that is implemented by types that can be cheaply converted to
1253/// a [`BlockIdentifier`]. This is esentially [`Into<BlockIdentifier>`] but
1254/// orphan rules prevent using that exactly.
1255///
1256/// This trait makes it convenient to use block hashes as input to functions
1257/// that take a block identifier.
1258pub trait IntoBlockIdentifier {
1259    fn into_block_identifier(self) -> BlockIdentifier;
1260}
1261
1262impl IntoBlockIdentifier for BlockIdentifier {
1263    fn into_block_identifier(self) -> BlockIdentifier { self }
1264}
1265
1266impl<X: IntoBlockIdentifier + Copy> IntoBlockIdentifier for &X {
1267    fn into_block_identifier(self) -> BlockIdentifier { (*self).into_block_identifier() }
1268}
1269
1270impl IntoBlockIdentifier for BlockHash {
1271    fn into_block_identifier(self) -> BlockIdentifier { BlockIdentifier::Given(self) }
1272}
1273
1274impl IntoBlockIdentifier for AbsoluteBlockHeight {
1275    fn into_block_identifier(self) -> BlockIdentifier { BlockIdentifier::AbsoluteHeight(self) }
1276}
1277
1278impl IntoBlockIdentifier for RelativeBlockHeight {
1279    fn into_block_identifier(self) -> BlockIdentifier { BlockIdentifier::RelativeHeight(self) }
1280}
1281
1282impl Client {
1283    /// Construct a new client connection to a concordium node.
1284    ///
1285    /// # Example
1286    /// Creates a new client. Note the example assumes access to a local running
1287    /// node.
1288    ///
1289    /// ```no_run
1290    /// # tokio_test::block_on(async {
1291    /// use concordium_rust_sdk::{endpoints::Endpoint, v2::Client};
1292    /// use std::str::FromStr;
1293    ///
1294    /// let mut client = Client::new("http://localhost:20001").await?;
1295    ///
1296    /// # Ok::<(), anyhow::Error>(())
1297    /// # });
1298    /// ```
1299    pub async fn new<E>(endpoint: E) -> Result<Self, tonic::transport::Error>
1300    where
1301        E: TryInto<tonic::transport::Endpoint>,
1302        E::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>, {
1303        let client = generated::queries_client::QueriesClient::connect(endpoint).await?;
1304        Ok(Self { client })
1305    }
1306
1307    /// Get the information for the given account in the given block. If either
1308    /// the block or the account do not exist [`QueryError::NotFound`] is
1309    /// returned.
1310    pub async fn get_account_info(
1311        &mut self,
1312        acc: &AccountIdentifier,
1313        bi: impl IntoBlockIdentifier,
1314    ) -> endpoints::QueryResult<QueryResponse<AccountInfo>> {
1315        let response = self
1316            .client
1317            .get_account_info((acc, &bi.into_block_identifier()))
1318            .await?;
1319        let block_hash = extract_metadata(&response)?;
1320        let response = AccountInfo::try_from(response.into_inner())?;
1321        Ok(QueryResponse {
1322            block_hash,
1323            response,
1324        })
1325    }
1326
1327    /// Get the next sequence number for the account, with information on how
1328    /// reliable the information is.
1329    pub async fn get_next_account_sequence_number(
1330        &mut self,
1331        account_address: &AccountAddress,
1332    ) -> endpoints::QueryResult<types::queries::AccountNonceResponse> {
1333        let response = self
1334            .client
1335            .get_next_account_sequence_number(account_address)
1336            .await?;
1337        let response = types::queries::AccountNonceResponse::try_from(response.into_inner())?;
1338        Ok(response)
1339    }
1340
1341    /// Get information about the current state of consensus. This is an
1342    /// overview of the node's current view of the chain.
1343    pub async fn get_consensus_info(
1344        &mut self,
1345    ) -> endpoints::QueryResult<types::queries::ConsensusInfo> {
1346        let response = self
1347            .client
1348            .get_consensus_info(generated::Empty::default())
1349            .await?;
1350        let response = types::queries::ConsensusInfo::try_from(response.into_inner())?;
1351        Ok(response)
1352    }
1353
1354    /// Get the currently used cryptographic parameters. If the block does
1355    /// not exist [`QueryError::NotFound`] is returned.
1356    pub async fn get_cryptographic_parameters(
1357        &mut self,
1358        bi: impl IntoBlockIdentifier,
1359    ) -> endpoints::QueryResult<QueryResponse<types::CryptographicParameters>> {
1360        let response = self
1361            .client
1362            .get_cryptographic_parameters(&bi.into_block_identifier())
1363            .await?;
1364        let block_hash = extract_metadata(&response)?;
1365        let response = types::CryptographicParameters::try_from(response.into_inner())?;
1366        Ok(QueryResponse {
1367            block_hash,
1368            response,
1369        })
1370    }
1371
1372    /// Get the list of accounts in the given block.
1373    /// The stream will end when all accounts that exist in the state at the end
1374    /// of the given block have been returned. If the block does not exist
1375    /// [`QueryError::NotFound`] is returned.
1376    pub async fn get_account_list(
1377        &mut self,
1378        bi: impl IntoBlockIdentifier,
1379    ) -> endpoints::QueryResult<
1380        QueryResponse<impl Stream<Item = Result<AccountAddress, tonic::Status>>>,
1381    > {
1382        let response = self
1383            .client
1384            .get_account_list(&bi.into_block_identifier())
1385            .await?;
1386        let block_hash = extract_metadata(&response)?;
1387        let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1388        Ok(QueryResponse {
1389            block_hash,
1390            response: stream,
1391        })
1392    }
1393
1394    /// Get a list of all smart contract modules. The stream will end
1395    /// when all modules that exist in the state at the end of the given
1396    /// block have been returned.
1397    /// If the block does not exist [`QueryError::NotFound`] is returned.
1398    pub async fn get_module_list(
1399        &mut self,
1400        bi: impl IntoBlockIdentifier,
1401    ) -> endpoints::QueryResult<
1402        QueryResponse<impl Stream<Item = Result<ModuleReference, tonic::Status>>>,
1403    > {
1404        let response = self
1405            .client
1406            .get_module_list(&bi.into_block_identifier())
1407            .await?;
1408        let block_hash = extract_metadata(&response)?;
1409        let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1410        Ok(QueryResponse {
1411            block_hash,
1412            response: stream,
1413        })
1414    }
1415
1416    /// Get the source of a smart contract module.
1417    /// If the block or module does not exist [`QueryError::NotFound`] is
1418    /// returned.
1419    pub async fn get_module_source(
1420        &mut self,
1421        module_ref: &ModuleReference,
1422        bi: impl IntoBlockIdentifier,
1423    ) -> endpoints::QueryResult<QueryResponse<types::smart_contracts::WasmModule>> {
1424        let response = self
1425            .client
1426            .get_module_source((module_ref, &bi.into_block_identifier()))
1427            .await?;
1428        let block_hash = extract_metadata(&response)?;
1429        let response = types::smart_contracts::WasmModule::try_from(response.into_inner())?;
1430        Ok(QueryResponse {
1431            block_hash,
1432            response,
1433        })
1434    }
1435
1436    /// Get the list of smart contract instances in a given block.
1437    /// The stream will end when all instances that exist in the state at the
1438    /// end of the given block have been returned. If the block does not
1439    /// exist [`QueryError::NotFound`] is returned.
1440    pub async fn get_instance_list(
1441        &mut self,
1442        bi: impl IntoBlockIdentifier,
1443    ) -> endpoints::QueryResult<
1444        QueryResponse<impl Stream<Item = Result<ContractAddress, tonic::Status>>>,
1445    > {
1446        let response = self
1447            .client
1448            .get_instance_list(&bi.into_block_identifier())
1449            .await?;
1450        let block_hash = extract_metadata(&response)?;
1451        let stream = response.into_inner().map(|x| x.map(From::from));
1452        Ok(QueryResponse {
1453            block_hash,
1454            response: stream,
1455        })
1456    }
1457
1458    /// Get information about a smart contract instance as it appears at the end
1459    /// of the given block. If the block or instance does not exist
1460    /// [`QueryError::NotFound`] is returned.
1461    pub async fn get_instance_info(
1462        &mut self,
1463        address: ContractAddress,
1464        bi: impl IntoBlockIdentifier,
1465    ) -> endpoints::QueryResult<QueryResponse<InstanceInfo>> {
1466        let response = self
1467            .client
1468            .get_instance_info((address, &bi.into_block_identifier()))
1469            .await?;
1470        let block_hash = extract_metadata(&response)?;
1471        let response = InstanceInfo::try_from(response.into_inner())?;
1472        Ok(QueryResponse {
1473            block_hash,
1474            response,
1475        })
1476    }
1477
1478    /// Get a stream of ancestors for the provided block.
1479    /// Starting with the provided block itself, moving backwards until no more
1480    /// ancestors or the requested number of ancestors have been returned.
1481    pub async fn get_ancestors(
1482        &mut self,
1483        bi: impl IntoBlockIdentifier,
1484        limit: u64,
1485    ) -> endpoints::QueryResult<QueryResponse<impl Stream<Item = Result<BlockHash, tonic::Status>>>>
1486    {
1487        let response = self
1488            .client
1489            .get_ancestors((&bi.into_block_identifier(), limit))
1490            .await?;
1491        let block_hash = extract_metadata(&response)?;
1492        let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1493        Ok(QueryResponse {
1494            block_hash,
1495            response: stream,
1496        })
1497    }
1498
1499    /// Return a stream of blocks that are finalized from the time the query is
1500    /// made onward.
1501    /// This can be used to listen for newly finalized blocks.
1502    ///
1503    /// Note: There is no guarantee that blocks will not be skipped if the
1504    /// client is too slow in processing the stream, however blocks will
1505    /// always be sent by increasing block height.
1506    pub async fn get_finalized_blocks(
1507        &mut self,
1508    ) -> endpoints::QueryResult<impl Stream<Item = Result<FinalizedBlockInfo, tonic::Status>>> {
1509        let response = self
1510            .client
1511            .get_finalized_blocks(generated::Empty::default())
1512            .await?;
1513        let stream = response.into_inner().map(|x| match x {
1514            Ok(v) => {
1515                let block_hash = v.hash.require().and_then(TryFrom::try_from)?;
1516                let height = v.height.require()?.into();
1517                Ok(FinalizedBlockInfo { block_hash, height })
1518            }
1519            Err(x) => Err(x),
1520        });
1521        Ok(stream)
1522    }
1523
1524    /// Get the exact state of a specific contract instance, streamed as a list
1525    /// of key-value pairs. The list is streamed in lexicographic order of
1526    /// keys.
1527    /// If the block or instance does not exist [`QueryError::NotFound`] is
1528    /// returned.
1529    pub async fn get_instance_state(
1530        &mut self,
1531        ca: ContractAddress,
1532        bi: impl IntoBlockIdentifier,
1533    ) -> endpoints::QueryResult<
1534        QueryResponse<impl Stream<Item = Result<(Vec<u8>, Vec<u8>), tonic::Status>>>,
1535    > {
1536        let response = self
1537            .client
1538            .get_instance_state((ca, &bi.into_block_identifier()))
1539            .await?;
1540        let block_hash = extract_metadata(&response)?;
1541        let stream = response.into_inner().map(|x| match x {
1542            Ok(v) => {
1543                let key = v.key;
1544                let value = v.value;
1545                Ok((key, value))
1546            }
1547            Err(x) => Err(x),
1548        });
1549        Ok(QueryResponse {
1550            block_hash,
1551            response: stream,
1552        })
1553    }
1554
1555    /// Get the value at a specific key of a contract state. In contrast to
1556    /// [`get_instance_state`](Self::get_instance_state) this is more efficient,
1557    /// but requires the user to know the specific key to look for.
1558    /// If the block or instance does not exist [`QueryError::NotFound`] is
1559    /// returned.
1560    pub async fn instance_state_lookup(
1561        &mut self,
1562        ca: ContractAddress,
1563        key: impl Into<Vec<u8>>,
1564        bi: impl IntoBlockIdentifier,
1565    ) -> endpoints::QueryResult<QueryResponse<Vec<u8>>> {
1566        let response = self
1567            .client
1568            .instance_state_lookup((ca, &bi.into_block_identifier(), key))
1569            .await?;
1570        let block_hash = extract_metadata(&response)?;
1571        Ok(QueryResponse {
1572            block_hash,
1573            response: response.into_inner().value,
1574        })
1575    }
1576
1577    /// Get the status of and information about a specific block item
1578    /// (transaction). If the block item does not exist
1579    /// [`QueryError::NotFound`] is returned.
1580    pub async fn get_block_item_status(
1581        &mut self,
1582        th: &TransactionHash,
1583    ) -> endpoints::QueryResult<TransactionStatus> {
1584        let response = self.client.get_block_item_status(th).await?;
1585        let response = TransactionStatus::try_from(response.into_inner())?;
1586        Ok(response)
1587    }
1588
1589    /// Send a block item. A block item is either an `AccountTransaction`, which
1590    /// is a transaction signed and paid for by an account, a
1591    /// `CredentialDeployment`, which creates a new account, or
1592    /// `UpdateInstruction`, which is an instruction to change some
1593    /// parameters of the chain. Update instructions can only be sent by the
1594    /// governance committee.
1595    pub async fn send_block_item<P: PayloadLike>(
1596        &mut self,
1597        bi: &transactions::BlockItem<P>,
1598    ) -> endpoints::RPCResult<TransactionHash> {
1599        let response = self.client.send_block_item(bi).await?;
1600        let response = TransactionHash::try_from(response.into_inner())?;
1601        Ok(response)
1602    }
1603
1604    /// Send an account transaction. This is just a helper around
1605    /// [`send_block_item`](Self::send_block_item) block item for convenience.
1606    pub async fn send_account_transaction<P: PayloadLike>(
1607        &mut self,
1608        at: transactions::AccountTransaction<P>,
1609    ) -> endpoints::RPCResult<TransactionHash> {
1610        self.send_block_item(&at.into()).await
1611    }
1612
1613    /// Get the hash to be signed for an account transaction from the node. The
1614    /// hash returned can then be used for signing when constructing
1615    /// [`TransactionSignature`] as part of calling [`Client::send_block_item`].
1616    ///
1617    /// This is provided as a convenience to support cases where the right SDK
1618    /// is not available for interacting with the node.
1619    ///
1620    /// This SDK can compute the hash off-line and it is not recommended to use
1621    /// this endpoint, instead use [`compute_transaction_sign_hash`].
1622    ///
1623    /// [`compute_transaction_sign_hash`]:
1624    /// types::transactions::compute_transaction_sign_hash
1625    pub async fn get_account_transaction_sign_hash(
1626        &mut self,
1627        header: &transactions::TransactionHeader,
1628        payload: &transactions::Payload,
1629    ) -> endpoints::RPCResult<TransactionSignHash> {
1630        let response = self
1631            .client
1632            .get_account_transaction_sign_hash((header, payload))
1633            .await?;
1634        let response = TransactionSignHash::try_from(response.into_inner())?;
1635        Ok(response)
1636    }
1637
1638    /// Wait until the transaction is finalized. Returns
1639    /// [`NotFound`](QueryError::NotFound) in case the transaction is not
1640    /// known to the node. In case of success, the return value is a pair of the
1641    /// block hash of the block that contains the transactions, and its
1642    /// outcome in the block.
1643    ///
1644    /// Since this can take an indefinite amount of time in general, users of
1645    /// this function might wish to wrap it inside
1646    /// [`timeout`](tokio::time::timeout) handler and handle the resulting
1647    /// failure.
1648    pub async fn wait_until_finalized(
1649        &mut self,
1650        hash: &types::hashes::TransactionHash,
1651    ) -> endpoints::QueryResult<(types::hashes::BlockHash, types::BlockItemSummary)> {
1652        let hash = *hash;
1653        let process_response = |response| {
1654            if let types::TransactionStatus::Finalized(blocks) = response {
1655                let mut iter = blocks.into_iter();
1656                if let Some(rv) = iter.next() {
1657                    if iter.next().is_some() {
1658                        Err(tonic::Status::internal(
1659                            "Finalized transaction finalized into multiple blocks. This cannot \
1660                             happen.",
1661                        )
1662                        .into())
1663                    } else {
1664                        Ok::<_, QueryError>(Some(rv))
1665                    }
1666                } else {
1667                    Err(tonic::Status::internal(
1668                        "Finalized transaction finalized into no blocks. This cannot happen.",
1669                    )
1670                    .into())
1671                }
1672            } else {
1673                Ok(None)
1674            }
1675        };
1676
1677        match process_response(self.get_block_item_status(&hash).await?)? {
1678            Some(rv) => Ok(rv),
1679            None => {
1680                // if the first query did not succeed then start listening for finalized blocks.
1681                // and on each new block try to query the status.
1682                let mut blocks = self.get_finalized_blocks().await?;
1683                while blocks.next().await.transpose()?.is_some() {
1684                    if let Some(rv) = process_response(self.get_block_item_status(&hash).await?)? {
1685                        return Ok(rv);
1686                    }
1687                }
1688                Err(QueryError::NotFound)
1689            }
1690        }
1691    }
1692
1693    /// Run the smart contract instance entrypoint in a given context and in the
1694    /// state at the end of the given block and return the results.
1695    /// If the block does not exist [`QueryError::NotFound`] is returned.
1696    pub async fn invoke_instance(
1697        &mut self,
1698        bi: impl IntoBlockIdentifier,
1699        context: &ContractContext,
1700    ) -> endpoints::QueryResult<QueryResponse<InvokeContractResult>> {
1701        let response = self
1702            .client
1703            .invoke_instance((&bi.into_block_identifier(), context))
1704            .await?;
1705        let block_hash = extract_metadata(&response)?;
1706        let response = InvokeContractResult::try_from(response.into_inner())?;
1707        Ok(QueryResponse {
1708            block_hash,
1709            response,
1710        })
1711    }
1712
1713    /// Start a dry-run sequence that can be used to simulate a series of
1714    /// transactions and other operations on the node.
1715    ///
1716    /// Before invoking any other operations on the [`dry_run::DryRun`] object,
1717    /// the state must be loaded by calling
1718    /// [`dry_run::DryRun::load_block_state`].
1719    pub async fn begin_dry_run(&mut self) -> endpoints::QueryResult<dry_run::DryRun> {
1720        Ok(dry_run::DryRun::new(&mut self.client).await?)
1721    }
1722
1723    /// Start a dry-run sequence that can be used to simulate a series of
1724    /// transactions and other operations on the node, starting from the
1725    /// specified block.
1726    pub async fn dry_run(
1727        &mut self,
1728        bi: impl IntoBlockIdentifier,
1729    ) -> dry_run::DryRunResult<(dry_run::DryRun, dry_run::BlockStateLoaded)> {
1730        let mut runner = dry_run::DryRun::new(&mut self.client).await?;
1731        let load_result = runner.load_block_state(bi).await?;
1732        Ok(WithRemainingQuota {
1733            inner:           (runner, load_result.inner),
1734            quota_remaining: load_result.quota_remaining,
1735        })
1736    }
1737
1738    /// Get information, such as height, timings, and transaction counts for the
1739    /// given block. If the block does not exist [`QueryError::NotFound`] is
1740    /// returned.
1741    pub async fn get_block_info(
1742        &mut self,
1743        bi: impl IntoBlockIdentifier,
1744    ) -> endpoints::QueryResult<QueryResponse<types::queries::BlockInfo>> {
1745        let response = self
1746            .client
1747            .get_block_info(&bi.into_block_identifier())
1748            .await?;
1749        let block_hash = extract_metadata(&response)?;
1750        let response = types::queries::BlockInfo::try_from(response.into_inner())?;
1751        Ok(QueryResponse {
1752            block_hash,
1753            response,
1754        })
1755    }
1756
1757    /// Get information about whether a block identified by `bi` is a payday
1758    /// block or not. This will always return `false` for blocks produced prior
1759    /// to protocol version 4. If the block does not exits
1760    /// [`QueryError::NotFound`] is returned.
1761    pub async fn is_payday_block(
1762        &mut self,
1763        bi: impl IntoBlockIdentifier,
1764    ) -> endpoints::QueryResult<QueryResponse<bool>> {
1765        let mut special_events = self.get_block_special_events(bi).await?;
1766        let block_hash = special_events.block_hash;
1767
1768        while let Some(event) = special_events.response.next().await.transpose()? {
1769            let has_payday_event = matches!(
1770                event,
1771                SpecialTransactionOutcome::PaydayPoolReward { .. }
1772                    | SpecialTransactionOutcome::PaydayAccountReward { .. }
1773                    | SpecialTransactionOutcome::PaydayFoundationReward { .. }
1774            );
1775
1776            if has_payday_event {
1777                return Ok(QueryResponse {
1778                    block_hash,
1779                    response: true,
1780                });
1781            };
1782        }
1783
1784        Ok(QueryResponse {
1785            block_hash,
1786            response: false,
1787        })
1788    }
1789
1790    /// Get all the bakers at the end of the given block.
1791    /// If the block does not exist [`QueryError::NotFound`] is returned.
1792    pub async fn get_baker_list(
1793        &mut self,
1794        bi: impl IntoBlockIdentifier,
1795    ) -> endpoints::QueryResult<
1796        QueryResponse<impl Stream<Item = Result<types::BakerId, tonic::Status>>>,
1797    > {
1798        let response = self
1799            .client
1800            .get_baker_list(&bi.into_block_identifier())
1801            .await?;
1802        let block_hash = extract_metadata(&response)?;
1803        let stream = response.into_inner().map(|x| x.map(From::from));
1804        Ok(QueryResponse {
1805            block_hash,
1806            response: stream,
1807        })
1808    }
1809
1810    /// Get information about a given pool at the end of a given block.
1811    /// If the block does not exist or is prior to protocol version 4 then
1812    /// [`QueryError::NotFound`] is returned.
1813    pub async fn get_pool_info(
1814        &mut self,
1815        block_id: impl IntoBlockIdentifier,
1816        baker_id: types::BakerId,
1817    ) -> endpoints::QueryResult<QueryResponse<types::BakerPoolStatus>> {
1818        let response = self
1819            .client
1820            .get_pool_info((&block_id.into_block_identifier(), baker_id))
1821            .await?;
1822        let block_hash = extract_metadata(&response)?;
1823        let response = types::BakerPoolStatus::try_from(response.into_inner())?;
1824        Ok(QueryResponse {
1825            block_hash,
1826            response,
1827        })
1828    }
1829
1830    /// Get information about the passive delegators at the end of a given
1831    /// block.
1832    /// If the block does not exist or is prior to protocol version 4 then
1833    /// [`QueryError::NotFound`] is returned.
1834    pub async fn get_passive_delegation_info(
1835        &mut self,
1836        block_id: impl IntoBlockIdentifier,
1837    ) -> endpoints::QueryResult<QueryResponse<types::PassiveDelegationStatus>> {
1838        let response = self
1839            .client
1840            .get_passive_delegation_info(&block_id.into_block_identifier())
1841            .await?;
1842        let block_hash = extract_metadata(&response)?;
1843        let response = types::PassiveDelegationStatus::try_from(response.into_inner())?;
1844        Ok(QueryResponse {
1845            block_hash,
1846            response,
1847        })
1848    }
1849
1850    /// Get a list of live blocks at a given height.
1851    pub async fn get_blocks_at_height(
1852        &mut self,
1853        blocks_at_height_input: &endpoints::BlocksAtHeightInput,
1854    ) -> endpoints::QueryResult<Vec<BlockHash>> {
1855        let response = self
1856            .client
1857            .get_blocks_at_height(blocks_at_height_input)
1858            .await?;
1859        let blocks = response
1860            .into_inner()
1861            .blocks
1862            .into_iter()
1863            .map(TryFrom::try_from)
1864            .collect::<Result<_, tonic::Status>>()?;
1865        Ok(blocks)
1866    }
1867
1868    /// Get information about tokenomics at the end of a given block.
1869    /// If the block does not exist [`QueryError::NotFound`] is returned.
1870    pub async fn get_tokenomics_info(
1871        &mut self,
1872        block_id: impl IntoBlockIdentifier,
1873    ) -> endpoints::QueryResult<QueryResponse<types::RewardsOverview>> {
1874        let response = self
1875            .client
1876            .get_tokenomics_info(&block_id.into_block_identifier())
1877            .await?;
1878        let block_hash = extract_metadata(&response)?;
1879        let response = types::RewardsOverview::try_from(response.into_inner())?;
1880        Ok(QueryResponse {
1881            block_hash,
1882            response,
1883        })
1884    }
1885
1886    /// Get the registered delegators of a given pool at the end of a given
1887    /// block.
1888    /// If the block or baker ID does not exist [`QueryError::NotFound`] is
1889    /// returned, and if the block is baked prior to protocol version 4
1890    /// [`QueryError::RPCError`] with status [`Code::InvalidArgument`] is
1891    /// returned. The stream will end when all the delegators have been
1892    /// returned for the given block.
1893    ///
1894    /// In contrast to the [Client::get_pool_delegators_reward_period] which
1895    /// returns delegators that are fixed for the reward period of the
1896    /// block, this endpoint returns the list of delegators that are
1897    /// registered in the block. Any changes to delegators are immediately
1898    /// visible in this list.
1899    pub async fn get_pool_delegators(
1900        &mut self,
1901        bi: impl IntoBlockIdentifier,
1902        baker_id: types::BakerId,
1903    ) -> endpoints::QueryResult<
1904        QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1905    > {
1906        let response = self
1907            .client
1908            .get_pool_delegators((&bi.into_block_identifier(), baker_id))
1909            .await?;
1910        let block_hash = extract_metadata(&response)?;
1911        let stream = response.into_inner().map(|result| match result {
1912            Ok(delegator) => delegator.try_into(),
1913            Err(err) => Err(err),
1914        });
1915        Ok(QueryResponse {
1916            block_hash,
1917            response: stream,
1918        })
1919    }
1920
1921    /// Get the fixed delegators of a given pool for the reward period of the
1922    /// given block.
1923    /// If the block or baker ID does not exist [`QueryError::NotFound`] is
1924    /// returned, and if the block is baked prior to protocol version 4
1925    /// [`QueryError::RPCError`] with status [`Code::InvalidArgument`] is
1926    /// returned. The stream will end when all the delegators have been
1927    /// returned.
1928    ///
1929    /// In contrast to the [Client::get_pool_delegators] which
1930    /// returns delegators registered for the given block, this endpoint
1931    /// returns the fixed delegators contributing stake in the reward period
1932    /// containing the given block.
1933    pub async fn get_pool_delegators_reward_period(
1934        &mut self,
1935        bi: impl IntoBlockIdentifier,
1936        baker_id: types::BakerId,
1937    ) -> endpoints::QueryResult<
1938        QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1939    > {
1940        let response = self
1941            .client
1942            .get_pool_delegators_reward_period((&bi.into_block_identifier(), baker_id))
1943            .await?;
1944        let block_hash = extract_metadata(&response)?;
1945        let stream = response.into_inner().map(|result| match result {
1946            Ok(delegator) => delegator.try_into(),
1947            Err(err) => Err(err),
1948        });
1949        Ok(QueryResponse {
1950            block_hash,
1951            response: stream,
1952        })
1953    }
1954
1955    /// Get the registered passive delegators at the end of a given block.
1956    /// If the block does not exist [`QueryError::NotFound`] is returned, and if
1957    /// the block is baked prior to protocol version 4 [`QueryError::
1958    /// RPCError`] with status [`Code::InvalidArgument`] is returned. The stream
1959    /// will end when all the delegators have been returned.
1960    ///
1961    /// In contrast to the [`Client::get_passive_delegators_reward_period`]
1962    /// which returns delegators that are fixed for the reward period of the
1963    /// block, this endpoint returns the list of delegators that are
1964    /// registered in the block. Any changes to delegators are immediately
1965    /// visible in this list.
1966    pub async fn get_passive_delegators(
1967        &mut self,
1968        bi: impl IntoBlockIdentifier,
1969    ) -> endpoints::QueryResult<
1970        QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1971    > {
1972        let response = self
1973            .client
1974            .get_passive_delegators(&bi.into_block_identifier())
1975            .await?;
1976        let block_hash = extract_metadata(&response)?;
1977        let stream = response.into_inner().map(|result| match result {
1978            Ok(delegator) => delegator.try_into(),
1979            Err(err) => Err(err),
1980        });
1981        Ok(QueryResponse {
1982            block_hash,
1983            response: stream,
1984        })
1985    }
1986
1987    /// Get the fixed passive delegators for the reward period of the given
1988    /// block.
1989    /// If the block does not exist [`QueryError::NotFound`] is returned.
1990    /// If the block is baked prior to protocol version 4,
1991    /// [`QueryError::RPCError`] with status [`Code::InvalidArgument`] is
1992    /// returned. The stream will end when all the delegators have been
1993    /// returned.
1994    ///
1995    /// In contrast to the `GetPassiveDelegators` which returns delegators
1996    /// registered for the given block, this endpoint returns the fixed
1997    /// delegators contributing stake in the reward period containing the
1998    /// given block.
1999    pub async fn get_passive_delegators_reward_period(
2000        &mut self,
2001        bi: impl IntoBlockIdentifier,
2002    ) -> endpoints::QueryResult<
2003        QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
2004    > {
2005        let response = self
2006            .client
2007            .get_passive_delegators_reward_period(&bi.into_block_identifier())
2008            .await?;
2009        let block_hash = extract_metadata(&response)?;
2010        let stream = response.into_inner().map(|result| match result {
2011            Ok(delegator) => delegator.try_into(),
2012            Err(err) => Err(err),
2013        });
2014        Ok(QueryResponse {
2015            block_hash,
2016            response: stream,
2017        })
2018    }
2019
2020    /// Get the current branches of blocks starting from and including the last
2021    /// finalized block.
2022    ///
2023    /// Branches are all live blocks that are successors of the last finalized
2024    /// block. In particular this means that blocks which do not have a
2025    /// parent are not included in this response.
2026    pub async fn get_branches(&mut self) -> endpoints::QueryResult<types::queries::Branch> {
2027        let response = self
2028            .client
2029            .get_branches(generated::Empty::default())
2030            .await?;
2031        let response = types::queries::Branch::try_from(response.into_inner())?;
2032        Ok(response)
2033    }
2034
2035    /// Get information related to the baker election for a particular block.
2036    /// If the block does not exist [`QueryError::NotFound`] is returned.
2037    pub async fn get_election_info(
2038        &mut self,
2039        bi: impl IntoBlockIdentifier,
2040    ) -> endpoints::QueryResult<QueryResponse<types::BirkParameters>> {
2041        let response = self
2042            .client
2043            .get_election_info(&bi.into_block_identifier())
2044            .await?;
2045        let block_hash = extract_metadata(&response)?;
2046        let response = types::BirkParameters::try_from(response.into_inner())?;
2047        Ok(QueryResponse {
2048            block_hash,
2049            response,
2050        })
2051    }
2052
2053    /// Get the identity providers registered as of the end of a given block.
2054    /// If the block does not exist [`QueryError::NotFound`] is returned.
2055    /// The stream will end when all the identity providers have been returned.
2056    pub async fn get_identity_providers(
2057        &mut self,
2058        bi: impl IntoBlockIdentifier,
2059    ) -> endpoints::QueryResult<
2060        QueryResponse<
2061            impl Stream<
2062                Item = Result<
2063                    crate::id::types::IpInfo<crate::id::constants::IpPairing>,
2064                    tonic::Status,
2065                >,
2066            >,
2067        >,
2068    > {
2069        let response = self
2070            .client
2071            .get_identity_providers(&bi.into_block_identifier())
2072            .await?;
2073        let block_hash = extract_metadata(&response)?;
2074        let stream = response.into_inner().map(|result| match result {
2075            Ok(ip_info) => ip_info.try_into(),
2076            Err(err) => Err(err),
2077        });
2078        Ok(QueryResponse {
2079            block_hash,
2080            response: stream,
2081        })
2082    }
2083
2084    /// Get the list of anonymity revokers in the given block.
2085    /// If the block does not exist [`QueryError::NotFound`] is returned.
2086    /// The stream will end when all the anonymity revokers have been returned.
2087    pub async fn get_anonymity_revokers(
2088        &mut self,
2089        bi: impl IntoBlockIdentifier,
2090    ) -> endpoints::QueryResult<
2091        QueryResponse<
2092            impl Stream<
2093                Item = Result<
2094                    crate::id::types::ArInfo<crate::id::constants::ArCurve>,
2095                    tonic::Status,
2096                >,
2097            >,
2098        >,
2099    > {
2100        let response = self
2101            .client
2102            .get_anonymity_revokers(&bi.into_block_identifier())
2103            .await?;
2104        let block_hash = extract_metadata(&response)?;
2105        let stream = response.into_inner().map(|result| match result {
2106            Ok(ar_info) => ar_info.try_into(),
2107            Err(err) => Err(err),
2108        });
2109        Ok(QueryResponse {
2110            block_hash,
2111            response: stream,
2112        })
2113    }
2114
2115    /// Get the list of transactions hashes for transactions that claim to be
2116    /// from the given account, but which are not yet finalized.
2117    /// They are either committed to a block or still pending.
2118    /// The stream will end when all the non-finalized transaction hashes have
2119    /// been returned. If the account does not exist an empty list will be
2120    /// returned.
2121    ///
2122    /// This endpoint is not expected to return a large amount of data in most
2123    /// cases, but in bad network condtions it might.
2124    pub async fn get_account_non_finalized_transactions(
2125        &mut self,
2126        account_address: &AccountAddress,
2127    ) -> endpoints::QueryResult<impl Stream<Item = Result<TransactionHash, tonic::Status>>> {
2128        let response = self
2129            .client
2130            .get_account_non_finalized_transactions(account_address)
2131            .await?;
2132        let stream = response.into_inner().map(|result| match result {
2133            Ok(transaction_hash) => transaction_hash.try_into(),
2134            Err(err) => Err(err),
2135        });
2136        Ok(stream)
2137    }
2138
2139    /// Get the block items included in a given block.
2140    /// If the block does not exist [`QueryError::NotFound`] is returned.
2141    /// The stream will end when all the block items in the given block have
2142    /// been returned.
2143    pub async fn get_block_items(
2144        &mut self,
2145        bi: impl IntoBlockIdentifier,
2146    ) -> endpoints::QueryResult<
2147        QueryResponse<impl Stream<Item = Result<BlockItem<EncodedPayload>, tonic::Status>>>,
2148    > {
2149        let response = self
2150            .client
2151            .get_block_items(&bi.into_block_identifier())
2152            .await?;
2153        let block_hash = extract_metadata(&response)?;
2154        let stream = response.into_inner().map(|result| match result {
2155            Ok(summary) => summary.try_into(),
2156            Err(err) => Err(err),
2157        });
2158        Ok(QueryResponse {
2159            block_hash,
2160            response: stream,
2161        })
2162    }
2163
2164    /// Get the specific **block item** if it is finalized.
2165    /// If the transaction does not exist in a finalized block
2166    /// [`QueryError::NotFound`] is returned.
2167    ///
2168    /// **Note that this is not an efficient method** since the node API does
2169    /// not allow for retrieving just the specific block item, but rather
2170    /// requires retrieving the full block. Use it for testing and debugging
2171    /// only.
2172    ///
2173    /// The return value is a triple of the [`BlockItem`], the hash of the block
2174    /// in which it is finalized, and the outcome in the form of
2175    /// [`BlockItemSummary`].
2176    pub async fn get_finalized_block_item(
2177        &mut self,
2178        th: TransactionHash,
2179    ) -> endpoints::QueryResult<(BlockItem<EncodedPayload>, BlockHash, BlockItemSummary)> {
2180        let status = self.get_block_item_status(&th).await?;
2181        let Some((bh, status)) = status.is_finalized() else {
2182            return Err(QueryError::NotFound);
2183        };
2184        let mut response = self.get_block_items(bh).await?.response;
2185        while let Some(tx) = response.try_next().await? {
2186            if tx.hash() == th {
2187                return Ok((tx, *bh, status.clone()));
2188            }
2189        }
2190        Err(endpoints::QueryError::NotFound)
2191    }
2192
2193    /// Shut down the node.
2194    /// Return a GRPC error if the shutdown failed.
2195    pub async fn shutdown(&mut self) -> endpoints::RPCResult<()> {
2196        self.client.shutdown(generated::Empty::default()).await?;
2197        Ok(())
2198    }
2199
2200    /// Suggest a peer to connect to the submitted peer details.
2201    /// This, if successful, adds the peer to the list of given addresses.
2202    /// Otherwise return a GRPC error.
2203    ///
2204    /// Note: The peer might not be connected to instantly, in that case
2205    /// the node will try to establish the connection in near future. This
2206    /// function returns a GRPC status 'Ok' in this case.
2207    pub async fn peer_connect(&mut self, addr: std::net::SocketAddr) -> endpoints::RPCResult<()> {
2208        let peer_connection = generated::IpSocketAddress {
2209            ip:   Some(generated::IpAddress {
2210                value: addr.ip().to_string(),
2211            }),
2212            port: Some(generated::Port {
2213                value: addr.port() as u32,
2214            }),
2215        };
2216        self.client.peer_connect(peer_connection).await?;
2217        Ok(())
2218    }
2219
2220    /// Disconnect from the peer and remove them from the given addresses list
2221    /// if they are on it. Return if the request was processed successfully.
2222    /// Otherwise return a GRPC error.
2223    pub async fn peer_disconnect(
2224        &mut self,
2225        addr: std::net::SocketAddr,
2226    ) -> endpoints::RPCResult<()> {
2227        let peer_connection = generated::IpSocketAddress {
2228            ip:   Some(generated::IpAddress {
2229                value: addr.ip().to_string(),
2230            }),
2231            port: Some(generated::Port {
2232                value: addr.port() as u32,
2233            }),
2234        };
2235        self.client.peer_disconnect(peer_connection).await?;
2236        Ok(())
2237    }
2238
2239    /// Get a vector of the banned peers.
2240    pub async fn get_banned_peers(
2241        &mut self,
2242    ) -> endpoints::RPCResult<Vec<super::types::network::BannedPeer>> {
2243        Ok(self
2244            .client
2245            .get_banned_peers(generated::Empty::default())
2246            .await?
2247            .into_inner()
2248            .peers
2249            .into_iter()
2250            .map(super::types::network::BannedPeer::try_from)
2251            .collect::<anyhow::Result<Vec<super::types::network::BannedPeer>>>()?)
2252    }
2253
2254    /// Ban a peer.
2255    /// When successful return `Ok(())`, and otherwise return an error
2256    /// describing the issue.
2257    pub async fn ban_peer(
2258        &mut self,
2259        peer_to_ban: super::types::network::PeerToBan,
2260    ) -> endpoints::RPCResult<()> {
2261        self.client.ban_peer(peer_to_ban).await?;
2262        Ok(())
2263    }
2264
2265    /// Unban a peer.
2266    /// When successful return `Ok(())`, and otherwise return an error
2267    /// describing the issue.
2268    pub async fn unban_peer(
2269        &mut self,
2270        banned_peer: &super::types::network::BannedPeer,
2271    ) -> endpoints::RPCResult<()> {
2272        self.client.unban_peer(banned_peer).await?;
2273        Ok(())
2274    }
2275
2276    /// Start a network dump if the feature is enabled on the node.
2277    /// This writes all the network packets into the given file.
2278    /// Return `Ok(())` if a network dump has been initiated, and an error
2279    /// otherwise.
2280    ///
2281    /// * file - The file to write to.
2282    /// * raw - Whether raw packets should be included in the dump or not.
2283    ///
2284    /// Note. If the feature 'network_dump' is not enabled on the node then this
2285    /// will return a 'Precondition failed' error.
2286    pub async fn dump_start(
2287        &mut self,
2288        file: &std::path::Path,
2289        raw: bool,
2290    ) -> endpoints::RPCResult<()> {
2291        let file_str = file.to_str().ok_or_else(|| {
2292            tonic::Status::invalid_argument(
2293                "The provided path cannot is not a valid UTF8 string, so cannot be used.",
2294            )
2295        })?;
2296
2297        self.client
2298            .dump_start(generated::DumpRequest {
2299                file: file_str.to_string(),
2300                raw,
2301            })
2302            .await?;
2303        Ok(())
2304    }
2305
2306    /// Stop an ongoing network dump.
2307    /// Return nothing if it was successfully stopped, and otherwise return an
2308    /// error.
2309    ///
2310    /// Note. If the feature 'network_dump' is not enabled on the node then this
2311    /// will return a 'Precondition failed' error.
2312    pub async fn dump_stop(&mut self) -> endpoints::RPCResult<()> {
2313        self.client.dump_stop(generated::Empty::default()).await?;
2314        Ok(())
2315    }
2316
2317    /// Get a list of the peers that the node is connected to and associated
2318    /// network related information for each peer.
2319    pub async fn get_peers_info(&mut self) -> endpoints::RPCResult<types::network::PeersInfo> {
2320        let response = self
2321            .client
2322            .get_peers_info(generated::Empty::default())
2323            .await?;
2324        let peers_info = types::network::PeersInfo::try_from(response.into_inner())?;
2325        Ok(peers_info)
2326    }
2327
2328    /// Retrieve information about the node.
2329    /// The response contains meta information about the node
2330    /// such as the version of the software, the local time of the node etc.
2331    ///
2332    /// The response also yields network related information such as the node
2333    /// ID, bytes sent/received etc.
2334    ///
2335    /// Finally depending on the type of the node (regular node or
2336    /// 'bootstrapper') the response also yields baking information if
2337    /// the node is configured with baker credentials.
2338    ///
2339    /// Bootstrappers do no reveal any consensus information as they do not run
2340    /// the consensus protocol.
2341    pub async fn get_node_info(&mut self) -> endpoints::RPCResult<types::NodeInfo> {
2342        let response = self
2343            .client
2344            .get_node_info(generated::Empty::default())
2345            .await?;
2346        let node_info = types::NodeInfo::try_from(response.into_inner())?;
2347        Ok(node_info)
2348    }
2349
2350    /// Get the projected earliest time a baker wins the opportunity to bake a
2351    /// block.
2352    /// If the baker is not a baker for the current reward period then then the
2353    /// timestamp returned is the projected time of the first block of the
2354    /// new reward period.
2355    /// Note that the endpoint is only available on a node running at least
2356    /// protocol version 6.
2357    pub async fn get_baker_earliest_win_time(
2358        &mut self,
2359        bid: types::BakerId,
2360    ) -> endpoints::RPCResult<chrono::DateTime<chrono::Utc>> {
2361        let ts = self.client.get_baker_earliest_win_time(bid).await?;
2362        let local_time = ts.into_inner().try_into()?;
2363        Ok(local_time)
2364    }
2365
2366    /// Get the transaction events in a given block. If the block does not exist
2367    /// [`QueryError::NotFound`] is returned. The stream will end when all the
2368    /// transaction events for a given block have been returned.
2369    pub async fn get_block_transaction_events(
2370        &mut self,
2371        bi: impl IntoBlockIdentifier,
2372    ) -> endpoints::QueryResult<
2373        QueryResponse<impl Stream<Item = Result<types::BlockItemSummary, tonic::Status>>>,
2374    > {
2375        let response = self
2376            .client
2377            .get_block_transaction_events(&bi.into_block_identifier())
2378            .await?;
2379        let block_hash = extract_metadata(&response)?;
2380        let stream = response.into_inner().map(|result| match result {
2381            Ok(summary) => summary.try_into(),
2382            Err(err) => Err(err),
2383        });
2384        Ok(QueryResponse {
2385            block_hash,
2386            response: stream,
2387        })
2388    }
2389
2390    /// Get a the special events in a given block. If the block does not exist
2391    /// [`QueryError::NotFound`] is returned. The stream will end when all the
2392    /// special events for a given block have been returned.
2393    ///
2394    /// These are events generated by the protocol, such as minting and reward
2395    /// payouts. They are not directly generated by any transaction.
2396    pub async fn get_block_special_events(
2397        &mut self,
2398        bi: impl IntoBlockIdentifier,
2399    ) -> endpoints::QueryResult<
2400        QueryResponse<impl Stream<Item = Result<types::SpecialTransactionOutcome, tonic::Status>>>,
2401    > {
2402        let response = self
2403            .client
2404            .get_block_special_events(&bi.into_block_identifier())
2405            .await?;
2406        let block_hash = extract_metadata(&response)?;
2407        let stream = response.into_inner().map(|result| match result {
2408            Ok(summary) => summary.try_into(),
2409            Err(err) => Err(err),
2410        });
2411        Ok(QueryResponse {
2412            block_hash,
2413            response: stream,
2414        })
2415    }
2416
2417    /// Get the pending updates to chain parameters at the end of a given block.
2418    /// If the block does not exist [`QueryError::NotFound`] is returned.
2419    /// The stream will end when all the pending updates for a given block have
2420    /// been returned.
2421    pub async fn get_block_pending_updates(
2422        &mut self,
2423        bi: impl IntoBlockIdentifier,
2424    ) -> endpoints::QueryResult<
2425        QueryResponse<impl Stream<Item = Result<types::queries::PendingUpdate, tonic::Status>>>,
2426    > {
2427        let response = self
2428            .client
2429            .get_block_pending_updates(&bi.into_block_identifier())
2430            .await?;
2431        let block_hash = extract_metadata(&response)?;
2432        let stream = response.into_inner().map(|result| match result {
2433            Ok(update) => update.try_into(),
2434            Err(err) => Err(err),
2435        });
2436        Ok(QueryResponse {
2437            block_hash,
2438            response: stream,
2439        })
2440    }
2441
2442    /// Get the winning bakers of an historical `Epoch`.
2443    /// Hence, when this function is invoked using [`EpochIdentifier::Block`]
2444    /// and the [`BlockIdentifier`] is either [`BlockIdentifier::Best`] or
2445    /// [`BlockIdentifier::LastFinal`], then [`tonic::Code::Unavailable`] is
2446    /// returned, as these identifiers are not historical by definition.
2447    ///
2448    /// The stream ends when there
2449    /// are no more rounds for the epoch specified. This only works for
2450    /// epochs in at least protocol version 6. Note that the endpoint is
2451    /// only available on a node running at least protocol version 6.
2452    pub async fn get_winning_bakers_epoch(
2453        &mut self,
2454        ei: impl Into<EpochIdentifier>,
2455    ) -> endpoints::QueryResult<impl Stream<Item = Result<types::WinningBaker, tonic::Status>>>
2456    {
2457        let response = self.client.get_winning_bakers_epoch(&ei.into()).await?;
2458        let stream = response.into_inner().map(|result| match result {
2459            Ok(wb) => wb.try_into(),
2460            Err(err) => Err(err),
2461        });
2462        Ok(stream)
2463    }
2464
2465    /// Get the first block of the epoch.
2466    pub async fn get_first_block_epoch(
2467        &mut self,
2468        ei: impl Into<EpochIdentifier>,
2469    ) -> endpoints::QueryResult<BlockHash> {
2470        let response = self.client.get_first_block_epoch(&ei.into()).await?;
2471        Ok(response.into_inner().try_into()?)
2472    }
2473
2474    /// Get the detailed status of the consensus. This is only available for
2475    /// consensus version 1. If the genesis index is not specified, the
2476    /// status for the current genesis index is returned.
2477    pub async fn get_consensus_detailed_status(
2478        &mut self,
2479        genesis_index: Option<GenesisIndex>,
2480    ) -> endpoints::RPCResult<ConsensusDetailedStatus> {
2481        let query = generated::ConsensusDetailedStatusQuery {
2482            genesis_index: genesis_index.map(Into::into),
2483        };
2484        let response = self.client.get_consensus_detailed_status(query).await?;
2485        Ok(response.into_inner().try_into()?)
2486    }
2487
2488    /// Get next available sequence numbers for updating chain parameters after
2489    /// a given block. If the block does not exist then [`QueryError::NotFound`]
2490    /// is returned.
2491    pub async fn get_next_update_sequence_numbers(
2492        &mut self,
2493        block_id: impl IntoBlockIdentifier,
2494    ) -> endpoints::QueryResult<QueryResponse<types::queries::NextUpdateSequenceNumbers>> {
2495        let response = self
2496            .client
2497            .get_next_update_sequence_numbers(&block_id.into_block_identifier())
2498            .await?;
2499        let block_hash = extract_metadata(&response)?;
2500        let response = types::queries::NextUpdateSequenceNumbers::try_from(response.into_inner())?;
2501        Ok(QueryResponse {
2502            block_hash,
2503            response,
2504        })
2505    }
2506
2507    /// Get all accounts that have scheduled releases, with the timestamp of the
2508    /// first pending scheduled release for that account. (Note, this only
2509    /// identifies accounts by index, and only indicates the first pending
2510    /// release for each account.)
2511    pub async fn get_scheduled_release_accounts(
2512        &mut self,
2513        block_id: impl IntoBlockIdentifier,
2514    ) -> endpoints::QueryResult<
2515        QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2516    > {
2517        let response = self
2518            .client
2519            .get_scheduled_release_accounts(&block_id.into_block_identifier())
2520            .await?;
2521        let block_hash = extract_metadata(&response)?;
2522        let stream = response.into_inner().map(|result| match result {
2523            Ok(pending) => pending.try_into(),
2524            Err(err) => Err(err),
2525        });
2526        Ok(QueryResponse {
2527            block_hash,
2528            response: stream,
2529        })
2530    }
2531
2532    /// Get all accounts that have stake in cooldown, with the timestamp of the
2533    /// first pending cooldown expiry for each account. (Note, this only
2534    /// identifies accounts by index, and only indicates the first pending
2535    /// cooldown for each account.) Prior to protocol version 7, the
2536    /// resulting stream will always be empty.
2537    pub async fn get_cooldown_accounts(
2538        &mut self,
2539        block_id: impl IntoBlockIdentifier,
2540    ) -> endpoints::QueryResult<
2541        QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2542    > {
2543        let response = self
2544            .client
2545            .get_cooldown_accounts(&block_id.into_block_identifier())
2546            .await?;
2547        let block_hash = extract_metadata(&response)?;
2548        let stream = response.into_inner().map(|result| match result {
2549            Ok(pending) => pending.try_into(),
2550            Err(err) => Err(err),
2551        });
2552        Ok(QueryResponse {
2553            block_hash,
2554            response: stream,
2555        })
2556    }
2557
2558    /// Get all accounts that have stake in pre-cooldown.
2559    /// (This only identifies accounts by index.)
2560    /// Prior to protocol version 7, the resulting stream will always be empty.
2561    pub async fn get_pre_cooldown_accounts(
2562        &mut self,
2563        block_id: impl IntoBlockIdentifier,
2564    ) -> endpoints::QueryResult<
2565        QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2566    > {
2567        let response = self
2568            .client
2569            .get_pre_cooldown_accounts(&block_id.into_block_identifier())
2570            .await?;
2571        let block_hash = extract_metadata(&response)?;
2572        let stream = response.into_inner().map(|result| match result {
2573            Ok(account) => Ok(account.into()),
2574            Err(err) => Err(err),
2575        });
2576        Ok(QueryResponse {
2577            block_hash,
2578            response: stream,
2579        })
2580    }
2581
2582    /// Get all accounts that have stake in pre-pre-cooldown.
2583    /// (This only identifies accounts by index.)
2584    /// Prior to protocol version 7, the resulting stream will always be empty.
2585    pub async fn get_pre_pre_cooldown_accounts(
2586        &mut self,
2587        block_id: impl IntoBlockIdentifier,
2588    ) -> endpoints::QueryResult<
2589        QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2590    > {
2591        let response = self
2592            .client
2593            .get_pre_pre_cooldown_accounts(&block_id.into_block_identifier())
2594            .await?;
2595        let block_hash = extract_metadata(&response)?;
2596        let stream = response.into_inner().map(|result| match result {
2597            Ok(account) => Ok(account.into()),
2598            Err(err) => Err(err),
2599        });
2600        Ok(QueryResponse {
2601            block_hash,
2602            response: stream,
2603        })
2604    }
2605
2606    /// Get the chain parameters in effect after a given block.
2607    /// If the block does not exist [`QueryError::NotFound`] is returned.
2608    pub async fn get_block_chain_parameters(
2609        &mut self,
2610        block_id: impl IntoBlockIdentifier,
2611    ) -> endpoints::QueryResult<QueryResponse<ChainParameters>> {
2612        let response = self
2613            .client
2614            .get_block_chain_parameters(&block_id.into_block_identifier())
2615            .await?;
2616        let block_hash = extract_metadata(&response)?;
2617        let response = ChainParameters::try_from(response.into_inner())?;
2618        Ok(QueryResponse {
2619            block_hash,
2620            response,
2621        })
2622    }
2623
2624    /// For a non-genesis block, this returns the
2625    /// [`QuorumCertificate`](block_certificates::QuorumCertificate), a
2626    /// [`TimeoutCertificate`](block_certificates::TimeoutCertificate) (if
2627    /// present)
2628    /// and [`EpochFinalizationEntry`](block_certificates::EpochFinalizationEntry) (if
2629    /// present).
2630    /// If the block being pointed to is *not* from protocol version 6 or
2631    /// above, then [`InvalidArgument`](`tonic::Code::InvalidArgument`)
2632    /// is returned.
2633    pub async fn get_block_certificates(
2634        &mut self,
2635        bi: impl IntoBlockIdentifier,
2636    ) -> endpoints::QueryResult<QueryResponse<block_certificates::BlockCertificates>> {
2637        let response = self
2638            .client
2639            .get_block_certificates(&bi.into_block_identifier())
2640            .await?;
2641        let block_hash = extract_metadata(&response)?;
2642        let response = block_certificates::BlockCertificates::try_from(response.into_inner())?;
2643        Ok(QueryResponse {
2644            block_hash,
2645            response,
2646        })
2647    }
2648
2649    /// Get the information about a finalization record in a block.
2650    /// A block can contain zero or one finalization record. If a record is
2651    /// contained then this query will return information about the finalization
2652    /// session that produced it, including the finalizers eligible for the
2653    /// session, their power, and whether they signed this particular record. If
2654    /// the block does not exist [`QueryError::NotFound`] is returned.
2655    pub async fn get_block_finalization_summary(
2656        &mut self,
2657        block_id: impl IntoBlockIdentifier,
2658    ) -> endpoints::QueryResult<QueryResponse<Option<types::FinalizationSummary>>> {
2659        let response = self
2660            .client
2661            .get_block_finalization_summary(&block_id.into_block_identifier())
2662            .await?;
2663        let block_hash = extract_metadata(&response)?;
2664        let response = response.into_inner().try_into()?;
2665        Ok(QueryResponse {
2666            block_hash,
2667            response,
2668        })
2669    }
2670
2671    /// Get a continous stream of finalized blocks starting from a given height.
2672    /// This function starts a background task (a `tokio` task) that listens for
2673    /// new finalized blocks. This task is killed when the
2674    /// [`FinalizedBlocksStream`] is dropped.
2675    pub async fn get_finalized_blocks_from(
2676        &mut self,
2677        start_height: AbsoluteBlockHeight,
2678    ) -> endpoints::QueryResult<FinalizedBlocksStream> {
2679        let mut fin_height = self.get_consensus_info().await?.last_finalized_block_height;
2680        let (sender, receiver) = tokio::sync::mpsc::channel(100);
2681        let mut client = self.clone();
2682        let handle = tokio::spawn(async move {
2683            let mut height = start_height;
2684            loop {
2685                if height > fin_height {
2686                    fin_height = client
2687                        .get_consensus_info()
2688                        .await?
2689                        .last_finalized_block_height;
2690                    if height > fin_height {
2691                        break;
2692                    }
2693                } else {
2694                    let mut bi = client.get_blocks_at_height(&height.into()).await?;
2695                    let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2696                    let info = FinalizedBlockInfo { block_hash, height };
2697                    if sender.send(info).await.is_err() {
2698                        return Ok(());
2699                    }
2700                    height = height.next();
2701                }
2702            }
2703            let mut stream = client.get_finalized_blocks().await?;
2704            while let Some(fbi) = stream.next().await.transpose()? {
2705                // recover missed blocks.
2706                while height < fbi.height {
2707                    let mut bi = client.get_blocks_at_height(&height.into()).await?;
2708                    let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2709                    let info = FinalizedBlockInfo { block_hash, height };
2710                    if sender.send(info).await.is_err() {
2711                        return Ok(());
2712                    }
2713                    height = height.next();
2714                }
2715                if sender.send(fbi).await.is_err() {
2716                    return Ok(());
2717                }
2718                height = height.next();
2719            }
2720            Ok(())
2721        });
2722        Ok(FinalizedBlocksStream { handle, receiver })
2723    }
2724
2725    /// Find a block in which the account was created, if it exists and is
2726    /// finalized. The return value is a triple of the absolute block height and
2727    /// the corresponding block hash, and the account information at the
2728    /// end of that block. The block is the first block in which the account
2729    /// appears.
2730    ///
2731    /// Note that this is not necessarily the initial state of the account
2732    /// since there can be transactions updating it in the same block that it is
2733    /// created.
2734    ///
2735    /// Optional bounds can be provided, and the search will only
2736    /// consider blocks in that range. If the lower bound is not
2737    /// provided it defaults to 0, if the upper bound is not provided it
2738    /// defaults to the last finalized block at the time of the call.
2739    ///
2740    /// If the account cannot be found [`QueryError::NotFound`] is returned.
2741    pub async fn find_account_creation(
2742        &mut self,
2743        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2744        addr: AccountAddress,
2745    ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, AccountInfo)> {
2746        self.find_at_lowest_height(range, |mut client, height| async move {
2747            match client.get_account_info(&addr.into(), &height).await {
2748                Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2749                Err(e) if e.is_not_found() => Ok(None),
2750                Err(e) => Err(e),
2751            }
2752        })
2753        .await
2754    }
2755
2756    /// Find a block in which the instance was created, if it exists and is
2757    /// finalized. The return value is a triple of the absolute block height and
2758    /// the corresponding block hash, and the instance information at the
2759    /// end of that block. The block is the first block in which the instance
2760    /// appears.
2761    ///
2762    /// Note that this is not necessarily the initial state of the instance
2763    /// since there can be transactions updating the instance in the same block
2764    /// as the initialization transaction.
2765    ///
2766    /// Optional bounds can be provided, and the search will only
2767    /// consider blocks in that range. If the lower bound is not
2768    /// provided it defaults to 0, if the upper bound is not provided it
2769    /// defaults to the last finalized block at the time of the call.
2770    ///
2771    /// If the instance cannot be found [`QueryError::NotFound`] is returned.
2772    pub async fn find_instance_creation(
2773        &mut self,
2774        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2775        addr: ContractAddress,
2776    ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, InstanceInfo)> {
2777        self.find_at_lowest_height(range, |mut client, height| async move {
2778            match client.get_instance_info(addr, &height).await {
2779                Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2780                Err(e) if e.is_not_found() => Ok(None),
2781                Err(e) => Err(e),
2782            }
2783        })
2784        .await
2785    }
2786
2787    /// Find the first (i.e., earliest) finalized block whose slot time is no
2788    /// earlier than the specified time. If a block is not found return
2789    /// [`QueryError::NotFound`].
2790    ///
2791    /// The search is limited to the bounds specified. If the lower bound is not
2792    /// provided it defaults to 0, if the upper bound is not provided it
2793    /// defaults to the last finalized block at the time of the call.
2794    pub async fn find_first_finalized_block_no_earlier_than(
2795        &mut self,
2796        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2797        time: chrono::DateTime<chrono::Utc>,
2798    ) -> QueryResult<types::queries::BlockInfo> {
2799        self.find_at_lowest_height(range, move |mut client, height| async move {
2800            let info = client.get_block_info(&height).await?.response;
2801            if info.block_slot_time >= time {
2802                Ok(Some(info))
2803            } else {
2804                Ok(None)
2805            }
2806        })
2807        .await
2808    }
2809
2810    /// Find a **finalized** block with lowest height that satisfies the given
2811    /// condition. If a block is not found return [`QueryError::NotFound`].
2812    ///
2813    /// The `test` method should return `Some` if the object is found in the
2814    /// block, and `None` otherwise. It can also signal errors which will
2815    /// terminate search immediately.
2816    ///
2817    /// The precondition for this method is that the `test` method is monotone,
2818    /// i.e., if block at height `h` satisfies the test then also a block at
2819    /// height `h+1` does.
2820    /// If this precondition does not hold then the return value from this
2821    /// method is unspecified.
2822    ///
2823    /// The search is limited to at most the given range, the upper bound is
2824    /// always at most the last finalized block at the time of the call. If the
2825    /// lower bound is not provided it defaults to 0, if the upper bound is
2826    /// not provided it defaults to the last finalized block at the time of
2827    /// the call.
2828    pub async fn find_at_lowest_height<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2829        &mut self,
2830        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2831        test: impl Fn(Self, AbsoluteBlockHeight) -> F,
2832    ) -> QueryResult<A> {
2833        let mut start = match range.start_bound() {
2834            std::ops::Bound::Included(s) => u64::from(*s),
2835            std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2836            std::ops::Bound::Unbounded => 0,
2837        };
2838        let mut end = {
2839            let ci = self.get_consensus_info().await?;
2840            let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2841            match range.end_bound() {
2842                std::ops::Bound::Included(e) => bound(u64::from(*e)),
2843                std::ops::Bound::Excluded(e) => {
2844                    bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2845                }
2846                std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2847            }
2848        };
2849        if end < start {
2850            return Err(QueryError::NotFound);
2851        }
2852        let mut last_found = None;
2853        while start < end {
2854            let mid = start + (end - start) / 2;
2855            let ok = test(self.clone(), mid.into()).await?;
2856            if ok.is_some() {
2857                end = mid;
2858                last_found = ok;
2859            } else {
2860                start = mid + 1;
2861            }
2862        }
2863        last_found.ok_or(QueryError::NotFound)
2864    }
2865
2866    #[deprecated(note = "Use [`find_at_lowest_height`](./struct.Client.html#method.\
2867                         find_at_lowest_height) instead since it avoids an extra call.")]
2868    pub async fn find_earliest_finalized<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2869        &mut self,
2870        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2871        test: impl Fn(Self, AbsoluteBlockHeight, BlockHash) -> F,
2872    ) -> QueryResult<A> {
2873        let mut start = match range.start_bound() {
2874            std::ops::Bound::Included(s) => u64::from(*s),
2875            std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2876            std::ops::Bound::Unbounded => 0,
2877        };
2878        let mut end = {
2879            let ci = self.get_consensus_info().await?;
2880            let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2881            match range.end_bound() {
2882                std::ops::Bound::Included(e) => bound(u64::from(*e)),
2883                std::ops::Bound::Excluded(e) => {
2884                    bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2885                }
2886                std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2887            }
2888        };
2889        if end < start {
2890            return Err(QueryError::NotFound);
2891        }
2892        let mut last_found = None;
2893        while start < end {
2894            let mid = start + (end - start) / 2;
2895            let bh = self
2896                .get_blocks_at_height(&AbsoluteBlockHeight::from(mid).into())
2897                .await?[0]; // using [0] is safe since we are only looking at finalized blocks.
2898            let ok = test(self.clone(), mid.into(), bh).await?;
2899            if ok.is_some() {
2900                end = mid;
2901                last_found = ok;
2902            } else {
2903                start = mid + 1;
2904            }
2905        }
2906        last_found.ok_or(QueryError::NotFound)
2907    }
2908
2909    /// Get all bakers in the reward period of a block.
2910    /// This endpoint is only supported for protocol version 4 and onwards.
2911    /// If the protocol does not support the endpoint then an
2912    /// [`IllegalArgument`](tonic::Code::InvalidArgument) is returned.
2913    pub async fn get_bakers_reward_period(
2914        &mut self,
2915        bi: impl IntoBlockIdentifier,
2916    ) -> endpoints::QueryResult<
2917        QueryResponse<impl Stream<Item = Result<types::BakerRewardPeriodInfo, tonic::Status>>>,
2918    > {
2919        let response = self
2920            .client
2921            .get_bakers_reward_period(&bi.into_block_identifier())
2922            .await?;
2923        let block_hash = extract_metadata(&response)?;
2924        let stream = response.into_inner().map(|result| match result {
2925            Ok(baker) => baker.try_into(),
2926            Err(err) => Err(err),
2927        });
2928        Ok(QueryResponse {
2929            block_hash,
2930            response: stream,
2931        })
2932    }
2933
2934    /// Retrieve the list of protocol level tokens that exist at the end of the
2935    /// given block.
2936    ///
2937    /// This endpoint is only relevant starting from Concordium Protocol Version
2938    /// 9 and onwards.
2939    pub async fn get_token_list(
2940        &mut self,
2941        bi: impl IntoBlockIdentifier,
2942    ) -> endpoints::QueryResult<
2943        QueryResponse<impl Stream<Item = Result<protocol_level_tokens::TokenId, tonic::Status>>>,
2944    > {
2945        let response = self
2946            .client
2947            .get_token_list(&bi.into_block_identifier())
2948            .await?;
2949        let block_hash = extract_metadata(&response)?;
2950        let stream = response.into_inner().map(|result| match result {
2951            Ok(token_id) => protocol_level_tokens::TokenId::try_from(token_id),
2952            Err(err) => Err(err),
2953        });
2954        Ok(QueryResponse {
2955            block_hash,
2956            response: stream,
2957        })
2958    }
2959
2960    /// Retrieve the information about the given protocol level token in the
2961    /// given block.
2962    ///
2963    /// This endpoint is only relevant starting from Concordium Protocol Version
2964    /// 9 and onwards.
2965    pub async fn get_token_info(
2966        &mut self,
2967        token_id: protocol_level_tokens::TokenId,
2968        bi: impl IntoBlockIdentifier,
2969    ) -> endpoints::QueryResult<QueryResponse<protocol_level_tokens::TokenInfo>> {
2970        let request = generated::TokenInfoRequest {
2971            block_hash: Some((&bi.into_block_identifier()).into()),
2972            token_id:   Some(token_id.into()),
2973        };
2974        let response = self.client.get_token_info(request).await?;
2975        let block_hash = extract_metadata(&response)?;
2976        let response = protocol_level_tokens::TokenInfo::try_from(response.into_inner())?;
2977        Ok(QueryResponse {
2978            block_hash,
2979            response,
2980        })
2981    }
2982}
2983
2984/// A stream of finalized blocks. This contains a background task that polls
2985/// for new finalized blocks indefinitely. The task can be stopped by dropping
2986/// the object.
2987pub struct FinalizedBlocksStream {
2988    handle:   tokio::task::JoinHandle<endpoints::QueryResult<()>>,
2989    receiver: tokio::sync::mpsc::Receiver<FinalizedBlockInfo>,
2990}
2991
2992// Make sure to abort the background task so that those resources are cleaned up
2993// before we drop the handle.
2994impl Drop for FinalizedBlocksStream {
2995    fn drop(&mut self) { self.handle.abort(); }
2996}
2997
2998impl FinalizedBlocksStream {
2999    /// Retrieves the next finalized block from the stream. This function will
3000    /// block until a finalized block becomes available. To avoid waiting
3001    /// indefinitely, consider using [`FinalizedBlocksStream::next_timeout`]
3002    /// instead. If the channel is closed, the next element is `None`.
3003    pub async fn next(&mut self) -> Option<FinalizedBlockInfo> { self.receiver.recv().await }
3004
3005    /// Similar to [`FinalizedBlocksStream::next`], but with a maximum wait time
3006    /// defined by the specified duration between each finalized block.
3007    pub async fn next_timeout(
3008        &mut self,
3009        duration: std::time::Duration,
3010    ) -> Result<Option<FinalizedBlockInfo>, tokio::time::error::Elapsed> {
3011        tokio::time::timeout(duration, async move { self.next().await }).await
3012    }
3013
3014    /// Get the next chunk of blocks. If the finalized block poller has been
3015    /// disconnected this will return `Err(blocks)` where `blocks` are the
3016    /// finalized blocks that were retrieved before closure. In that case
3017    /// all further calls will return `Err(Vec::new())`.
3018    ///
3019    /// In case of success up to `max(1, n)` elements will be returned. This
3020    /// function will block so it always returns at least one element, and
3021    /// will retrieve up to `n` elements without blocking further once at least
3022    /// one element has been acquired.
3023    pub async fn next_chunk(
3024        &mut self,
3025        n: usize,
3026    ) -> Result<Vec<FinalizedBlockInfo>, Vec<FinalizedBlockInfo>> {
3027        let mut out = Vec::with_capacity(n);
3028        let first = self.receiver.recv().await;
3029        match first {
3030            Some(v) => out.push(v),
3031            None => {
3032                return Err(out);
3033            }
3034        }
3035        for _ in 1..n {
3036            match self.receiver.try_recv() {
3037                Ok(v) => {
3038                    out.push(v);
3039                }
3040                Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
3041                    break;
3042                }
3043                Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return Err(out),
3044            }
3045        }
3046        Ok(out)
3047    }
3048
3049    /// Like [`next_chunk`](Self::next_chunk), but waits no more than the given
3050    /// duration for the block. The boolean signifies whether an error
3051    /// occurred (it is `true` if an error occurred) while getting blocks.
3052    /// If that is the case further calls will always yield an error.
3053    ///
3054    /// The first field of the response indicates if an error occurred. This
3055    /// will only happen if the stream of finalized blocks has unexpectedly
3056    /// dropped.
3057    pub async fn next_chunk_timeout(
3058        &mut self,
3059        n: usize,
3060        duration: std::time::Duration,
3061    ) -> Result<(bool, Vec<FinalizedBlockInfo>), tokio::time::error::Elapsed> {
3062        let mut out = Vec::with_capacity(n);
3063        let first = self.next_timeout(duration).await?;
3064        match first {
3065            Some(v) => out.push(v),
3066            None => return Ok((true, out)),
3067        }
3068        for _ in 1..n {
3069            match self.receiver.try_recv() {
3070                Ok(v) => {
3071                    out.push(v);
3072                }
3073                Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
3074                    break;
3075                }
3076                Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
3077                    return Ok((true, out))
3078                }
3079            }
3080        }
3081        Ok((false, out))
3082    }
3083}
3084
3085fn extract_metadata<T>(response: &tonic::Response<T>) -> endpoints::RPCResult<BlockHash> {
3086    match response.metadata().get("blockhash") {
3087        Some(bytes) => {
3088            let bytes = bytes.as_bytes();
3089            if bytes.len() == 64 {
3090                let mut hash = [0u8; 32];
3091                if hex::decode_to_slice(bytes, &mut hash).is_err() {
3092                    tonic::Status::unknown("Response does correctly encode the block hash.");
3093                }
3094                Ok(hash.into())
3095            } else {
3096                Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3097                    "Response does not include the expected metadata.",
3098                )))
3099            }
3100        }
3101        None => Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3102            "Response does not include the expected metadata.",
3103        ))),
3104    }
3105}
3106
3107/// A helper trait to make it simpler to require specific fields when parsing a
3108/// protobuf message by allowing us to use method calling syntax and
3109/// constructing responses that match the calling context, allowing us to use
3110/// the `?` syntax.
3111///
3112/// The main reason for needing this is that in proto3 all fields are optional,
3113/// so it is up to the application to validate inputs if they are required.
3114pub(crate) trait Require<E> {
3115    type A;
3116    fn require(self) -> Result<Self::A, E>;
3117}
3118
3119impl<A> Require<tonic::Status> for Option<A> {
3120    type A = A;
3121
3122    fn require(self) -> Result<Self::A, tonic::Status> {
3123        match self {
3124            Some(v) => Ok(v),
3125            None => Err(tonic::Status::invalid_argument("missing field in response")),
3126        }
3127    }
3128}
3129
3130#[cfg(test)]
3131mod tests {
3132    use super::*;
3133    #[test]
3134    /// Test the different cases when parsing BlockIdentifiers.
3135    fn block_ident_from_str() -> anyhow::Result<()> {
3136        let b1 = "best".parse::<BlockIdentifier>()?;
3137        assert_eq!(b1, BlockIdentifier::Best);
3138
3139        let b2 = "lastFinal".parse::<BlockIdentifier>()?;
3140        assert_eq!(b2, BlockIdentifier::LastFinal);
3141
3142        let b3 = "lastfinal".parse::<BlockIdentifier>()?;
3143        assert_eq!(b3, BlockIdentifier::LastFinal);
3144
3145        let b4 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
3146            .parse::<BlockIdentifier>()?;
3147        assert_eq!(
3148            b4,
3149            BlockIdentifier::Given(
3150                "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()?
3151            )
3152        );
3153
3154        let b5 = "@33".parse::<BlockIdentifier>()?;
3155        assert_eq!(b5, BlockIdentifier::AbsoluteHeight(33.into()));
3156
3157        let b6 = "@33/3".parse::<BlockIdentifier>()?;
3158        assert_eq!(
3159            b6,
3160            BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3161                genesis_index: 3.into(),
3162                height:        33.into(),
3163                restrict:      false,
3164            })
3165        );
3166
3167        let b7 = "@33/3!".parse::<BlockIdentifier>()?;
3168        assert_eq!(
3169            b7,
3170            BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3171                genesis_index: 3.into(),
3172                height:        33.into(),
3173                restrict:      true,
3174            })
3175        );
3176
3177        Ok(())
3178    }
3179}