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