1use crate::{
5 endpoints,
6 id::{self, types::AccountCredentialMessage},
7 protocol_level_tokens,
8 types::{
9 self, block_certificates,
10 hashes::{self, BlockHash, TransactionHash, TransactionSignHash},
11 queries::ConsensusDetailedStatus,
12 smart_contracts::{
13 ContractContext, InstanceInfo, InvokeContractResult, ModuleReference, WasmModule,
14 },
15 transactions::{self, InitContractPayload, UpdateContractPayload, UpdateInstruction},
16 AbsoluteBlockHeight, AccountInfo, AccountPending, BlockItemSummary,
17 CredentialRegistrationID, Energy, Memo, Nonce, RegisteredData, SpecialTransactionOutcome,
18 TransactionStatus, UpdateSequenceNumber,
19 },
20};
21use anyhow::Context;
22use concordium_base::{
23 base::{
24 AccountIndex, BlockHeight, ChainParameterVersion0, ChainParameterVersion1,
25 CredentialsPerBlockLimit, ElectionDifficulty, Epoch, ExchangeRate, GenesisIndex,
26 MintDistributionV0, MintDistributionV1,
27 },
28 common::{
29 self,
30 types::{TransactionSignature, TransactionTime},
31 },
32 contracts_common::{
33 AccountAddress, AccountAddressParseError, Amount, ContractAddress, Duration,
34 OwnedContractName, OwnedParameter, OwnedReceiveName, ReceiveName,
35 },
36 hashes::HashFromStrError,
37 transactions::{BlockItem, EncodedPayload, PayloadLike},
38 updates::{
39 AuthorizationsV0, CooldownParameters, FinalizationCommitteeParameters, GASRewards,
40 GASRewardsV1, PoolParameters, TimeParameters, TimeoutParameters,
41 TransactionFeeDistribution, ValidatorScoreParameters,
42 },
43};
44pub use endpoints::{QueryError, QueryResult, RPCError, RPCResult};
45use futures::{Stream, StreamExt, TryStreamExt};
46pub use http::uri::Scheme;
47use num::{BigUint, ToPrimitive};
48use std::{collections::HashMap, num::ParseIntError, str::FromStr};
49use tonic::IntoRequest;
50pub use tonic::{
51 transport::{Endpoint, Error},
52 Code, Status,
53};
54
55use self::dry_run::WithRemainingQuota;
56
57mod conversions;
58pub mod dry_run;
59#[path = "generated/mod.rs"]
60#[allow(
61 clippy::large_enum_variant,
62 clippy::enum_variant_names,
63 clippy::derive_partial_eq_without_eq
64)]
65#[rustfmt::skip]
66mod gen;
67pub use gen::concordium::v2 as generated;
68pub mod proto_schema_version;
69
70#[derive(Clone, Debug)]
102pub struct Client {
103 client: generated::queries_client::QueriesClient<tonic::transport::Channel>,
104}
105
106#[derive(Clone, Copy, Debug)]
110pub struct QueryResponse<A> {
111 pub block_hash: BlockHash,
113 pub response: A,
115}
116
117impl<A> AsRef<A> for QueryResponse<A> {
118 fn as_ref(&self) -> &A { &self.response }
119}
120
121#[derive(Copy, Clone, Debug, derive_more::From, PartialEq, Eq)]
123pub enum BlockIdentifier {
124 Best,
126 LastFinal,
129 Given(BlockHash),
131 AbsoluteHeight(AbsoluteBlockHeight),
135 RelativeHeight(RelativeBlockHeight),
139}
140
141#[derive(Debug, thiserror::Error)]
142pub enum BlockIdentifierFromStrError {
143 #[error("The input is not recognized.")]
144 InvalidFormat,
145 #[error("The input is not a valid hash: {0}.")]
146 InvalidHash(#[from] HashFromStrError),
147 #[error("The input is not a valid unsigned integer: {0}.")]
148 InvalidInteger(#[from] ParseIntError),
149}
150
151impl std::fmt::Display for BlockIdentifier {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 match self {
156 BlockIdentifier::Best => "best".fmt(f),
157 BlockIdentifier::LastFinal => "lastfinal".fmt(f),
158 BlockIdentifier::Given(bh) => bh.fmt(f),
159 BlockIdentifier::AbsoluteHeight(h) => write!(f, "@{h}"),
160 BlockIdentifier::RelativeHeight(rh) => {
161 write!(
162 f,
163 "@{}/{}{}",
164 rh.height,
165 rh.genesis_index,
166 if rh.restrict { "!" } else { "" }
167 )
168 }
169 }
170 }
171}
172
173impl std::str::FromStr for BlockIdentifier {
186 type Err = BlockIdentifierFromStrError;
187
188 fn from_str(s: &str) -> Result<Self, Self::Err> {
189 match s {
190 "best" => Ok(Self::Best),
191 "lastFinal" => Ok(Self::LastFinal),
192 "lastfinal" => Ok(Self::LastFinal),
193 _ => {
194 if let Some(rest) = s.strip_prefix('@') {
195 if let Some((height_str, gen_idx_str)) = rest.split_once('/') {
196 let height = BlockHeight::from_str(height_str)?;
197 if let Some(gen_idx) = gen_idx_str.strip_suffix('!') {
198 let genesis_index = GenesisIndex::from_str(gen_idx)?;
199 Ok(Self::RelativeHeight(RelativeBlockHeight {
200 genesis_index,
201 height,
202 restrict: true,
203 }))
204 } else {
205 let genesis_index = GenesisIndex::from_str(gen_idx_str)?;
206 Ok(Self::RelativeHeight(RelativeBlockHeight {
207 genesis_index,
208 height,
209 restrict: false,
210 }))
211 }
212 } else {
213 let h = AbsoluteBlockHeight::from_str(rest)?;
214 Ok(Self::AbsoluteHeight(h))
215 }
216 } else {
217 let h = BlockHash::from_str(s)?;
218 Ok(Self::Given(h))
219 }
220 }
221 }
222 }
223}
224
225#[derive(Copy, Clone, Debug, PartialEq, Eq)]
227pub struct RelativeBlockHeight {
228 pub genesis_index: types::GenesisIndex,
230 pub height: types::BlockHeight,
232 pub restrict: bool,
236}
237
238#[derive(Copy, Clone, Debug, derive_more::From, derive_more::Display)]
240pub enum AccountIdentifier {
241 #[display(fmt = "{_0}")]
243 Address(AccountAddress),
244 #[display(fmt = "{_0}")]
246 CredId(CredentialRegistrationID),
247 #[display(fmt = "{_0}")]
249 Index(crate::types::AccountIndex),
250}
251
252impl FromStr for AccountIdentifier {
253 type Err = AccountAddressParseError;
254
255 fn from_str(s: &str) -> Result<Self, Self::Err> {
256 if let Ok(ai) = s.parse::<crate::types::AccountIndex>() {
257 return Ok(Self::Index(ai));
258 }
259 if let Ok(cid) = s.parse::<CredentialRegistrationID>() {
260 return Ok(Self::CredId(cid));
261 }
262 s.parse().map(Self::Address)
263 }
264}
265
266#[derive(Debug, Copy, Clone)]
268pub struct SpecifiedEpoch {
269 pub genesis_index: types::GenesisIndex,
271 pub epoch: types::Epoch,
273}
274
275#[derive(Copy, Clone, Debug, derive_more::From)]
277pub enum EpochIdentifier {
278 Specified(SpecifiedEpoch),
280 Block(BlockIdentifier),
282}
283
284#[derive(Debug, thiserror::Error)]
288pub enum EpochIdentifierFromStrError {
289 #[error("The input is not recognized.")]
290 InvalidFormat,
291 #[error("The genesis index is not a valid unsigned integer")]
292 InvalidGenesis,
293 #[error("The epoch index is not a valid unsigned integer")]
294 InvalidEpoch,
295 #[error("The input is not a valid block identifier: {0}.")]
296 InvalidBlockIdentifier(#[from] BlockIdentifierFromStrError),
297}
298
299impl std::str::FromStr for EpochIdentifier {
308 type Err = EpochIdentifierFromStrError;
309
310 fn from_str(s: &str) -> Result<Self, Self::Err> {
311 if let Some(rest) = s.strip_prefix('%') {
312 if let Some((gen_idx_str, epoch_str)) = rest.split_once(',') {
313 let genesis_index = GenesisIndex::from_str(gen_idx_str)
314 .map_err(|_| EpochIdentifierFromStrError::InvalidGenesis)?;
315 let epoch = Epoch::from_str(epoch_str)
316 .map_err(|_| EpochIdentifierFromStrError::InvalidEpoch)?;
317 Ok(Self::Specified(SpecifiedEpoch {
318 genesis_index,
319 epoch,
320 }))
321 } else {
322 Err(EpochIdentifierFromStrError::InvalidFormat)
323 }
324 } else {
325 Ok(Self::Block(BlockIdentifier::from_str(s)?))
326 }
327 }
328}
329
330impl IntoRequest<generated::EpochRequest> for &EpochIdentifier {
331 fn into_request(self) -> tonic::Request<generated::EpochRequest> {
332 tonic::Request::new((*self).into())
333 }
334}
335
336impl From<EpochIdentifier> for generated::EpochRequest {
337 fn from(ei: EpochIdentifier) -> Self {
338 match ei {
339 EpochIdentifier::Specified(SpecifiedEpoch {
340 genesis_index,
341 epoch,
342 }) => generated::EpochRequest {
343 epoch_request_input: Some(
344 generated::epoch_request::EpochRequestInput::RelativeEpoch(
345 generated::epoch_request::RelativeEpoch {
346 genesis_index: Some(generated::GenesisIndex {
347 value: genesis_index.height,
348 }),
349 epoch: Some(generated::Epoch { value: epoch.epoch }),
350 },
351 ),
352 ),
353 },
354 EpochIdentifier::Block(bi) => generated::EpochRequest {
355 epoch_request_input: Some(generated::epoch_request::EpochRequestInput::BlockHash(
356 (&bi).into(),
357 )),
358 },
359 }
360 }
361}
362
363#[derive(Copy, Clone, Debug)]
365pub struct FinalizedBlockInfo {
366 pub block_hash: BlockHash,
368 pub height: AbsoluteBlockHeight,
370}
371
372#[derive(Debug, Clone)]
373pub struct ChainParametersV0 {
376 pub election_difficulty: ElectionDifficulty,
378 pub euro_per_energy: ExchangeRate,
380 pub micro_ccd_per_euro: ExchangeRate,
382 pub baker_cooldown_epochs: Epoch,
385 pub account_creation_limit: CredentialsPerBlockLimit,
387 pub mint_distribution: MintDistributionV0,
389 pub transaction_fee_distribution: TransactionFeeDistribution,
391 pub gas_rewards: GASRewards,
393 pub foundation_account: AccountAddress,
395 pub minimum_threshold_for_baking: Amount,
397 pub keys: types::UpdateKeysCollection<ChainParameterVersion0>,
399}
400
401#[derive(Debug, Clone)]
402pub struct ChainParametersV1 {
405 pub election_difficulty: ElectionDifficulty,
407 pub euro_per_energy: ExchangeRate,
409 pub micro_ccd_per_euro: ExchangeRate,
411 pub cooldown_parameters: CooldownParameters,
412 pub time_parameters: TimeParameters,
413 pub account_creation_limit: CredentialsPerBlockLimit,
415 pub mint_distribution: MintDistributionV1,
417 pub transaction_fee_distribution: TransactionFeeDistribution,
419 pub gas_rewards: GASRewards,
421 pub foundation_account: AccountAddress,
423 pub pool_parameters: PoolParameters,
425 pub keys: types::UpdateKeysCollection<ChainParameterVersion1>,
427}
428
429#[derive(Debug, Clone)]
430pub struct ChainParametersV2 {
433 pub timeout_parameters: TimeoutParameters,
435 pub min_block_time: Duration,
437 pub block_energy_limit: Energy,
439 pub euro_per_energy: ExchangeRate,
441 pub micro_ccd_per_euro: ExchangeRate,
443 pub cooldown_parameters: CooldownParameters,
445 pub time_parameters: TimeParameters,
447 pub account_creation_limit: CredentialsPerBlockLimit,
449 pub mint_distribution: MintDistributionV1,
451 pub transaction_fee_distribution: TransactionFeeDistribution,
453 pub gas_rewards: GASRewardsV1,
455 pub foundation_account: AccountAddress,
457 pub pool_parameters: PoolParameters,
459 pub finalization_committee_parameters: FinalizationCommitteeParameters,
461 pub keys: types::UpdateKeysCollection<ChainParameterVersion1>,
463}
464
465#[derive(Debug, Clone)]
466pub struct ChainParametersV3 {
469 pub timeout_parameters: TimeoutParameters,
471 pub min_block_time: Duration,
473 pub block_energy_limit: Energy,
475 pub euro_per_energy: ExchangeRate,
477 pub micro_ccd_per_euro: ExchangeRate,
479 pub cooldown_parameters: CooldownParameters,
481 pub time_parameters: TimeParameters,
483 pub account_creation_limit: CredentialsPerBlockLimit,
485 pub mint_distribution: MintDistributionV1,
487 pub transaction_fee_distribution: TransactionFeeDistribution,
489 pub gas_rewards: GASRewardsV1,
491 pub foundation_account: AccountAddress,
493 pub pool_parameters: PoolParameters,
495 pub finalization_committee_parameters: FinalizationCommitteeParameters,
497 pub validator_score_parameters: ValidatorScoreParameters,
499 pub keys: types::UpdateKeysCollection<ChainParameterVersion1>,
501}
502
503#[derive(Debug, Clone)]
507pub enum ChainParameters {
508 V0(ChainParametersV0),
509 V1(ChainParametersV1),
510 V2(ChainParametersV2),
511 V3(ChainParametersV3),
512}
513
514impl ChainParameters {
515 pub fn common_update_keys(&self) -> &AuthorizationsV0 {
517 match self {
518 Self::V0(data) => &data.keys.level_2_keys,
519 Self::V1(data) => &data.keys.level_2_keys.v0,
520 Self::V2(data) => &data.keys.level_2_keys.v0,
521 Self::V3(data) => &data.keys.level_2_keys.v0,
522 }
523 }
524}
525
526impl ChainParameters {
527 pub fn micro_ccd_per_energy(&self) -> num::rational::Ratio<u128> {
529 let (num, denom) = match self {
530 ChainParameters::V0(v0) => {
531 let x = v0.micro_ccd_per_euro;
532 let y = v0.euro_per_energy;
533 (
534 u128::from(x.numerator()) * u128::from(y.numerator()),
535 u128::from(x.denominator()) * u128::from(y.denominator()),
536 )
537 }
538 ChainParameters::V1(v1) => {
539 let x = v1.micro_ccd_per_euro;
540 let y = v1.euro_per_energy;
541 (
542 u128::from(x.numerator()) * u128::from(y.numerator()),
543 u128::from(x.denominator()) * u128::from(y.denominator()),
544 )
545 }
546 ChainParameters::V2(v2) => {
547 let x = v2.micro_ccd_per_euro;
548 let y = v2.euro_per_energy;
549 (
550 u128::from(x.numerator()) * u128::from(y.numerator()),
551 u128::from(x.denominator()) * u128::from(y.denominator()),
552 )
553 }
554 ChainParameters::V3(v3) => {
555 let x = v3.micro_ccd_per_euro;
556 let y = v3.euro_per_energy;
557 (
558 u128::from(x.numerator()) * u128::from(y.numerator()),
559 u128::from(x.denominator()) * u128::from(y.denominator()),
560 )
561 }
562 };
563 num::rational::Ratio::new(num, denom)
564 }
565
566 pub fn ccd_cost(&self, nrg: Energy) -> Amount {
569 let ratio = self.micro_ccd_per_energy();
570 let numer = BigUint::from(*ratio.numer()) * nrg.energy;
571 let denomer = BigUint::from(*ratio.denom());
572 let cost = num::rational::Ratio::new(numer, denomer);
573 let i = cost.ceil().to_integer();
574 let micro = i % u64::MAX;
580 Amount::from_micro_ccd(micro.to_u64().expect("Value is known to be under u64::MAX"))
581 }
582
583 pub fn foundation_account(&self) -> AccountAddress {
585 match self {
586 ChainParameters::V0(v0) => v0.foundation_account,
587 ChainParameters::V1(v1) => v1.foundation_account,
588 ChainParameters::V2(v2) => v2.foundation_account,
589 ChainParameters::V3(v3) => v3.foundation_account,
590 }
591 }
592}
593
594impl From<&BlockIdentifier> for generated::BlockHashInput {
595 fn from(bi: &BlockIdentifier) -> Self {
596 let block_hash_input = match bi {
597 BlockIdentifier::Best => {
598 generated::block_hash_input::BlockHashInput::Best(Default::default())
599 }
600 BlockIdentifier::LastFinal => {
601 generated::block_hash_input::BlockHashInput::LastFinal(Default::default())
602 }
603 BlockIdentifier::Given(h) => {
604 generated::block_hash_input::BlockHashInput::Given(generated::BlockHash {
605 value: h.as_ref().to_vec(),
606 })
607 }
608 &BlockIdentifier::AbsoluteHeight(h) => {
609 generated::block_hash_input::BlockHashInput::AbsoluteHeight(h.into())
610 }
611 &BlockIdentifier::RelativeHeight(h) => {
612 generated::block_hash_input::BlockHashInput::RelativeHeight(h.into())
613 }
614 };
615 generated::BlockHashInput {
616 block_hash_input: Some(block_hash_input),
617 }
618 }
619}
620
621impl IntoRequest<generated::BlockHashInput> for &BlockIdentifier {
622 fn into_request(self) -> tonic::Request<generated::BlockHashInput> {
623 tonic::Request::new(self.into())
624 }
625}
626
627impl From<&AccountAddress> for generated::AccountAddress {
628 fn from(addr: &AccountAddress) -> Self {
629 generated::AccountAddress {
630 value: concordium_base::common::to_bytes(addr),
631 }
632 }
633}
634
635impl From<AccountAddress> for generated::AccountAddress {
636 fn from(addr: AccountAddress) -> Self {
637 generated::AccountAddress {
638 value: common::to_bytes(&addr),
639 }
640 }
641}
642
643impl From<&super::types::Address> for generated::Address {
644 fn from(addr: &super::types::Address) -> Self {
645 let ty = match addr {
646 super::types::Address::Account(account) => {
647 generated::address::Type::Account(account.into())
648 }
649 super::types::Address::Contract(contract) => {
650 generated::address::Type::Contract(contract.into())
651 }
652 };
653 generated::Address { r#type: Some(ty) }
654 }
655}
656
657impl From<&Memo> for generated::Memo {
658 fn from(v: &Memo) -> Self {
659 Self {
660 value: v.as_ref().clone(),
661 }
662 }
663}
664
665impl<'a> From<ReceiveName<'a>> for generated::ReceiveName {
666 fn from(a: ReceiveName<'a>) -> Self {
667 generated::ReceiveName {
668 value: a.get_chain_name().to_string(),
669 }
670 }
671}
672
673impl From<&RegisteredData> for generated::RegisteredData {
674 fn from(v: &RegisteredData) -> Self {
675 Self {
676 value: v.as_ref().clone(),
677 }
678 }
679}
680impl From<&[u8]> for generated::Parameter {
681 fn from(a: &[u8]) -> Self { generated::Parameter { value: a.to_vec() } }
682}
683
684impl From<&TransactionHash> for generated::TransactionHash {
685 fn from(th: &TransactionHash) -> Self { generated::TransactionHash { value: th.to_vec() } }
686}
687
688impl From<&AccountIdentifier> for generated::AccountIdentifierInput {
689 fn from(ai: &AccountIdentifier) -> Self {
690 let account_identifier_input = match ai {
691 AccountIdentifier::Address(addr) => {
692 generated::account_identifier_input::AccountIdentifierInput::Address(addr.into())
693 }
694 AccountIdentifier::CredId(credid) => {
695 let credid = generated::CredentialRegistrationId {
696 value: concordium_base::common::to_bytes(credid),
697 };
698 generated::account_identifier_input::AccountIdentifierInput::CredId(credid)
699 }
700 AccountIdentifier::Index(index) => {
701 generated::account_identifier_input::AccountIdentifierInput::AccountIndex(
702 (*index).into(),
703 )
704 }
705 };
706 generated::AccountIdentifierInput {
707 account_identifier_input: Some(account_identifier_input),
708 }
709 }
710}
711
712impl From<&ModuleReference> for generated::ModuleRef {
713 fn from(mr: &ModuleReference) -> Self { Self { value: mr.to_vec() } }
714}
715
716impl From<ModuleReference> for generated::ModuleRef {
717 fn from(mr: ModuleReference) -> Self { Self { value: mr.to_vec() } }
718}
719
720impl From<&WasmModule> for generated::VersionedModuleSource {
721 fn from(v: &WasmModule) -> Self {
722 Self {
723 module: Some(match v.version {
724 types::smart_contracts::WasmVersion::V0 => {
725 generated::versioned_module_source::Module::V0(
726 generated::versioned_module_source::ModuleSourceV0 {
727 value: v.source.as_ref().clone(),
728 },
729 )
730 }
731 types::smart_contracts::WasmVersion::V1 => {
732 generated::versioned_module_source::Module::V1(
733 generated::versioned_module_source::ModuleSourceV1 {
734 value: v.source.as_ref().clone(),
735 },
736 )
737 }
738 }),
739 }
740 }
741}
742
743impl From<&OwnedContractName> for generated::InitName {
744 fn from(v: &OwnedContractName) -> Self {
745 Self {
746 value: v.as_contract_name().get_chain_name().to_string(),
747 }
748 }
749}
750
751impl From<&OwnedReceiveName> for generated::ReceiveName {
752 fn from(v: &OwnedReceiveName) -> Self {
753 Self {
754 value: v.as_receive_name().get_chain_name().to_string(),
755 }
756 }
757}
758
759impl From<&OwnedParameter> for generated::Parameter {
760 fn from(v: &OwnedParameter) -> Self {
761 Self {
762 value: v.as_ref().to_vec(),
763 }
764 }
765}
766
767impl From<&InitContractPayload> for generated::InitContractPayload {
768 fn from(v: &InitContractPayload) -> Self {
769 Self {
770 amount: Some(v.amount.into()),
771 module_ref: Some(v.mod_ref.into()),
772 init_name: Some((&v.init_name).into()),
773 parameter: Some((&v.param).into()),
774 }
775 }
776}
777
778impl From<&UpdateContractPayload> for generated::UpdateContractPayload {
779 fn from(v: &UpdateContractPayload) -> Self {
780 Self {
781 amount: Some(v.amount.into()),
782 address: Some(v.address.into()),
783 receive_name: Some((&v.receive_name).into()),
784 parameter: Some((&v.message).into()),
785 }
786 }
787}
788
789impl From<&ContractAddress> for generated::ContractAddress {
790 fn from(ca: &ContractAddress) -> Self {
791 Self {
792 index: ca.index,
793 subindex: ca.subindex,
794 }
795 }
796}
797
798impl From<Nonce> for generated::SequenceNumber {
799 fn from(v: Nonce) -> Self { generated::SequenceNumber { value: v.nonce } }
800}
801
802impl From<UpdateSequenceNumber> for generated::UpdateSequenceNumber {
803 fn from(v: UpdateSequenceNumber) -> Self { generated::UpdateSequenceNumber { value: v.number } }
804}
805
806impl From<Energy> for generated::Energy {
807 fn from(v: Energy) -> Self { generated::Energy { value: v.energy } }
808}
809
810impl From<TransactionTime> for generated::TransactionTime {
811 fn from(v: TransactionTime) -> Self { generated::TransactionTime { value: v.seconds } }
812}
813
814impl From<&Amount> for generated::Amount {
815 fn from(v: &Amount) -> Self { Self { value: v.micro_ccd } }
816}
817
818impl From<Amount> for generated::Amount {
819 fn from(v: Amount) -> Self { Self { value: v.micro_ccd } }
820}
821
822impl
823 From<
824 &AccountCredentialMessage<
825 id::constants::IpPairing,
826 id::constants::ArCurve,
827 id::constants::AttributeKind,
828 >,
829 > for generated::CredentialDeployment
830{
831 fn from(
832 v: &AccountCredentialMessage<
833 id::constants::IpPairing,
834 id::constants::ArCurve,
835 id::constants::AttributeKind,
836 >,
837 ) -> Self {
838 Self {
839 message_expiry: Some(v.message_expiry.into()),
840 payload: Some(generated::credential_deployment::Payload::RawPayload(
841 common::to_bytes(&v.credential),
842 )),
843 }
844 }
845}
846
847impl From<&UpdateInstruction> for generated::UpdateInstruction {
848 fn from(v: &UpdateInstruction) -> Self {
849 Self {
850 signatures: Some(generated::SignatureMap {
851 signatures: {
852 let mut hm = HashMap::new();
853 for (key_idx, sig) in v.signatures.signatures.iter() {
854 hm.insert(key_idx.index.into(), generated::Signature {
855 value: sig.sig.to_owned(),
856 });
857 }
858 hm
859 },
860 }),
861 header: Some(generated::UpdateInstructionHeader {
862 sequence_number: Some(v.header.seq_number.into()),
863 effective_time: Some(v.header.effective_time.into()),
864 timeout: Some(v.header.timeout.into()),
865 }),
866 payload: Some(generated::UpdateInstructionPayload {
867 payload: Some(generated::update_instruction_payload::Payload::RawPayload(
868 common::to_bytes(&v.payload),
869 )),
870 }),
871 }
872 }
873}
874
875impl IntoRequest<generated::AccountInfoRequest> for (&AccountIdentifier, &BlockIdentifier) {
876 fn into_request(self) -> tonic::Request<generated::AccountInfoRequest> {
877 let ai = generated::AccountInfoRequest {
878 block_hash: Some(self.1.into()),
879 account_identifier: Some(self.0.into()),
880 };
881 tonic::Request::new(ai)
882 }
883}
884
885impl IntoRequest<generated::AncestorsRequest> for (&BlockIdentifier, u64) {
886 fn into_request(self) -> tonic::Request<generated::AncestorsRequest> {
887 let ar = generated::AncestorsRequest {
888 block_hash: Some(self.0.into()),
889 amount: self.1,
890 };
891 tonic::Request::new(ar)
892 }
893}
894
895impl IntoRequest<generated::ModuleSourceRequest> for (&ModuleReference, &BlockIdentifier) {
896 fn into_request(self) -> tonic::Request<generated::ModuleSourceRequest> {
897 let r = generated::ModuleSourceRequest {
898 block_hash: Some(self.1.into()),
899 module_ref: Some(self.0.into()),
900 };
901 tonic::Request::new(r)
902 }
903}
904
905impl IntoRequest<generated::InstanceInfoRequest> for (ContractAddress, &BlockIdentifier) {
906 fn into_request(self) -> tonic::Request<generated::InstanceInfoRequest> {
907 let r = generated::InstanceInfoRequest {
908 block_hash: Some(self.1.into()),
909 address: Some(self.0.into()),
910 };
911 tonic::Request::new(r)
912 }
913}
914
915impl<V: Into<Vec<u8>>> IntoRequest<generated::InstanceStateLookupRequest>
916 for (ContractAddress, &BlockIdentifier, V)
917{
918 fn into_request(self) -> tonic::Request<generated::InstanceStateLookupRequest> {
919 let r = generated::InstanceStateLookupRequest {
920 block_hash: Some(self.1.into()),
921 address: Some(self.0.into()),
922 key: self.2.into(),
923 };
924 tonic::Request::new(r)
925 }
926}
927
928impl IntoRequest<generated::TransactionHash> for &TransactionHash {
929 fn into_request(self) -> tonic::Request<generated::TransactionHash> {
930 tonic::Request::new(self.into())
931 }
932}
933
934impl IntoRequest<generated::AccountIdentifierInput> for &AccountIdentifier {
935 fn into_request(self) -> tonic::Request<generated::AccountIdentifierInput> {
936 tonic::Request::new(self.into())
937 }
938}
939
940impl IntoRequest<generated::AccountAddress> for &AccountAddress {
941 fn into_request(self) -> tonic::Request<generated::AccountAddress> {
942 tonic::Request::new(self.into())
943 }
944}
945
946impl From<transactions::TransactionHeader> for generated::AccountTransactionHeader {
947 fn from(v: transactions::TransactionHeader) -> Self { (&v).into() }
948}
949
950impl From<&transactions::TransactionHeader> for generated::AccountTransactionHeader {
951 fn from(v: &transactions::TransactionHeader) -> Self {
952 Self {
953 sender: Some(generated::AccountAddress::from(v.sender)),
954 sequence_number: Some(v.nonce.into()),
955 energy_amount: Some(v.energy_amount.into()),
956 expiry: Some(v.expiry.into()),
957 }
958 }
959}
960
961impl From<TransactionSignature> for generated::AccountTransactionSignature {
962 fn from(v: TransactionSignature) -> Self { (&v).into() }
963}
964
965impl From<&TransactionSignature> for generated::AccountTransactionSignature {
966 fn from(v: &TransactionSignature) -> Self {
967 Self {
968 signatures: {
969 let mut cred_map: HashMap<u32, generated::AccountSignatureMap> = HashMap::new();
970 for (cred_idx, sig_map) in v.signatures.iter() {
971 let mut acc_sig_map: HashMap<u32, generated::Signature> = HashMap::new();
972 for (key_idx, sig) in sig_map.iter() {
973 acc_sig_map.insert(key_idx.0.into(), generated::Signature {
974 value: sig.sig.to_owned(),
975 });
976 }
977 cred_map.insert(cred_idx.index.into(), generated::AccountSignatureMap {
978 signatures: acc_sig_map,
979 });
980 }
981 cred_map
982 },
983 }
984 }
985}
986
987impl IntoRequest<generated::PreAccountTransaction>
988 for (&transactions::TransactionHeader, &transactions::Payload)
989{
990 fn into_request(self) -> tonic::Request<generated::PreAccountTransaction> {
991 let request = generated::PreAccountTransaction {
992 header: Some(self.0.into()),
993 payload: Some(generated::AccountTransactionPayload {
994 payload: Some(generated::account_transaction_payload::Payload::RawPayload(
995 self.1.encode().into(),
996 )),
997 }),
998 };
999 tonic::Request::new(request)
1000 }
1001}
1002
1003impl<P: PayloadLike> IntoRequest<generated::SendBlockItemRequest> for &transactions::BlockItem<P> {
1004 fn into_request(self) -> tonic::Request<generated::SendBlockItemRequest> {
1005 let request = match self {
1006 transactions::BlockItem::AccountTransaction(v) => {
1007 generated::SendBlockItemRequest {
1008 block_item: Some(
1009 generated::send_block_item_request::BlockItem::AccountTransaction(
1010 generated::AccountTransaction {
1011 signature: Some((&v.signature).into()),
1012 header: Some((&v.header).into()),
1013 payload: {
1014 let atp = generated::AccountTransactionPayload{
1015 payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())),
1016 };
1017 Some(atp)
1018 },
1019 },
1020 ),
1021 ),
1022 }
1023 }
1024 transactions::BlockItem::CredentialDeployment(v) => generated::SendBlockItemRequest {
1025 block_item: Some(
1026 generated::send_block_item_request::BlockItem::CredentialDeployment(
1027 v.as_ref().into(),
1028 ),
1029 ),
1030 },
1031 transactions::BlockItem::UpdateInstruction(v) => generated::SendBlockItemRequest {
1032 block_item: Some(
1033 generated::send_block_item_request::BlockItem::UpdateInstruction(v.into()),
1034 ),
1035 },
1036 };
1037 tonic::Request::new(request)
1038 }
1039}
1040
1041impl IntoRequest<generated::InvokeInstanceRequest> for (&BlockIdentifier, &ContractContext) {
1042 fn into_request(self) -> tonic::Request<generated::InvokeInstanceRequest> {
1043 let (block, context) = self;
1044 tonic::Request::new(generated::InvokeInstanceRequest {
1045 block_hash: Some(block.into()),
1046 invoker: context.invoker.as_ref().map(|a| a.into()),
1047 instance: Some((&context.contract).into()),
1048 amount: Some(context.amount.into()),
1049 entrypoint: Some(context.method.as_receive_name().into()),
1050 parameter: Some(context.parameter.as_ref().into()),
1051 energy: context.energy.map(From::from),
1052 })
1053 }
1054}
1055
1056impl IntoRequest<generated::PoolInfoRequest> for (&BlockIdentifier, types::BakerId) {
1057 fn into_request(self) -> tonic::Request<generated::PoolInfoRequest> {
1058 let req = generated::PoolInfoRequest {
1059 block_hash: Some(self.0.into()),
1060 baker: Some(self.1.into()),
1061 };
1062 tonic::Request::new(req)
1063 }
1064}
1065
1066impl IntoRequest<generated::BakerId> for types::BakerId {
1067 fn into_request(self) -> tonic::Request<generated::BakerId> {
1068 tonic::Request::new(generated::BakerId {
1069 value: self.id.index,
1070 })
1071 }
1072}
1073
1074impl IntoRequest<generated::BlocksAtHeightRequest> for &endpoints::BlocksAtHeightInput {
1075 fn into_request(self) -> tonic::Request<generated::BlocksAtHeightRequest> {
1076 tonic::Request::new(self.into())
1077 }
1078}
1079
1080impl IntoRequest<generated::GetPoolDelegatorsRequest> for (&BlockIdentifier, types::BakerId) {
1081 fn into_request(self) -> tonic::Request<generated::GetPoolDelegatorsRequest> {
1082 let req = generated::GetPoolDelegatorsRequest {
1083 block_hash: Some(self.0.into()),
1084 baker: Some(self.1.into()),
1085 };
1086 tonic::Request::new(req)
1087 }
1088}
1089
1090impl TryFrom<crate::v2::generated::BannedPeer> for types::network::BannedPeer {
1091 type Error = anyhow::Error;
1092
1093 fn try_from(value: crate::v2::generated::BannedPeer) -> Result<Self, Self::Error> {
1094 Ok(types::network::BannedPeer(
1095 <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip_address.require()?.value)?,
1096 ))
1097 }
1098}
1099
1100impl TryFrom<generated::IpSocketAddress> for std::net::SocketAddr {
1101 type Error = anyhow::Error;
1102
1103 fn try_from(value: generated::IpSocketAddress) -> Result<Self, Self::Error> {
1104 Ok(std::net::SocketAddr::new(
1105 <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip.require()?.value)?,
1106 value.port.require()?.value as u16,
1107 ))
1108 }
1109}
1110
1111impl IntoRequest<crate::v2::generated::BannedPeer> for &types::network::BannedPeer {
1112 fn into_request(self) -> tonic::Request<crate::v2::generated::BannedPeer> {
1113 tonic::Request::new(crate::v2::generated::BannedPeer {
1114 ip_address: Some(crate::v2::generated::IpAddress {
1115 value: self.0.to_string(),
1116 }),
1117 })
1118 }
1119}
1120
1121impl From<generated::PeerId> for types::network::PeerId {
1122 fn from(value: generated::PeerId) -> Self { types::network::PeerId(value.value) }
1123}
1124
1125impl TryFrom<generated::PeersInfo> for types::network::PeersInfo {
1126 type Error = anyhow::Error;
1127
1128 fn try_from(peers_info: generated::PeersInfo) -> Result<Self, Self::Error> {
1129 let peers = peers_info
1135 .peers
1136 .into_iter()
1137 .map(|peer| {
1138 let peer_consensus_info = match peer.consensus_info.require()? {
1140 generated::peers_info::peer::ConsensusInfo::Bootstrapper(_) => {
1141 types::network::PeerConsensusInfo::Bootstrapper
1142 }
1143 generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(0) => {
1144 types::network::PeerConsensusInfo::Node(
1145 types::network::PeerCatchupStatus::UpToDate,
1146 )
1147 }
1148 generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(1) => {
1149 types::network::PeerConsensusInfo::Node(
1150 types::network::PeerCatchupStatus::Pending,
1151 )
1152 }
1153 generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(2) => {
1154 types::network::PeerConsensusInfo::Node(
1155 types::network::PeerCatchupStatus::CatchingUp,
1156 )
1157 }
1158 _ => anyhow::bail!("Malformed catchup status from peer."),
1159 };
1160 let stats = peer.network_stats.require()?;
1162 let network_stats = types::network::NetworkStats {
1163 packets_sent: stats.packets_sent,
1164 packets_received: stats.packets_received,
1165 latency: stats.latency,
1166 };
1167 Ok(types::network::Peer {
1168 peer_id: peer.peer_id.require()?.into(),
1169 consensus_info: peer_consensus_info,
1170 network_stats,
1171 addr: peer.socket_address.require()?.try_into()?,
1172 })
1173 })
1174 .collect::<anyhow::Result<Vec<types::network::Peer>>>()?;
1175 Ok(types::network::PeersInfo { peers })
1176 }
1177}
1178
1179impl TryFrom<generated::node_info::NetworkInfo> for types::NetworkInfo {
1180 type Error = anyhow::Error;
1181
1182 fn try_from(network_info: generated::node_info::NetworkInfo) -> Result<Self, Self::Error> {
1183 Ok(types::NetworkInfo {
1184 node_id: network_info.node_id.require()?.value,
1185 peer_total_sent: network_info.peer_total_sent,
1186 peer_total_received: network_info.peer_total_received,
1187 avg_bps_in: network_info.avg_bps_in,
1188 avg_bps_out: network_info.avg_bps_out,
1189 })
1190 }
1191}
1192
1193impl IntoRequest<crate::v2::generated::PeerToBan> for types::network::PeerToBan {
1194 fn into_request(self) -> tonic::Request<crate::v2::generated::PeerToBan> {
1195 tonic::Request::new(match self {
1196 types::network::PeerToBan::IpAddr(ip_addr) => crate::v2::generated::PeerToBan {
1197 ip_address: Some(crate::v2::generated::IpAddress {
1198 value: ip_addr.to_string(),
1199 }),
1200 },
1201 })
1202 }
1203}
1204
1205impl TryFrom<generated::NodeInfo> for types::NodeInfo {
1206 type Error = anyhow::Error;
1207
1208 fn try_from(node_info: generated::NodeInfo) -> Result<Self, Self::Error> {
1209 let version = semver::Version::parse(&node_info.peer_version)?;
1210 let local_time = chrono::DateTime::<chrono::Utc>::from(std::time::UNIX_EPOCH)
1211 + chrono::TimeDelta::try_milliseconds(node_info.local_time.require()?.value as i64)
1212 .context("Node local time out of bounds!")?;
1213 let uptime = chrono::Duration::try_from(types::DurationSeconds::from(
1214 node_info.peer_uptime.require()?.value,
1215 ))?;
1216 let network_info = node_info.network_info.require()?.try_into()?;
1217 let details = match node_info.details.require()? {
1218 generated::node_info::Details::Bootstrapper(_) => types::NodeDetails::Bootstrapper,
1219 generated::node_info::Details::Node(status) => {
1220 let consensus_status = match status.consensus_status.require()? {
1221 generated::node_info::node::ConsensusStatus::NotRunning(_) => {
1222 types::NodeConsensusStatus::ConsensusNotRunning
1223 }
1224 generated::node_info::node::ConsensusStatus::Passive(_) => {
1225 types::NodeConsensusStatus::ConsensusPassive
1226 }
1227 generated::node_info::node::ConsensusStatus::Active(baker) => {
1228 let baker_id = baker.baker_id.require()?.into();
1229 match baker.status.require()? {
1230 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(0) => types::NodeConsensusStatus::NotInCommittee(baker_id),
1231 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(1) => types::NodeConsensusStatus::AddedButNotActiveInCommittee(baker_id),
1232 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(2) => types::NodeConsensusStatus::AddedButWrongKeys(baker_id),
1233 generated::node_info::baker_consensus_info::Status::ActiveBakerCommitteeInfo(_) => types::NodeConsensusStatus::Baker(baker_id),
1234 generated::node_info::baker_consensus_info::Status::ActiveFinalizerCommitteeInfo(_) => types::NodeConsensusStatus::Finalizer(baker_id),
1235 _ => anyhow::bail!("Malformed baker status")
1236 }
1237 }
1238 };
1239 types::NodeDetails::Node(consensus_status)
1240 }
1241 };
1242 Ok(types::NodeInfo {
1243 version,
1244 local_time,
1245 uptime,
1246 network_info,
1247 details,
1248 })
1249 }
1250}
1251
1252pub trait IntoBlockIdentifier {
1259 fn into_block_identifier(self) -> BlockIdentifier;
1260}
1261
1262impl IntoBlockIdentifier for BlockIdentifier {
1263 fn into_block_identifier(self) -> BlockIdentifier { self }
1264}
1265
1266impl<X: IntoBlockIdentifier + Copy> IntoBlockIdentifier for &X {
1267 fn into_block_identifier(self) -> BlockIdentifier { (*self).into_block_identifier() }
1268}
1269
1270impl IntoBlockIdentifier for BlockHash {
1271 fn into_block_identifier(self) -> BlockIdentifier { BlockIdentifier::Given(self) }
1272}
1273
1274impl IntoBlockIdentifier for AbsoluteBlockHeight {
1275 fn into_block_identifier(self) -> BlockIdentifier { BlockIdentifier::AbsoluteHeight(self) }
1276}
1277
1278impl IntoBlockIdentifier for RelativeBlockHeight {
1279 fn into_block_identifier(self) -> BlockIdentifier { BlockIdentifier::RelativeHeight(self) }
1280}
1281
1282impl Client {
1283 pub async fn new<E>(endpoint: E) -> Result<Self, tonic::transport::Error>
1300 where
1301 E: TryInto<tonic::transport::Endpoint>,
1302 E::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>, {
1303 let client = generated::queries_client::QueriesClient::connect(endpoint).await?;
1304 Ok(Self { client })
1305 }
1306
1307 pub async fn get_account_info(
1311 &mut self,
1312 acc: &AccountIdentifier,
1313 bi: impl IntoBlockIdentifier,
1314 ) -> endpoints::QueryResult<QueryResponse<AccountInfo>> {
1315 let response = self
1316 .client
1317 .get_account_info((acc, &bi.into_block_identifier()))
1318 .await?;
1319 let block_hash = extract_metadata(&response)?;
1320 let response = AccountInfo::try_from(response.into_inner())?;
1321 Ok(QueryResponse {
1322 block_hash,
1323 response,
1324 })
1325 }
1326
1327 pub async fn get_next_account_sequence_number(
1330 &mut self,
1331 account_address: &AccountAddress,
1332 ) -> endpoints::QueryResult<types::queries::AccountNonceResponse> {
1333 let response = self
1334 .client
1335 .get_next_account_sequence_number(account_address)
1336 .await?;
1337 let response = types::queries::AccountNonceResponse::try_from(response.into_inner())?;
1338 Ok(response)
1339 }
1340
1341 pub async fn get_consensus_info(
1344 &mut self,
1345 ) -> endpoints::QueryResult<types::queries::ConsensusInfo> {
1346 let response = self
1347 .client
1348 .get_consensus_info(generated::Empty::default())
1349 .await?;
1350 let response = types::queries::ConsensusInfo::try_from(response.into_inner())?;
1351 Ok(response)
1352 }
1353
1354 pub async fn get_cryptographic_parameters(
1357 &mut self,
1358 bi: impl IntoBlockIdentifier,
1359 ) -> endpoints::QueryResult<QueryResponse<types::CryptographicParameters>> {
1360 let response = self
1361 .client
1362 .get_cryptographic_parameters(&bi.into_block_identifier())
1363 .await?;
1364 let block_hash = extract_metadata(&response)?;
1365 let response = types::CryptographicParameters::try_from(response.into_inner())?;
1366 Ok(QueryResponse {
1367 block_hash,
1368 response,
1369 })
1370 }
1371
1372 pub async fn get_account_list(
1377 &mut self,
1378 bi: impl IntoBlockIdentifier,
1379 ) -> endpoints::QueryResult<
1380 QueryResponse<impl Stream<Item = Result<AccountAddress, tonic::Status>>>,
1381 > {
1382 let response = self
1383 .client
1384 .get_account_list(&bi.into_block_identifier())
1385 .await?;
1386 let block_hash = extract_metadata(&response)?;
1387 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1388 Ok(QueryResponse {
1389 block_hash,
1390 response: stream,
1391 })
1392 }
1393
1394 pub async fn get_module_list(
1399 &mut self,
1400 bi: impl IntoBlockIdentifier,
1401 ) -> endpoints::QueryResult<
1402 QueryResponse<impl Stream<Item = Result<ModuleReference, tonic::Status>>>,
1403 > {
1404 let response = self
1405 .client
1406 .get_module_list(&bi.into_block_identifier())
1407 .await?;
1408 let block_hash = extract_metadata(&response)?;
1409 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1410 Ok(QueryResponse {
1411 block_hash,
1412 response: stream,
1413 })
1414 }
1415
1416 pub async fn get_module_source(
1420 &mut self,
1421 module_ref: &ModuleReference,
1422 bi: impl IntoBlockIdentifier,
1423 ) -> endpoints::QueryResult<QueryResponse<types::smart_contracts::WasmModule>> {
1424 let response = self
1425 .client
1426 .get_module_source((module_ref, &bi.into_block_identifier()))
1427 .await?;
1428 let block_hash = extract_metadata(&response)?;
1429 let response = types::smart_contracts::WasmModule::try_from(response.into_inner())?;
1430 Ok(QueryResponse {
1431 block_hash,
1432 response,
1433 })
1434 }
1435
1436 pub async fn get_instance_list(
1441 &mut self,
1442 bi: impl IntoBlockIdentifier,
1443 ) -> endpoints::QueryResult<
1444 QueryResponse<impl Stream<Item = Result<ContractAddress, tonic::Status>>>,
1445 > {
1446 let response = self
1447 .client
1448 .get_instance_list(&bi.into_block_identifier())
1449 .await?;
1450 let block_hash = extract_metadata(&response)?;
1451 let stream = response.into_inner().map(|x| x.map(From::from));
1452 Ok(QueryResponse {
1453 block_hash,
1454 response: stream,
1455 })
1456 }
1457
1458 pub async fn get_instance_info(
1462 &mut self,
1463 address: ContractAddress,
1464 bi: impl IntoBlockIdentifier,
1465 ) -> endpoints::QueryResult<QueryResponse<InstanceInfo>> {
1466 let response = self
1467 .client
1468 .get_instance_info((address, &bi.into_block_identifier()))
1469 .await?;
1470 let block_hash = extract_metadata(&response)?;
1471 let response = InstanceInfo::try_from(response.into_inner())?;
1472 Ok(QueryResponse {
1473 block_hash,
1474 response,
1475 })
1476 }
1477
1478 pub async fn get_ancestors(
1482 &mut self,
1483 bi: impl IntoBlockIdentifier,
1484 limit: u64,
1485 ) -> endpoints::QueryResult<QueryResponse<impl Stream<Item = Result<BlockHash, tonic::Status>>>>
1486 {
1487 let response = self
1488 .client
1489 .get_ancestors((&bi.into_block_identifier(), limit))
1490 .await?;
1491 let block_hash = extract_metadata(&response)?;
1492 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1493 Ok(QueryResponse {
1494 block_hash,
1495 response: stream,
1496 })
1497 }
1498
1499 pub async fn get_finalized_blocks(
1507 &mut self,
1508 ) -> endpoints::QueryResult<impl Stream<Item = Result<FinalizedBlockInfo, tonic::Status>>> {
1509 let response = self
1510 .client
1511 .get_finalized_blocks(generated::Empty::default())
1512 .await?;
1513 let stream = response.into_inner().map(|x| match x {
1514 Ok(v) => {
1515 let block_hash = v.hash.require().and_then(TryFrom::try_from)?;
1516 let height = v.height.require()?.into();
1517 Ok(FinalizedBlockInfo { block_hash, height })
1518 }
1519 Err(x) => Err(x),
1520 });
1521 Ok(stream)
1522 }
1523
1524 pub async fn get_instance_state(
1530 &mut self,
1531 ca: ContractAddress,
1532 bi: impl IntoBlockIdentifier,
1533 ) -> endpoints::QueryResult<
1534 QueryResponse<impl Stream<Item = Result<(Vec<u8>, Vec<u8>), tonic::Status>>>,
1535 > {
1536 let response = self
1537 .client
1538 .get_instance_state((ca, &bi.into_block_identifier()))
1539 .await?;
1540 let block_hash = extract_metadata(&response)?;
1541 let stream = response.into_inner().map(|x| match x {
1542 Ok(v) => {
1543 let key = v.key;
1544 let value = v.value;
1545 Ok((key, value))
1546 }
1547 Err(x) => Err(x),
1548 });
1549 Ok(QueryResponse {
1550 block_hash,
1551 response: stream,
1552 })
1553 }
1554
1555 pub async fn instance_state_lookup(
1561 &mut self,
1562 ca: ContractAddress,
1563 key: impl Into<Vec<u8>>,
1564 bi: impl IntoBlockIdentifier,
1565 ) -> endpoints::QueryResult<QueryResponse<Vec<u8>>> {
1566 let response = self
1567 .client
1568 .instance_state_lookup((ca, &bi.into_block_identifier(), key))
1569 .await?;
1570 let block_hash = extract_metadata(&response)?;
1571 Ok(QueryResponse {
1572 block_hash,
1573 response: response.into_inner().value,
1574 })
1575 }
1576
1577 pub async fn get_block_item_status(
1581 &mut self,
1582 th: &TransactionHash,
1583 ) -> endpoints::QueryResult<TransactionStatus> {
1584 let response = self.client.get_block_item_status(th).await?;
1585 let response = TransactionStatus::try_from(response.into_inner())?;
1586 Ok(response)
1587 }
1588
1589 pub async fn send_block_item<P: PayloadLike>(
1596 &mut self,
1597 bi: &transactions::BlockItem<P>,
1598 ) -> endpoints::RPCResult<TransactionHash> {
1599 let response = self.client.send_block_item(bi).await?;
1600 let response = TransactionHash::try_from(response.into_inner())?;
1601 Ok(response)
1602 }
1603
1604 pub async fn send_account_transaction<P: PayloadLike>(
1607 &mut self,
1608 at: transactions::AccountTransaction<P>,
1609 ) -> endpoints::RPCResult<TransactionHash> {
1610 self.send_block_item(&at.into()).await
1611 }
1612
1613 pub async fn get_account_transaction_sign_hash(
1626 &mut self,
1627 header: &transactions::TransactionHeader,
1628 payload: &transactions::Payload,
1629 ) -> endpoints::RPCResult<TransactionSignHash> {
1630 let response = self
1631 .client
1632 .get_account_transaction_sign_hash((header, payload))
1633 .await?;
1634 let response = TransactionSignHash::try_from(response.into_inner())?;
1635 Ok(response)
1636 }
1637
1638 pub async fn wait_until_finalized(
1649 &mut self,
1650 hash: &types::hashes::TransactionHash,
1651 ) -> endpoints::QueryResult<(types::hashes::BlockHash, types::BlockItemSummary)> {
1652 let hash = *hash;
1653 let process_response = |response| {
1654 if let types::TransactionStatus::Finalized(blocks) = response {
1655 let mut iter = blocks.into_iter();
1656 if let Some(rv) = iter.next() {
1657 if iter.next().is_some() {
1658 Err(tonic::Status::internal(
1659 "Finalized transaction finalized into multiple blocks. This cannot \
1660 happen.",
1661 )
1662 .into())
1663 } else {
1664 Ok::<_, QueryError>(Some(rv))
1665 }
1666 } else {
1667 Err(tonic::Status::internal(
1668 "Finalized transaction finalized into no blocks. This cannot happen.",
1669 )
1670 .into())
1671 }
1672 } else {
1673 Ok(None)
1674 }
1675 };
1676
1677 match process_response(self.get_block_item_status(&hash).await?)? {
1678 Some(rv) => Ok(rv),
1679 None => {
1680 let mut blocks = self.get_finalized_blocks().await?;
1683 while blocks.next().await.transpose()?.is_some() {
1684 if let Some(rv) = process_response(self.get_block_item_status(&hash).await?)? {
1685 return Ok(rv);
1686 }
1687 }
1688 Err(QueryError::NotFound)
1689 }
1690 }
1691 }
1692
1693 pub async fn invoke_instance(
1697 &mut self,
1698 bi: impl IntoBlockIdentifier,
1699 context: &ContractContext,
1700 ) -> endpoints::QueryResult<QueryResponse<InvokeContractResult>> {
1701 let response = self
1702 .client
1703 .invoke_instance((&bi.into_block_identifier(), context))
1704 .await?;
1705 let block_hash = extract_metadata(&response)?;
1706 let response = InvokeContractResult::try_from(response.into_inner())?;
1707 Ok(QueryResponse {
1708 block_hash,
1709 response,
1710 })
1711 }
1712
1713 pub async fn begin_dry_run(&mut self) -> endpoints::QueryResult<dry_run::DryRun> {
1720 Ok(dry_run::DryRun::new(&mut self.client).await?)
1721 }
1722
1723 pub async fn dry_run(
1727 &mut self,
1728 bi: impl IntoBlockIdentifier,
1729 ) -> dry_run::DryRunResult<(dry_run::DryRun, dry_run::BlockStateLoaded)> {
1730 let mut runner = dry_run::DryRun::new(&mut self.client).await?;
1731 let load_result = runner.load_block_state(bi).await?;
1732 Ok(WithRemainingQuota {
1733 inner: (runner, load_result.inner),
1734 quota_remaining: load_result.quota_remaining,
1735 })
1736 }
1737
1738 pub async fn get_block_info(
1742 &mut self,
1743 bi: impl IntoBlockIdentifier,
1744 ) -> endpoints::QueryResult<QueryResponse<types::queries::BlockInfo>> {
1745 let response = self
1746 .client
1747 .get_block_info(&bi.into_block_identifier())
1748 .await?;
1749 let block_hash = extract_metadata(&response)?;
1750 let response = types::queries::BlockInfo::try_from(response.into_inner())?;
1751 Ok(QueryResponse {
1752 block_hash,
1753 response,
1754 })
1755 }
1756
1757 pub async fn is_payday_block(
1762 &mut self,
1763 bi: impl IntoBlockIdentifier,
1764 ) -> endpoints::QueryResult<QueryResponse<bool>> {
1765 let mut special_events = self.get_block_special_events(bi).await?;
1766 let block_hash = special_events.block_hash;
1767
1768 while let Some(event) = special_events.response.next().await.transpose()? {
1769 let has_payday_event = matches!(
1770 event,
1771 SpecialTransactionOutcome::PaydayPoolReward { .. }
1772 | SpecialTransactionOutcome::PaydayAccountReward { .. }
1773 | SpecialTransactionOutcome::PaydayFoundationReward { .. }
1774 );
1775
1776 if has_payday_event {
1777 return Ok(QueryResponse {
1778 block_hash,
1779 response: true,
1780 });
1781 };
1782 }
1783
1784 Ok(QueryResponse {
1785 block_hash,
1786 response: false,
1787 })
1788 }
1789
1790 pub async fn get_baker_list(
1793 &mut self,
1794 bi: impl IntoBlockIdentifier,
1795 ) -> endpoints::QueryResult<
1796 QueryResponse<impl Stream<Item = Result<types::BakerId, tonic::Status>>>,
1797 > {
1798 let response = self
1799 .client
1800 .get_baker_list(&bi.into_block_identifier())
1801 .await?;
1802 let block_hash = extract_metadata(&response)?;
1803 let stream = response.into_inner().map(|x| x.map(From::from));
1804 Ok(QueryResponse {
1805 block_hash,
1806 response: stream,
1807 })
1808 }
1809
1810 pub async fn get_pool_info(
1814 &mut self,
1815 block_id: impl IntoBlockIdentifier,
1816 baker_id: types::BakerId,
1817 ) -> endpoints::QueryResult<QueryResponse<types::BakerPoolStatus>> {
1818 let response = self
1819 .client
1820 .get_pool_info((&block_id.into_block_identifier(), baker_id))
1821 .await?;
1822 let block_hash = extract_metadata(&response)?;
1823 let response = types::BakerPoolStatus::try_from(response.into_inner())?;
1824 Ok(QueryResponse {
1825 block_hash,
1826 response,
1827 })
1828 }
1829
1830 pub async fn get_passive_delegation_info(
1835 &mut self,
1836 block_id: impl IntoBlockIdentifier,
1837 ) -> endpoints::QueryResult<QueryResponse<types::PassiveDelegationStatus>> {
1838 let response = self
1839 .client
1840 .get_passive_delegation_info(&block_id.into_block_identifier())
1841 .await?;
1842 let block_hash = extract_metadata(&response)?;
1843 let response = types::PassiveDelegationStatus::try_from(response.into_inner())?;
1844 Ok(QueryResponse {
1845 block_hash,
1846 response,
1847 })
1848 }
1849
1850 pub async fn get_blocks_at_height(
1852 &mut self,
1853 blocks_at_height_input: &endpoints::BlocksAtHeightInput,
1854 ) -> endpoints::QueryResult<Vec<BlockHash>> {
1855 let response = self
1856 .client
1857 .get_blocks_at_height(blocks_at_height_input)
1858 .await?;
1859 let blocks = response
1860 .into_inner()
1861 .blocks
1862 .into_iter()
1863 .map(TryFrom::try_from)
1864 .collect::<Result<_, tonic::Status>>()?;
1865 Ok(blocks)
1866 }
1867
1868 pub async fn get_tokenomics_info(
1871 &mut self,
1872 block_id: impl IntoBlockIdentifier,
1873 ) -> endpoints::QueryResult<QueryResponse<types::RewardsOverview>> {
1874 let response = self
1875 .client
1876 .get_tokenomics_info(&block_id.into_block_identifier())
1877 .await?;
1878 let block_hash = extract_metadata(&response)?;
1879 let response = types::RewardsOverview::try_from(response.into_inner())?;
1880 Ok(QueryResponse {
1881 block_hash,
1882 response,
1883 })
1884 }
1885
1886 pub async fn get_pool_delegators(
1900 &mut self,
1901 bi: impl IntoBlockIdentifier,
1902 baker_id: types::BakerId,
1903 ) -> endpoints::QueryResult<
1904 QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1905 > {
1906 let response = self
1907 .client
1908 .get_pool_delegators((&bi.into_block_identifier(), baker_id))
1909 .await?;
1910 let block_hash = extract_metadata(&response)?;
1911 let stream = response.into_inner().map(|result| match result {
1912 Ok(delegator) => delegator.try_into(),
1913 Err(err) => Err(err),
1914 });
1915 Ok(QueryResponse {
1916 block_hash,
1917 response: stream,
1918 })
1919 }
1920
1921 pub async fn get_pool_delegators_reward_period(
1934 &mut self,
1935 bi: impl IntoBlockIdentifier,
1936 baker_id: types::BakerId,
1937 ) -> endpoints::QueryResult<
1938 QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1939 > {
1940 let response = self
1941 .client
1942 .get_pool_delegators_reward_period((&bi.into_block_identifier(), baker_id))
1943 .await?;
1944 let block_hash = extract_metadata(&response)?;
1945 let stream = response.into_inner().map(|result| match result {
1946 Ok(delegator) => delegator.try_into(),
1947 Err(err) => Err(err),
1948 });
1949 Ok(QueryResponse {
1950 block_hash,
1951 response: stream,
1952 })
1953 }
1954
1955 pub async fn get_passive_delegators(
1967 &mut self,
1968 bi: impl IntoBlockIdentifier,
1969 ) -> endpoints::QueryResult<
1970 QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1971 > {
1972 let response = self
1973 .client
1974 .get_passive_delegators(&bi.into_block_identifier())
1975 .await?;
1976 let block_hash = extract_metadata(&response)?;
1977 let stream = response.into_inner().map(|result| match result {
1978 Ok(delegator) => delegator.try_into(),
1979 Err(err) => Err(err),
1980 });
1981 Ok(QueryResponse {
1982 block_hash,
1983 response: stream,
1984 })
1985 }
1986
1987 pub async fn get_passive_delegators_reward_period(
2000 &mut self,
2001 bi: impl IntoBlockIdentifier,
2002 ) -> endpoints::QueryResult<
2003 QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
2004 > {
2005 let response = self
2006 .client
2007 .get_passive_delegators_reward_period(&bi.into_block_identifier())
2008 .await?;
2009 let block_hash = extract_metadata(&response)?;
2010 let stream = response.into_inner().map(|result| match result {
2011 Ok(delegator) => delegator.try_into(),
2012 Err(err) => Err(err),
2013 });
2014 Ok(QueryResponse {
2015 block_hash,
2016 response: stream,
2017 })
2018 }
2019
2020 pub async fn get_branches(&mut self) -> endpoints::QueryResult<types::queries::Branch> {
2027 let response = self
2028 .client
2029 .get_branches(generated::Empty::default())
2030 .await?;
2031 let response = types::queries::Branch::try_from(response.into_inner())?;
2032 Ok(response)
2033 }
2034
2035 pub async fn get_election_info(
2038 &mut self,
2039 bi: impl IntoBlockIdentifier,
2040 ) -> endpoints::QueryResult<QueryResponse<types::BirkParameters>> {
2041 let response = self
2042 .client
2043 .get_election_info(&bi.into_block_identifier())
2044 .await?;
2045 let block_hash = extract_metadata(&response)?;
2046 let response = types::BirkParameters::try_from(response.into_inner())?;
2047 Ok(QueryResponse {
2048 block_hash,
2049 response,
2050 })
2051 }
2052
2053 pub async fn get_identity_providers(
2057 &mut self,
2058 bi: impl IntoBlockIdentifier,
2059 ) -> endpoints::QueryResult<
2060 QueryResponse<
2061 impl Stream<
2062 Item = Result<
2063 crate::id::types::IpInfo<crate::id::constants::IpPairing>,
2064 tonic::Status,
2065 >,
2066 >,
2067 >,
2068 > {
2069 let response = self
2070 .client
2071 .get_identity_providers(&bi.into_block_identifier())
2072 .await?;
2073 let block_hash = extract_metadata(&response)?;
2074 let stream = response.into_inner().map(|result| match result {
2075 Ok(ip_info) => ip_info.try_into(),
2076 Err(err) => Err(err),
2077 });
2078 Ok(QueryResponse {
2079 block_hash,
2080 response: stream,
2081 })
2082 }
2083
2084 pub async fn get_anonymity_revokers(
2088 &mut self,
2089 bi: impl IntoBlockIdentifier,
2090 ) -> endpoints::QueryResult<
2091 QueryResponse<
2092 impl Stream<
2093 Item = Result<
2094 crate::id::types::ArInfo<crate::id::constants::ArCurve>,
2095 tonic::Status,
2096 >,
2097 >,
2098 >,
2099 > {
2100 let response = self
2101 .client
2102 .get_anonymity_revokers(&bi.into_block_identifier())
2103 .await?;
2104 let block_hash = extract_metadata(&response)?;
2105 let stream = response.into_inner().map(|result| match result {
2106 Ok(ar_info) => ar_info.try_into(),
2107 Err(err) => Err(err),
2108 });
2109 Ok(QueryResponse {
2110 block_hash,
2111 response: stream,
2112 })
2113 }
2114
2115 pub async fn get_account_non_finalized_transactions(
2125 &mut self,
2126 account_address: &AccountAddress,
2127 ) -> endpoints::QueryResult<impl Stream<Item = Result<TransactionHash, tonic::Status>>> {
2128 let response = self
2129 .client
2130 .get_account_non_finalized_transactions(account_address)
2131 .await?;
2132 let stream = response.into_inner().map(|result| match result {
2133 Ok(transaction_hash) => transaction_hash.try_into(),
2134 Err(err) => Err(err),
2135 });
2136 Ok(stream)
2137 }
2138
2139 pub async fn get_block_items(
2144 &mut self,
2145 bi: impl IntoBlockIdentifier,
2146 ) -> endpoints::QueryResult<
2147 QueryResponse<impl Stream<Item = Result<BlockItem<EncodedPayload>, tonic::Status>>>,
2148 > {
2149 let response = self
2150 .client
2151 .get_block_items(&bi.into_block_identifier())
2152 .await?;
2153 let block_hash = extract_metadata(&response)?;
2154 let stream = response.into_inner().map(|result| match result {
2155 Ok(summary) => summary.try_into(),
2156 Err(err) => Err(err),
2157 });
2158 Ok(QueryResponse {
2159 block_hash,
2160 response: stream,
2161 })
2162 }
2163
2164 pub async fn get_finalized_block_item(
2177 &mut self,
2178 th: TransactionHash,
2179 ) -> endpoints::QueryResult<(BlockItem<EncodedPayload>, BlockHash, BlockItemSummary)> {
2180 let status = self.get_block_item_status(&th).await?;
2181 let Some((bh, status)) = status.is_finalized() else {
2182 return Err(QueryError::NotFound);
2183 };
2184 let mut response = self.get_block_items(bh).await?.response;
2185 while let Some(tx) = response.try_next().await? {
2186 if tx.hash() == th {
2187 return Ok((tx, *bh, status.clone()));
2188 }
2189 }
2190 Err(endpoints::QueryError::NotFound)
2191 }
2192
2193 pub async fn shutdown(&mut self) -> endpoints::RPCResult<()> {
2196 self.client.shutdown(generated::Empty::default()).await?;
2197 Ok(())
2198 }
2199
2200 pub async fn peer_connect(&mut self, addr: std::net::SocketAddr) -> endpoints::RPCResult<()> {
2208 let peer_connection = generated::IpSocketAddress {
2209 ip: Some(generated::IpAddress {
2210 value: addr.ip().to_string(),
2211 }),
2212 port: Some(generated::Port {
2213 value: addr.port() as u32,
2214 }),
2215 };
2216 self.client.peer_connect(peer_connection).await?;
2217 Ok(())
2218 }
2219
2220 pub async fn peer_disconnect(
2224 &mut self,
2225 addr: std::net::SocketAddr,
2226 ) -> endpoints::RPCResult<()> {
2227 let peer_connection = generated::IpSocketAddress {
2228 ip: Some(generated::IpAddress {
2229 value: addr.ip().to_string(),
2230 }),
2231 port: Some(generated::Port {
2232 value: addr.port() as u32,
2233 }),
2234 };
2235 self.client.peer_disconnect(peer_connection).await?;
2236 Ok(())
2237 }
2238
2239 pub async fn get_banned_peers(
2241 &mut self,
2242 ) -> endpoints::RPCResult<Vec<super::types::network::BannedPeer>> {
2243 Ok(self
2244 .client
2245 .get_banned_peers(generated::Empty::default())
2246 .await?
2247 .into_inner()
2248 .peers
2249 .into_iter()
2250 .map(super::types::network::BannedPeer::try_from)
2251 .collect::<anyhow::Result<Vec<super::types::network::BannedPeer>>>()?)
2252 }
2253
2254 pub async fn ban_peer(
2258 &mut self,
2259 peer_to_ban: super::types::network::PeerToBan,
2260 ) -> endpoints::RPCResult<()> {
2261 self.client.ban_peer(peer_to_ban).await?;
2262 Ok(())
2263 }
2264
2265 pub async fn unban_peer(
2269 &mut self,
2270 banned_peer: &super::types::network::BannedPeer,
2271 ) -> endpoints::RPCResult<()> {
2272 self.client.unban_peer(banned_peer).await?;
2273 Ok(())
2274 }
2275
2276 pub async fn dump_start(
2287 &mut self,
2288 file: &std::path::Path,
2289 raw: bool,
2290 ) -> endpoints::RPCResult<()> {
2291 let file_str = file.to_str().ok_or_else(|| {
2292 tonic::Status::invalid_argument(
2293 "The provided path cannot is not a valid UTF8 string, so cannot be used.",
2294 )
2295 })?;
2296
2297 self.client
2298 .dump_start(generated::DumpRequest {
2299 file: file_str.to_string(),
2300 raw,
2301 })
2302 .await?;
2303 Ok(())
2304 }
2305
2306 pub async fn dump_stop(&mut self) -> endpoints::RPCResult<()> {
2313 self.client.dump_stop(generated::Empty::default()).await?;
2314 Ok(())
2315 }
2316
2317 pub async fn get_peers_info(&mut self) -> endpoints::RPCResult<types::network::PeersInfo> {
2320 let response = self
2321 .client
2322 .get_peers_info(generated::Empty::default())
2323 .await?;
2324 let peers_info = types::network::PeersInfo::try_from(response.into_inner())?;
2325 Ok(peers_info)
2326 }
2327
2328 pub async fn get_node_info(&mut self) -> endpoints::RPCResult<types::NodeInfo> {
2342 let response = self
2343 .client
2344 .get_node_info(generated::Empty::default())
2345 .await?;
2346 let node_info = types::NodeInfo::try_from(response.into_inner())?;
2347 Ok(node_info)
2348 }
2349
2350 pub async fn get_baker_earliest_win_time(
2358 &mut self,
2359 bid: types::BakerId,
2360 ) -> endpoints::RPCResult<chrono::DateTime<chrono::Utc>> {
2361 let ts = self.client.get_baker_earliest_win_time(bid).await?;
2362 let local_time = ts.into_inner().try_into()?;
2363 Ok(local_time)
2364 }
2365
2366 pub async fn get_block_transaction_events(
2370 &mut self,
2371 bi: impl IntoBlockIdentifier,
2372 ) -> endpoints::QueryResult<
2373 QueryResponse<impl Stream<Item = Result<types::BlockItemSummary, tonic::Status>>>,
2374 > {
2375 let response = self
2376 .client
2377 .get_block_transaction_events(&bi.into_block_identifier())
2378 .await?;
2379 let block_hash = extract_metadata(&response)?;
2380 let stream = response.into_inner().map(|result| match result {
2381 Ok(summary) => summary.try_into(),
2382 Err(err) => Err(err),
2383 });
2384 Ok(QueryResponse {
2385 block_hash,
2386 response: stream,
2387 })
2388 }
2389
2390 pub async fn get_block_special_events(
2397 &mut self,
2398 bi: impl IntoBlockIdentifier,
2399 ) -> endpoints::QueryResult<
2400 QueryResponse<impl Stream<Item = Result<types::SpecialTransactionOutcome, tonic::Status>>>,
2401 > {
2402 let response = self
2403 .client
2404 .get_block_special_events(&bi.into_block_identifier())
2405 .await?;
2406 let block_hash = extract_metadata(&response)?;
2407 let stream = response.into_inner().map(|result| match result {
2408 Ok(summary) => summary.try_into(),
2409 Err(err) => Err(err),
2410 });
2411 Ok(QueryResponse {
2412 block_hash,
2413 response: stream,
2414 })
2415 }
2416
2417 pub async fn get_block_pending_updates(
2422 &mut self,
2423 bi: impl IntoBlockIdentifier,
2424 ) -> endpoints::QueryResult<
2425 QueryResponse<impl Stream<Item = Result<types::queries::PendingUpdate, tonic::Status>>>,
2426 > {
2427 let response = self
2428 .client
2429 .get_block_pending_updates(&bi.into_block_identifier())
2430 .await?;
2431 let block_hash = extract_metadata(&response)?;
2432 let stream = response.into_inner().map(|result| match result {
2433 Ok(update) => update.try_into(),
2434 Err(err) => Err(err),
2435 });
2436 Ok(QueryResponse {
2437 block_hash,
2438 response: stream,
2439 })
2440 }
2441
2442 pub async fn get_winning_bakers_epoch(
2453 &mut self,
2454 ei: impl Into<EpochIdentifier>,
2455 ) -> endpoints::QueryResult<impl Stream<Item = Result<types::WinningBaker, tonic::Status>>>
2456 {
2457 let response = self.client.get_winning_bakers_epoch(&ei.into()).await?;
2458 let stream = response.into_inner().map(|result| match result {
2459 Ok(wb) => wb.try_into(),
2460 Err(err) => Err(err),
2461 });
2462 Ok(stream)
2463 }
2464
2465 pub async fn get_first_block_epoch(
2467 &mut self,
2468 ei: impl Into<EpochIdentifier>,
2469 ) -> endpoints::QueryResult<BlockHash> {
2470 let response = self.client.get_first_block_epoch(&ei.into()).await?;
2471 Ok(response.into_inner().try_into()?)
2472 }
2473
2474 pub async fn get_consensus_detailed_status(
2478 &mut self,
2479 genesis_index: Option<GenesisIndex>,
2480 ) -> endpoints::RPCResult<ConsensusDetailedStatus> {
2481 let query = generated::ConsensusDetailedStatusQuery {
2482 genesis_index: genesis_index.map(Into::into),
2483 };
2484 let response = self.client.get_consensus_detailed_status(query).await?;
2485 Ok(response.into_inner().try_into()?)
2486 }
2487
2488 pub async fn get_next_update_sequence_numbers(
2492 &mut self,
2493 block_id: impl IntoBlockIdentifier,
2494 ) -> endpoints::QueryResult<QueryResponse<types::queries::NextUpdateSequenceNumbers>> {
2495 let response = self
2496 .client
2497 .get_next_update_sequence_numbers(&block_id.into_block_identifier())
2498 .await?;
2499 let block_hash = extract_metadata(&response)?;
2500 let response = types::queries::NextUpdateSequenceNumbers::try_from(response.into_inner())?;
2501 Ok(QueryResponse {
2502 block_hash,
2503 response,
2504 })
2505 }
2506
2507 pub async fn get_scheduled_release_accounts(
2512 &mut self,
2513 block_id: impl IntoBlockIdentifier,
2514 ) -> endpoints::QueryResult<
2515 QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2516 > {
2517 let response = self
2518 .client
2519 .get_scheduled_release_accounts(&block_id.into_block_identifier())
2520 .await?;
2521 let block_hash = extract_metadata(&response)?;
2522 let stream = response.into_inner().map(|result| match result {
2523 Ok(pending) => pending.try_into(),
2524 Err(err) => Err(err),
2525 });
2526 Ok(QueryResponse {
2527 block_hash,
2528 response: stream,
2529 })
2530 }
2531
2532 pub async fn get_cooldown_accounts(
2538 &mut self,
2539 block_id: impl IntoBlockIdentifier,
2540 ) -> endpoints::QueryResult<
2541 QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2542 > {
2543 let response = self
2544 .client
2545 .get_cooldown_accounts(&block_id.into_block_identifier())
2546 .await?;
2547 let block_hash = extract_metadata(&response)?;
2548 let stream = response.into_inner().map(|result| match result {
2549 Ok(pending) => pending.try_into(),
2550 Err(err) => Err(err),
2551 });
2552 Ok(QueryResponse {
2553 block_hash,
2554 response: stream,
2555 })
2556 }
2557
2558 pub async fn get_pre_cooldown_accounts(
2562 &mut self,
2563 block_id: impl IntoBlockIdentifier,
2564 ) -> endpoints::QueryResult<
2565 QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2566 > {
2567 let response = self
2568 .client
2569 .get_pre_cooldown_accounts(&block_id.into_block_identifier())
2570 .await?;
2571 let block_hash = extract_metadata(&response)?;
2572 let stream = response.into_inner().map(|result| match result {
2573 Ok(account) => Ok(account.into()),
2574 Err(err) => Err(err),
2575 });
2576 Ok(QueryResponse {
2577 block_hash,
2578 response: stream,
2579 })
2580 }
2581
2582 pub async fn get_pre_pre_cooldown_accounts(
2586 &mut self,
2587 block_id: impl IntoBlockIdentifier,
2588 ) -> endpoints::QueryResult<
2589 QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2590 > {
2591 let response = self
2592 .client
2593 .get_pre_pre_cooldown_accounts(&block_id.into_block_identifier())
2594 .await?;
2595 let block_hash = extract_metadata(&response)?;
2596 let stream = response.into_inner().map(|result| match result {
2597 Ok(account) => Ok(account.into()),
2598 Err(err) => Err(err),
2599 });
2600 Ok(QueryResponse {
2601 block_hash,
2602 response: stream,
2603 })
2604 }
2605
2606 pub async fn get_block_chain_parameters(
2609 &mut self,
2610 block_id: impl IntoBlockIdentifier,
2611 ) -> endpoints::QueryResult<QueryResponse<ChainParameters>> {
2612 let response = self
2613 .client
2614 .get_block_chain_parameters(&block_id.into_block_identifier())
2615 .await?;
2616 let block_hash = extract_metadata(&response)?;
2617 let response = ChainParameters::try_from(response.into_inner())?;
2618 Ok(QueryResponse {
2619 block_hash,
2620 response,
2621 })
2622 }
2623
2624 pub async fn get_block_certificates(
2634 &mut self,
2635 bi: impl IntoBlockIdentifier,
2636 ) -> endpoints::QueryResult<QueryResponse<block_certificates::BlockCertificates>> {
2637 let response = self
2638 .client
2639 .get_block_certificates(&bi.into_block_identifier())
2640 .await?;
2641 let block_hash = extract_metadata(&response)?;
2642 let response = block_certificates::BlockCertificates::try_from(response.into_inner())?;
2643 Ok(QueryResponse {
2644 block_hash,
2645 response,
2646 })
2647 }
2648
2649 pub async fn get_block_finalization_summary(
2656 &mut self,
2657 block_id: impl IntoBlockIdentifier,
2658 ) -> endpoints::QueryResult<QueryResponse<Option<types::FinalizationSummary>>> {
2659 let response = self
2660 .client
2661 .get_block_finalization_summary(&block_id.into_block_identifier())
2662 .await?;
2663 let block_hash = extract_metadata(&response)?;
2664 let response = response.into_inner().try_into()?;
2665 Ok(QueryResponse {
2666 block_hash,
2667 response,
2668 })
2669 }
2670
2671 pub async fn get_finalized_blocks_from(
2676 &mut self,
2677 start_height: AbsoluteBlockHeight,
2678 ) -> endpoints::QueryResult<FinalizedBlocksStream> {
2679 let mut fin_height = self.get_consensus_info().await?.last_finalized_block_height;
2680 let (sender, receiver) = tokio::sync::mpsc::channel(100);
2681 let mut client = self.clone();
2682 let handle = tokio::spawn(async move {
2683 let mut height = start_height;
2684 loop {
2685 if height > fin_height {
2686 fin_height = client
2687 .get_consensus_info()
2688 .await?
2689 .last_finalized_block_height;
2690 if height > fin_height {
2691 break;
2692 }
2693 } else {
2694 let mut bi = client.get_blocks_at_height(&height.into()).await?;
2695 let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2696 let info = FinalizedBlockInfo { block_hash, height };
2697 if sender.send(info).await.is_err() {
2698 return Ok(());
2699 }
2700 height = height.next();
2701 }
2702 }
2703 let mut stream = client.get_finalized_blocks().await?;
2704 while let Some(fbi) = stream.next().await.transpose()? {
2705 while height < fbi.height {
2707 let mut bi = client.get_blocks_at_height(&height.into()).await?;
2708 let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2709 let info = FinalizedBlockInfo { block_hash, height };
2710 if sender.send(info).await.is_err() {
2711 return Ok(());
2712 }
2713 height = height.next();
2714 }
2715 if sender.send(fbi).await.is_err() {
2716 return Ok(());
2717 }
2718 height = height.next();
2719 }
2720 Ok(())
2721 });
2722 Ok(FinalizedBlocksStream { handle, receiver })
2723 }
2724
2725 pub async fn find_account_creation(
2742 &mut self,
2743 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2744 addr: AccountAddress,
2745 ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, AccountInfo)> {
2746 self.find_at_lowest_height(range, |mut client, height| async move {
2747 match client.get_account_info(&addr.into(), &height).await {
2748 Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2749 Err(e) if e.is_not_found() => Ok(None),
2750 Err(e) => Err(e),
2751 }
2752 })
2753 .await
2754 }
2755
2756 pub async fn find_instance_creation(
2773 &mut self,
2774 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2775 addr: ContractAddress,
2776 ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, InstanceInfo)> {
2777 self.find_at_lowest_height(range, |mut client, height| async move {
2778 match client.get_instance_info(addr, &height).await {
2779 Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2780 Err(e) if e.is_not_found() => Ok(None),
2781 Err(e) => Err(e),
2782 }
2783 })
2784 .await
2785 }
2786
2787 pub async fn find_first_finalized_block_no_earlier_than(
2795 &mut self,
2796 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2797 time: chrono::DateTime<chrono::Utc>,
2798 ) -> QueryResult<types::queries::BlockInfo> {
2799 self.find_at_lowest_height(range, move |mut client, height| async move {
2800 let info = client.get_block_info(&height).await?.response;
2801 if info.block_slot_time >= time {
2802 Ok(Some(info))
2803 } else {
2804 Ok(None)
2805 }
2806 })
2807 .await
2808 }
2809
2810 pub async fn find_at_lowest_height<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2829 &mut self,
2830 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2831 test: impl Fn(Self, AbsoluteBlockHeight) -> F,
2832 ) -> QueryResult<A> {
2833 let mut start = match range.start_bound() {
2834 std::ops::Bound::Included(s) => u64::from(*s),
2835 std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2836 std::ops::Bound::Unbounded => 0,
2837 };
2838 let mut end = {
2839 let ci = self.get_consensus_info().await?;
2840 let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2841 match range.end_bound() {
2842 std::ops::Bound::Included(e) => bound(u64::from(*e)),
2843 std::ops::Bound::Excluded(e) => {
2844 bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2845 }
2846 std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2847 }
2848 };
2849 if end < start {
2850 return Err(QueryError::NotFound);
2851 }
2852 let mut last_found = None;
2853 while start < end {
2854 let mid = start + (end - start) / 2;
2855 let ok = test(self.clone(), mid.into()).await?;
2856 if ok.is_some() {
2857 end = mid;
2858 last_found = ok;
2859 } else {
2860 start = mid + 1;
2861 }
2862 }
2863 last_found.ok_or(QueryError::NotFound)
2864 }
2865
2866 #[deprecated(note = "Use [`find_at_lowest_height`](./struct.Client.html#method.\
2867 find_at_lowest_height) instead since it avoids an extra call.")]
2868 pub async fn find_earliest_finalized<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2869 &mut self,
2870 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2871 test: impl Fn(Self, AbsoluteBlockHeight, BlockHash) -> F,
2872 ) -> QueryResult<A> {
2873 let mut start = match range.start_bound() {
2874 std::ops::Bound::Included(s) => u64::from(*s),
2875 std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2876 std::ops::Bound::Unbounded => 0,
2877 };
2878 let mut end = {
2879 let ci = self.get_consensus_info().await?;
2880 let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2881 match range.end_bound() {
2882 std::ops::Bound::Included(e) => bound(u64::from(*e)),
2883 std::ops::Bound::Excluded(e) => {
2884 bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2885 }
2886 std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2887 }
2888 };
2889 if end < start {
2890 return Err(QueryError::NotFound);
2891 }
2892 let mut last_found = None;
2893 while start < end {
2894 let mid = start + (end - start) / 2;
2895 let bh = self
2896 .get_blocks_at_height(&AbsoluteBlockHeight::from(mid).into())
2897 .await?[0]; let ok = test(self.clone(), mid.into(), bh).await?;
2899 if ok.is_some() {
2900 end = mid;
2901 last_found = ok;
2902 } else {
2903 start = mid + 1;
2904 }
2905 }
2906 last_found.ok_or(QueryError::NotFound)
2907 }
2908
2909 pub async fn get_bakers_reward_period(
2914 &mut self,
2915 bi: impl IntoBlockIdentifier,
2916 ) -> endpoints::QueryResult<
2917 QueryResponse<impl Stream<Item = Result<types::BakerRewardPeriodInfo, tonic::Status>>>,
2918 > {
2919 let response = self
2920 .client
2921 .get_bakers_reward_period(&bi.into_block_identifier())
2922 .await?;
2923 let block_hash = extract_metadata(&response)?;
2924 let stream = response.into_inner().map(|result| match result {
2925 Ok(baker) => baker.try_into(),
2926 Err(err) => Err(err),
2927 });
2928 Ok(QueryResponse {
2929 block_hash,
2930 response: stream,
2931 })
2932 }
2933
2934 pub async fn get_token_list(
2940 &mut self,
2941 bi: impl IntoBlockIdentifier,
2942 ) -> endpoints::QueryResult<
2943 QueryResponse<impl Stream<Item = Result<protocol_level_tokens::TokenId, tonic::Status>>>,
2944 > {
2945 let response = self
2946 .client
2947 .get_token_list(&bi.into_block_identifier())
2948 .await?;
2949 let block_hash = extract_metadata(&response)?;
2950 let stream = response.into_inner().map(|result| match result {
2951 Ok(token_id) => protocol_level_tokens::TokenId::try_from(token_id),
2952 Err(err) => Err(err),
2953 });
2954 Ok(QueryResponse {
2955 block_hash,
2956 response: stream,
2957 })
2958 }
2959
2960 pub async fn get_token_info(
2966 &mut self,
2967 token_id: protocol_level_tokens::TokenId,
2968 bi: impl IntoBlockIdentifier,
2969 ) -> endpoints::QueryResult<QueryResponse<protocol_level_tokens::TokenInfo>> {
2970 let request = generated::TokenInfoRequest {
2971 block_hash: Some((&bi.into_block_identifier()).into()),
2972 token_id: Some(token_id.into()),
2973 };
2974 let response = self.client.get_token_info(request).await?;
2975 let block_hash = extract_metadata(&response)?;
2976 let response = protocol_level_tokens::TokenInfo::try_from(response.into_inner())?;
2977 Ok(QueryResponse {
2978 block_hash,
2979 response,
2980 })
2981 }
2982}
2983
2984pub struct FinalizedBlocksStream {
2988 handle: tokio::task::JoinHandle<endpoints::QueryResult<()>>,
2989 receiver: tokio::sync::mpsc::Receiver<FinalizedBlockInfo>,
2990}
2991
2992impl Drop for FinalizedBlocksStream {
2995 fn drop(&mut self) { self.handle.abort(); }
2996}
2997
2998impl FinalizedBlocksStream {
2999 pub async fn next(&mut self) -> Option<FinalizedBlockInfo> { self.receiver.recv().await }
3004
3005 pub async fn next_timeout(
3008 &mut self,
3009 duration: std::time::Duration,
3010 ) -> Result<Option<FinalizedBlockInfo>, tokio::time::error::Elapsed> {
3011 tokio::time::timeout(duration, async move { self.next().await }).await
3012 }
3013
3014 pub async fn next_chunk(
3024 &mut self,
3025 n: usize,
3026 ) -> Result<Vec<FinalizedBlockInfo>, Vec<FinalizedBlockInfo>> {
3027 let mut out = Vec::with_capacity(n);
3028 let first = self.receiver.recv().await;
3029 match first {
3030 Some(v) => out.push(v),
3031 None => {
3032 return Err(out);
3033 }
3034 }
3035 for _ in 1..n {
3036 match self.receiver.try_recv() {
3037 Ok(v) => {
3038 out.push(v);
3039 }
3040 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
3041 break;
3042 }
3043 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return Err(out),
3044 }
3045 }
3046 Ok(out)
3047 }
3048
3049 pub async fn next_chunk_timeout(
3058 &mut self,
3059 n: usize,
3060 duration: std::time::Duration,
3061 ) -> Result<(bool, Vec<FinalizedBlockInfo>), tokio::time::error::Elapsed> {
3062 let mut out = Vec::with_capacity(n);
3063 let first = self.next_timeout(duration).await?;
3064 match first {
3065 Some(v) => out.push(v),
3066 None => return Ok((true, out)),
3067 }
3068 for _ in 1..n {
3069 match self.receiver.try_recv() {
3070 Ok(v) => {
3071 out.push(v);
3072 }
3073 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
3074 break;
3075 }
3076 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
3077 return Ok((true, out))
3078 }
3079 }
3080 }
3081 Ok((false, out))
3082 }
3083}
3084
3085fn extract_metadata<T>(response: &tonic::Response<T>) -> endpoints::RPCResult<BlockHash> {
3086 match response.metadata().get("blockhash") {
3087 Some(bytes) => {
3088 let bytes = bytes.as_bytes();
3089 if bytes.len() == 64 {
3090 let mut hash = [0u8; 32];
3091 if hex::decode_to_slice(bytes, &mut hash).is_err() {
3092 tonic::Status::unknown("Response does correctly encode the block hash.");
3093 }
3094 Ok(hash.into())
3095 } else {
3096 Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3097 "Response does not include the expected metadata.",
3098 )))
3099 }
3100 }
3101 None => Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3102 "Response does not include the expected metadata.",
3103 ))),
3104 }
3105}
3106
3107pub(crate) trait Require<E> {
3115 type A;
3116 fn require(self) -> Result<Self::A, E>;
3117}
3118
3119impl<A> Require<tonic::Status> for Option<A> {
3120 type A = A;
3121
3122 fn require(self) -> Result<Self::A, tonic::Status> {
3123 match self {
3124 Some(v) => Ok(v),
3125 None => Err(tonic::Status::invalid_argument("missing field in response")),
3126 }
3127 }
3128}
3129
3130#[cfg(test)]
3131mod tests {
3132 use super::*;
3133 #[test]
3134 fn block_ident_from_str() -> anyhow::Result<()> {
3136 let b1 = "best".parse::<BlockIdentifier>()?;
3137 assert_eq!(b1, BlockIdentifier::Best);
3138
3139 let b2 = "lastFinal".parse::<BlockIdentifier>()?;
3140 assert_eq!(b2, BlockIdentifier::LastFinal);
3141
3142 let b3 = "lastfinal".parse::<BlockIdentifier>()?;
3143 assert_eq!(b3, BlockIdentifier::LastFinal);
3144
3145 let b4 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
3146 .parse::<BlockIdentifier>()?;
3147 assert_eq!(
3148 b4,
3149 BlockIdentifier::Given(
3150 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()?
3151 )
3152 );
3153
3154 let b5 = "@33".parse::<BlockIdentifier>()?;
3155 assert_eq!(b5, BlockIdentifier::AbsoluteHeight(33.into()));
3156
3157 let b6 = "@33/3".parse::<BlockIdentifier>()?;
3158 assert_eq!(
3159 b6,
3160 BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3161 genesis_index: 3.into(),
3162 height: 33.into(),
3163 restrict: false,
3164 })
3165 );
3166
3167 let b7 = "@33/3!".parse::<BlockIdentifier>()?;
3168 assert_eq!(
3169 b7,
3170 BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3171 genesis_index: 3.into(),
3172 height: 33.into(),
3173 restrict: true,
3174 })
3175 );
3176
3177 Ok(())
3178 }
3179}