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