Skip to main content

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, TransactionSignaturesV1, 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<&transactions::TransactionHeaderV1> for generated::AccountTransactionHeaderV1 {
759    fn from(v: &transactions::TransactionHeaderV1) -> Self {
760        Self {
761            sender: Some(generated::AccountAddress::from(v.sender)),
762            sponsor: v.sponsor.map(generated::AccountAddress::from),
763            sequence_number: Some(v.nonce.into()),
764            energy_amount: Some(v.energy_amount.into()),
765            expiry: Some(v.expiry.into()),
766        }
767    }
768}
769
770impl From<TransactionSignature> for generated::AccountTransactionSignature {
771    fn from(v: TransactionSignature) -> Self {
772        (&v).into()
773    }
774}
775
776impl From<&TransactionSignature> for generated::AccountTransactionSignature {
777    fn from(v: &TransactionSignature) -> Self {
778        Self {
779            signatures: {
780                let mut cred_map: HashMap<u32, generated::AccountSignatureMap> = HashMap::new();
781                for (cred_idx, sig_map) in v.signatures.iter() {
782                    let mut acc_sig_map: HashMap<u32, generated::Signature> = HashMap::new();
783                    for (key_idx, sig) in sig_map.iter() {
784                        acc_sig_map.insert(
785                            key_idx.0.into(),
786                            generated::Signature {
787                                value: sig.sig.to_owned(),
788                            },
789                        );
790                    }
791                    cred_map.insert(
792                        cred_idx.index.into(),
793                        generated::AccountSignatureMap {
794                            signatures: acc_sig_map,
795                        },
796                    );
797                }
798                cred_map
799            },
800        }
801    }
802}
803
804impl From<&TransactionSignaturesV1> for generated::AccountTransactionV1Signatures {
805    fn from(v: &TransactionSignaturesV1) -> Self {
806        Self {
807            sender_signatures: Some(v.sender.to_owned().into()),
808            sponsor_signatures: v.sponsor.to_owned().map(|s| s.into()),
809        }
810    }
811}
812
813impl IntoRequest<generated::PreAccountTransaction>
814    for (&transactions::TransactionHeader, &transactions::Payload)
815{
816    fn into_request(self) -> tonic::Request<generated::PreAccountTransaction> {
817        let request = generated::PreAccountTransaction {
818            header: Some(self.0.into()),
819            payload: Some(generated::AccountTransactionPayload {
820                payload: Some(generated::account_transaction_payload::Payload::RawPayload(
821                    self.1.encode().into(),
822                )),
823            }),
824        };
825        tonic::Request::new(request)
826    }
827}
828
829impl<P: PayloadLike> IntoRequest<generated::SendBlockItemRequest> for &transactions::BlockItem<P> {
830    fn into_request(self) -> tonic::Request<generated::SendBlockItemRequest> {
831        let request = match self {
832            transactions::BlockItem::AccountTransaction(v) => {
833                generated::SendBlockItemRequest {
834                    block_item: Some(
835                        generated::send_block_item_request::BlockItem::AccountTransaction(
836                            generated::AccountTransaction {
837                                signature: Some((&v.signature).into()),
838                                header: Some((&v.header).into()),
839                                payload: {
840                                    let atp = generated::AccountTransactionPayload{
841                                    payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())),
842                                };
843                                    Some(atp)
844                                },
845                            },
846                        ),
847                    ),
848                }
849            }
850            transactions::BlockItem::CredentialDeployment(v) => generated::SendBlockItemRequest {
851                block_item: Some(
852                    generated::send_block_item_request::BlockItem::CredentialDeployment(
853                        v.as_ref().into(),
854                    ),
855                ),
856            },
857            transactions::BlockItem::UpdateInstruction(v) => generated::SendBlockItemRequest {
858                block_item: Some(
859                    generated::send_block_item_request::BlockItem::UpdateInstruction(v.into()),
860                ),
861            },
862            transactions::BlockItem::AccountTransactionV1(v) => {
863                generated::SendBlockItemRequest {
864                    block_item: Some(
865                        generated::send_block_item_request::BlockItem::AccountTransactionV1(
866                            generated::AccountTransactionV1 {
867                                signatures: Some((&v.signatures).into()),
868                                header: Some((&v.header).into()),
869                                payload: {
870                                    let atp = generated::AccountTransactionPayload{
871                                    payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())),
872                                };
873                                    Some(atp)
874                                },
875                            },
876                        ),
877                    ),
878                }
879            }
880        };
881        tonic::Request::new(request)
882    }
883}
884
885impl IntoRequest<generated::InvokeInstanceRequest> for (&BlockIdentifier, &ContractContext) {
886    fn into_request(self) -> tonic::Request<generated::InvokeInstanceRequest> {
887        let (block, context) = self;
888        tonic::Request::new(generated::InvokeInstanceRequest {
889            block_hash: Some(block.into()),
890            invoker: context.invoker.as_ref().map(|a| a.into()),
891            instance: Some((&context.contract).into()),
892            amount: Some(context.amount.into()),
893            entrypoint: Some(context.method.as_receive_name().into()),
894            parameter: Some(context.parameter.as_ref().into()),
895            energy: context.energy.map(From::from),
896        })
897    }
898}
899
900impl IntoRequest<generated::PoolInfoRequest> for (&BlockIdentifier, types::BakerId) {
901    fn into_request(self) -> tonic::Request<generated::PoolInfoRequest> {
902        let req = generated::PoolInfoRequest {
903            block_hash: Some(self.0.into()),
904            baker: Some(self.1.into()),
905        };
906        tonic::Request::new(req)
907    }
908}
909
910impl IntoRequest<generated::BakerId> for types::BakerId {
911    fn into_request(self) -> tonic::Request<generated::BakerId> {
912        tonic::Request::new(generated::BakerId {
913            value: self.id.index,
914        })
915    }
916}
917
918impl IntoRequest<generated::BlocksAtHeightRequest> for &endpoints::BlocksAtHeightInput {
919    fn into_request(self) -> tonic::Request<generated::BlocksAtHeightRequest> {
920        tonic::Request::new(self.into())
921    }
922}
923
924impl IntoRequest<generated::GetPoolDelegatorsRequest> for (&BlockIdentifier, types::BakerId) {
925    fn into_request(self) -> tonic::Request<generated::GetPoolDelegatorsRequest> {
926        let req = generated::GetPoolDelegatorsRequest {
927            block_hash: Some(self.0.into()),
928            baker: Some(self.1.into()),
929        };
930        tonic::Request::new(req)
931    }
932}
933
934impl TryFrom<crate::v2::generated::BannedPeer> for types::network::BannedPeer {
935    type Error = anyhow::Error;
936
937    fn try_from(value: crate::v2::generated::BannedPeer) -> Result<Self, Self::Error> {
938        Ok(types::network::BannedPeer(
939            <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip_address.require()?.value)?,
940        ))
941    }
942}
943
944impl TryFrom<generated::IpSocketAddress> for std::net::SocketAddr {
945    type Error = anyhow::Error;
946
947    fn try_from(value: generated::IpSocketAddress) -> Result<Self, Self::Error> {
948        Ok(std::net::SocketAddr::new(
949            <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip.require()?.value)?,
950            value.port.require()?.value as u16,
951        ))
952    }
953}
954
955impl IntoRequest<crate::v2::generated::BannedPeer> for &types::network::BannedPeer {
956    fn into_request(self) -> tonic::Request<crate::v2::generated::BannedPeer> {
957        tonic::Request::new(crate::v2::generated::BannedPeer {
958            ip_address: Some(crate::v2::generated::IpAddress {
959                value: self.0.to_string(),
960            }),
961        })
962    }
963}
964
965impl From<generated::PeerId> for types::network::PeerId {
966    fn from(value: generated::PeerId) -> Self {
967        types::network::PeerId(value.value)
968    }
969}
970
971impl TryFrom<generated::PeersInfo> for types::network::PeersInfo {
972    type Error = anyhow::Error;
973
974    fn try_from(peers_info: generated::PeersInfo) -> Result<Self, Self::Error> {
975        // Get information of the peers that the node is connected to.
976        // Note. If one peer contains malformed data then this function does not
977        // return any information about the others.
978        // This should only happen in cases where the sdk and node is not on the same
979        // major version.
980        let peers = peers_info
981            .peers
982            .into_iter()
983            .map(|peer| {
984                // Parse the catchup status of the peer.
985                let peer_consensus_info =
986                    Upward::from(peer.consensus_info).and_then(|info| match info {
987                        generated::peers_info::peer::ConsensusInfo::Bootstrapper(_) => {
988                            Upward::Known(types::network::PeerConsensusInfo::Bootstrapper)
989                        }
990                        generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(status) => {
991                            let Upward::Known(status) = Upward::from(
992                                generated::peers_info::peer::CatchupStatus::try_from(status).ok(),
993                            ) else {
994                                return Upward::Known(types::network::PeerConsensusInfo::Node(
995                                    Upward::Unknown(()),
996                                ));
997                            };
998                            let status = match status {
999                                generated::peers_info::peer::CatchupStatus::Uptodate => {
1000                                    types::network::PeerCatchupStatus::UpToDate
1001                                }
1002                                generated::peers_info::peer::CatchupStatus::Pending => {
1003                                    types::network::PeerCatchupStatus::Pending
1004                                }
1005                                generated::peers_info::peer::CatchupStatus::Catchingup => {
1006                                    types::network::PeerCatchupStatus::CatchingUp
1007                                }
1008                            };
1009
1010                            Upward::Known(types::network::PeerConsensusInfo::Node(Upward::Known(
1011                                status,
1012                            )))
1013                        }
1014                    });
1015                // Parse the network statistics for the peer.
1016                let stats = peer.network_stats.require()?;
1017                let network_stats = types::network::NetworkStats {
1018                    packets_sent: stats.packets_sent,
1019                    packets_received: stats.packets_received,
1020                    latency: stats.latency,
1021                };
1022                Ok(types::network::Peer {
1023                    peer_id: peer.peer_id.require()?.into(),
1024                    consensus_info: peer_consensus_info,
1025                    network_stats,
1026                    addr: peer.socket_address.require()?.try_into()?,
1027                })
1028            })
1029            .collect::<anyhow::Result<Vec<types::network::Peer>>>()?;
1030        Ok(types::network::PeersInfo { peers })
1031    }
1032}
1033
1034impl TryFrom<generated::node_info::NetworkInfo> for types::NetworkInfo {
1035    type Error = anyhow::Error;
1036
1037    fn try_from(network_info: generated::node_info::NetworkInfo) -> Result<Self, Self::Error> {
1038        Ok(types::NetworkInfo {
1039            node_id: network_info.node_id.require()?.value,
1040            peer_total_sent: network_info.peer_total_sent,
1041            peer_total_received: network_info.peer_total_received,
1042            avg_bps_in: network_info.avg_bps_in,
1043            avg_bps_out: network_info.avg_bps_out,
1044        })
1045    }
1046}
1047
1048impl IntoRequest<crate::v2::generated::PeerToBan> for types::network::PeerToBan {
1049    fn into_request(self) -> tonic::Request<crate::v2::generated::PeerToBan> {
1050        tonic::Request::new(match self {
1051            types::network::PeerToBan::IpAddr(ip_addr) => crate::v2::generated::PeerToBan {
1052                ip_address: Some(crate::v2::generated::IpAddress {
1053                    value: ip_addr.to_string(),
1054                }),
1055            },
1056        })
1057    }
1058}
1059
1060impl TryFrom<generated::node_info::Details> for types::NodeDetails {
1061    type Error = anyhow::Error;
1062
1063    fn try_from(details: generated::node_info::Details) -> Result<Self, Self::Error> {
1064        match details {
1065            generated::node_info::Details::Bootstrapper(_) => Ok(types::NodeDetails::Bootstrapper),
1066            generated::node_info::Details::Node(status) => {
1067                let Upward::Known(consensus_status) = Upward::from(status.consensus_status) else {
1068                    return Ok(types::NodeDetails::Node(Upward::Unknown(())));
1069                };
1070                let consensus_status = match consensus_status {
1071                    generated::node_info::node::ConsensusStatus::NotRunning(_) => {
1072                        types::NodeConsensusStatus::ConsensusNotRunning
1073                    }
1074                    generated::node_info::node::ConsensusStatus::Passive(_) => {
1075                        types::NodeConsensusStatus::ConsensusPassive
1076                    }
1077                    generated::node_info::node::ConsensusStatus::Active(baker) => {
1078                        let baker_id = baker.baker_id.require()?.into();
1079                        let Upward::Known(status) = Upward::from(baker.status) else {
1080                            return Ok(types::NodeDetails::Node(Upward::Unknown(())));
1081                        };
1082
1083                        match status {
1084                            generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(0) => types::NodeConsensusStatus::NotInCommittee(baker_id),
1085                            generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(1) => types::NodeConsensusStatus::AddedButNotActiveInCommittee(baker_id),
1086                            generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(2) => types::NodeConsensusStatus::AddedButWrongKeys(baker_id),
1087                            generated::node_info::baker_consensus_info::Status::ActiveBakerCommitteeInfo(_) => types::NodeConsensusStatus::Baker(baker_id),
1088                            generated::node_info::baker_consensus_info::Status::ActiveFinalizerCommitteeInfo(_) => types::NodeConsensusStatus::Finalizer(baker_id),
1089                            _ => anyhow::bail!("Malformed baker status")
1090                        }
1091                    }
1092                };
1093                Ok(types::NodeDetails::Node(Upward::Known(consensus_status)))
1094            }
1095        }
1096    }
1097}
1098
1099impl TryFrom<generated::NodeInfo> for types::NodeInfo {
1100    type Error = anyhow::Error;
1101
1102    fn try_from(node_info: generated::NodeInfo) -> Result<Self, Self::Error> {
1103        let version = semver::Version::parse(&node_info.peer_version)?;
1104        let local_time = chrono::DateTime::<chrono::Utc>::from(std::time::UNIX_EPOCH)
1105            + chrono::TimeDelta::try_milliseconds(node_info.local_time.require()?.value as i64)
1106                .context("Node local time out of bounds!")?;
1107        let uptime = chrono::Duration::try_from(types::DurationSeconds::from(
1108            node_info.peer_uptime.require()?.value,
1109        ))?;
1110        let network_info = node_info.network_info.require()?.try_into()?;
1111        let details = Upward::from(node_info.details)
1112            .map(types::NodeDetails::try_from)
1113            .transpose()?;
1114        Ok(types::NodeInfo {
1115            version,
1116            local_time,
1117            uptime,
1118            network_info,
1119            details,
1120        })
1121    }
1122}
1123
1124/// A helper trait that is implemented by types that can be cheaply converted to
1125/// a [`BlockIdentifier`]. This is esentially [`Into<BlockIdentifier>`] but
1126/// orphan rules prevent using that exactly.
1127///
1128/// This trait makes it convenient to use block hashes as input to functions
1129/// that take a block identifier.
1130pub trait IntoBlockIdentifier {
1131    fn into_block_identifier(self) -> BlockIdentifier;
1132}
1133
1134impl IntoBlockIdentifier for BlockIdentifier {
1135    fn into_block_identifier(self) -> BlockIdentifier {
1136        self
1137    }
1138}
1139
1140impl<X: IntoBlockIdentifier + Copy> IntoBlockIdentifier for &X {
1141    fn into_block_identifier(self) -> BlockIdentifier {
1142        (*self).into_block_identifier()
1143    }
1144}
1145
1146impl IntoBlockIdentifier for BlockHash {
1147    fn into_block_identifier(self) -> BlockIdentifier {
1148        BlockIdentifier::Given(self)
1149    }
1150}
1151
1152impl IntoBlockIdentifier for AbsoluteBlockHeight {
1153    fn into_block_identifier(self) -> BlockIdentifier {
1154        BlockIdentifier::AbsoluteHeight(self)
1155    }
1156}
1157
1158impl IntoBlockIdentifier for RelativeBlockHeight {
1159    fn into_block_identifier(self) -> BlockIdentifier {
1160        BlockIdentifier::RelativeHeight(self)
1161    }
1162}
1163
1164impl Client {
1165    /// Construct a new client connection to a concordium node.
1166    ///
1167    /// # Example
1168    /// Creates a new client. Note the example assumes access to a local running
1169    /// node.
1170    ///
1171    /// ```no_run
1172    /// # tokio_test::block_on(async {
1173    /// use concordium_rust_sdk::{endpoints::Endpoint, v2::Client};
1174    /// use std::str::FromStr;
1175    ///
1176    /// let mut client = Client::new("http://localhost:20001").await?;
1177    ///
1178    /// # Ok::<(), anyhow::Error>(())
1179    /// # });
1180    /// ```
1181    pub async fn new<E>(endpoint: E) -> Result<Self, tonic::transport::Error>
1182    where
1183        E: TryInto<tonic::transport::Endpoint>,
1184        E::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
1185    {
1186        let client = generated::queries_client::QueriesClient::connect(endpoint).await?;
1187        Ok(Self { client })
1188    }
1189
1190    /// Get the information for the given account in the given block. If either
1191    /// the block or the account do not exist [`QueryError::NotFound`] is
1192    /// returned.
1193    pub async fn get_account_info(
1194        &mut self,
1195        acc: &AccountIdentifier,
1196        bi: impl IntoBlockIdentifier,
1197    ) -> endpoints::QueryResult<QueryResponse<AccountInfo>> {
1198        let response = self
1199            .client
1200            .get_account_info((acc, &bi.into_block_identifier()))
1201            .await?;
1202        let block_hash = extract_metadata(&response)?;
1203        let response = AccountInfo::try_from(response.into_inner())?;
1204        Ok(QueryResponse {
1205            block_hash,
1206            response,
1207        })
1208    }
1209
1210    /// Get the next sequence number for the account, with information on how
1211    /// reliable the information is.
1212    pub async fn get_next_account_sequence_number(
1213        &mut self,
1214        account_address: &AccountAddress,
1215    ) -> endpoints::QueryResult<types::queries::AccountNonceResponse> {
1216        let response = self
1217            .client
1218            .get_next_account_sequence_number(account_address)
1219            .await?;
1220        let response = types::queries::AccountNonceResponse::try_from(response.into_inner())?;
1221        Ok(response)
1222    }
1223
1224    /// Get information about the current state of consensus. This is an
1225    /// overview of the node's current view of the chain.
1226    pub async fn get_consensus_info(
1227        &mut self,
1228    ) -> endpoints::QueryResult<types::queries::ConsensusInfo> {
1229        let response = self
1230            .client
1231            .get_consensus_info(generated::Empty::default())
1232            .await?;
1233        let response = types::queries::ConsensusInfo::try_from(response.into_inner())?;
1234        Ok(response)
1235    }
1236
1237    /// Get the currently used cryptographic parameters. If the block does
1238    /// not exist [`QueryError::NotFound`] is returned.
1239    pub async fn get_cryptographic_parameters(
1240        &mut self,
1241        bi: impl IntoBlockIdentifier,
1242    ) -> endpoints::QueryResult<QueryResponse<types::CryptographicParameters>> {
1243        let response = self
1244            .client
1245            .get_cryptographic_parameters(&bi.into_block_identifier())
1246            .await?;
1247        let block_hash = extract_metadata(&response)?;
1248        let response = types::CryptographicParameters::try_from(response.into_inner())?;
1249        Ok(QueryResponse {
1250            block_hash,
1251            response,
1252        })
1253    }
1254
1255    /// Get the list of accounts in the given block.
1256    /// The stream will end when all accounts that exist in the state at the end
1257    /// of the given block have been returned. If the block does not exist
1258    /// [`QueryError::NotFound`] is returned.
1259    pub async fn get_account_list(
1260        &mut self,
1261        bi: impl IntoBlockIdentifier,
1262    ) -> endpoints::QueryResult<
1263        QueryResponse<impl Stream<Item = Result<AccountAddress, tonic::Status>>>,
1264    > {
1265        let response = self
1266            .client
1267            .get_account_list(&bi.into_block_identifier())
1268            .await?;
1269        let block_hash = extract_metadata(&response)?;
1270        let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1271        Ok(QueryResponse {
1272            block_hash,
1273            response: stream,
1274        })
1275    }
1276
1277    /// Get a list of all smart contract modules. The stream will end
1278    /// when all modules that exist in the state at the end of the given
1279    /// block have been returned.
1280    /// If the block does not exist [`QueryError::NotFound`] is returned.
1281    pub async fn get_module_list(
1282        &mut self,
1283        bi: impl IntoBlockIdentifier,
1284    ) -> endpoints::QueryResult<
1285        QueryResponse<impl Stream<Item = Result<ModuleReference, tonic::Status>>>,
1286    > {
1287        let response = self
1288            .client
1289            .get_module_list(&bi.into_block_identifier())
1290            .await?;
1291        let block_hash = extract_metadata(&response)?;
1292        let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1293        Ok(QueryResponse {
1294            block_hash,
1295            response: stream,
1296        })
1297    }
1298
1299    /// Get the source of a smart contract module.
1300    /// If the block or module does not exist [`QueryError::NotFound`] is
1301    /// returned.
1302    pub async fn get_module_source(
1303        &mut self,
1304        module_ref: &ModuleReference,
1305        bi: impl IntoBlockIdentifier,
1306    ) -> endpoints::QueryResult<QueryResponse<types::smart_contracts::WasmModule>> {
1307        let response = self
1308            .client
1309            .get_module_source((module_ref, &bi.into_block_identifier()))
1310            .await?;
1311        let block_hash = extract_metadata(&response)?;
1312        let response = types::smart_contracts::WasmModule::try_from(response.into_inner())?;
1313        Ok(QueryResponse {
1314            block_hash,
1315            response,
1316        })
1317    }
1318
1319    /// Get the list of smart contract instances in a given block.
1320    /// The stream will end when all instances that exist in the state at the
1321    /// end of the given block have been returned. If the block does not
1322    /// exist [`QueryError::NotFound`] is returned.
1323    pub async fn get_instance_list(
1324        &mut self,
1325        bi: impl IntoBlockIdentifier,
1326    ) -> endpoints::QueryResult<
1327        QueryResponse<impl Stream<Item = Result<ContractAddress, tonic::Status>>>,
1328    > {
1329        let response = self
1330            .client
1331            .get_instance_list(&bi.into_block_identifier())
1332            .await?;
1333        let block_hash = extract_metadata(&response)?;
1334        let stream = response.into_inner().map(|x| x.map(From::from));
1335        Ok(QueryResponse {
1336            block_hash,
1337            response: stream,
1338        })
1339    }
1340
1341    /// Get information about a smart contract instance as it appears at the end
1342    /// of the given block. If the block or instance does not exist
1343    /// [`QueryError::NotFound`] is returned.
1344    pub async fn get_instance_info(
1345        &mut self,
1346        address: ContractAddress,
1347        bi: impl IntoBlockIdentifier,
1348    ) -> endpoints::QueryResult<QueryResponse<InstanceInfo>> {
1349        let response = self
1350            .client
1351            .get_instance_info((address, &bi.into_block_identifier()))
1352            .await?;
1353        let block_hash = extract_metadata(&response)?;
1354        let response = InstanceInfo::try_from(response.into_inner())?;
1355        Ok(QueryResponse {
1356            block_hash,
1357            response,
1358        })
1359    }
1360
1361    /// Get a stream of ancestors for the provided block.
1362    /// Starting with the provided block itself, moving backwards until no more
1363    /// ancestors or the requested number of ancestors have been returned.
1364    pub async fn get_ancestors(
1365        &mut self,
1366        bi: impl IntoBlockIdentifier,
1367        limit: u64,
1368    ) -> endpoints::QueryResult<QueryResponse<impl Stream<Item = Result<BlockHash, tonic::Status>>>>
1369    {
1370        let response = self
1371            .client
1372            .get_ancestors((&bi.into_block_identifier(), limit))
1373            .await?;
1374        let block_hash = extract_metadata(&response)?;
1375        let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1376        Ok(QueryResponse {
1377            block_hash,
1378            response: stream,
1379        })
1380    }
1381
1382    /// Return a stream of blocks that are finalized from the time the query is
1383    /// made onward.
1384    /// This can be used to listen for newly finalized blocks.
1385    ///
1386    /// Note: There is no guarantee that blocks will not be skipped if the
1387    /// client is too slow in processing the stream, however blocks will
1388    /// always be sent by increasing block height.
1389    pub async fn get_finalized_blocks(
1390        &mut self,
1391    ) -> endpoints::QueryResult<impl Stream<Item = Result<FinalizedBlockInfo, tonic::Status>>> {
1392        let response = self
1393            .client
1394            .get_finalized_blocks(generated::Empty::default())
1395            .await?;
1396        let stream = response.into_inner().map(|x| match x {
1397            Ok(v) => {
1398                let block_hash = v.hash.require().and_then(TryFrom::try_from)?;
1399                let height = v.height.require()?.into();
1400                Ok(FinalizedBlockInfo { block_hash, height })
1401            }
1402            Err(x) => Err(x),
1403        });
1404        Ok(stream)
1405    }
1406
1407    /// Get the exact state of a specific contract instance, streamed as a list
1408    /// of key-value pairs. The list is streamed in lexicographic order of
1409    /// keys.
1410    /// If the block or instance does not exist [`QueryError::NotFound`] is
1411    /// returned.
1412    pub async fn get_instance_state(
1413        &mut self,
1414        ca: ContractAddress,
1415        bi: impl IntoBlockIdentifier,
1416    ) -> endpoints::QueryResult<
1417        QueryResponse<impl Stream<Item = Result<(Vec<u8>, Vec<u8>), tonic::Status>>>,
1418    > {
1419        let response = self
1420            .client
1421            .get_instance_state((ca, &bi.into_block_identifier()))
1422            .await?;
1423        let block_hash = extract_metadata(&response)?;
1424        let stream = response.into_inner().map(|x| match x {
1425            Ok(v) => {
1426                let key = v.key;
1427                let value = v.value;
1428                Ok((key, value))
1429            }
1430            Err(x) => Err(x),
1431        });
1432        Ok(QueryResponse {
1433            block_hash,
1434            response: stream,
1435        })
1436    }
1437
1438    /// Get the value at a specific key of a contract state. In contrast to
1439    /// [`get_instance_state`](Self::get_instance_state) this is more efficient,
1440    /// but requires the user to know the specific key to look for.
1441    /// If the block or instance does not exist [`QueryError::NotFound`] is
1442    /// returned.
1443    pub async fn instance_state_lookup(
1444        &mut self,
1445        ca: ContractAddress,
1446        key: impl Into<Vec<u8>>,
1447        bi: impl IntoBlockIdentifier,
1448    ) -> endpoints::QueryResult<QueryResponse<Vec<u8>>> {
1449        let response = self
1450            .client
1451            .instance_state_lookup((ca, &bi.into_block_identifier(), key))
1452            .await?;
1453        let block_hash = extract_metadata(&response)?;
1454        Ok(QueryResponse {
1455            block_hash,
1456            response: response.into_inner().value,
1457        })
1458    }
1459
1460    /// Get the status of and information about a specific block item
1461    /// (transaction). If the block item does not exist
1462    /// [`QueryError::NotFound`] is returned.
1463    pub async fn get_block_item_status(
1464        &mut self,
1465        th: &TransactionHash,
1466    ) -> endpoints::QueryResult<TransactionStatus> {
1467        let response = self.client.get_block_item_status(th).await?;
1468        let response = TransactionStatus::try_from(response.into_inner())?;
1469        Ok(response)
1470    }
1471
1472    /// Send a block item. A block item is either an `AccountTransaction`, which
1473    /// is a transaction signed and paid for by an account, a
1474    /// `CredentialDeployment`, which creates a new account, or
1475    /// `UpdateInstruction`, which is an instruction to change some
1476    /// parameters of the chain. Update instructions can only be sent by the
1477    /// governance committee.
1478    pub async fn send_block_item<P: PayloadLike>(
1479        &mut self,
1480        bi: &transactions::BlockItem<P>,
1481    ) -> endpoints::RPCResult<TransactionHash> {
1482        let response = self.client.send_block_item(bi).await?;
1483        let response = TransactionHash::try_from(response.into_inner())?;
1484        Ok(response)
1485    }
1486
1487    /// Send an account transaction. This is just a helper around
1488    /// [`send_block_item`](Self::send_block_item) block item for convenience.
1489    pub async fn send_account_transaction<P: PayloadLike>(
1490        &mut self,
1491        at: transactions::AccountTransaction<P>,
1492    ) -> endpoints::RPCResult<TransactionHash> {
1493        self.send_block_item(&at.into()).await
1494    }
1495
1496    /// Get the hash to be signed for an account transaction from the node. The
1497    /// hash returned can then be used for signing when constructing
1498    /// [`TransactionSignature`] as part of calling [`Client::send_block_item`].
1499    ///
1500    /// This is provided as a convenience to support cases where the right SDK
1501    /// is not available for interacting with the node.
1502    ///
1503    /// This SDK can compute the hash off-line and it is not recommended to use
1504    /// this endpoint, instead use [`compute_transaction_sign_hash`].
1505    ///
1506    /// [`compute_transaction_sign_hash`]:
1507    /// types::transactions::compute_transaction_sign_hash
1508    pub async fn get_account_transaction_sign_hash(
1509        &mut self,
1510        header: &transactions::TransactionHeader,
1511        payload: &transactions::Payload,
1512    ) -> endpoints::RPCResult<TransactionSignHash> {
1513        let response = self
1514            .client
1515            .get_account_transaction_sign_hash((header, payload))
1516            .await?;
1517        let response = TransactionSignHash::try_from(response.into_inner())?;
1518        Ok(response)
1519    }
1520
1521    /// Wait until the transaction is finalized. Returns
1522    /// [`NotFound`](QueryError::NotFound) in case the transaction is not
1523    /// known to the node. In case of success, the return value is a pair of the
1524    /// block hash of the block that contains the transactions, and its
1525    /// outcome in the block.
1526    ///
1527    /// Since this can take an indefinite amount of time in general, users of
1528    /// this function might wish to wrap it inside
1529    /// [`timeout`](tokio::time::timeout) handler and handle the resulting
1530    /// failure.
1531    pub async fn wait_until_finalized(
1532        &mut self,
1533        hash: &types::hashes::TransactionHash,
1534    ) -> endpoints::QueryResult<(types::hashes::BlockHash, types::BlockItemSummary)> {
1535        let hash = *hash;
1536        let process_response = |response| {
1537            if let types::TransactionStatus::Finalized(blocks) = response {
1538                let mut iter = blocks.into_iter();
1539                if let Some(rv) = iter.next() {
1540                    if iter.next().is_some() {
1541                        Err(tonic::Status::internal(
1542                            "Finalized transaction finalized into multiple blocks. This cannot \
1543                             happen.",
1544                        )
1545                        .into())
1546                    } else {
1547                        Ok::<_, QueryError>(Some(rv))
1548                    }
1549                } else {
1550                    Err(tonic::Status::internal(
1551                        "Finalized transaction finalized into no blocks. This cannot happen.",
1552                    )
1553                    .into())
1554                }
1555            } else {
1556                Ok(None)
1557            }
1558        };
1559
1560        match process_response(self.get_block_item_status(&hash).await?)? {
1561            Some(rv) => Ok(rv),
1562            None => {
1563                // if the first query did not succeed then start listening for finalized blocks.
1564                // and on each new block try to query the status.
1565                let mut blocks = self.get_finalized_blocks().await?;
1566                while blocks.next().await.transpose()?.is_some() {
1567                    if let Some(rv) = process_response(self.get_block_item_status(&hash).await?)? {
1568                        return Ok(rv);
1569                    }
1570                }
1571                Err(QueryError::NotFound)
1572            }
1573        }
1574    }
1575
1576    /// Run the smart contract instance entrypoint in a given context and in the
1577    /// state at the end of the given block and return the results.
1578    /// If the block does not exist [`QueryError::NotFound`] is returned.
1579    pub async fn invoke_instance(
1580        &mut self,
1581        bi: impl IntoBlockIdentifier,
1582        context: &ContractContext,
1583    ) -> endpoints::QueryResult<QueryResponse<InvokeContractResult>> {
1584        let response = self
1585            .client
1586            .invoke_instance((&bi.into_block_identifier(), context))
1587            .await?;
1588        let block_hash = extract_metadata(&response)?;
1589        let response = InvokeContractResult::try_from(response.into_inner())?;
1590        Ok(QueryResponse {
1591            block_hash,
1592            response,
1593        })
1594    }
1595
1596    /// Start a dry-run sequence that can be used to simulate a series of
1597    /// transactions and other operations on the node.
1598    ///
1599    /// Before invoking any other operations on the [`dry_run::DryRun`] object,
1600    /// the state must be loaded by calling
1601    /// [`dry_run::DryRun::load_block_state`].
1602    pub async fn begin_dry_run(&mut self) -> endpoints::QueryResult<dry_run::DryRun> {
1603        Ok(dry_run::DryRun::new(&mut self.client).await?)
1604    }
1605
1606    /// Start a dry-run sequence that can be used to simulate a series of
1607    /// transactions and other operations on the node, starting from the
1608    /// specified block.
1609    pub async fn dry_run(
1610        &mut self,
1611        bi: impl IntoBlockIdentifier,
1612    ) -> dry_run::DryRunResult<(dry_run::DryRun, dry_run::BlockStateLoaded)> {
1613        let mut runner = dry_run::DryRun::new(&mut self.client).await?;
1614        let load_result = runner.load_block_state(bi).await?;
1615        Ok(WithRemainingQuota {
1616            inner: (runner, load_result.inner),
1617            quota_remaining: load_result.quota_remaining,
1618        })
1619    }
1620
1621    /// Get information, such as height, timings, and transaction counts for the
1622    /// given block. If the block does not exist [`QueryError::NotFound`] is
1623    /// returned.
1624    pub async fn get_block_info(
1625        &mut self,
1626        bi: impl IntoBlockIdentifier,
1627    ) -> endpoints::QueryResult<QueryResponse<types::queries::BlockInfo>> {
1628        let response = self
1629            .client
1630            .get_block_info(&bi.into_block_identifier())
1631            .await?;
1632        let block_hash = extract_metadata(&response)?;
1633        let response = types::queries::BlockInfo::try_from(response.into_inner())?;
1634        Ok(QueryResponse {
1635            block_hash,
1636            response,
1637        })
1638    }
1639
1640    /// Get information about whether a block identified by `bi` is a payday
1641    /// block or not. This will always return `false` for blocks produced prior
1642    /// to protocol version 4. If the block does not exits
1643    /// [`QueryError::NotFound`] is returned.
1644    pub async fn is_payday_block(
1645        &mut self,
1646        bi: impl IntoBlockIdentifier,
1647    ) -> endpoints::QueryResult<QueryResponse<bool>> {
1648        let mut special_events = self.get_block_special_events(bi).await?;
1649        let block_hash = special_events.block_hash;
1650
1651        while let Some(event) = special_events.response.next().await.transpose()? {
1652            let Upward::Known(event) = event else {
1653                // Ignore new unknown block special events.
1654                continue;
1655            };
1656            let has_payday_event = matches!(
1657                event,
1658                SpecialTransactionOutcome::PaydayPoolReward { .. }
1659                    | SpecialTransactionOutcome::PaydayAccountReward { .. }
1660                    | SpecialTransactionOutcome::PaydayFoundationReward { .. }
1661            );
1662
1663            if has_payday_event {
1664                return Ok(QueryResponse {
1665                    block_hash,
1666                    response: true,
1667                });
1668            };
1669        }
1670
1671        Ok(QueryResponse {
1672            block_hash,
1673            response: false,
1674        })
1675    }
1676
1677    /// Get all the bakers at the end of the given block.
1678    /// If the block does not exist [`QueryError::NotFound`] is returned.
1679    pub async fn get_baker_list(
1680        &mut self,
1681        bi: impl IntoBlockIdentifier,
1682    ) -> endpoints::QueryResult<
1683        QueryResponse<impl Stream<Item = Result<types::BakerId, tonic::Status>>>,
1684    > {
1685        let response = self
1686            .client
1687            .get_baker_list(&bi.into_block_identifier())
1688            .await?;
1689        let block_hash = extract_metadata(&response)?;
1690        let stream = response.into_inner().map(|x| x.map(From::from));
1691        Ok(QueryResponse {
1692            block_hash,
1693            response: stream,
1694        })
1695    }
1696
1697    /// Get information about a given pool at the end of a given block.
1698    /// If the block does not exist or is prior to protocol version 4 then
1699    /// [`QueryError::NotFound`] is returned.
1700    pub async fn get_pool_info(
1701        &mut self,
1702        block_id: impl IntoBlockIdentifier,
1703        baker_id: types::BakerId,
1704    ) -> endpoints::QueryResult<QueryResponse<types::BakerPoolStatus>> {
1705        let response = self
1706            .client
1707            .get_pool_info((&block_id.into_block_identifier(), baker_id))
1708            .await?;
1709        let block_hash = extract_metadata(&response)?;
1710        let response = types::BakerPoolStatus::try_from(response.into_inner())?;
1711        Ok(QueryResponse {
1712            block_hash,
1713            response,
1714        })
1715    }
1716
1717    /// Get information about the passive delegators at the end of a given
1718    /// block.
1719    /// If the block does not exist or is prior to protocol version 4 then
1720    /// [`QueryError::NotFound`] is returned.
1721    pub async fn get_passive_delegation_info(
1722        &mut self,
1723        block_id: impl IntoBlockIdentifier,
1724    ) -> endpoints::QueryResult<QueryResponse<types::PassiveDelegationStatus>> {
1725        let response = self
1726            .client
1727            .get_passive_delegation_info(&block_id.into_block_identifier())
1728            .await?;
1729        let block_hash = extract_metadata(&response)?;
1730        let response = types::PassiveDelegationStatus::try_from(response.into_inner())?;
1731        Ok(QueryResponse {
1732            block_hash,
1733            response,
1734        })
1735    }
1736
1737    /// Get a list of live blocks at a given height.
1738    pub async fn get_blocks_at_height(
1739        &mut self,
1740        blocks_at_height_input: &endpoints::BlocksAtHeightInput,
1741    ) -> endpoints::QueryResult<Vec<BlockHash>> {
1742        let response = self
1743            .client
1744            .get_blocks_at_height(blocks_at_height_input)
1745            .await?;
1746        let blocks = response
1747            .into_inner()
1748            .blocks
1749            .into_iter()
1750            .map(TryFrom::try_from)
1751            .collect::<Result<_, tonic::Status>>()?;
1752        Ok(blocks)
1753    }
1754
1755    /// Get information about tokenomics at the end of a given block.
1756    /// If the block does not exist [`QueryError::NotFound`] is returned.
1757    pub async fn get_tokenomics_info(
1758        &mut self,
1759        block_id: impl IntoBlockIdentifier,
1760    ) -> endpoints::QueryResult<QueryResponse<types::RewardsOverview>> {
1761        let response = self
1762            .client
1763            .get_tokenomics_info(&block_id.into_block_identifier())
1764            .await?;
1765        let block_hash = extract_metadata(&response)?;
1766        let response = types::RewardsOverview::try_from(response.into_inner())?;
1767        Ok(QueryResponse {
1768            block_hash,
1769            response,
1770        })
1771    }
1772
1773    /// Get the registered delegators of a given pool at the end of a given
1774    /// block.
1775    /// If the block or baker ID does not exist [`QueryError::NotFound`] is
1776    /// returned, and if the block is baked prior to protocol version 4
1777    /// [`QueryError::RPCError`] with status [`Code::InvalidArgument`] is
1778    /// returned. The stream will end when all the delegators have been
1779    /// returned for the given block.
1780    ///
1781    /// In contrast to the [Client::get_pool_delegators_reward_period] which
1782    /// returns delegators that are fixed for the reward period of the
1783    /// block, this endpoint returns the list of delegators that are
1784    /// registered in the block. Any changes to delegators are immediately
1785    /// visible in this list.
1786    pub async fn get_pool_delegators(
1787        &mut self,
1788        bi: impl IntoBlockIdentifier,
1789        baker_id: types::BakerId,
1790    ) -> endpoints::QueryResult<
1791        QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1792    > {
1793        let response = self
1794            .client
1795            .get_pool_delegators((&bi.into_block_identifier(), baker_id))
1796            .await?;
1797        let block_hash = extract_metadata(&response)?;
1798        let stream = response.into_inner().map(|result| match result {
1799            Ok(delegator) => delegator.try_into(),
1800            Err(err) => Err(err),
1801        });
1802        Ok(QueryResponse {
1803            block_hash,
1804            response: stream,
1805        })
1806    }
1807
1808    /// Get the fixed delegators of a given pool for the reward period of the
1809    /// given block.
1810    /// If the block or baker ID does not exist [`QueryError::NotFound`] is
1811    /// returned, and if the block is baked prior to protocol version 4
1812    /// [`QueryError::RPCError`] with status [`Code::InvalidArgument`] is
1813    /// returned. The stream will end when all the delegators have been
1814    /// returned.
1815    ///
1816    /// In contrast to the [Client::get_pool_delegators] which
1817    /// returns delegators registered for the given block, this endpoint
1818    /// returns the fixed delegators contributing stake in the reward period
1819    /// containing the given block.
1820    pub async fn get_pool_delegators_reward_period(
1821        &mut self,
1822        bi: impl IntoBlockIdentifier,
1823        baker_id: types::BakerId,
1824    ) -> endpoints::QueryResult<
1825        QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1826    > {
1827        let response = self
1828            .client
1829            .get_pool_delegators_reward_period((&bi.into_block_identifier(), baker_id))
1830            .await?;
1831        let block_hash = extract_metadata(&response)?;
1832        let stream = response.into_inner().map(|result| match result {
1833            Ok(delegator) => delegator.try_into(),
1834            Err(err) => Err(err),
1835        });
1836        Ok(QueryResponse {
1837            block_hash,
1838            response: stream,
1839        })
1840    }
1841
1842    /// Get the registered passive delegators at the end of a given block.
1843    /// If the block does not exist [`QueryError::NotFound`] is returned, and if
1844    /// the block is baked prior to protocol version 4 [`QueryError::
1845    /// RPCError`] with status [`Code::InvalidArgument`] is returned. The stream
1846    /// will end when all the delegators have been returned.
1847    ///
1848    /// In contrast to the [`Client::get_passive_delegators_reward_period`]
1849    /// which returns delegators that are fixed for the reward period of the
1850    /// block, this endpoint returns the list of delegators that are
1851    /// registered in the block. Any changes to delegators are immediately
1852    /// visible in this list.
1853    pub async fn get_passive_delegators(
1854        &mut self,
1855        bi: impl IntoBlockIdentifier,
1856    ) -> endpoints::QueryResult<
1857        QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1858    > {
1859        let response = self
1860            .client
1861            .get_passive_delegators(&bi.into_block_identifier())
1862            .await?;
1863        let block_hash = extract_metadata(&response)?;
1864        let stream = response.into_inner().map(|result| match result {
1865            Ok(delegator) => delegator.try_into(),
1866            Err(err) => Err(err),
1867        });
1868        Ok(QueryResponse {
1869            block_hash,
1870            response: stream,
1871        })
1872    }
1873
1874    /// Get the fixed passive delegators for the reward period of the given
1875    /// block.
1876    /// If the block does not exist [`QueryError::NotFound`] is returned.
1877    /// If the block is baked prior to protocol version 4,
1878    /// [`QueryError::RPCError`] with status [`Code::InvalidArgument`] is
1879    /// returned. The stream will end when all the delegators have been
1880    /// returned.
1881    ///
1882    /// In contrast to the `GetPassiveDelegators` which returns delegators
1883    /// registered for the given block, this endpoint returns the fixed
1884    /// delegators contributing stake in the reward period containing the
1885    /// given block.
1886    pub async fn get_passive_delegators_reward_period(
1887        &mut self,
1888        bi: impl IntoBlockIdentifier,
1889    ) -> endpoints::QueryResult<
1890        QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1891    > {
1892        let response = self
1893            .client
1894            .get_passive_delegators_reward_period(&bi.into_block_identifier())
1895            .await?;
1896        let block_hash = extract_metadata(&response)?;
1897        let stream = response.into_inner().map(|result| match result {
1898            Ok(delegator) => delegator.try_into(),
1899            Err(err) => Err(err),
1900        });
1901        Ok(QueryResponse {
1902            block_hash,
1903            response: stream,
1904        })
1905    }
1906
1907    /// Get the current branches of blocks starting from and including the last
1908    /// finalized block.
1909    ///
1910    /// Branches are all live blocks that are successors of the last finalized
1911    /// block. In particular this means that blocks which do not have a
1912    /// parent are not included in this response.
1913    pub async fn get_branches(&mut self) -> endpoints::QueryResult<types::queries::Branch> {
1914        let response = self
1915            .client
1916            .get_branches(generated::Empty::default())
1917            .await?;
1918        let response = types::queries::Branch::try_from(response.into_inner())?;
1919        Ok(response)
1920    }
1921
1922    /// Get information related to the baker election for a particular block.
1923    /// If the block does not exist [`QueryError::NotFound`] is returned.
1924    pub async fn get_election_info(
1925        &mut self,
1926        bi: impl IntoBlockIdentifier,
1927    ) -> endpoints::QueryResult<QueryResponse<types::BirkParameters>> {
1928        let response = self
1929            .client
1930            .get_election_info(&bi.into_block_identifier())
1931            .await?;
1932        let block_hash = extract_metadata(&response)?;
1933        let response = types::BirkParameters::try_from(response.into_inner())?;
1934        Ok(QueryResponse {
1935            block_hash,
1936            response,
1937        })
1938    }
1939
1940    /// Get the identity providers registered as of the end of a given block.
1941    /// If the block does not exist [`QueryError::NotFound`] is returned.
1942    /// The stream will end when all the identity providers have been returned.
1943    pub async fn get_identity_providers(
1944        &mut self,
1945        bi: impl IntoBlockIdentifier,
1946    ) -> endpoints::QueryResult<
1947        QueryResponse<
1948            impl Stream<
1949                Item = Result<
1950                    crate::id::types::IpInfo<crate::id::constants::IpPairing>,
1951                    tonic::Status,
1952                >,
1953            >,
1954        >,
1955    > {
1956        let response = self
1957            .client
1958            .get_identity_providers(&bi.into_block_identifier())
1959            .await?;
1960        let block_hash = extract_metadata(&response)?;
1961        let stream = response.into_inner().map(|result| match result {
1962            Ok(ip_info) => ip_info.try_into(),
1963            Err(err) => Err(err),
1964        });
1965        Ok(QueryResponse {
1966            block_hash,
1967            response: stream,
1968        })
1969    }
1970
1971    /// Get the list of anonymity revokers in the given block.
1972    /// If the block does not exist [`QueryError::NotFound`] is returned.
1973    /// The stream will end when all the anonymity revokers have been returned.
1974    pub async fn get_anonymity_revokers(
1975        &mut self,
1976        bi: impl IntoBlockIdentifier,
1977    ) -> endpoints::QueryResult<
1978        QueryResponse<
1979            impl Stream<
1980                Item = Result<
1981                    crate::id::types::ArInfo<crate::id::constants::ArCurve>,
1982                    tonic::Status,
1983                >,
1984            >,
1985        >,
1986    > {
1987        let response = self
1988            .client
1989            .get_anonymity_revokers(&bi.into_block_identifier())
1990            .await?;
1991        let block_hash = extract_metadata(&response)?;
1992        let stream = response.into_inner().map(|result| match result {
1993            Ok(ar_info) => ar_info.try_into(),
1994            Err(err) => Err(err),
1995        });
1996        Ok(QueryResponse {
1997            block_hash,
1998            response: stream,
1999        })
2000    }
2001
2002    /// Get the list of transactions hashes for transactions that claim to be
2003    /// from the given account, but which are not yet finalized.
2004    /// They are either committed to a block or still pending.
2005    /// The stream will end when all the non-finalized transaction hashes have
2006    /// been returned. If the account does not exist an empty list will be
2007    /// returned.
2008    ///
2009    /// This endpoint is not expected to return a large amount of data in most
2010    /// cases, but in bad network conditions it might.
2011    pub async fn get_account_non_finalized_transactions(
2012        &mut self,
2013        account_address: &AccountAddress,
2014    ) -> endpoints::QueryResult<impl Stream<Item = Result<TransactionHash, tonic::Status>>> {
2015        let response = self
2016            .client
2017            .get_account_non_finalized_transactions(account_address)
2018            .await?;
2019        let stream = response.into_inner().map(|result| match result {
2020            Ok(transaction_hash) => transaction_hash.try_into(),
2021            Err(err) => Err(err),
2022        });
2023        Ok(stream)
2024    }
2025
2026    /// Get the block items included in a given block.
2027    /// If the block does not exist [`QueryError::NotFound`] is returned.
2028    /// The stream will end when all the block items in the given block have
2029    /// been returned.
2030    /// To allow for forward-compatibility [`Upward::Unknown`] is returned
2031    /// if/when encountering a unknown future type of [`BlockItem`].
2032    pub async fn get_block_items(
2033        &mut self,
2034        bi: impl IntoBlockIdentifier,
2035    ) -> endpoints::QueryResult<
2036        QueryResponse<impl Stream<Item = Result<Upward<BlockItem<EncodedPayload>>, tonic::Status>>>,
2037    > {
2038        let response = self
2039            .client
2040            .get_block_items(&bi.into_block_identifier())
2041            .await?;
2042        let block_hash = extract_metadata(&response)?;
2043        let stream = response.into_inner().map(|result| match result {
2044            Ok(summary) => summary.try_into(),
2045            Err(err) => Err(err),
2046        });
2047        Ok(QueryResponse {
2048            block_hash,
2049            response: stream,
2050        })
2051    }
2052
2053    /// Get the specific **block item** if it is finalized.
2054    /// If the transaction does not exist in a finalized block
2055    /// [`QueryError::NotFound`] is returned.
2056    ///
2057    /// **Note that this is not an efficient method** since the node API does
2058    /// not allow for retrieving just the specific block item, but rather
2059    /// requires retrieving the full block. Use it for testing and debugging
2060    /// only.
2061    ///
2062    /// The return value is a triple of the [`BlockItem`], the hash of the block
2063    /// in which it is finalized, and the outcome in the form of
2064    /// [`BlockItemSummary`].
2065    /// To allow for forward-compatibility [`Upward::Unknown`] is returned
2066    /// if/when encountering a unknown future type of [`BlockItem`].
2067    pub async fn get_finalized_block_item(
2068        &mut self,
2069        th: TransactionHash,
2070    ) -> endpoints::QueryResult<(
2071        Upward<BlockItem<EncodedPayload>>,
2072        BlockHash,
2073        BlockItemSummary,
2074    )> {
2075        let status = self.get_block_item_status(&th).await?;
2076        let Some((bh, status)) = status.is_finalized() else {
2077            return Err(QueryError::NotFound);
2078        };
2079        let mut response = self
2080            .client
2081            .get_block_items(&bh.into_block_identifier())
2082            .await?
2083            .into_inner();
2084        while let Some(tx) = response.try_next().await? {
2085            let tx_hash = TransactionHash::try_from(tx.hash.clone().require()?)?;
2086            if tx_hash == th {
2087                return Ok((tx.try_into()?, *bh, status.clone()));
2088            }
2089        }
2090        Err(endpoints::QueryError::NotFound)
2091    }
2092
2093    /// Shut down the node.
2094    /// Return a GRPC error if the shutdown failed.
2095    pub async fn shutdown(&mut self) -> endpoints::RPCResult<()> {
2096        self.client.shutdown(generated::Empty::default()).await?;
2097        Ok(())
2098    }
2099
2100    /// Suggest a peer to connect to the submitted peer details.
2101    /// This, if successful, adds the peer to the list of given addresses.
2102    /// Otherwise return a GRPC error.
2103    ///
2104    /// Note: The peer might not be connected to instantly, in that case
2105    /// the node will try to establish the connection in near future. This
2106    /// function returns a GRPC status 'Ok' in this case.
2107    pub async fn peer_connect(&mut self, addr: std::net::SocketAddr) -> endpoints::RPCResult<()> {
2108        let peer_connection = generated::IpSocketAddress {
2109            ip: Some(generated::IpAddress {
2110                value: addr.ip().to_string(),
2111            }),
2112            port: Some(generated::Port {
2113                value: addr.port() as u32,
2114            }),
2115        };
2116        self.client.peer_connect(peer_connection).await?;
2117        Ok(())
2118    }
2119
2120    /// Disconnect from the peer and remove them from the given addresses list
2121    /// if they are on it. Return if the request was processed successfully.
2122    /// Otherwise return a GRPC error.
2123    pub async fn peer_disconnect(
2124        &mut self,
2125        addr: std::net::SocketAddr,
2126    ) -> endpoints::RPCResult<()> {
2127        let peer_connection = generated::IpSocketAddress {
2128            ip: Some(generated::IpAddress {
2129                value: addr.ip().to_string(),
2130            }),
2131            port: Some(generated::Port {
2132                value: addr.port() as u32,
2133            }),
2134        };
2135        self.client.peer_disconnect(peer_connection).await?;
2136        Ok(())
2137    }
2138
2139    /// Get a vector of the banned peers.
2140    pub async fn get_banned_peers(
2141        &mut self,
2142    ) -> endpoints::RPCResult<Vec<super::types::network::BannedPeer>> {
2143        Ok(self
2144            .client
2145            .get_banned_peers(generated::Empty::default())
2146            .await?
2147            .into_inner()
2148            .peers
2149            .into_iter()
2150            .map(super::types::network::BannedPeer::try_from)
2151            .collect::<anyhow::Result<Vec<super::types::network::BannedPeer>>>()?)
2152    }
2153
2154    /// Ban a peer.
2155    /// When successful return `Ok(())`, and otherwise return an error
2156    /// describing the issue.
2157    pub async fn ban_peer(
2158        &mut self,
2159        peer_to_ban: super::types::network::PeerToBan,
2160    ) -> endpoints::RPCResult<()> {
2161        self.client.ban_peer(peer_to_ban).await?;
2162        Ok(())
2163    }
2164
2165    /// Unban a peer.
2166    /// When successful return `Ok(())`, and otherwise return an error
2167    /// describing the issue.
2168    pub async fn unban_peer(
2169        &mut self,
2170        banned_peer: &super::types::network::BannedPeer,
2171    ) -> endpoints::RPCResult<()> {
2172        self.client.unban_peer(banned_peer).await?;
2173        Ok(())
2174    }
2175
2176    /// Start a network dump if the feature is enabled on the node.
2177    /// This writes all the network packets into the given file.
2178    /// Return `Ok(())` if a network dump has been initiated, and an error
2179    /// otherwise.
2180    ///
2181    /// * file - The file to write to.
2182    /// * raw - Whether raw packets should be included in the dump or not.
2183    ///
2184    /// Note. If the feature 'network_dump' is not enabled on the node then this
2185    /// will return a 'Precondition failed' error.
2186    pub async fn dump_start(
2187        &mut self,
2188        file: &std::path::Path,
2189        raw: bool,
2190    ) -> endpoints::RPCResult<()> {
2191        let file_str = file.to_str().ok_or_else(|| {
2192            tonic::Status::invalid_argument(
2193                "The provided path cannot is not a valid UTF8 string, so cannot be used.",
2194            )
2195        })?;
2196
2197        self.client
2198            .dump_start(generated::DumpRequest {
2199                file: file_str.to_string(),
2200                raw,
2201            })
2202            .await?;
2203        Ok(())
2204    }
2205
2206    /// Stop an ongoing network dump.
2207    /// Return nothing if it was successfully stopped, and otherwise return an
2208    /// error.
2209    ///
2210    /// Note. If the feature 'network_dump' is not enabled on the node then this
2211    /// will return a 'Precondition failed' error.
2212    pub async fn dump_stop(&mut self) -> endpoints::RPCResult<()> {
2213        self.client.dump_stop(generated::Empty::default()).await?;
2214        Ok(())
2215    }
2216
2217    /// Get a list of the peers that the node is connected to and associated
2218    /// network related information for each peer.
2219    pub async fn get_peers_info(&mut self) -> endpoints::RPCResult<types::network::PeersInfo> {
2220        let response = self
2221            .client
2222            .get_peers_info(generated::Empty::default())
2223            .await?;
2224        let peers_info = types::network::PeersInfo::try_from(response.into_inner())?;
2225        Ok(peers_info)
2226    }
2227
2228    /// Retrieve information about the node.
2229    /// The response contains meta information about the node
2230    /// such as the version of the software, the local time of the node etc.
2231    ///
2232    /// The response also yields network related information such as the node
2233    /// ID, bytes sent/received etc.
2234    ///
2235    /// Finally depending on the type of the node (regular node or
2236    /// 'bootstrapper') the response also yields baking information if
2237    /// the node is configured with baker credentials.
2238    ///
2239    /// Bootstrappers do no reveal any consensus information as they do not run
2240    /// the consensus protocol.
2241    pub async fn get_node_info(&mut self) -> endpoints::RPCResult<types::NodeInfo> {
2242        let response = self
2243            .client
2244            .get_node_info(generated::Empty::default())
2245            .await?;
2246        let node_info = types::NodeInfo::try_from(response.into_inner())?;
2247        Ok(node_info)
2248    }
2249
2250    /// Get the projected earliest time a baker wins the opportunity to bake a
2251    /// block.
2252    /// If the baker is not a baker for the current reward period then then the
2253    /// timestamp returned is the projected time of the first block of the
2254    /// new reward period.
2255    /// Note that the endpoint is only available on a node running at least
2256    /// protocol version 6.
2257    pub async fn get_baker_earliest_win_time(
2258        &mut self,
2259        bid: types::BakerId,
2260    ) -> endpoints::RPCResult<chrono::DateTime<chrono::Utc>> {
2261        let ts = self.client.get_baker_earliest_win_time(bid).await?;
2262        let local_time = ts.into_inner().try_into()?;
2263        Ok(local_time)
2264    }
2265
2266    /// Get the transaction events in a given block. If the block does not exist
2267    /// [`QueryError::NotFound`] is returned. The stream will end when all the
2268    /// transaction events for a given block have been returned.
2269    pub async fn get_block_transaction_events(
2270        &mut self,
2271        bi: impl IntoBlockIdentifier,
2272    ) -> endpoints::QueryResult<
2273        QueryResponse<impl Stream<Item = Result<types::BlockItemSummary, tonic::Status>>>,
2274    > {
2275        let response = self
2276            .client
2277            .get_block_transaction_events(&bi.into_block_identifier())
2278            .await?;
2279        let block_hash = extract_metadata(&response)?;
2280        let stream = response.into_inner().map(|result| match result {
2281            Ok(summary) => summary.try_into(),
2282            Err(err) => Err(err),
2283        });
2284        Ok(QueryResponse {
2285            block_hash,
2286            response: stream,
2287        })
2288    }
2289
2290    /// Get a the special events in a given block. If the block does not exist
2291    /// [`QueryError::NotFound`] is returned. The stream will end when all the
2292    /// special events for a given block have been returned.
2293    ///
2294    /// These are events generated by the protocol, such as minting and reward
2295    /// payouts. They are not directly generated by any transaction.
2296    pub async fn get_block_special_events(
2297        &mut self,
2298        bi: impl IntoBlockIdentifier,
2299    ) -> endpoints::QueryResult<
2300        QueryResponse<
2301            impl Stream<Item = Result<Upward<types::SpecialTransactionOutcome>, tonic::Status>>,
2302        >,
2303    > {
2304        let response = self
2305            .client
2306            .get_block_special_events(&bi.into_block_identifier())
2307            .await?;
2308        let block_hash = extract_metadata(&response)?;
2309        let stream = response.into_inner().map(|result| match result {
2310            Ok(summary) => summary.try_into(),
2311            Err(err) => Err(err),
2312        });
2313        Ok(QueryResponse {
2314            block_hash,
2315            response: stream,
2316        })
2317    }
2318
2319    /// Get the pending updates to chain parameters at the end of a given block.
2320    /// If the block does not exist [`QueryError::NotFound`] is returned.
2321    /// The stream will end when all the pending updates for a given block have
2322    /// been returned.
2323    pub async fn get_block_pending_updates(
2324        &mut self,
2325        bi: impl IntoBlockIdentifier,
2326    ) -> endpoints::QueryResult<
2327        QueryResponse<impl Stream<Item = Result<types::queries::PendingUpdate, tonic::Status>>>,
2328    > {
2329        let response = self
2330            .client
2331            .get_block_pending_updates(&bi.into_block_identifier())
2332            .await?;
2333        let block_hash = extract_metadata(&response)?;
2334        let stream = response.into_inner().map(|result| match result {
2335            Ok(update) => update.try_into(),
2336            Err(err) => Err(err),
2337        });
2338        Ok(QueryResponse {
2339            block_hash,
2340            response: stream,
2341        })
2342    }
2343
2344    /// Get the winning bakers of an historical `Epoch`.
2345    /// Hence, when this function is invoked using [`EpochIdentifier::Block`]
2346    /// and the [`BlockIdentifier`] is either [`BlockIdentifier::Best`] or
2347    /// [`BlockIdentifier::LastFinal`], then [`tonic::Code::Unavailable`] is
2348    /// returned, as these identifiers are not historical by definition.
2349    ///
2350    /// The stream ends when there
2351    /// are no more rounds for the epoch specified. This only works for
2352    /// epochs in at least protocol version 6. Note that the endpoint is
2353    /// only available on a node running at least protocol version 6.
2354    pub async fn get_winning_bakers_epoch(
2355        &mut self,
2356        ei: impl Into<EpochIdentifier>,
2357    ) -> endpoints::QueryResult<impl Stream<Item = Result<types::WinningBaker, tonic::Status>>>
2358    {
2359        let response = self.client.get_winning_bakers_epoch(&ei.into()).await?;
2360        let stream = response.into_inner().map(|result| match result {
2361            Ok(wb) => wb.try_into(),
2362            Err(err) => Err(err),
2363        });
2364        Ok(stream)
2365    }
2366
2367    /// Get the first block of the epoch.
2368    pub async fn get_first_block_epoch(
2369        &mut self,
2370        ei: impl Into<EpochIdentifier>,
2371    ) -> endpoints::QueryResult<BlockHash> {
2372        let response = self.client.get_first_block_epoch(&ei.into()).await?;
2373        Ok(response.into_inner().try_into()?)
2374    }
2375
2376    /// Get the detailed status of the consensus. This is only available for
2377    /// consensus version 1. If the genesis index is not specified, the
2378    /// status for the current genesis index is returned.
2379    pub async fn get_consensus_detailed_status(
2380        &mut self,
2381        genesis_index: Option<GenesisIndex>,
2382    ) -> endpoints::RPCResult<ConsensusDetailedStatus> {
2383        let query = generated::ConsensusDetailedStatusQuery {
2384            genesis_index: genesis_index.map(Into::into),
2385        };
2386        let response = self.client.get_consensus_detailed_status(query).await?;
2387        Ok(response.into_inner().try_into()?)
2388    }
2389
2390    /// Get next available sequence numbers for updating chain parameters after
2391    /// a given block. If the block does not exist then [`QueryError::NotFound`]
2392    /// is returned.
2393    pub async fn get_next_update_sequence_numbers(
2394        &mut self,
2395        block_id: impl IntoBlockIdentifier,
2396    ) -> endpoints::QueryResult<QueryResponse<types::queries::NextUpdateSequenceNumbers>> {
2397        let response = self
2398            .client
2399            .get_next_update_sequence_numbers(&block_id.into_block_identifier())
2400            .await?;
2401        let block_hash = extract_metadata(&response)?;
2402        let response = types::queries::NextUpdateSequenceNumbers::try_from(response.into_inner())?;
2403        Ok(QueryResponse {
2404            block_hash,
2405            response,
2406        })
2407    }
2408
2409    /// Get all accounts that have scheduled releases, with the timestamp of the
2410    /// first pending scheduled release for that account. (Note, this only
2411    /// identifies accounts by index, and only indicates the first pending
2412    /// release for each account.)
2413    pub async fn get_scheduled_release_accounts(
2414        &mut self,
2415        block_id: impl IntoBlockIdentifier,
2416    ) -> endpoints::QueryResult<
2417        QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2418    > {
2419        let response = self
2420            .client
2421            .get_scheduled_release_accounts(&block_id.into_block_identifier())
2422            .await?;
2423        let block_hash = extract_metadata(&response)?;
2424        let stream = response.into_inner().map(|result| match result {
2425            Ok(pending) => pending.try_into(),
2426            Err(err) => Err(err),
2427        });
2428        Ok(QueryResponse {
2429            block_hash,
2430            response: stream,
2431        })
2432    }
2433
2434    /// Get all accounts that have stake in cooldown, with the timestamp of the
2435    /// first pending cooldown expiry for each account. (Note, this only
2436    /// identifies accounts by index, and only indicates the first pending
2437    /// cooldown for each account.) Prior to protocol version 7, the
2438    /// resulting stream will always be empty.
2439    pub async fn get_cooldown_accounts(
2440        &mut self,
2441        block_id: impl IntoBlockIdentifier,
2442    ) -> endpoints::QueryResult<
2443        QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2444    > {
2445        let response = self
2446            .client
2447            .get_cooldown_accounts(&block_id.into_block_identifier())
2448            .await?;
2449        let block_hash = extract_metadata(&response)?;
2450        let stream = response.into_inner().map(|result| match result {
2451            Ok(pending) => pending.try_into(),
2452            Err(err) => Err(err),
2453        });
2454        Ok(QueryResponse {
2455            block_hash,
2456            response: stream,
2457        })
2458    }
2459
2460    /// Get all accounts that have stake in pre-cooldown.
2461    /// (This only identifies accounts by index.)
2462    /// Prior to protocol version 7, the resulting stream will always be empty.
2463    pub async fn get_pre_cooldown_accounts(
2464        &mut self,
2465        block_id: impl IntoBlockIdentifier,
2466    ) -> endpoints::QueryResult<
2467        QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2468    > {
2469        let response = self
2470            .client
2471            .get_pre_cooldown_accounts(&block_id.into_block_identifier())
2472            .await?;
2473        let block_hash = extract_metadata(&response)?;
2474        let stream = response.into_inner().map(|result| match result {
2475            Ok(account) => Ok(account.into()),
2476            Err(err) => Err(err),
2477        });
2478        Ok(QueryResponse {
2479            block_hash,
2480            response: stream,
2481        })
2482    }
2483
2484    /// Get all accounts that have stake in pre-pre-cooldown.
2485    /// (This only identifies accounts by index.)
2486    /// Prior to protocol version 7, the resulting stream will always be empty.
2487    pub async fn get_pre_pre_cooldown_accounts(
2488        &mut self,
2489        block_id: impl IntoBlockIdentifier,
2490    ) -> endpoints::QueryResult<
2491        QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2492    > {
2493        let response = self
2494            .client
2495            .get_pre_pre_cooldown_accounts(&block_id.into_block_identifier())
2496            .await?;
2497        let block_hash = extract_metadata(&response)?;
2498        let stream = response.into_inner().map(|result| match result {
2499            Ok(account) => Ok(account.into()),
2500            Err(err) => Err(err),
2501        });
2502        Ok(QueryResponse {
2503            block_hash,
2504            response: stream,
2505        })
2506    }
2507
2508    /// Get the chain parameters in effect after a given block.
2509    /// If the block does not exist [`QueryError::NotFound`] is returned.
2510    pub async fn get_block_chain_parameters(
2511        &mut self,
2512        block_id: impl IntoBlockIdentifier,
2513    ) -> endpoints::QueryResult<QueryResponse<ChainParameters>> {
2514        let response = self
2515            .client
2516            .get_block_chain_parameters(&block_id.into_block_identifier())
2517            .await?;
2518        let block_hash = extract_metadata(&response)?;
2519        let response = ChainParameters::try_from(response.into_inner())?;
2520        Ok(QueryResponse {
2521            block_hash,
2522            response,
2523        })
2524    }
2525
2526    /// For a non-genesis block, this returns the
2527    /// [`QuorumCertificate`](block_certificates::QuorumCertificate), a
2528    /// [`TimeoutCertificate`](block_certificates::TimeoutCertificate) (if
2529    /// present)
2530    /// and [`EpochFinalizationEntry`](block_certificates::EpochFinalizationEntry) (if
2531    /// present).
2532    /// If the block being pointed to is *not* from protocol version 6 or
2533    /// above, then [`InvalidArgument`](`tonic::Code::InvalidArgument`)
2534    /// is returned.
2535    pub async fn get_block_certificates(
2536        &mut self,
2537        bi: impl IntoBlockIdentifier,
2538    ) -> endpoints::QueryResult<QueryResponse<block_certificates::BlockCertificates>> {
2539        let response = self
2540            .client
2541            .get_block_certificates(&bi.into_block_identifier())
2542            .await?;
2543        let block_hash = extract_metadata(&response)?;
2544        let response = block_certificates::BlockCertificates::try_from(response.into_inner())?;
2545        Ok(QueryResponse {
2546            block_hash,
2547            response,
2548        })
2549    }
2550
2551    /// Get the information about a finalization record in a block.
2552    /// A block can contain zero or one finalization record. If a record is
2553    /// contained then this query will return information about the finalization
2554    /// session that produced it, including the finalizers eligible for the
2555    /// session, their power, and whether they signed this particular record. If
2556    /// the block does not exist [`QueryError::NotFound`] is returned.
2557    pub async fn get_block_finalization_summary(
2558        &mut self,
2559        block_id: impl IntoBlockIdentifier,
2560    ) -> endpoints::QueryResult<QueryResponse<Option<types::FinalizationSummary>>> {
2561        let response = self
2562            .client
2563            .get_block_finalization_summary(&block_id.into_block_identifier())
2564            .await?;
2565        let block_hash = extract_metadata(&response)?;
2566        let response = response.into_inner().try_into()?;
2567        Ok(QueryResponse {
2568            block_hash,
2569            response,
2570        })
2571    }
2572
2573    /// Get a continous stream of finalized blocks starting from a given height.
2574    /// This function starts a background task (a `tokio` task) that listens for
2575    /// new finalized blocks. This task is killed when the
2576    /// [`FinalizedBlocksStream`] is dropped.
2577    pub async fn get_finalized_blocks_from(
2578        &mut self,
2579        start_height: AbsoluteBlockHeight,
2580    ) -> endpoints::QueryResult<FinalizedBlocksStream> {
2581        let mut fin_height = self.get_consensus_info().await?.last_finalized_block_height;
2582        let (sender, receiver) = tokio::sync::mpsc::channel(100);
2583        let mut client = self.clone();
2584        let handle = tokio::spawn(async move {
2585            let mut height = start_height;
2586            loop {
2587                if height > fin_height {
2588                    fin_height = client
2589                        .get_consensus_info()
2590                        .await?
2591                        .last_finalized_block_height;
2592                    if height > fin_height {
2593                        break;
2594                    }
2595                } else {
2596                    let mut bi = client.get_blocks_at_height(&height.into()).await?;
2597                    let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2598                    let info = FinalizedBlockInfo { block_hash, height };
2599                    if sender.send(info).await.is_err() {
2600                        return Ok(());
2601                    }
2602                    height = height.next();
2603                }
2604            }
2605            let mut stream = client.get_finalized_blocks().await?;
2606            while let Some(fbi) = stream.next().await.transpose()? {
2607                // recover missed blocks.
2608                while height < fbi.height {
2609                    let mut bi = client.get_blocks_at_height(&height.into()).await?;
2610                    let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2611                    let info = FinalizedBlockInfo { block_hash, height };
2612                    if sender.send(info).await.is_err() {
2613                        return Ok(());
2614                    }
2615                    height = height.next();
2616                }
2617                if sender.send(fbi).await.is_err() {
2618                    return Ok(());
2619                }
2620                height = height.next();
2621            }
2622            Ok(())
2623        });
2624        Ok(FinalizedBlocksStream { handle, receiver })
2625    }
2626
2627    /// Find a block in which the account was created, if it exists and is
2628    /// finalized. The return value is a triple of the absolute block height and
2629    /// the corresponding block hash, and the account information at the
2630    /// end of that block. The block is the first block in which the account
2631    /// appears.
2632    ///
2633    /// Note that this is not necessarily the initial state of the account
2634    /// since there can be transactions updating it in the same block that it is
2635    /// created.
2636    ///
2637    /// Optional bounds can be provided, and the search will only
2638    /// consider blocks in that range. If the lower bound is not
2639    /// provided it defaults to 0, if the upper bound is not provided it
2640    /// defaults to the last finalized block at the time of the call.
2641    ///
2642    /// If the account cannot be found [`QueryError::NotFound`] is returned.
2643    pub async fn find_account_creation(
2644        &mut self,
2645        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2646        addr: AccountAddress,
2647    ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, AccountInfo)> {
2648        self.find_at_lowest_height(range, |mut client, height| async move {
2649            match client.get_account_info(&addr.into(), &height).await {
2650                Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2651                Err(e) if e.is_not_found() => Ok(None),
2652                Err(e) => Err(e),
2653            }
2654        })
2655        .await
2656    }
2657
2658    /// Find a block in which the instance was created, if it exists and is
2659    /// finalized. The return value is a triple of the absolute block height and
2660    /// the corresponding block hash, and the instance information at the
2661    /// end of that block. The block is the first block in which the instance
2662    /// appears.
2663    ///
2664    /// Note that this is not necessarily the initial state of the instance
2665    /// since there can be transactions updating the instance in the same block
2666    /// as the initialization transaction.
2667    ///
2668    /// Optional bounds can be provided, and the search will only
2669    /// consider blocks in that range. If the lower bound is not
2670    /// provided it defaults to 0, if the upper bound is not provided it
2671    /// defaults to the last finalized block at the time of the call.
2672    ///
2673    /// If the instance cannot be found [`QueryError::NotFound`] is returned.
2674    pub async fn find_instance_creation(
2675        &mut self,
2676        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2677        addr: ContractAddress,
2678    ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, InstanceInfo)> {
2679        self.find_at_lowest_height(range, |mut client, height| async move {
2680            match client.get_instance_info(addr, &height).await {
2681                Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2682                Err(e) if e.is_not_found() => Ok(None),
2683                Err(e) => Err(e),
2684            }
2685        })
2686        .await
2687    }
2688
2689    /// Find the first (i.e., earliest) finalized block whose slot time is no
2690    /// earlier than the specified time. If a block is not found return
2691    /// [`QueryError::NotFound`].
2692    ///
2693    /// The search is limited to the bounds specified. If the lower bound is not
2694    /// provided it defaults to 0, if the upper bound is not provided it
2695    /// defaults to the last finalized block at the time of the call.
2696    pub async fn find_first_finalized_block_no_earlier_than(
2697        &mut self,
2698        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2699        time: chrono::DateTime<chrono::Utc>,
2700    ) -> QueryResult<types::queries::BlockInfo> {
2701        self.find_at_lowest_height(range, move |mut client, height| async move {
2702            let info = client.get_block_info(&height).await?.response;
2703            if info.block_slot_time >= time {
2704                Ok(Some(info))
2705            } else {
2706                Ok(None)
2707            }
2708        })
2709        .await
2710    }
2711
2712    /// Find a **finalized** block with lowest height that satisfies the given
2713    /// condition. If a block is not found return [`QueryError::NotFound`].
2714    ///
2715    /// The `test` method should return `Some` if the object is found in the
2716    /// block, and `None` otherwise. It can also signal errors which will
2717    /// terminate search immediately.
2718    ///
2719    /// The precondition for this method is that the `test` method is monotone,
2720    /// i.e., if block at height `h` satisfies the test then also a block at
2721    /// height `h+1` does.
2722    /// If this precondition does not hold then the return value from this
2723    /// method is unspecified.
2724    ///
2725    /// The search is limited to at most the given range, the upper bound is
2726    /// always at most the last finalized block at the time of the call. If the
2727    /// lower bound is not provided it defaults to 0, if the upper bound is
2728    /// not provided it defaults to the last finalized block at the time of
2729    /// the call.
2730    pub async fn find_at_lowest_height<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2731        &mut self,
2732        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2733        test: impl Fn(Self, AbsoluteBlockHeight) -> F,
2734    ) -> QueryResult<A> {
2735        let mut start = match range.start_bound() {
2736            std::ops::Bound::Included(s) => u64::from(*s),
2737            std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2738            std::ops::Bound::Unbounded => 0,
2739        };
2740        let mut end = {
2741            let ci = self.get_consensus_info().await?;
2742            let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2743            match range.end_bound() {
2744                std::ops::Bound::Included(e) => bound(u64::from(*e)),
2745                std::ops::Bound::Excluded(e) => {
2746                    bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2747                }
2748                std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2749            }
2750        };
2751        if end < start {
2752            return Err(QueryError::NotFound);
2753        }
2754        let mut last_found = None;
2755        while start < end {
2756            let mid = start + (end - start) / 2;
2757            let ok = test(self.clone(), mid.into()).await?;
2758            if ok.is_some() {
2759                end = mid;
2760                last_found = ok;
2761            } else {
2762                start = mid + 1;
2763            }
2764        }
2765        last_found.ok_or(QueryError::NotFound)
2766    }
2767
2768    #[deprecated(note = "Use [`find_at_lowest_height`](./struct.Client.html#method.\
2769                         find_at_lowest_height) instead since it avoids an extra call.")]
2770    pub async fn find_earliest_finalized<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2771        &mut self,
2772        range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2773        test: impl Fn(Self, AbsoluteBlockHeight, BlockHash) -> F,
2774    ) -> QueryResult<A> {
2775        let mut start = match range.start_bound() {
2776            std::ops::Bound::Included(s) => u64::from(*s),
2777            std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2778            std::ops::Bound::Unbounded => 0,
2779        };
2780        let mut end = {
2781            let ci = self.get_consensus_info().await?;
2782            let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2783            match range.end_bound() {
2784                std::ops::Bound::Included(e) => bound(u64::from(*e)),
2785                std::ops::Bound::Excluded(e) => {
2786                    bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2787                }
2788                std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2789            }
2790        };
2791        if end < start {
2792            return Err(QueryError::NotFound);
2793        }
2794        let mut last_found = None;
2795        while start < end {
2796            let mid = start + (end - start) / 2;
2797            let bh = self
2798                .get_blocks_at_height(&AbsoluteBlockHeight::from(mid).into())
2799                .await?[0]; // using [0] is safe since we are only looking at finalized blocks.
2800            let ok = test(self.clone(), mid.into(), bh).await?;
2801            if ok.is_some() {
2802                end = mid;
2803                last_found = ok;
2804            } else {
2805                start = mid + 1;
2806            }
2807        }
2808        last_found.ok_or(QueryError::NotFound)
2809    }
2810
2811    /// Get all bakers in the reward period of a block.
2812    /// This endpoint is only supported for protocol version 4 and onwards.
2813    /// If the protocol does not support the endpoint then an
2814    /// [`IllegalArgument`](tonic::Code::InvalidArgument) is returned.
2815    pub async fn get_bakers_reward_period(
2816        &mut self,
2817        bi: impl IntoBlockIdentifier,
2818    ) -> endpoints::QueryResult<
2819        QueryResponse<impl Stream<Item = Result<types::BakerRewardPeriodInfo, tonic::Status>>>,
2820    > {
2821        let response = self
2822            .client
2823            .get_bakers_reward_period(&bi.into_block_identifier())
2824            .await?;
2825        let block_hash = extract_metadata(&response)?;
2826        let stream = response.into_inner().map(|result| match result {
2827            Ok(baker) => baker.try_into(),
2828            Err(err) => Err(err),
2829        });
2830        Ok(QueryResponse {
2831            block_hash,
2832            response: stream,
2833        })
2834    }
2835
2836    /// Retrieve the list of protocol level tokens that exist at the end of the
2837    /// given block.
2838    ///
2839    /// This endpoint is only relevant starting from Concordium Protocol Version
2840    /// 9 and onwards.
2841    pub async fn get_token_list(
2842        &mut self,
2843        bi: impl IntoBlockIdentifier,
2844    ) -> endpoints::QueryResult<
2845        QueryResponse<impl Stream<Item = Result<protocol_level_tokens::TokenId, tonic::Status>>>,
2846    > {
2847        let response = self
2848            .client
2849            .get_token_list(&bi.into_block_identifier())
2850            .await?;
2851        let block_hash = extract_metadata(&response)?;
2852        let stream = response.into_inner().map(|result| match result {
2853            Ok(token_id) => protocol_level_tokens::TokenId::try_from(token_id),
2854            Err(err) => Err(err),
2855        });
2856        Ok(QueryResponse {
2857            block_hash,
2858            response: stream,
2859        })
2860    }
2861
2862    /// Retrieve the information about the given protocol level token in the
2863    /// given block.
2864    ///
2865    /// This endpoint is only relevant starting from Concordium Protocol Version
2866    /// 9 and onwards.
2867    pub async fn get_token_info(
2868        &mut self,
2869        token_id: protocol_level_tokens::TokenId,
2870        bi: impl IntoBlockIdentifier,
2871    ) -> endpoints::QueryResult<QueryResponse<protocol_level_tokens::TokenInfo>> {
2872        let request = generated::TokenInfoRequest {
2873            block_hash: Some((&bi.into_block_identifier()).into()),
2874            token_id: Some(token_id.into()),
2875        };
2876        let response = self.client.get_token_info(request).await?;
2877        let block_hash = extract_metadata(&response)?;
2878        let response = protocol_level_tokens::TokenInfo::try_from(response.into_inner())?;
2879        Ok(QueryResponse {
2880            block_hash,
2881            response,
2882        })
2883    }
2884}
2885
2886/// A stream of finalized blocks. This contains a background task that polls
2887/// for new finalized blocks indefinitely. The task can be stopped by dropping
2888/// the object.
2889pub struct FinalizedBlocksStream {
2890    handle: tokio::task::JoinHandle<endpoints::QueryResult<()>>,
2891    receiver: tokio::sync::mpsc::Receiver<FinalizedBlockInfo>,
2892}
2893
2894// Make sure to abort the background task so that those resources are cleaned up
2895// before we drop the handle.
2896impl Drop for FinalizedBlocksStream {
2897    fn drop(&mut self) {
2898        self.handle.abort();
2899    }
2900}
2901
2902impl FinalizedBlocksStream {
2903    /// Retrieves the next finalized block from the stream. This function will
2904    /// block until a finalized block becomes available. To avoid waiting
2905    /// indefinitely, consider using [`FinalizedBlocksStream::next_timeout`]
2906    /// instead. If the channel is closed, the next element is `None`.
2907    pub async fn next(&mut self) -> Option<FinalizedBlockInfo> {
2908        self.receiver.recv().await
2909    }
2910
2911    /// Similar to [`FinalizedBlocksStream::next`], but with a maximum wait time
2912    /// defined by the specified duration between each finalized block.
2913    pub async fn next_timeout(
2914        &mut self,
2915        duration: std::time::Duration,
2916    ) -> Result<Option<FinalizedBlockInfo>, tokio::time::error::Elapsed> {
2917        tokio::time::timeout(duration, async move { self.next().await }).await
2918    }
2919
2920    /// Get the next chunk of blocks. If the finalized block poller has been
2921    /// disconnected this will return `Err(blocks)` where `blocks` are the
2922    /// finalized blocks that were retrieved before closure. In that case
2923    /// all further calls will return `Err(Vec::new())`.
2924    ///
2925    /// In case of success up to `max(1, n)` elements will be returned. This
2926    /// function will block so it always returns at least one element, and
2927    /// will retrieve up to `n` elements without blocking further once at least
2928    /// one element has been acquired.
2929    pub async fn next_chunk(
2930        &mut self,
2931        n: usize,
2932    ) -> Result<Vec<FinalizedBlockInfo>, Vec<FinalizedBlockInfo>> {
2933        let mut out = Vec::with_capacity(n);
2934        let first = self.receiver.recv().await;
2935        match first {
2936            Some(v) => out.push(v),
2937            None => {
2938                return Err(out);
2939            }
2940        }
2941        for _ in 1..n {
2942            match self.receiver.try_recv() {
2943                Ok(v) => {
2944                    out.push(v);
2945                }
2946                Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
2947                    break;
2948                }
2949                Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return Err(out),
2950            }
2951        }
2952        Ok(out)
2953    }
2954
2955    /// Like [`next_chunk`](Self::next_chunk), but waits no more than the given
2956    /// duration for the block. The boolean signifies whether an error
2957    /// occurred (it is `true` if an error occurred) while getting blocks.
2958    /// If that is the case further calls will always yield an error.
2959    ///
2960    /// The first field of the response indicates if an error occurred. This
2961    /// will only happen if the stream of finalized blocks has unexpectedly
2962    /// dropped.
2963    pub async fn next_chunk_timeout(
2964        &mut self,
2965        n: usize,
2966        duration: std::time::Duration,
2967    ) -> Result<(bool, Vec<FinalizedBlockInfo>), tokio::time::error::Elapsed> {
2968        let mut out = Vec::with_capacity(n);
2969        let first = self.next_timeout(duration).await?;
2970        match first {
2971            Some(v) => out.push(v),
2972            None => return Ok((true, out)),
2973        }
2974        for _ in 1..n {
2975            match self.receiver.try_recv() {
2976                Ok(v) => {
2977                    out.push(v);
2978                }
2979                Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
2980                    break;
2981                }
2982                Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
2983                    return Ok((true, out))
2984                }
2985            }
2986        }
2987        Ok((false, out))
2988    }
2989}
2990
2991fn extract_metadata<T>(response: &tonic::Response<T>) -> endpoints::RPCResult<BlockHash> {
2992    match response.metadata().get("blockhash") {
2993        Some(bytes) => {
2994            let bytes = bytes.as_bytes();
2995            if bytes.len() == 64 {
2996                let mut hash = [0u8; 32];
2997                if hex::decode_to_slice(bytes, &mut hash).is_err() {
2998                    tonic::Status::unknown("Response does correctly encode the block hash.");
2999                }
3000                Ok(hash.into())
3001            } else {
3002                Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3003                    "Response does not include the expected metadata.",
3004                )))
3005            }
3006        }
3007        None => Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3008            "Response does not include the expected metadata.",
3009        ))),
3010    }
3011}
3012
3013/// A helper trait to make it simpler to require specific fields when parsing a
3014/// protobuf message by allowing us to use method calling syntax and
3015/// constructing responses that match the calling context, allowing us to use
3016/// the `?` syntax.
3017///
3018/// The main reason for needing this is that in proto3 all fields are optional,
3019/// so it is up to the application to validate inputs if they are required.
3020pub(crate) trait Require<E> {
3021    type A;
3022    fn require(self) -> Result<Self::A, E>;
3023}
3024
3025impl<A> Require<tonic::Status> for Option<A> {
3026    type A = A;
3027
3028    fn require(self) -> Result<Self::A, tonic::Status> {
3029        match self {
3030            Some(v) => Ok(v),
3031            None => Err(tonic::Status::invalid_argument("missing field in response")),
3032        }
3033    }
3034}
3035
3036#[cfg(test)]
3037mod tests {
3038    use super::*;
3039    #[test]
3040    /// Test the different cases when parsing BlockIdentifiers.
3041    fn block_ident_from_str() -> anyhow::Result<()> {
3042        let b1 = "best".parse::<BlockIdentifier>()?;
3043        assert_eq!(b1, BlockIdentifier::Best);
3044
3045        let b2 = "lastFinal".parse::<BlockIdentifier>()?;
3046        assert_eq!(b2, BlockIdentifier::LastFinal);
3047
3048        let b3 = "lastfinal".parse::<BlockIdentifier>()?;
3049        assert_eq!(b3, BlockIdentifier::LastFinal);
3050
3051        let b4 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
3052            .parse::<BlockIdentifier>()?;
3053        assert_eq!(
3054            b4,
3055            BlockIdentifier::Given(
3056                "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()?
3057            )
3058        );
3059
3060        let b5 = "@33".parse::<BlockIdentifier>()?;
3061        assert_eq!(b5, BlockIdentifier::AbsoluteHeight(33.into()));
3062
3063        let b6 = "@33/3".parse::<BlockIdentifier>()?;
3064        assert_eq!(
3065            b6,
3066            BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3067                genesis_index: 3.into(),
3068                height: 33.into(),
3069                restrict: false,
3070            })
3071        );
3072
3073        let b7 = "@33/3!".parse::<BlockIdentifier>()?;
3074        assert_eq!(
3075            b7,
3076            BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3077                genesis_index: 3.into(),
3078                height: 33.into(),
3079                restrict: true,
3080            })
3081        );
3082
3083        Ok(())
3084    }
3085}