1use crate::{
5 endpoints,
6 id::{self, types::AccountCredentialMessage},
7 protocol_level_tokens,
8 types::{
9 self, block_certificates,
10 chain_parameters::ChainParameters,
11 hashes::{self, BlockHash, TransactionHash, TransactionSignHash},
12 queries::ConsensusDetailedStatus,
13 smart_contracts::{
14 ContractContext, InstanceInfo, InvokeContractResult, ModuleReference, WasmModule,
15 },
16 transactions::{self, InitContractPayload, UpdateContractPayload, UpdateInstruction},
17 AbsoluteBlockHeight, AccountInfo, AccountPending, BlockItemSummary,
18 CredentialRegistrationID, Energy, Memo, Nonce, RegisteredData, SpecialTransactionOutcome,
19 TransactionStatus, UpdateSequenceNumber,
20 },
21};
22use anyhow::Context;
23pub use concordium_base::common::upward::{self, Upward};
24use concordium_base::{
25 base::{AccountIndex, BlockHeight, Epoch, GenesisIndex},
26 common::{
27 self,
28 types::{TransactionSignature, TransactionSignaturesV1, TransactionTime},
29 },
30 contracts_common::{
31 AccountAddress, AccountAddressParseError, Amount, ContractAddress, OwnedContractName,
32 OwnedParameter, OwnedReceiveName, ReceiveName,
33 },
34 hashes::HashFromStrError,
35 transactions::{BlockItem, EncodedPayload, PayloadLike},
36};
37pub use endpoints::{QueryError, QueryResult, RPCError, RPCResult};
38use futures::{Stream, StreamExt, TryStreamExt};
39pub use http::uri::Scheme;
40use std::{collections::HashMap, num::ParseIntError, str::FromStr};
41use tonic::IntoRequest;
42pub use tonic::{
43 transport::{Endpoint, Error},
44 Code, Status,
45};
46
47use self::dry_run::WithRemainingQuota;
48
49mod conversions;
50pub mod dry_run;
51#[path = "generated/mod.rs"]
52#[allow(
53 clippy::large_enum_variant,
54 clippy::enum_variant_names,
55 clippy::derive_partial_eq_without_eq
56)]
57#[rustfmt::skip]
58mod gen;
59pub use gen::concordium::v2 as generated;
60pub mod proto_schema_version;
61
62#[derive(Clone, Debug)]
94pub struct Client {
95 client: generated::queries_client::QueriesClient<tonic::transport::Channel>,
96}
97
98#[derive(Clone, Copy, Debug)]
102pub struct QueryResponse<A> {
103 pub block_hash: BlockHash,
105 pub response: A,
107}
108
109impl<A> AsRef<A> for QueryResponse<A> {
110 fn as_ref(&self) -> &A {
111 &self.response
112 }
113}
114
115#[derive(Copy, Clone, Debug, derive_more::From, PartialEq, Eq)]
117pub enum BlockIdentifier {
118 Best,
120 LastFinal,
123 Given(BlockHash),
125 AbsoluteHeight(AbsoluteBlockHeight),
129 RelativeHeight(RelativeBlockHeight),
133}
134
135#[derive(Debug, thiserror::Error)]
136pub enum BlockIdentifierFromStrError {
137 #[error("The input is not recognized.")]
138 InvalidFormat,
139 #[error("The input is not a valid hash: {0}.")]
140 InvalidHash(#[from] HashFromStrError),
141 #[error("The input is not a valid unsigned integer: {0}.")]
142 InvalidInteger(#[from] ParseIntError),
143}
144
145impl std::fmt::Display for BlockIdentifier {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match self {
150 BlockIdentifier::Best => "best".fmt(f),
151 BlockIdentifier::LastFinal => "lastfinal".fmt(f),
152 BlockIdentifier::Given(bh) => bh.fmt(f),
153 BlockIdentifier::AbsoluteHeight(h) => write!(f, "@{h}"),
154 BlockIdentifier::RelativeHeight(rh) => {
155 write!(
156 f,
157 "@{}/{}{}",
158 rh.height,
159 rh.genesis_index,
160 if rh.restrict { "!" } else { "" }
161 )
162 }
163 }
164 }
165}
166
167impl std::str::FromStr for BlockIdentifier {
180 type Err = BlockIdentifierFromStrError;
181
182 fn from_str(s: &str) -> Result<Self, Self::Err> {
183 match s {
184 "best" => Ok(Self::Best),
185 "lastFinal" => Ok(Self::LastFinal),
186 "lastfinal" => Ok(Self::LastFinal),
187 _ => {
188 if let Some(rest) = s.strip_prefix('@') {
189 if let Some((height_str, gen_idx_str)) = rest.split_once('/') {
190 let height = BlockHeight::from_str(height_str)?;
191 if let Some(gen_idx) = gen_idx_str.strip_suffix('!') {
192 let genesis_index = GenesisIndex::from_str(gen_idx)?;
193 Ok(Self::RelativeHeight(RelativeBlockHeight {
194 genesis_index,
195 height,
196 restrict: true,
197 }))
198 } else {
199 let genesis_index = GenesisIndex::from_str(gen_idx_str)?;
200 Ok(Self::RelativeHeight(RelativeBlockHeight {
201 genesis_index,
202 height,
203 restrict: false,
204 }))
205 }
206 } else {
207 let h = AbsoluteBlockHeight::from_str(rest)?;
208 Ok(Self::AbsoluteHeight(h))
209 }
210 } else {
211 let h = BlockHash::from_str(s)?;
212 Ok(Self::Given(h))
213 }
214 }
215 }
216 }
217}
218
219#[derive(Copy, Clone, Debug, PartialEq, Eq)]
221pub struct RelativeBlockHeight {
222 pub genesis_index: types::GenesisIndex,
224 pub height: types::BlockHeight,
226 pub restrict: bool,
230}
231
232#[derive(Copy, Clone, Debug, derive_more::From, derive_more::Display)]
234pub enum AccountIdentifier {
235 #[display(fmt = "{_0}")]
237 Address(AccountAddress),
238 #[display(fmt = "{_0}")]
240 CredId(CredentialRegistrationID),
241 #[display(fmt = "{_0}")]
243 Index(crate::types::AccountIndex),
244}
245
246impl FromStr for AccountIdentifier {
247 type Err = AccountAddressParseError;
248
249 fn from_str(s: &str) -> Result<Self, Self::Err> {
250 if let Ok(ai) = s.parse::<crate::types::AccountIndex>() {
251 return Ok(Self::Index(ai));
252 }
253 if let Ok(cid) = s.parse::<CredentialRegistrationID>() {
254 return Ok(Self::CredId(cid));
255 }
256 s.parse().map(Self::Address)
257 }
258}
259
260#[derive(Debug, Copy, Clone)]
262pub struct SpecifiedEpoch {
263 pub genesis_index: types::GenesisIndex,
265 pub epoch: types::Epoch,
267}
268
269#[derive(Copy, Clone, Debug, derive_more::From)]
271pub enum EpochIdentifier {
272 Specified(SpecifiedEpoch),
274 Block(BlockIdentifier),
276}
277
278#[derive(Debug, thiserror::Error)]
282pub enum EpochIdentifierFromStrError {
283 #[error("The input is not recognized.")]
284 InvalidFormat,
285 #[error("The genesis index is not a valid unsigned integer")]
286 InvalidGenesis,
287 #[error("The epoch index is not a valid unsigned integer")]
288 InvalidEpoch,
289 #[error("The input is not a valid block identifier: {0}.")]
290 InvalidBlockIdentifier(#[from] BlockIdentifierFromStrError),
291}
292
293impl std::str::FromStr for EpochIdentifier {
302 type Err = EpochIdentifierFromStrError;
303
304 fn from_str(s: &str) -> Result<Self, Self::Err> {
305 if let Some(rest) = s.strip_prefix('%') {
306 if let Some((gen_idx_str, epoch_str)) = rest.split_once(',') {
307 let genesis_index = GenesisIndex::from_str(gen_idx_str)
308 .map_err(|_| EpochIdentifierFromStrError::InvalidGenesis)?;
309 let epoch = Epoch::from_str(epoch_str)
310 .map_err(|_| EpochIdentifierFromStrError::InvalidEpoch)?;
311 Ok(Self::Specified(SpecifiedEpoch {
312 genesis_index,
313 epoch,
314 }))
315 } else {
316 Err(EpochIdentifierFromStrError::InvalidFormat)
317 }
318 } else {
319 Ok(Self::Block(BlockIdentifier::from_str(s)?))
320 }
321 }
322}
323
324impl IntoRequest<generated::EpochRequest> for &EpochIdentifier {
325 fn into_request(self) -> tonic::Request<generated::EpochRequest> {
326 tonic::Request::new((*self).into())
327 }
328}
329
330impl From<EpochIdentifier> for generated::EpochRequest {
331 fn from(ei: EpochIdentifier) -> Self {
332 match ei {
333 EpochIdentifier::Specified(SpecifiedEpoch {
334 genesis_index,
335 epoch,
336 }) => generated::EpochRequest {
337 epoch_request_input: Some(
338 generated::epoch_request::EpochRequestInput::RelativeEpoch(
339 generated::epoch_request::RelativeEpoch {
340 genesis_index: Some(generated::GenesisIndex {
341 value: genesis_index.height,
342 }),
343 epoch: Some(generated::Epoch { value: epoch.epoch }),
344 },
345 ),
346 ),
347 },
348 EpochIdentifier::Block(bi) => generated::EpochRequest {
349 epoch_request_input: Some(generated::epoch_request::EpochRequestInput::BlockHash(
350 (&bi).into(),
351 )),
352 },
353 }
354 }
355}
356
357#[derive(Copy, Clone, Debug, Eq, PartialEq)]
359pub struct FinalizedBlockInfo {
360 pub block_hash: BlockHash,
362 pub height: AbsoluteBlockHeight,
364}
365
366impl From<&BlockIdentifier> for generated::BlockHashInput {
367 fn from(bi: &BlockIdentifier) -> Self {
368 let block_hash_input = match bi {
369 BlockIdentifier::Best => {
370 generated::block_hash_input::BlockHashInput::Best(Default::default())
371 }
372 BlockIdentifier::LastFinal => {
373 generated::block_hash_input::BlockHashInput::LastFinal(Default::default())
374 }
375 BlockIdentifier::Given(h) => {
376 generated::block_hash_input::BlockHashInput::Given(generated::BlockHash {
377 value: h.as_ref().to_vec(),
378 })
379 }
380 &BlockIdentifier::AbsoluteHeight(h) => {
381 generated::block_hash_input::BlockHashInput::AbsoluteHeight(h.into())
382 }
383 &BlockIdentifier::RelativeHeight(h) => {
384 generated::block_hash_input::BlockHashInput::RelativeHeight(h.into())
385 }
386 };
387 generated::BlockHashInput {
388 block_hash_input: Some(block_hash_input),
389 }
390 }
391}
392
393impl IntoRequest<generated::BlockHashInput> for &BlockIdentifier {
394 fn into_request(self) -> tonic::Request<generated::BlockHashInput> {
395 tonic::Request::new(self.into())
396 }
397}
398
399impl From<&AccountAddress> for generated::AccountAddress {
400 fn from(addr: &AccountAddress) -> Self {
401 generated::AccountAddress {
402 value: concordium_base::common::to_bytes(addr),
403 }
404 }
405}
406
407impl From<AccountAddress> for generated::AccountAddress {
408 fn from(addr: AccountAddress) -> Self {
409 generated::AccountAddress {
410 value: common::to_bytes(&addr),
411 }
412 }
413}
414
415impl From<&super::types::Address> for generated::Address {
416 fn from(addr: &super::types::Address) -> Self {
417 let ty = match addr {
418 super::types::Address::Account(account) => {
419 generated::address::Type::Account(account.into())
420 }
421 super::types::Address::Contract(contract) => {
422 generated::address::Type::Contract(contract.into())
423 }
424 };
425 generated::Address { r#type: Some(ty) }
426 }
427}
428
429impl From<&Memo> for generated::Memo {
430 fn from(v: &Memo) -> Self {
431 Self {
432 value: v.as_ref().clone(),
433 }
434 }
435}
436
437impl<'a> From<ReceiveName<'a>> for generated::ReceiveName {
438 fn from(a: ReceiveName<'a>) -> Self {
439 generated::ReceiveName {
440 value: a.get_chain_name().to_string(),
441 }
442 }
443}
444
445impl From<&RegisteredData> for generated::RegisteredData {
446 fn from(v: &RegisteredData) -> Self {
447 Self {
448 value: v.as_ref().clone(),
449 }
450 }
451}
452impl From<&[u8]> for generated::Parameter {
453 fn from(a: &[u8]) -> Self {
454 generated::Parameter { value: a.to_vec() }
455 }
456}
457
458impl From<&TransactionHash> for generated::TransactionHash {
459 fn from(th: &TransactionHash) -> Self {
460 generated::TransactionHash { value: th.to_vec() }
461 }
462}
463
464impl From<&AccountIdentifier> for generated::AccountIdentifierInput {
465 fn from(ai: &AccountIdentifier) -> Self {
466 let account_identifier_input = match ai {
467 AccountIdentifier::Address(addr) => {
468 generated::account_identifier_input::AccountIdentifierInput::Address(addr.into())
469 }
470 AccountIdentifier::CredId(credid) => {
471 let credid = generated::CredentialRegistrationId {
472 value: concordium_base::common::to_bytes(credid),
473 };
474 generated::account_identifier_input::AccountIdentifierInput::CredId(credid)
475 }
476 AccountIdentifier::Index(index) => {
477 generated::account_identifier_input::AccountIdentifierInput::AccountIndex(
478 (*index).into(),
479 )
480 }
481 };
482 generated::AccountIdentifierInput {
483 account_identifier_input: Some(account_identifier_input),
484 }
485 }
486}
487
488impl From<&ModuleReference> for generated::ModuleRef {
489 fn from(mr: &ModuleReference) -> Self {
490 Self { value: mr.to_vec() }
491 }
492}
493
494impl From<ModuleReference> for generated::ModuleRef {
495 fn from(mr: ModuleReference) -> Self {
496 Self { value: mr.to_vec() }
497 }
498}
499
500impl From<&WasmModule> for generated::VersionedModuleSource {
501 fn from(v: &WasmModule) -> Self {
502 Self {
503 module: Some(match v.version {
504 types::smart_contracts::WasmVersion::V0 => {
505 generated::versioned_module_source::Module::V0(
506 generated::versioned_module_source::ModuleSourceV0 {
507 value: v.source.as_ref().clone(),
508 },
509 )
510 }
511 types::smart_contracts::WasmVersion::V1 => {
512 generated::versioned_module_source::Module::V1(
513 generated::versioned_module_source::ModuleSourceV1 {
514 value: v.source.as_ref().clone(),
515 },
516 )
517 }
518 }),
519 }
520 }
521}
522
523impl From<&OwnedContractName> for generated::InitName {
524 fn from(v: &OwnedContractName) -> Self {
525 Self {
526 value: v.as_contract_name().get_chain_name().to_string(),
527 }
528 }
529}
530
531impl From<&OwnedReceiveName> for generated::ReceiveName {
532 fn from(v: &OwnedReceiveName) -> Self {
533 Self {
534 value: v.as_receive_name().get_chain_name().to_string(),
535 }
536 }
537}
538
539impl From<&OwnedParameter> for generated::Parameter {
540 fn from(v: &OwnedParameter) -> Self {
541 Self {
542 value: v.as_ref().to_vec(),
543 }
544 }
545}
546
547impl From<&InitContractPayload> for generated::InitContractPayload {
548 fn from(v: &InitContractPayload) -> Self {
549 Self {
550 amount: Some(v.amount.into()),
551 module_ref: Some(v.mod_ref.into()),
552 init_name: Some((&v.init_name).into()),
553 parameter: Some((&v.param).into()),
554 }
555 }
556}
557
558impl From<&UpdateContractPayload> for generated::UpdateContractPayload {
559 fn from(v: &UpdateContractPayload) -> Self {
560 Self {
561 amount: Some(v.amount.into()),
562 address: Some(v.address.into()),
563 receive_name: Some((&v.receive_name).into()),
564 parameter: Some((&v.message).into()),
565 }
566 }
567}
568
569impl From<&ContractAddress> for generated::ContractAddress {
570 fn from(ca: &ContractAddress) -> Self {
571 Self {
572 index: ca.index,
573 subindex: ca.subindex,
574 }
575 }
576}
577
578impl From<Nonce> for generated::SequenceNumber {
579 fn from(v: Nonce) -> Self {
580 generated::SequenceNumber { value: v.nonce }
581 }
582}
583
584impl From<UpdateSequenceNumber> for generated::UpdateSequenceNumber {
585 fn from(v: UpdateSequenceNumber) -> Self {
586 generated::UpdateSequenceNumber { value: v.number }
587 }
588}
589
590impl From<Energy> for generated::Energy {
591 fn from(v: Energy) -> Self {
592 generated::Energy { value: v.energy }
593 }
594}
595
596impl From<TransactionTime> for generated::TransactionTime {
597 fn from(v: TransactionTime) -> Self {
598 generated::TransactionTime { value: v.seconds }
599 }
600}
601
602impl From<&Amount> for generated::Amount {
603 fn from(v: &Amount) -> Self {
604 Self { value: v.micro_ccd }
605 }
606}
607
608impl From<Amount> for generated::Amount {
609 fn from(v: Amount) -> Self {
610 Self { value: v.micro_ccd }
611 }
612}
613
614impl
615 From<
616 &AccountCredentialMessage<
617 id::constants::IpPairing,
618 id::constants::ArCurve,
619 id::constants::AttributeKind,
620 >,
621 > for generated::CredentialDeployment
622{
623 fn from(
624 v: &AccountCredentialMessage<
625 id::constants::IpPairing,
626 id::constants::ArCurve,
627 id::constants::AttributeKind,
628 >,
629 ) -> Self {
630 Self {
631 message_expiry: Some(v.message_expiry.into()),
632 payload: Some(generated::credential_deployment::Payload::RawPayload(
633 common::to_bytes(&v.credential),
634 )),
635 }
636 }
637}
638
639impl From<&UpdateInstruction> for generated::UpdateInstruction {
640 fn from(v: &UpdateInstruction) -> Self {
641 Self {
642 signatures: Some(generated::SignatureMap {
643 signatures: {
644 let mut hm = HashMap::new();
645 for (key_idx, sig) in v.signatures.signatures.iter() {
646 hm.insert(
647 key_idx.index.into(),
648 generated::Signature {
649 value: sig.sig.to_owned(),
650 },
651 );
652 }
653 hm
654 },
655 }),
656 header: Some(generated::UpdateInstructionHeader {
657 sequence_number: Some(v.header.seq_number.into()),
658 effective_time: Some(v.header.effective_time.into()),
659 timeout: Some(v.header.timeout.into()),
660 }),
661 payload: Some(generated::UpdateInstructionPayload {
662 payload: Some(generated::update_instruction_payload::Payload::RawPayload(
663 common::to_bytes(&v.payload),
664 )),
665 }),
666 }
667 }
668}
669
670impl IntoRequest<generated::AccountInfoRequest> for (&AccountIdentifier, &BlockIdentifier) {
671 fn into_request(self) -> tonic::Request<generated::AccountInfoRequest> {
672 let ai = generated::AccountInfoRequest {
673 block_hash: Some(self.1.into()),
674 account_identifier: Some(self.0.into()),
675 };
676 tonic::Request::new(ai)
677 }
678}
679
680impl IntoRequest<generated::AncestorsRequest> for (&BlockIdentifier, u64) {
681 fn into_request(self) -> tonic::Request<generated::AncestorsRequest> {
682 let ar = generated::AncestorsRequest {
683 block_hash: Some(self.0.into()),
684 amount: self.1,
685 };
686 tonic::Request::new(ar)
687 }
688}
689
690impl IntoRequest<generated::ModuleSourceRequest> for (&ModuleReference, &BlockIdentifier) {
691 fn into_request(self) -> tonic::Request<generated::ModuleSourceRequest> {
692 let r = generated::ModuleSourceRequest {
693 block_hash: Some(self.1.into()),
694 module_ref: Some(self.0.into()),
695 };
696 tonic::Request::new(r)
697 }
698}
699
700impl IntoRequest<generated::InstanceInfoRequest> for (ContractAddress, &BlockIdentifier) {
701 fn into_request(self) -> tonic::Request<generated::InstanceInfoRequest> {
702 let r = generated::InstanceInfoRequest {
703 block_hash: Some(self.1.into()),
704 address: Some(self.0.into()),
705 };
706 tonic::Request::new(r)
707 }
708}
709
710impl<V: Into<Vec<u8>>> IntoRequest<generated::InstanceStateLookupRequest>
711 for (ContractAddress, &BlockIdentifier, V)
712{
713 fn into_request(self) -> tonic::Request<generated::InstanceStateLookupRequest> {
714 let r = generated::InstanceStateLookupRequest {
715 block_hash: Some(self.1.into()),
716 address: Some(self.0.into()),
717 key: self.2.into(),
718 };
719 tonic::Request::new(r)
720 }
721}
722
723impl IntoRequest<generated::TransactionHash> for &TransactionHash {
724 fn into_request(self) -> tonic::Request<generated::TransactionHash> {
725 tonic::Request::new(self.into())
726 }
727}
728
729impl IntoRequest<generated::AccountIdentifierInput> for &AccountIdentifier {
730 fn into_request(self) -> tonic::Request<generated::AccountIdentifierInput> {
731 tonic::Request::new(self.into())
732 }
733}
734
735impl IntoRequest<generated::AccountAddress> for &AccountAddress {
736 fn into_request(self) -> tonic::Request<generated::AccountAddress> {
737 tonic::Request::new(self.into())
738 }
739}
740
741impl From<transactions::TransactionHeader> for generated::AccountTransactionHeader {
742 fn from(v: transactions::TransactionHeader) -> Self {
743 (&v).into()
744 }
745}
746
747impl From<&transactions::TransactionHeader> for generated::AccountTransactionHeader {
748 fn from(v: &transactions::TransactionHeader) -> Self {
749 Self {
750 sender: Some(generated::AccountAddress::from(v.sender)),
751 sequence_number: Some(v.nonce.into()),
752 energy_amount: Some(v.energy_amount.into()),
753 expiry: Some(v.expiry.into()),
754 }
755 }
756}
757
758impl From<&transactions::TransactionHeaderV1> for generated::AccountTransactionHeaderV1 {
759 fn from(v: &transactions::TransactionHeaderV1) -> Self {
760 Self {
761 sender: Some(generated::AccountAddress::from(v.sender)),
762 sponsor: v.sponsor.map(generated::AccountAddress::from),
763 sequence_number: Some(v.nonce.into()),
764 energy_amount: Some(v.energy_amount.into()),
765 expiry: Some(v.expiry.into()),
766 }
767 }
768}
769
770impl From<TransactionSignature> for generated::AccountTransactionSignature {
771 fn from(v: TransactionSignature) -> Self {
772 (&v).into()
773 }
774}
775
776impl From<&TransactionSignature> for generated::AccountTransactionSignature {
777 fn from(v: &TransactionSignature) -> Self {
778 Self {
779 signatures: {
780 let mut cred_map: HashMap<u32, generated::AccountSignatureMap> = HashMap::new();
781 for (cred_idx, sig_map) in v.signatures.iter() {
782 let mut acc_sig_map: HashMap<u32, generated::Signature> = HashMap::new();
783 for (key_idx, sig) in sig_map.iter() {
784 acc_sig_map.insert(
785 key_idx.0.into(),
786 generated::Signature {
787 value: sig.sig.to_owned(),
788 },
789 );
790 }
791 cred_map.insert(
792 cred_idx.index.into(),
793 generated::AccountSignatureMap {
794 signatures: acc_sig_map,
795 },
796 );
797 }
798 cred_map
799 },
800 }
801 }
802}
803
804impl From<&TransactionSignaturesV1> for generated::AccountTransactionV1Signatures {
805 fn from(v: &TransactionSignaturesV1) -> Self {
806 Self {
807 sender_signatures: Some(v.sender.to_owned().into()),
808 sponsor_signatures: v.sponsor.to_owned().map(|s| s.into()),
809 }
810 }
811}
812
813impl IntoRequest<generated::PreAccountTransaction>
814 for (&transactions::TransactionHeader, &transactions::Payload)
815{
816 fn into_request(self) -> tonic::Request<generated::PreAccountTransaction> {
817 let request = generated::PreAccountTransaction {
818 header: Some(self.0.into()),
819 payload: Some(generated::AccountTransactionPayload {
820 payload: Some(generated::account_transaction_payload::Payload::RawPayload(
821 self.1.encode().into(),
822 )),
823 }),
824 };
825 tonic::Request::new(request)
826 }
827}
828
829impl<P: PayloadLike> IntoRequest<generated::SendBlockItemRequest> for &transactions::BlockItem<P> {
830 fn into_request(self) -> tonic::Request<generated::SendBlockItemRequest> {
831 let request = match self {
832 transactions::BlockItem::AccountTransaction(v) => {
833 generated::SendBlockItemRequest {
834 block_item: Some(
835 generated::send_block_item_request::BlockItem::AccountTransaction(
836 generated::AccountTransaction {
837 signature: Some((&v.signature).into()),
838 header: Some((&v.header).into()),
839 payload: {
840 let atp = generated::AccountTransactionPayload{
841 payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())),
842 };
843 Some(atp)
844 },
845 },
846 ),
847 ),
848 }
849 }
850 transactions::BlockItem::CredentialDeployment(v) => generated::SendBlockItemRequest {
851 block_item: Some(
852 generated::send_block_item_request::BlockItem::CredentialDeployment(
853 v.as_ref().into(),
854 ),
855 ),
856 },
857 transactions::BlockItem::UpdateInstruction(v) => generated::SendBlockItemRequest {
858 block_item: Some(
859 generated::send_block_item_request::BlockItem::UpdateInstruction(v.into()),
860 ),
861 },
862 transactions::BlockItem::AccountTransactionV1(v) => {
863 generated::SendBlockItemRequest {
864 block_item: Some(
865 generated::send_block_item_request::BlockItem::AccountTransactionV1(
866 generated::AccountTransactionV1 {
867 signatures: Some((&v.signatures).into()),
868 header: Some((&v.header).into()),
869 payload: {
870 let atp = generated::AccountTransactionPayload{
871 payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())),
872 };
873 Some(atp)
874 },
875 },
876 ),
877 ),
878 }
879 }
880 };
881 tonic::Request::new(request)
882 }
883}
884
885impl IntoRequest<generated::InvokeInstanceRequest> for (&BlockIdentifier, &ContractContext) {
886 fn into_request(self) -> tonic::Request<generated::InvokeInstanceRequest> {
887 let (block, context) = self;
888 tonic::Request::new(generated::InvokeInstanceRequest {
889 block_hash: Some(block.into()),
890 invoker: context.invoker.as_ref().map(|a| a.into()),
891 instance: Some((&context.contract).into()),
892 amount: Some(context.amount.into()),
893 entrypoint: Some(context.method.as_receive_name().into()),
894 parameter: Some(context.parameter.as_ref().into()),
895 energy: context.energy.map(From::from),
896 })
897 }
898}
899
900impl IntoRequest<generated::PoolInfoRequest> for (&BlockIdentifier, types::BakerId) {
901 fn into_request(self) -> tonic::Request<generated::PoolInfoRequest> {
902 let req = generated::PoolInfoRequest {
903 block_hash: Some(self.0.into()),
904 baker: Some(self.1.into()),
905 };
906 tonic::Request::new(req)
907 }
908}
909
910impl IntoRequest<generated::BakerId> for types::BakerId {
911 fn into_request(self) -> tonic::Request<generated::BakerId> {
912 tonic::Request::new(generated::BakerId {
913 value: self.id.index,
914 })
915 }
916}
917
918impl IntoRequest<generated::BlocksAtHeightRequest> for &endpoints::BlocksAtHeightInput {
919 fn into_request(self) -> tonic::Request<generated::BlocksAtHeightRequest> {
920 tonic::Request::new(self.into())
921 }
922}
923
924impl IntoRequest<generated::GetPoolDelegatorsRequest> for (&BlockIdentifier, types::BakerId) {
925 fn into_request(self) -> tonic::Request<generated::GetPoolDelegatorsRequest> {
926 let req = generated::GetPoolDelegatorsRequest {
927 block_hash: Some(self.0.into()),
928 baker: Some(self.1.into()),
929 };
930 tonic::Request::new(req)
931 }
932}
933
934impl TryFrom<crate::v2::generated::BannedPeer> for types::network::BannedPeer {
935 type Error = anyhow::Error;
936
937 fn try_from(value: crate::v2::generated::BannedPeer) -> Result<Self, Self::Error> {
938 Ok(types::network::BannedPeer(
939 <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip_address.require()?.value)?,
940 ))
941 }
942}
943
944impl TryFrom<generated::IpSocketAddress> for std::net::SocketAddr {
945 type Error = anyhow::Error;
946
947 fn try_from(value: generated::IpSocketAddress) -> Result<Self, Self::Error> {
948 Ok(std::net::SocketAddr::new(
949 <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip.require()?.value)?,
950 value.port.require()?.value as u16,
951 ))
952 }
953}
954
955impl IntoRequest<crate::v2::generated::BannedPeer> for &types::network::BannedPeer {
956 fn into_request(self) -> tonic::Request<crate::v2::generated::BannedPeer> {
957 tonic::Request::new(crate::v2::generated::BannedPeer {
958 ip_address: Some(crate::v2::generated::IpAddress {
959 value: self.0.to_string(),
960 }),
961 })
962 }
963}
964
965impl From<generated::PeerId> for types::network::PeerId {
966 fn from(value: generated::PeerId) -> Self {
967 types::network::PeerId(value.value)
968 }
969}
970
971impl TryFrom<generated::PeersInfo> for types::network::PeersInfo {
972 type Error = anyhow::Error;
973
974 fn try_from(peers_info: generated::PeersInfo) -> Result<Self, Self::Error> {
975 let peers = peers_info
981 .peers
982 .into_iter()
983 .map(|peer| {
984 let peer_consensus_info =
986 Upward::from(peer.consensus_info).and_then(|info| match info {
987 generated::peers_info::peer::ConsensusInfo::Bootstrapper(_) => {
988 Upward::Known(types::network::PeerConsensusInfo::Bootstrapper)
989 }
990 generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(status) => {
991 let Upward::Known(status) = Upward::from(
992 generated::peers_info::peer::CatchupStatus::try_from(status).ok(),
993 ) else {
994 return Upward::Known(types::network::PeerConsensusInfo::Node(
995 Upward::Unknown(()),
996 ));
997 };
998 let status = match status {
999 generated::peers_info::peer::CatchupStatus::Uptodate => {
1000 types::network::PeerCatchupStatus::UpToDate
1001 }
1002 generated::peers_info::peer::CatchupStatus::Pending => {
1003 types::network::PeerCatchupStatus::Pending
1004 }
1005 generated::peers_info::peer::CatchupStatus::Catchingup => {
1006 types::network::PeerCatchupStatus::CatchingUp
1007 }
1008 };
1009
1010 Upward::Known(types::network::PeerConsensusInfo::Node(Upward::Known(
1011 status,
1012 )))
1013 }
1014 });
1015 let stats = peer.network_stats.require()?;
1017 let network_stats = types::network::NetworkStats {
1018 packets_sent: stats.packets_sent,
1019 packets_received: stats.packets_received,
1020 latency: stats.latency,
1021 };
1022 Ok(types::network::Peer {
1023 peer_id: peer.peer_id.require()?.into(),
1024 consensus_info: peer_consensus_info,
1025 network_stats,
1026 addr: peer.socket_address.require()?.try_into()?,
1027 })
1028 })
1029 .collect::<anyhow::Result<Vec<types::network::Peer>>>()?;
1030 Ok(types::network::PeersInfo { peers })
1031 }
1032}
1033
1034impl TryFrom<generated::node_info::NetworkInfo> for types::NetworkInfo {
1035 type Error = anyhow::Error;
1036
1037 fn try_from(network_info: generated::node_info::NetworkInfo) -> Result<Self, Self::Error> {
1038 Ok(types::NetworkInfo {
1039 node_id: network_info.node_id.require()?.value,
1040 peer_total_sent: network_info.peer_total_sent,
1041 peer_total_received: network_info.peer_total_received,
1042 avg_bps_in: network_info.avg_bps_in,
1043 avg_bps_out: network_info.avg_bps_out,
1044 })
1045 }
1046}
1047
1048impl IntoRequest<crate::v2::generated::PeerToBan> for types::network::PeerToBan {
1049 fn into_request(self) -> tonic::Request<crate::v2::generated::PeerToBan> {
1050 tonic::Request::new(match self {
1051 types::network::PeerToBan::IpAddr(ip_addr) => crate::v2::generated::PeerToBan {
1052 ip_address: Some(crate::v2::generated::IpAddress {
1053 value: ip_addr.to_string(),
1054 }),
1055 },
1056 })
1057 }
1058}
1059
1060impl TryFrom<generated::node_info::Details> for types::NodeDetails {
1061 type Error = anyhow::Error;
1062
1063 fn try_from(details: generated::node_info::Details) -> Result<Self, Self::Error> {
1064 match details {
1065 generated::node_info::Details::Bootstrapper(_) => Ok(types::NodeDetails::Bootstrapper),
1066 generated::node_info::Details::Node(status) => {
1067 let Upward::Known(consensus_status) = Upward::from(status.consensus_status) else {
1068 return Ok(types::NodeDetails::Node(Upward::Unknown(())));
1069 };
1070 let consensus_status = match consensus_status {
1071 generated::node_info::node::ConsensusStatus::NotRunning(_) => {
1072 types::NodeConsensusStatus::ConsensusNotRunning
1073 }
1074 generated::node_info::node::ConsensusStatus::Passive(_) => {
1075 types::NodeConsensusStatus::ConsensusPassive
1076 }
1077 generated::node_info::node::ConsensusStatus::Active(baker) => {
1078 let baker_id = baker.baker_id.require()?.into();
1079 let Upward::Known(status) = Upward::from(baker.status) else {
1080 return Ok(types::NodeDetails::Node(Upward::Unknown(())));
1081 };
1082
1083 match status {
1084 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(0) => types::NodeConsensusStatus::NotInCommittee(baker_id),
1085 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(1) => types::NodeConsensusStatus::AddedButNotActiveInCommittee(baker_id),
1086 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(2) => types::NodeConsensusStatus::AddedButWrongKeys(baker_id),
1087 generated::node_info::baker_consensus_info::Status::ActiveBakerCommitteeInfo(_) => types::NodeConsensusStatus::Baker(baker_id),
1088 generated::node_info::baker_consensus_info::Status::ActiveFinalizerCommitteeInfo(_) => types::NodeConsensusStatus::Finalizer(baker_id),
1089 _ => anyhow::bail!("Malformed baker status")
1090 }
1091 }
1092 };
1093 Ok(types::NodeDetails::Node(Upward::Known(consensus_status)))
1094 }
1095 }
1096 }
1097}
1098
1099impl TryFrom<generated::NodeInfo> for types::NodeInfo {
1100 type Error = anyhow::Error;
1101
1102 fn try_from(node_info: generated::NodeInfo) -> Result<Self, Self::Error> {
1103 let version = semver::Version::parse(&node_info.peer_version)?;
1104 let local_time = chrono::DateTime::<chrono::Utc>::from(std::time::UNIX_EPOCH)
1105 + chrono::TimeDelta::try_milliseconds(node_info.local_time.require()?.value as i64)
1106 .context("Node local time out of bounds!")?;
1107 let uptime = chrono::Duration::try_from(types::DurationSeconds::from(
1108 node_info.peer_uptime.require()?.value,
1109 ))?;
1110 let network_info = node_info.network_info.require()?.try_into()?;
1111 let details = Upward::from(node_info.details)
1112 .map(types::NodeDetails::try_from)
1113 .transpose()?;
1114 Ok(types::NodeInfo {
1115 version,
1116 local_time,
1117 uptime,
1118 network_info,
1119 details,
1120 })
1121 }
1122}
1123
1124pub trait IntoBlockIdentifier {
1131 fn into_block_identifier(self) -> BlockIdentifier;
1132}
1133
1134impl IntoBlockIdentifier for BlockIdentifier {
1135 fn into_block_identifier(self) -> BlockIdentifier {
1136 self
1137 }
1138}
1139
1140impl<X: IntoBlockIdentifier + Copy> IntoBlockIdentifier for &X {
1141 fn into_block_identifier(self) -> BlockIdentifier {
1142 (*self).into_block_identifier()
1143 }
1144}
1145
1146impl IntoBlockIdentifier for BlockHash {
1147 fn into_block_identifier(self) -> BlockIdentifier {
1148 BlockIdentifier::Given(self)
1149 }
1150}
1151
1152impl IntoBlockIdentifier for AbsoluteBlockHeight {
1153 fn into_block_identifier(self) -> BlockIdentifier {
1154 BlockIdentifier::AbsoluteHeight(self)
1155 }
1156}
1157
1158impl IntoBlockIdentifier for RelativeBlockHeight {
1159 fn into_block_identifier(self) -> BlockIdentifier {
1160 BlockIdentifier::RelativeHeight(self)
1161 }
1162}
1163
1164impl Client {
1165 pub async fn new<E>(endpoint: E) -> Result<Self, tonic::transport::Error>
1182 where
1183 E: TryInto<tonic::transport::Endpoint>,
1184 E::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
1185 {
1186 let client = generated::queries_client::QueriesClient::connect(endpoint).await?;
1187 Ok(Self { client })
1188 }
1189
1190 pub async fn get_account_info(
1194 &mut self,
1195 acc: &AccountIdentifier,
1196 bi: impl IntoBlockIdentifier,
1197 ) -> endpoints::QueryResult<QueryResponse<AccountInfo>> {
1198 let response = self
1199 .client
1200 .get_account_info((acc, &bi.into_block_identifier()))
1201 .await?;
1202 let block_hash = extract_metadata(&response)?;
1203 let response = AccountInfo::try_from(response.into_inner())?;
1204 Ok(QueryResponse {
1205 block_hash,
1206 response,
1207 })
1208 }
1209
1210 pub async fn get_next_account_sequence_number(
1213 &mut self,
1214 account_address: &AccountAddress,
1215 ) -> endpoints::QueryResult<types::queries::AccountNonceResponse> {
1216 let response = self
1217 .client
1218 .get_next_account_sequence_number(account_address)
1219 .await?;
1220 let response = types::queries::AccountNonceResponse::try_from(response.into_inner())?;
1221 Ok(response)
1222 }
1223
1224 pub async fn get_consensus_info(
1227 &mut self,
1228 ) -> endpoints::QueryResult<types::queries::ConsensusInfo> {
1229 let response = self
1230 .client
1231 .get_consensus_info(generated::Empty::default())
1232 .await?;
1233 let response = types::queries::ConsensusInfo::try_from(response.into_inner())?;
1234 Ok(response)
1235 }
1236
1237 pub async fn get_cryptographic_parameters(
1240 &mut self,
1241 bi: impl IntoBlockIdentifier,
1242 ) -> endpoints::QueryResult<QueryResponse<types::CryptographicParameters>> {
1243 let response = self
1244 .client
1245 .get_cryptographic_parameters(&bi.into_block_identifier())
1246 .await?;
1247 let block_hash = extract_metadata(&response)?;
1248 let response = types::CryptographicParameters::try_from(response.into_inner())?;
1249 Ok(QueryResponse {
1250 block_hash,
1251 response,
1252 })
1253 }
1254
1255 pub async fn get_account_list(
1260 &mut self,
1261 bi: impl IntoBlockIdentifier,
1262 ) -> endpoints::QueryResult<
1263 QueryResponse<impl Stream<Item = Result<AccountAddress, tonic::Status>>>,
1264 > {
1265 let response = self
1266 .client
1267 .get_account_list(&bi.into_block_identifier())
1268 .await?;
1269 let block_hash = extract_metadata(&response)?;
1270 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1271 Ok(QueryResponse {
1272 block_hash,
1273 response: stream,
1274 })
1275 }
1276
1277 pub async fn get_module_list(
1282 &mut self,
1283 bi: impl IntoBlockIdentifier,
1284 ) -> endpoints::QueryResult<
1285 QueryResponse<impl Stream<Item = Result<ModuleReference, tonic::Status>>>,
1286 > {
1287 let response = self
1288 .client
1289 .get_module_list(&bi.into_block_identifier())
1290 .await?;
1291 let block_hash = extract_metadata(&response)?;
1292 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1293 Ok(QueryResponse {
1294 block_hash,
1295 response: stream,
1296 })
1297 }
1298
1299 pub async fn get_module_source(
1303 &mut self,
1304 module_ref: &ModuleReference,
1305 bi: impl IntoBlockIdentifier,
1306 ) -> endpoints::QueryResult<QueryResponse<types::smart_contracts::WasmModule>> {
1307 let response = self
1308 .client
1309 .get_module_source((module_ref, &bi.into_block_identifier()))
1310 .await?;
1311 let block_hash = extract_metadata(&response)?;
1312 let response = types::smart_contracts::WasmModule::try_from(response.into_inner())?;
1313 Ok(QueryResponse {
1314 block_hash,
1315 response,
1316 })
1317 }
1318
1319 pub async fn get_instance_list(
1324 &mut self,
1325 bi: impl IntoBlockIdentifier,
1326 ) -> endpoints::QueryResult<
1327 QueryResponse<impl Stream<Item = Result<ContractAddress, tonic::Status>>>,
1328 > {
1329 let response = self
1330 .client
1331 .get_instance_list(&bi.into_block_identifier())
1332 .await?;
1333 let block_hash = extract_metadata(&response)?;
1334 let stream = response.into_inner().map(|x| x.map(From::from));
1335 Ok(QueryResponse {
1336 block_hash,
1337 response: stream,
1338 })
1339 }
1340
1341 pub async fn get_instance_info(
1345 &mut self,
1346 address: ContractAddress,
1347 bi: impl IntoBlockIdentifier,
1348 ) -> endpoints::QueryResult<QueryResponse<InstanceInfo>> {
1349 let response = self
1350 .client
1351 .get_instance_info((address, &bi.into_block_identifier()))
1352 .await?;
1353 let block_hash = extract_metadata(&response)?;
1354 let response = InstanceInfo::try_from(response.into_inner())?;
1355 Ok(QueryResponse {
1356 block_hash,
1357 response,
1358 })
1359 }
1360
1361 pub async fn get_ancestors(
1365 &mut self,
1366 bi: impl IntoBlockIdentifier,
1367 limit: u64,
1368 ) -> endpoints::QueryResult<QueryResponse<impl Stream<Item = Result<BlockHash, tonic::Status>>>>
1369 {
1370 let response = self
1371 .client
1372 .get_ancestors((&bi.into_block_identifier(), limit))
1373 .await?;
1374 let block_hash = extract_metadata(&response)?;
1375 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1376 Ok(QueryResponse {
1377 block_hash,
1378 response: stream,
1379 })
1380 }
1381
1382 pub async fn get_finalized_blocks(
1390 &mut self,
1391 ) -> endpoints::QueryResult<impl Stream<Item = Result<FinalizedBlockInfo, tonic::Status>>> {
1392 let response = self
1393 .client
1394 .get_finalized_blocks(generated::Empty::default())
1395 .await?;
1396 let stream = response.into_inner().map(|x| match x {
1397 Ok(v) => {
1398 let block_hash = v.hash.require().and_then(TryFrom::try_from)?;
1399 let height = v.height.require()?.into();
1400 Ok(FinalizedBlockInfo { block_hash, height })
1401 }
1402 Err(x) => Err(x),
1403 });
1404 Ok(stream)
1405 }
1406
1407 pub async fn get_instance_state(
1413 &mut self,
1414 ca: ContractAddress,
1415 bi: impl IntoBlockIdentifier,
1416 ) -> endpoints::QueryResult<
1417 QueryResponse<impl Stream<Item = Result<(Vec<u8>, Vec<u8>), tonic::Status>>>,
1418 > {
1419 let response = self
1420 .client
1421 .get_instance_state((ca, &bi.into_block_identifier()))
1422 .await?;
1423 let block_hash = extract_metadata(&response)?;
1424 let stream = response.into_inner().map(|x| match x {
1425 Ok(v) => {
1426 let key = v.key;
1427 let value = v.value;
1428 Ok((key, value))
1429 }
1430 Err(x) => Err(x),
1431 });
1432 Ok(QueryResponse {
1433 block_hash,
1434 response: stream,
1435 })
1436 }
1437
1438 pub async fn instance_state_lookup(
1444 &mut self,
1445 ca: ContractAddress,
1446 key: impl Into<Vec<u8>>,
1447 bi: impl IntoBlockIdentifier,
1448 ) -> endpoints::QueryResult<QueryResponse<Vec<u8>>> {
1449 let response = self
1450 .client
1451 .instance_state_lookup((ca, &bi.into_block_identifier(), key))
1452 .await?;
1453 let block_hash = extract_metadata(&response)?;
1454 Ok(QueryResponse {
1455 block_hash,
1456 response: response.into_inner().value,
1457 })
1458 }
1459
1460 pub async fn get_block_item_status(
1464 &mut self,
1465 th: &TransactionHash,
1466 ) -> endpoints::QueryResult<TransactionStatus> {
1467 let response = self.client.get_block_item_status(th).await?;
1468 let response = TransactionStatus::try_from(response.into_inner())?;
1469 Ok(response)
1470 }
1471
1472 pub async fn send_block_item<P: PayloadLike>(
1479 &mut self,
1480 bi: &transactions::BlockItem<P>,
1481 ) -> endpoints::RPCResult<TransactionHash> {
1482 let response = self.client.send_block_item(bi).await?;
1483 let response = TransactionHash::try_from(response.into_inner())?;
1484 Ok(response)
1485 }
1486
1487 pub async fn send_account_transaction<P: PayloadLike>(
1490 &mut self,
1491 at: transactions::AccountTransaction<P>,
1492 ) -> endpoints::RPCResult<TransactionHash> {
1493 self.send_block_item(&at.into()).await
1494 }
1495
1496 pub async fn get_account_transaction_sign_hash(
1509 &mut self,
1510 header: &transactions::TransactionHeader,
1511 payload: &transactions::Payload,
1512 ) -> endpoints::RPCResult<TransactionSignHash> {
1513 let response = self
1514 .client
1515 .get_account_transaction_sign_hash((header, payload))
1516 .await?;
1517 let response = TransactionSignHash::try_from(response.into_inner())?;
1518 Ok(response)
1519 }
1520
1521 pub async fn wait_until_finalized(
1532 &mut self,
1533 hash: &types::hashes::TransactionHash,
1534 ) -> endpoints::QueryResult<(types::hashes::BlockHash, types::BlockItemSummary)> {
1535 let hash = *hash;
1536 let process_response = |response| {
1537 if let types::TransactionStatus::Finalized(blocks) = response {
1538 let mut iter = blocks.into_iter();
1539 if let Some(rv) = iter.next() {
1540 if iter.next().is_some() {
1541 Err(tonic::Status::internal(
1542 "Finalized transaction finalized into multiple blocks. This cannot \
1543 happen.",
1544 )
1545 .into())
1546 } else {
1547 Ok::<_, QueryError>(Some(rv))
1548 }
1549 } else {
1550 Err(tonic::Status::internal(
1551 "Finalized transaction finalized into no blocks. This cannot happen.",
1552 )
1553 .into())
1554 }
1555 } else {
1556 Ok(None)
1557 }
1558 };
1559
1560 match process_response(self.get_block_item_status(&hash).await?)? {
1561 Some(rv) => Ok(rv),
1562 None => {
1563 let mut blocks = self.get_finalized_blocks().await?;
1566 while blocks.next().await.transpose()?.is_some() {
1567 if let Some(rv) = process_response(self.get_block_item_status(&hash).await?)? {
1568 return Ok(rv);
1569 }
1570 }
1571 Err(QueryError::NotFound)
1572 }
1573 }
1574 }
1575
1576 pub async fn invoke_instance(
1580 &mut self,
1581 bi: impl IntoBlockIdentifier,
1582 context: &ContractContext,
1583 ) -> endpoints::QueryResult<QueryResponse<InvokeContractResult>> {
1584 let response = self
1585 .client
1586 .invoke_instance((&bi.into_block_identifier(), context))
1587 .await?;
1588 let block_hash = extract_metadata(&response)?;
1589 let response = InvokeContractResult::try_from(response.into_inner())?;
1590 Ok(QueryResponse {
1591 block_hash,
1592 response,
1593 })
1594 }
1595
1596 pub async fn begin_dry_run(&mut self) -> endpoints::QueryResult<dry_run::DryRun> {
1603 Ok(dry_run::DryRun::new(&mut self.client).await?)
1604 }
1605
1606 pub async fn dry_run(
1610 &mut self,
1611 bi: impl IntoBlockIdentifier,
1612 ) -> dry_run::DryRunResult<(dry_run::DryRun, dry_run::BlockStateLoaded)> {
1613 let mut runner = dry_run::DryRun::new(&mut self.client).await?;
1614 let load_result = runner.load_block_state(bi).await?;
1615 Ok(WithRemainingQuota {
1616 inner: (runner, load_result.inner),
1617 quota_remaining: load_result.quota_remaining,
1618 })
1619 }
1620
1621 pub async fn get_block_info(
1625 &mut self,
1626 bi: impl IntoBlockIdentifier,
1627 ) -> endpoints::QueryResult<QueryResponse<types::queries::BlockInfo>> {
1628 let response = self
1629 .client
1630 .get_block_info(&bi.into_block_identifier())
1631 .await?;
1632 let block_hash = extract_metadata(&response)?;
1633 let response = types::queries::BlockInfo::try_from(response.into_inner())?;
1634 Ok(QueryResponse {
1635 block_hash,
1636 response,
1637 })
1638 }
1639
1640 pub async fn is_payday_block(
1645 &mut self,
1646 bi: impl IntoBlockIdentifier,
1647 ) -> endpoints::QueryResult<QueryResponse<bool>> {
1648 let mut special_events = self.get_block_special_events(bi).await?;
1649 let block_hash = special_events.block_hash;
1650
1651 while let Some(event) = special_events.response.next().await.transpose()? {
1652 let Upward::Known(event) = event else {
1653 continue;
1655 };
1656 let has_payday_event = matches!(
1657 event,
1658 SpecialTransactionOutcome::PaydayPoolReward { .. }
1659 | SpecialTransactionOutcome::PaydayAccountReward { .. }
1660 | SpecialTransactionOutcome::PaydayFoundationReward { .. }
1661 );
1662
1663 if has_payday_event {
1664 return Ok(QueryResponse {
1665 block_hash,
1666 response: true,
1667 });
1668 };
1669 }
1670
1671 Ok(QueryResponse {
1672 block_hash,
1673 response: false,
1674 })
1675 }
1676
1677 pub async fn get_baker_list(
1680 &mut self,
1681 bi: impl IntoBlockIdentifier,
1682 ) -> endpoints::QueryResult<
1683 QueryResponse<impl Stream<Item = Result<types::BakerId, tonic::Status>>>,
1684 > {
1685 let response = self
1686 .client
1687 .get_baker_list(&bi.into_block_identifier())
1688 .await?;
1689 let block_hash = extract_metadata(&response)?;
1690 let stream = response.into_inner().map(|x| x.map(From::from));
1691 Ok(QueryResponse {
1692 block_hash,
1693 response: stream,
1694 })
1695 }
1696
1697 pub async fn get_pool_info(
1701 &mut self,
1702 block_id: impl IntoBlockIdentifier,
1703 baker_id: types::BakerId,
1704 ) -> endpoints::QueryResult<QueryResponse<types::BakerPoolStatus>> {
1705 let response = self
1706 .client
1707 .get_pool_info((&block_id.into_block_identifier(), baker_id))
1708 .await?;
1709 let block_hash = extract_metadata(&response)?;
1710 let response = types::BakerPoolStatus::try_from(response.into_inner())?;
1711 Ok(QueryResponse {
1712 block_hash,
1713 response,
1714 })
1715 }
1716
1717 pub async fn get_passive_delegation_info(
1722 &mut self,
1723 block_id: impl IntoBlockIdentifier,
1724 ) -> endpoints::QueryResult<QueryResponse<types::PassiveDelegationStatus>> {
1725 let response = self
1726 .client
1727 .get_passive_delegation_info(&block_id.into_block_identifier())
1728 .await?;
1729 let block_hash = extract_metadata(&response)?;
1730 let response = types::PassiveDelegationStatus::try_from(response.into_inner())?;
1731 Ok(QueryResponse {
1732 block_hash,
1733 response,
1734 })
1735 }
1736
1737 pub async fn get_blocks_at_height(
1739 &mut self,
1740 blocks_at_height_input: &endpoints::BlocksAtHeightInput,
1741 ) -> endpoints::QueryResult<Vec<BlockHash>> {
1742 let response = self
1743 .client
1744 .get_blocks_at_height(blocks_at_height_input)
1745 .await?;
1746 let blocks = response
1747 .into_inner()
1748 .blocks
1749 .into_iter()
1750 .map(TryFrom::try_from)
1751 .collect::<Result<_, tonic::Status>>()?;
1752 Ok(blocks)
1753 }
1754
1755 pub async fn get_tokenomics_info(
1758 &mut self,
1759 block_id: impl IntoBlockIdentifier,
1760 ) -> endpoints::QueryResult<QueryResponse<types::RewardsOverview>> {
1761 let response = self
1762 .client
1763 .get_tokenomics_info(&block_id.into_block_identifier())
1764 .await?;
1765 let block_hash = extract_metadata(&response)?;
1766 let response = types::RewardsOverview::try_from(response.into_inner())?;
1767 Ok(QueryResponse {
1768 block_hash,
1769 response,
1770 })
1771 }
1772
1773 pub async fn get_pool_delegators(
1787 &mut self,
1788 bi: impl IntoBlockIdentifier,
1789 baker_id: types::BakerId,
1790 ) -> endpoints::QueryResult<
1791 QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1792 > {
1793 let response = self
1794 .client
1795 .get_pool_delegators((&bi.into_block_identifier(), baker_id))
1796 .await?;
1797 let block_hash = extract_metadata(&response)?;
1798 let stream = response.into_inner().map(|result| match result {
1799 Ok(delegator) => delegator.try_into(),
1800 Err(err) => Err(err),
1801 });
1802 Ok(QueryResponse {
1803 block_hash,
1804 response: stream,
1805 })
1806 }
1807
1808 pub async fn get_pool_delegators_reward_period(
1821 &mut self,
1822 bi: impl IntoBlockIdentifier,
1823 baker_id: types::BakerId,
1824 ) -> endpoints::QueryResult<
1825 QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1826 > {
1827 let response = self
1828 .client
1829 .get_pool_delegators_reward_period((&bi.into_block_identifier(), baker_id))
1830 .await?;
1831 let block_hash = extract_metadata(&response)?;
1832 let stream = response.into_inner().map(|result| match result {
1833 Ok(delegator) => delegator.try_into(),
1834 Err(err) => Err(err),
1835 });
1836 Ok(QueryResponse {
1837 block_hash,
1838 response: stream,
1839 })
1840 }
1841
1842 pub async fn get_passive_delegators(
1854 &mut self,
1855 bi: impl IntoBlockIdentifier,
1856 ) -> endpoints::QueryResult<
1857 QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1858 > {
1859 let response = self
1860 .client
1861 .get_passive_delegators(&bi.into_block_identifier())
1862 .await?;
1863 let block_hash = extract_metadata(&response)?;
1864 let stream = response.into_inner().map(|result| match result {
1865 Ok(delegator) => delegator.try_into(),
1866 Err(err) => Err(err),
1867 });
1868 Ok(QueryResponse {
1869 block_hash,
1870 response: stream,
1871 })
1872 }
1873
1874 pub async fn get_passive_delegators_reward_period(
1887 &mut self,
1888 bi: impl IntoBlockIdentifier,
1889 ) -> endpoints::QueryResult<
1890 QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1891 > {
1892 let response = self
1893 .client
1894 .get_passive_delegators_reward_period(&bi.into_block_identifier())
1895 .await?;
1896 let block_hash = extract_metadata(&response)?;
1897 let stream = response.into_inner().map(|result| match result {
1898 Ok(delegator) => delegator.try_into(),
1899 Err(err) => Err(err),
1900 });
1901 Ok(QueryResponse {
1902 block_hash,
1903 response: stream,
1904 })
1905 }
1906
1907 pub async fn get_branches(&mut self) -> endpoints::QueryResult<types::queries::Branch> {
1914 let response = self
1915 .client
1916 .get_branches(generated::Empty::default())
1917 .await?;
1918 let response = types::queries::Branch::try_from(response.into_inner())?;
1919 Ok(response)
1920 }
1921
1922 pub async fn get_election_info(
1925 &mut self,
1926 bi: impl IntoBlockIdentifier,
1927 ) -> endpoints::QueryResult<QueryResponse<types::BirkParameters>> {
1928 let response = self
1929 .client
1930 .get_election_info(&bi.into_block_identifier())
1931 .await?;
1932 let block_hash = extract_metadata(&response)?;
1933 let response = types::BirkParameters::try_from(response.into_inner())?;
1934 Ok(QueryResponse {
1935 block_hash,
1936 response,
1937 })
1938 }
1939
1940 pub async fn get_identity_providers(
1944 &mut self,
1945 bi: impl IntoBlockIdentifier,
1946 ) -> endpoints::QueryResult<
1947 QueryResponse<
1948 impl Stream<
1949 Item = Result<
1950 crate::id::types::IpInfo<crate::id::constants::IpPairing>,
1951 tonic::Status,
1952 >,
1953 >,
1954 >,
1955 > {
1956 let response = self
1957 .client
1958 .get_identity_providers(&bi.into_block_identifier())
1959 .await?;
1960 let block_hash = extract_metadata(&response)?;
1961 let stream = response.into_inner().map(|result| match result {
1962 Ok(ip_info) => ip_info.try_into(),
1963 Err(err) => Err(err),
1964 });
1965 Ok(QueryResponse {
1966 block_hash,
1967 response: stream,
1968 })
1969 }
1970
1971 pub async fn get_anonymity_revokers(
1975 &mut self,
1976 bi: impl IntoBlockIdentifier,
1977 ) -> endpoints::QueryResult<
1978 QueryResponse<
1979 impl Stream<
1980 Item = Result<
1981 crate::id::types::ArInfo<crate::id::constants::ArCurve>,
1982 tonic::Status,
1983 >,
1984 >,
1985 >,
1986 > {
1987 let response = self
1988 .client
1989 .get_anonymity_revokers(&bi.into_block_identifier())
1990 .await?;
1991 let block_hash = extract_metadata(&response)?;
1992 let stream = response.into_inner().map(|result| match result {
1993 Ok(ar_info) => ar_info.try_into(),
1994 Err(err) => Err(err),
1995 });
1996 Ok(QueryResponse {
1997 block_hash,
1998 response: stream,
1999 })
2000 }
2001
2002 pub async fn get_account_non_finalized_transactions(
2012 &mut self,
2013 account_address: &AccountAddress,
2014 ) -> endpoints::QueryResult<impl Stream<Item = Result<TransactionHash, tonic::Status>>> {
2015 let response = self
2016 .client
2017 .get_account_non_finalized_transactions(account_address)
2018 .await?;
2019 let stream = response.into_inner().map(|result| match result {
2020 Ok(transaction_hash) => transaction_hash.try_into(),
2021 Err(err) => Err(err),
2022 });
2023 Ok(stream)
2024 }
2025
2026 pub async fn get_block_items(
2033 &mut self,
2034 bi: impl IntoBlockIdentifier,
2035 ) -> endpoints::QueryResult<
2036 QueryResponse<impl Stream<Item = Result<Upward<BlockItem<EncodedPayload>>, tonic::Status>>>,
2037 > {
2038 let response = self
2039 .client
2040 .get_block_items(&bi.into_block_identifier())
2041 .await?;
2042 let block_hash = extract_metadata(&response)?;
2043 let stream = response.into_inner().map(|result| match result {
2044 Ok(summary) => summary.try_into(),
2045 Err(err) => Err(err),
2046 });
2047 Ok(QueryResponse {
2048 block_hash,
2049 response: stream,
2050 })
2051 }
2052
2053 pub async fn get_finalized_block_item(
2068 &mut self,
2069 th: TransactionHash,
2070 ) -> endpoints::QueryResult<(
2071 Upward<BlockItem<EncodedPayload>>,
2072 BlockHash,
2073 BlockItemSummary,
2074 )> {
2075 let status = self.get_block_item_status(&th).await?;
2076 let Some((bh, status)) = status.is_finalized() else {
2077 return Err(QueryError::NotFound);
2078 };
2079 let mut response = self
2080 .client
2081 .get_block_items(&bh.into_block_identifier())
2082 .await?
2083 .into_inner();
2084 while let Some(tx) = response.try_next().await? {
2085 let tx_hash = TransactionHash::try_from(tx.hash.clone().require()?)?;
2086 if tx_hash == th {
2087 return Ok((tx.try_into()?, *bh, status.clone()));
2088 }
2089 }
2090 Err(endpoints::QueryError::NotFound)
2091 }
2092
2093 pub async fn shutdown(&mut self) -> endpoints::RPCResult<()> {
2096 self.client.shutdown(generated::Empty::default()).await?;
2097 Ok(())
2098 }
2099
2100 pub async fn peer_connect(&mut self, addr: std::net::SocketAddr) -> endpoints::RPCResult<()> {
2108 let peer_connection = generated::IpSocketAddress {
2109 ip: Some(generated::IpAddress {
2110 value: addr.ip().to_string(),
2111 }),
2112 port: Some(generated::Port {
2113 value: addr.port() as u32,
2114 }),
2115 };
2116 self.client.peer_connect(peer_connection).await?;
2117 Ok(())
2118 }
2119
2120 pub async fn peer_disconnect(
2124 &mut self,
2125 addr: std::net::SocketAddr,
2126 ) -> endpoints::RPCResult<()> {
2127 let peer_connection = generated::IpSocketAddress {
2128 ip: Some(generated::IpAddress {
2129 value: addr.ip().to_string(),
2130 }),
2131 port: Some(generated::Port {
2132 value: addr.port() as u32,
2133 }),
2134 };
2135 self.client.peer_disconnect(peer_connection).await?;
2136 Ok(())
2137 }
2138
2139 pub async fn get_banned_peers(
2141 &mut self,
2142 ) -> endpoints::RPCResult<Vec<super::types::network::BannedPeer>> {
2143 Ok(self
2144 .client
2145 .get_banned_peers(generated::Empty::default())
2146 .await?
2147 .into_inner()
2148 .peers
2149 .into_iter()
2150 .map(super::types::network::BannedPeer::try_from)
2151 .collect::<anyhow::Result<Vec<super::types::network::BannedPeer>>>()?)
2152 }
2153
2154 pub async fn ban_peer(
2158 &mut self,
2159 peer_to_ban: super::types::network::PeerToBan,
2160 ) -> endpoints::RPCResult<()> {
2161 self.client.ban_peer(peer_to_ban).await?;
2162 Ok(())
2163 }
2164
2165 pub async fn unban_peer(
2169 &mut self,
2170 banned_peer: &super::types::network::BannedPeer,
2171 ) -> endpoints::RPCResult<()> {
2172 self.client.unban_peer(banned_peer).await?;
2173 Ok(())
2174 }
2175
2176 pub async fn dump_start(
2187 &mut self,
2188 file: &std::path::Path,
2189 raw: bool,
2190 ) -> endpoints::RPCResult<()> {
2191 let file_str = file.to_str().ok_or_else(|| {
2192 tonic::Status::invalid_argument(
2193 "The provided path cannot is not a valid UTF8 string, so cannot be used.",
2194 )
2195 })?;
2196
2197 self.client
2198 .dump_start(generated::DumpRequest {
2199 file: file_str.to_string(),
2200 raw,
2201 })
2202 .await?;
2203 Ok(())
2204 }
2205
2206 pub async fn dump_stop(&mut self) -> endpoints::RPCResult<()> {
2213 self.client.dump_stop(generated::Empty::default()).await?;
2214 Ok(())
2215 }
2216
2217 pub async fn get_peers_info(&mut self) -> endpoints::RPCResult<types::network::PeersInfo> {
2220 let response = self
2221 .client
2222 .get_peers_info(generated::Empty::default())
2223 .await?;
2224 let peers_info = types::network::PeersInfo::try_from(response.into_inner())?;
2225 Ok(peers_info)
2226 }
2227
2228 pub async fn get_node_info(&mut self) -> endpoints::RPCResult<types::NodeInfo> {
2242 let response = self
2243 .client
2244 .get_node_info(generated::Empty::default())
2245 .await?;
2246 let node_info = types::NodeInfo::try_from(response.into_inner())?;
2247 Ok(node_info)
2248 }
2249
2250 pub async fn get_baker_earliest_win_time(
2258 &mut self,
2259 bid: types::BakerId,
2260 ) -> endpoints::RPCResult<chrono::DateTime<chrono::Utc>> {
2261 let ts = self.client.get_baker_earliest_win_time(bid).await?;
2262 let local_time = ts.into_inner().try_into()?;
2263 Ok(local_time)
2264 }
2265
2266 pub async fn get_block_transaction_events(
2270 &mut self,
2271 bi: impl IntoBlockIdentifier,
2272 ) -> endpoints::QueryResult<
2273 QueryResponse<impl Stream<Item = Result<types::BlockItemSummary, tonic::Status>>>,
2274 > {
2275 let response = self
2276 .client
2277 .get_block_transaction_events(&bi.into_block_identifier())
2278 .await?;
2279 let block_hash = extract_metadata(&response)?;
2280 let stream = response.into_inner().map(|result| match result {
2281 Ok(summary) => summary.try_into(),
2282 Err(err) => Err(err),
2283 });
2284 Ok(QueryResponse {
2285 block_hash,
2286 response: stream,
2287 })
2288 }
2289
2290 pub async fn get_block_special_events(
2297 &mut self,
2298 bi: impl IntoBlockIdentifier,
2299 ) -> endpoints::QueryResult<
2300 QueryResponse<
2301 impl Stream<Item = Result<Upward<types::SpecialTransactionOutcome>, tonic::Status>>,
2302 >,
2303 > {
2304 let response = self
2305 .client
2306 .get_block_special_events(&bi.into_block_identifier())
2307 .await?;
2308 let block_hash = extract_metadata(&response)?;
2309 let stream = response.into_inner().map(|result| match result {
2310 Ok(summary) => summary.try_into(),
2311 Err(err) => Err(err),
2312 });
2313 Ok(QueryResponse {
2314 block_hash,
2315 response: stream,
2316 })
2317 }
2318
2319 pub async fn get_block_pending_updates(
2324 &mut self,
2325 bi: impl IntoBlockIdentifier,
2326 ) -> endpoints::QueryResult<
2327 QueryResponse<impl Stream<Item = Result<types::queries::PendingUpdate, tonic::Status>>>,
2328 > {
2329 let response = self
2330 .client
2331 .get_block_pending_updates(&bi.into_block_identifier())
2332 .await?;
2333 let block_hash = extract_metadata(&response)?;
2334 let stream = response.into_inner().map(|result| match result {
2335 Ok(update) => update.try_into(),
2336 Err(err) => Err(err),
2337 });
2338 Ok(QueryResponse {
2339 block_hash,
2340 response: stream,
2341 })
2342 }
2343
2344 pub async fn get_winning_bakers_epoch(
2355 &mut self,
2356 ei: impl Into<EpochIdentifier>,
2357 ) -> endpoints::QueryResult<impl Stream<Item = Result<types::WinningBaker, tonic::Status>>>
2358 {
2359 let response = self.client.get_winning_bakers_epoch(&ei.into()).await?;
2360 let stream = response.into_inner().map(|result| match result {
2361 Ok(wb) => wb.try_into(),
2362 Err(err) => Err(err),
2363 });
2364 Ok(stream)
2365 }
2366
2367 pub async fn get_first_block_epoch(
2369 &mut self,
2370 ei: impl Into<EpochIdentifier>,
2371 ) -> endpoints::QueryResult<BlockHash> {
2372 let response = self.client.get_first_block_epoch(&ei.into()).await?;
2373 Ok(response.into_inner().try_into()?)
2374 }
2375
2376 pub async fn get_consensus_detailed_status(
2380 &mut self,
2381 genesis_index: Option<GenesisIndex>,
2382 ) -> endpoints::RPCResult<ConsensusDetailedStatus> {
2383 let query = generated::ConsensusDetailedStatusQuery {
2384 genesis_index: genesis_index.map(Into::into),
2385 };
2386 let response = self.client.get_consensus_detailed_status(query).await?;
2387 Ok(response.into_inner().try_into()?)
2388 }
2389
2390 pub async fn get_next_update_sequence_numbers(
2394 &mut self,
2395 block_id: impl IntoBlockIdentifier,
2396 ) -> endpoints::QueryResult<QueryResponse<types::queries::NextUpdateSequenceNumbers>> {
2397 let response = self
2398 .client
2399 .get_next_update_sequence_numbers(&block_id.into_block_identifier())
2400 .await?;
2401 let block_hash = extract_metadata(&response)?;
2402 let response = types::queries::NextUpdateSequenceNumbers::try_from(response.into_inner())?;
2403 Ok(QueryResponse {
2404 block_hash,
2405 response,
2406 })
2407 }
2408
2409 pub async fn get_scheduled_release_accounts(
2414 &mut self,
2415 block_id: impl IntoBlockIdentifier,
2416 ) -> endpoints::QueryResult<
2417 QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2418 > {
2419 let response = self
2420 .client
2421 .get_scheduled_release_accounts(&block_id.into_block_identifier())
2422 .await?;
2423 let block_hash = extract_metadata(&response)?;
2424 let stream = response.into_inner().map(|result| match result {
2425 Ok(pending) => pending.try_into(),
2426 Err(err) => Err(err),
2427 });
2428 Ok(QueryResponse {
2429 block_hash,
2430 response: stream,
2431 })
2432 }
2433
2434 pub async fn get_cooldown_accounts(
2440 &mut self,
2441 block_id: impl IntoBlockIdentifier,
2442 ) -> endpoints::QueryResult<
2443 QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2444 > {
2445 let response = self
2446 .client
2447 .get_cooldown_accounts(&block_id.into_block_identifier())
2448 .await?;
2449 let block_hash = extract_metadata(&response)?;
2450 let stream = response.into_inner().map(|result| match result {
2451 Ok(pending) => pending.try_into(),
2452 Err(err) => Err(err),
2453 });
2454 Ok(QueryResponse {
2455 block_hash,
2456 response: stream,
2457 })
2458 }
2459
2460 pub async fn get_pre_cooldown_accounts(
2464 &mut self,
2465 block_id: impl IntoBlockIdentifier,
2466 ) -> endpoints::QueryResult<
2467 QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2468 > {
2469 let response = self
2470 .client
2471 .get_pre_cooldown_accounts(&block_id.into_block_identifier())
2472 .await?;
2473 let block_hash = extract_metadata(&response)?;
2474 let stream = response.into_inner().map(|result| match result {
2475 Ok(account) => Ok(account.into()),
2476 Err(err) => Err(err),
2477 });
2478 Ok(QueryResponse {
2479 block_hash,
2480 response: stream,
2481 })
2482 }
2483
2484 pub async fn get_pre_pre_cooldown_accounts(
2488 &mut self,
2489 block_id: impl IntoBlockIdentifier,
2490 ) -> endpoints::QueryResult<
2491 QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2492 > {
2493 let response = self
2494 .client
2495 .get_pre_pre_cooldown_accounts(&block_id.into_block_identifier())
2496 .await?;
2497 let block_hash = extract_metadata(&response)?;
2498 let stream = response.into_inner().map(|result| match result {
2499 Ok(account) => Ok(account.into()),
2500 Err(err) => Err(err),
2501 });
2502 Ok(QueryResponse {
2503 block_hash,
2504 response: stream,
2505 })
2506 }
2507
2508 pub async fn get_block_chain_parameters(
2511 &mut self,
2512 block_id: impl IntoBlockIdentifier,
2513 ) -> endpoints::QueryResult<QueryResponse<ChainParameters>> {
2514 let response = self
2515 .client
2516 .get_block_chain_parameters(&block_id.into_block_identifier())
2517 .await?;
2518 let block_hash = extract_metadata(&response)?;
2519 let response = ChainParameters::try_from(response.into_inner())?;
2520 Ok(QueryResponse {
2521 block_hash,
2522 response,
2523 })
2524 }
2525
2526 pub async fn get_block_certificates(
2536 &mut self,
2537 bi: impl IntoBlockIdentifier,
2538 ) -> endpoints::QueryResult<QueryResponse<block_certificates::BlockCertificates>> {
2539 let response = self
2540 .client
2541 .get_block_certificates(&bi.into_block_identifier())
2542 .await?;
2543 let block_hash = extract_metadata(&response)?;
2544 let response = block_certificates::BlockCertificates::try_from(response.into_inner())?;
2545 Ok(QueryResponse {
2546 block_hash,
2547 response,
2548 })
2549 }
2550
2551 pub async fn get_block_finalization_summary(
2558 &mut self,
2559 block_id: impl IntoBlockIdentifier,
2560 ) -> endpoints::QueryResult<QueryResponse<Option<types::FinalizationSummary>>> {
2561 let response = self
2562 .client
2563 .get_block_finalization_summary(&block_id.into_block_identifier())
2564 .await?;
2565 let block_hash = extract_metadata(&response)?;
2566 let response = response.into_inner().try_into()?;
2567 Ok(QueryResponse {
2568 block_hash,
2569 response,
2570 })
2571 }
2572
2573 pub async fn get_finalized_blocks_from(
2578 &mut self,
2579 start_height: AbsoluteBlockHeight,
2580 ) -> endpoints::QueryResult<FinalizedBlocksStream> {
2581 let mut fin_height = self.get_consensus_info().await?.last_finalized_block_height;
2582 let (sender, receiver) = tokio::sync::mpsc::channel(100);
2583 let mut client = self.clone();
2584 let handle = tokio::spawn(async move {
2585 let mut height = start_height;
2586 loop {
2587 if height > fin_height {
2588 fin_height = client
2589 .get_consensus_info()
2590 .await?
2591 .last_finalized_block_height;
2592 if height > fin_height {
2593 break;
2594 }
2595 } else {
2596 let mut bi = client.get_blocks_at_height(&height.into()).await?;
2597 let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2598 let info = FinalizedBlockInfo { block_hash, height };
2599 if sender.send(info).await.is_err() {
2600 return Ok(());
2601 }
2602 height = height.next();
2603 }
2604 }
2605 let mut stream = client.get_finalized_blocks().await?;
2606 while let Some(fbi) = stream.next().await.transpose()? {
2607 while height < fbi.height {
2609 let mut bi = client.get_blocks_at_height(&height.into()).await?;
2610 let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2611 let info = FinalizedBlockInfo { block_hash, height };
2612 if sender.send(info).await.is_err() {
2613 return Ok(());
2614 }
2615 height = height.next();
2616 }
2617 if sender.send(fbi).await.is_err() {
2618 return Ok(());
2619 }
2620 height = height.next();
2621 }
2622 Ok(())
2623 });
2624 Ok(FinalizedBlocksStream { handle, receiver })
2625 }
2626
2627 pub async fn find_account_creation(
2644 &mut self,
2645 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2646 addr: AccountAddress,
2647 ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, AccountInfo)> {
2648 self.find_at_lowest_height(range, |mut client, height| async move {
2649 match client.get_account_info(&addr.into(), &height).await {
2650 Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2651 Err(e) if e.is_not_found() => Ok(None),
2652 Err(e) => Err(e),
2653 }
2654 })
2655 .await
2656 }
2657
2658 pub async fn find_instance_creation(
2675 &mut self,
2676 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2677 addr: ContractAddress,
2678 ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, InstanceInfo)> {
2679 self.find_at_lowest_height(range, |mut client, height| async move {
2680 match client.get_instance_info(addr, &height).await {
2681 Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2682 Err(e) if e.is_not_found() => Ok(None),
2683 Err(e) => Err(e),
2684 }
2685 })
2686 .await
2687 }
2688
2689 pub async fn find_first_finalized_block_no_earlier_than(
2697 &mut self,
2698 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2699 time: chrono::DateTime<chrono::Utc>,
2700 ) -> QueryResult<types::queries::BlockInfo> {
2701 self.find_at_lowest_height(range, move |mut client, height| async move {
2702 let info = client.get_block_info(&height).await?.response;
2703 if info.block_slot_time >= time {
2704 Ok(Some(info))
2705 } else {
2706 Ok(None)
2707 }
2708 })
2709 .await
2710 }
2711
2712 pub async fn find_at_lowest_height<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2731 &mut self,
2732 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2733 test: impl Fn(Self, AbsoluteBlockHeight) -> F,
2734 ) -> QueryResult<A> {
2735 let mut start = match range.start_bound() {
2736 std::ops::Bound::Included(s) => u64::from(*s),
2737 std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2738 std::ops::Bound::Unbounded => 0,
2739 };
2740 let mut end = {
2741 let ci = self.get_consensus_info().await?;
2742 let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2743 match range.end_bound() {
2744 std::ops::Bound::Included(e) => bound(u64::from(*e)),
2745 std::ops::Bound::Excluded(e) => {
2746 bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2747 }
2748 std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2749 }
2750 };
2751 if end < start {
2752 return Err(QueryError::NotFound);
2753 }
2754 let mut last_found = None;
2755 while start < end {
2756 let mid = start + (end - start) / 2;
2757 let ok = test(self.clone(), mid.into()).await?;
2758 if ok.is_some() {
2759 end = mid;
2760 last_found = ok;
2761 } else {
2762 start = mid + 1;
2763 }
2764 }
2765 last_found.ok_or(QueryError::NotFound)
2766 }
2767
2768 #[deprecated(note = "Use [`find_at_lowest_height`](./struct.Client.html#method.\
2769 find_at_lowest_height) instead since it avoids an extra call.")]
2770 pub async fn find_earliest_finalized<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2771 &mut self,
2772 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2773 test: impl Fn(Self, AbsoluteBlockHeight, BlockHash) -> F,
2774 ) -> QueryResult<A> {
2775 let mut start = match range.start_bound() {
2776 std::ops::Bound::Included(s) => u64::from(*s),
2777 std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2778 std::ops::Bound::Unbounded => 0,
2779 };
2780 let mut end = {
2781 let ci = self.get_consensus_info().await?;
2782 let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2783 match range.end_bound() {
2784 std::ops::Bound::Included(e) => bound(u64::from(*e)),
2785 std::ops::Bound::Excluded(e) => {
2786 bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2787 }
2788 std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2789 }
2790 };
2791 if end < start {
2792 return Err(QueryError::NotFound);
2793 }
2794 let mut last_found = None;
2795 while start < end {
2796 let mid = start + (end - start) / 2;
2797 let bh = self
2798 .get_blocks_at_height(&AbsoluteBlockHeight::from(mid).into())
2799 .await?[0]; let ok = test(self.clone(), mid.into(), bh).await?;
2801 if ok.is_some() {
2802 end = mid;
2803 last_found = ok;
2804 } else {
2805 start = mid + 1;
2806 }
2807 }
2808 last_found.ok_or(QueryError::NotFound)
2809 }
2810
2811 pub async fn get_bakers_reward_period(
2816 &mut self,
2817 bi: impl IntoBlockIdentifier,
2818 ) -> endpoints::QueryResult<
2819 QueryResponse<impl Stream<Item = Result<types::BakerRewardPeriodInfo, tonic::Status>>>,
2820 > {
2821 let response = self
2822 .client
2823 .get_bakers_reward_period(&bi.into_block_identifier())
2824 .await?;
2825 let block_hash = extract_metadata(&response)?;
2826 let stream = response.into_inner().map(|result| match result {
2827 Ok(baker) => baker.try_into(),
2828 Err(err) => Err(err),
2829 });
2830 Ok(QueryResponse {
2831 block_hash,
2832 response: stream,
2833 })
2834 }
2835
2836 pub async fn get_token_list(
2842 &mut self,
2843 bi: impl IntoBlockIdentifier,
2844 ) -> endpoints::QueryResult<
2845 QueryResponse<impl Stream<Item = Result<protocol_level_tokens::TokenId, tonic::Status>>>,
2846 > {
2847 let response = self
2848 .client
2849 .get_token_list(&bi.into_block_identifier())
2850 .await?;
2851 let block_hash = extract_metadata(&response)?;
2852 let stream = response.into_inner().map(|result| match result {
2853 Ok(token_id) => protocol_level_tokens::TokenId::try_from(token_id),
2854 Err(err) => Err(err),
2855 });
2856 Ok(QueryResponse {
2857 block_hash,
2858 response: stream,
2859 })
2860 }
2861
2862 pub async fn get_token_info(
2868 &mut self,
2869 token_id: protocol_level_tokens::TokenId,
2870 bi: impl IntoBlockIdentifier,
2871 ) -> endpoints::QueryResult<QueryResponse<protocol_level_tokens::TokenInfo>> {
2872 let request = generated::TokenInfoRequest {
2873 block_hash: Some((&bi.into_block_identifier()).into()),
2874 token_id: Some(token_id.into()),
2875 };
2876 let response = self.client.get_token_info(request).await?;
2877 let block_hash = extract_metadata(&response)?;
2878 let response = protocol_level_tokens::TokenInfo::try_from(response.into_inner())?;
2879 Ok(QueryResponse {
2880 block_hash,
2881 response,
2882 })
2883 }
2884}
2885
2886pub struct FinalizedBlocksStream {
2890 handle: tokio::task::JoinHandle<endpoints::QueryResult<()>>,
2891 receiver: tokio::sync::mpsc::Receiver<FinalizedBlockInfo>,
2892}
2893
2894impl Drop for FinalizedBlocksStream {
2897 fn drop(&mut self) {
2898 self.handle.abort();
2899 }
2900}
2901
2902impl FinalizedBlocksStream {
2903 pub async fn next(&mut self) -> Option<FinalizedBlockInfo> {
2908 self.receiver.recv().await
2909 }
2910
2911 pub async fn next_timeout(
2914 &mut self,
2915 duration: std::time::Duration,
2916 ) -> Result<Option<FinalizedBlockInfo>, tokio::time::error::Elapsed> {
2917 tokio::time::timeout(duration, async move { self.next().await }).await
2918 }
2919
2920 pub async fn next_chunk(
2930 &mut self,
2931 n: usize,
2932 ) -> Result<Vec<FinalizedBlockInfo>, Vec<FinalizedBlockInfo>> {
2933 let mut out = Vec::with_capacity(n);
2934 let first = self.receiver.recv().await;
2935 match first {
2936 Some(v) => out.push(v),
2937 None => {
2938 return Err(out);
2939 }
2940 }
2941 for _ in 1..n {
2942 match self.receiver.try_recv() {
2943 Ok(v) => {
2944 out.push(v);
2945 }
2946 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
2947 break;
2948 }
2949 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return Err(out),
2950 }
2951 }
2952 Ok(out)
2953 }
2954
2955 pub async fn next_chunk_timeout(
2964 &mut self,
2965 n: usize,
2966 duration: std::time::Duration,
2967 ) -> Result<(bool, Vec<FinalizedBlockInfo>), tokio::time::error::Elapsed> {
2968 let mut out = Vec::with_capacity(n);
2969 let first = self.next_timeout(duration).await?;
2970 match first {
2971 Some(v) => out.push(v),
2972 None => return Ok((true, out)),
2973 }
2974 for _ in 1..n {
2975 match self.receiver.try_recv() {
2976 Ok(v) => {
2977 out.push(v);
2978 }
2979 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
2980 break;
2981 }
2982 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
2983 return Ok((true, out))
2984 }
2985 }
2986 }
2987 Ok((false, out))
2988 }
2989}
2990
2991fn extract_metadata<T>(response: &tonic::Response<T>) -> endpoints::RPCResult<BlockHash> {
2992 match response.metadata().get("blockhash") {
2993 Some(bytes) => {
2994 let bytes = bytes.as_bytes();
2995 if bytes.len() == 64 {
2996 let mut hash = [0u8; 32];
2997 if hex::decode_to_slice(bytes, &mut hash).is_err() {
2998 tonic::Status::unknown("Response does correctly encode the block hash.");
2999 }
3000 Ok(hash.into())
3001 } else {
3002 Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3003 "Response does not include the expected metadata.",
3004 )))
3005 }
3006 }
3007 None => Err(endpoints::RPCError::CallError(tonic::Status::unknown(
3008 "Response does not include the expected metadata.",
3009 ))),
3010 }
3011}
3012
3013pub(crate) trait Require<E> {
3021 type A;
3022 fn require(self) -> Result<Self::A, E>;
3023}
3024
3025impl<A> Require<tonic::Status> for Option<A> {
3026 type A = A;
3027
3028 fn require(self) -> Result<Self::A, tonic::Status> {
3029 match self {
3030 Some(v) => Ok(v),
3031 None => Err(tonic::Status::invalid_argument("missing field in response")),
3032 }
3033 }
3034}
3035
3036#[cfg(test)]
3037mod tests {
3038 use super::*;
3039 #[test]
3040 fn block_ident_from_str() -> anyhow::Result<()> {
3042 let b1 = "best".parse::<BlockIdentifier>()?;
3043 assert_eq!(b1, BlockIdentifier::Best);
3044
3045 let b2 = "lastFinal".parse::<BlockIdentifier>()?;
3046 assert_eq!(b2, BlockIdentifier::LastFinal);
3047
3048 let b3 = "lastfinal".parse::<BlockIdentifier>()?;
3049 assert_eq!(b3, BlockIdentifier::LastFinal);
3050
3051 let b4 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
3052 .parse::<BlockIdentifier>()?;
3053 assert_eq!(
3054 b4,
3055 BlockIdentifier::Given(
3056 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()?
3057 )
3058 );
3059
3060 let b5 = "@33".parse::<BlockIdentifier>()?;
3061 assert_eq!(b5, BlockIdentifier::AbsoluteHeight(33.into()));
3062
3063 let b6 = "@33/3".parse::<BlockIdentifier>()?;
3064 assert_eq!(
3065 b6,
3066 BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3067 genesis_index: 3.into(),
3068 height: 33.into(),
3069 restrict: false,
3070 })
3071 );
3072
3073 let b7 = "@33/3!".parse::<BlockIdentifier>()?;
3074 assert_eq!(
3075 b7,
3076 BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3077 genesis_index: 3.into(),
3078 height: 33.into(),
3079 restrict: true,
3080 })
3081 );
3082
3083 Ok(())
3084 }
3085}