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, 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<TransactionSignature> for generated::AccountTransactionSignature {
759 fn from(v: TransactionSignature) -> Self {
760 (&v).into()
761 }
762}
763
764impl From<&TransactionSignature> for generated::AccountTransactionSignature {
765 fn from(v: &TransactionSignature) -> Self {
766 Self {
767 signatures: {
768 let mut cred_map: HashMap<u32, generated::AccountSignatureMap> = HashMap::new();
769 for (cred_idx, sig_map) in v.signatures.iter() {
770 let mut acc_sig_map: HashMap<u32, generated::Signature> = HashMap::new();
771 for (key_idx, sig) in sig_map.iter() {
772 acc_sig_map.insert(
773 key_idx.0.into(),
774 generated::Signature {
775 value: sig.sig.to_owned(),
776 },
777 );
778 }
779 cred_map.insert(
780 cred_idx.index.into(),
781 generated::AccountSignatureMap {
782 signatures: acc_sig_map,
783 },
784 );
785 }
786 cred_map
787 },
788 }
789 }
790}
791
792impl IntoRequest<generated::PreAccountTransaction>
793 for (&transactions::TransactionHeader, &transactions::Payload)
794{
795 fn into_request(self) -> tonic::Request<generated::PreAccountTransaction> {
796 let request = generated::PreAccountTransaction {
797 header: Some(self.0.into()),
798 payload: Some(generated::AccountTransactionPayload {
799 payload: Some(generated::account_transaction_payload::Payload::RawPayload(
800 self.1.encode().into(),
801 )),
802 }),
803 };
804 tonic::Request::new(request)
805 }
806}
807
808impl<P: PayloadLike> IntoRequest<generated::SendBlockItemRequest> for &transactions::BlockItem<P> {
809 fn into_request(self) -> tonic::Request<generated::SendBlockItemRequest> {
810 let request = match self {
811 transactions::BlockItem::AccountTransaction(v) => {
812 generated::SendBlockItemRequest {
813 block_item: Some(
814 generated::send_block_item_request::BlockItem::AccountTransaction(
815 generated::AccountTransaction {
816 signature: Some((&v.signature).into()),
817 header: Some((&v.header).into()),
818 payload: {
819 let atp = generated::AccountTransactionPayload{
820 payload: Some(generated::account_transaction_payload::Payload::RawPayload(v.payload.encode().into())),
821 };
822 Some(atp)
823 },
824 },
825 ),
826 ),
827 }
828 }
829 transactions::BlockItem::CredentialDeployment(v) => generated::SendBlockItemRequest {
830 block_item: Some(
831 generated::send_block_item_request::BlockItem::CredentialDeployment(
832 v.as_ref().into(),
833 ),
834 ),
835 },
836 transactions::BlockItem::UpdateInstruction(v) => generated::SendBlockItemRequest {
837 block_item: Some(
838 generated::send_block_item_request::BlockItem::UpdateInstruction(v.into()),
839 ),
840 },
841 };
842 tonic::Request::new(request)
843 }
844}
845
846impl IntoRequest<generated::InvokeInstanceRequest> for (&BlockIdentifier, &ContractContext) {
847 fn into_request(self) -> tonic::Request<generated::InvokeInstanceRequest> {
848 let (block, context) = self;
849 tonic::Request::new(generated::InvokeInstanceRequest {
850 block_hash: Some(block.into()),
851 invoker: context.invoker.as_ref().map(|a| a.into()),
852 instance: Some((&context.contract).into()),
853 amount: Some(context.amount.into()),
854 entrypoint: Some(context.method.as_receive_name().into()),
855 parameter: Some(context.parameter.as_ref().into()),
856 energy: context.energy.map(From::from),
857 })
858 }
859}
860
861impl IntoRequest<generated::PoolInfoRequest> for (&BlockIdentifier, types::BakerId) {
862 fn into_request(self) -> tonic::Request<generated::PoolInfoRequest> {
863 let req = generated::PoolInfoRequest {
864 block_hash: Some(self.0.into()),
865 baker: Some(self.1.into()),
866 };
867 tonic::Request::new(req)
868 }
869}
870
871impl IntoRequest<generated::BakerId> for types::BakerId {
872 fn into_request(self) -> tonic::Request<generated::BakerId> {
873 tonic::Request::new(generated::BakerId {
874 value: self.id.index,
875 })
876 }
877}
878
879impl IntoRequest<generated::BlocksAtHeightRequest> for &endpoints::BlocksAtHeightInput {
880 fn into_request(self) -> tonic::Request<generated::BlocksAtHeightRequest> {
881 tonic::Request::new(self.into())
882 }
883}
884
885impl IntoRequest<generated::GetPoolDelegatorsRequest> for (&BlockIdentifier, types::BakerId) {
886 fn into_request(self) -> tonic::Request<generated::GetPoolDelegatorsRequest> {
887 let req = generated::GetPoolDelegatorsRequest {
888 block_hash: Some(self.0.into()),
889 baker: Some(self.1.into()),
890 };
891 tonic::Request::new(req)
892 }
893}
894
895impl TryFrom<crate::v2::generated::BannedPeer> for types::network::BannedPeer {
896 type Error = anyhow::Error;
897
898 fn try_from(value: crate::v2::generated::BannedPeer) -> Result<Self, Self::Error> {
899 Ok(types::network::BannedPeer(
900 <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip_address.require()?.value)?,
901 ))
902 }
903}
904
905impl TryFrom<generated::IpSocketAddress> for std::net::SocketAddr {
906 type Error = anyhow::Error;
907
908 fn try_from(value: generated::IpSocketAddress) -> Result<Self, Self::Error> {
909 Ok(std::net::SocketAddr::new(
910 <std::net::IpAddr as std::str::FromStr>::from_str(&value.ip.require()?.value)?,
911 value.port.require()?.value as u16,
912 ))
913 }
914}
915
916impl IntoRequest<crate::v2::generated::BannedPeer> for &types::network::BannedPeer {
917 fn into_request(self) -> tonic::Request<crate::v2::generated::BannedPeer> {
918 tonic::Request::new(crate::v2::generated::BannedPeer {
919 ip_address: Some(crate::v2::generated::IpAddress {
920 value: self.0.to_string(),
921 }),
922 })
923 }
924}
925
926impl From<generated::PeerId> for types::network::PeerId {
927 fn from(value: generated::PeerId) -> Self {
928 types::network::PeerId(value.value)
929 }
930}
931
932impl TryFrom<generated::PeersInfo> for types::network::PeersInfo {
933 type Error = anyhow::Error;
934
935 fn try_from(peers_info: generated::PeersInfo) -> Result<Self, Self::Error> {
936 let peers = peers_info
942 .peers
943 .into_iter()
944 .map(|peer| {
945 let peer_consensus_info =
947 Upward::from(peer.consensus_info).and_then(|info| match info {
948 generated::peers_info::peer::ConsensusInfo::Bootstrapper(_) => {
949 Upward::Known(types::network::PeerConsensusInfo::Bootstrapper)
950 }
951 generated::peers_info::peer::ConsensusInfo::NodeCatchupStatus(status) => {
952 let Upward::Known(status) = Upward::from(
953 generated::peers_info::peer::CatchupStatus::try_from(status).ok(),
954 ) else {
955 return Upward::Known(types::network::PeerConsensusInfo::Node(
956 Upward::Unknown(()),
957 ));
958 };
959 let status = match status {
960 generated::peers_info::peer::CatchupStatus::Uptodate => {
961 types::network::PeerCatchupStatus::UpToDate
962 }
963 generated::peers_info::peer::CatchupStatus::Pending => {
964 types::network::PeerCatchupStatus::Pending
965 }
966 generated::peers_info::peer::CatchupStatus::Catchingup => {
967 types::network::PeerCatchupStatus::CatchingUp
968 }
969 };
970
971 Upward::Known(types::network::PeerConsensusInfo::Node(Upward::Known(
972 status,
973 )))
974 }
975 });
976 let stats = peer.network_stats.require()?;
978 let network_stats = types::network::NetworkStats {
979 packets_sent: stats.packets_sent,
980 packets_received: stats.packets_received,
981 latency: stats.latency,
982 };
983 Ok(types::network::Peer {
984 peer_id: peer.peer_id.require()?.into(),
985 consensus_info: peer_consensus_info,
986 network_stats,
987 addr: peer.socket_address.require()?.try_into()?,
988 })
989 })
990 .collect::<anyhow::Result<Vec<types::network::Peer>>>()?;
991 Ok(types::network::PeersInfo { peers })
992 }
993}
994
995impl TryFrom<generated::node_info::NetworkInfo> for types::NetworkInfo {
996 type Error = anyhow::Error;
997
998 fn try_from(network_info: generated::node_info::NetworkInfo) -> Result<Self, Self::Error> {
999 Ok(types::NetworkInfo {
1000 node_id: network_info.node_id.require()?.value,
1001 peer_total_sent: network_info.peer_total_sent,
1002 peer_total_received: network_info.peer_total_received,
1003 avg_bps_in: network_info.avg_bps_in,
1004 avg_bps_out: network_info.avg_bps_out,
1005 })
1006 }
1007}
1008
1009impl IntoRequest<crate::v2::generated::PeerToBan> for types::network::PeerToBan {
1010 fn into_request(self) -> tonic::Request<crate::v2::generated::PeerToBan> {
1011 tonic::Request::new(match self {
1012 types::network::PeerToBan::IpAddr(ip_addr) => crate::v2::generated::PeerToBan {
1013 ip_address: Some(crate::v2::generated::IpAddress {
1014 value: ip_addr.to_string(),
1015 }),
1016 },
1017 })
1018 }
1019}
1020
1021impl TryFrom<generated::node_info::Details> for types::NodeDetails {
1022 type Error = anyhow::Error;
1023
1024 fn try_from(details: generated::node_info::Details) -> Result<Self, Self::Error> {
1025 match details {
1026 generated::node_info::Details::Bootstrapper(_) => Ok(types::NodeDetails::Bootstrapper),
1027 generated::node_info::Details::Node(status) => {
1028 let Upward::Known(consensus_status) = Upward::from(status.consensus_status) else {
1029 return Ok(types::NodeDetails::Node(Upward::Unknown(())));
1030 };
1031 let consensus_status = match consensus_status {
1032 generated::node_info::node::ConsensusStatus::NotRunning(_) => {
1033 types::NodeConsensusStatus::ConsensusNotRunning
1034 }
1035 generated::node_info::node::ConsensusStatus::Passive(_) => {
1036 types::NodeConsensusStatus::ConsensusPassive
1037 }
1038 generated::node_info::node::ConsensusStatus::Active(baker) => {
1039 let baker_id = baker.baker_id.require()?.into();
1040 let Upward::Known(status) = Upward::from(baker.status) else {
1041 return Ok(types::NodeDetails::Node(Upward::Unknown(())));
1042 };
1043
1044 match status {
1045 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(0) => types::NodeConsensusStatus::NotInCommittee(baker_id),
1046 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(1) => types::NodeConsensusStatus::AddedButNotActiveInCommittee(baker_id),
1047 generated::node_info::baker_consensus_info::Status::PassiveCommitteeInfo(2) => types::NodeConsensusStatus::AddedButWrongKeys(baker_id),
1048 generated::node_info::baker_consensus_info::Status::ActiveBakerCommitteeInfo(_) => types::NodeConsensusStatus::Baker(baker_id),
1049 generated::node_info::baker_consensus_info::Status::ActiveFinalizerCommitteeInfo(_) => types::NodeConsensusStatus::Finalizer(baker_id),
1050 _ => anyhow::bail!("Malformed baker status")
1051 }
1052 }
1053 };
1054 Ok(types::NodeDetails::Node(Upward::Known(consensus_status)))
1055 }
1056 }
1057 }
1058}
1059
1060impl TryFrom<generated::NodeInfo> for types::NodeInfo {
1061 type Error = anyhow::Error;
1062
1063 fn try_from(node_info: generated::NodeInfo) -> Result<Self, Self::Error> {
1064 let version = semver::Version::parse(&node_info.peer_version)?;
1065 let local_time = chrono::DateTime::<chrono::Utc>::from(std::time::UNIX_EPOCH)
1066 + chrono::TimeDelta::try_milliseconds(node_info.local_time.require()?.value as i64)
1067 .context("Node local time out of bounds!")?;
1068 let uptime = chrono::Duration::try_from(types::DurationSeconds::from(
1069 node_info.peer_uptime.require()?.value,
1070 ))?;
1071 let network_info = node_info.network_info.require()?.try_into()?;
1072 let details = Upward::from(node_info.details)
1073 .map(types::NodeDetails::try_from)
1074 .transpose()?;
1075 Ok(types::NodeInfo {
1076 version,
1077 local_time,
1078 uptime,
1079 network_info,
1080 details,
1081 })
1082 }
1083}
1084
1085pub trait IntoBlockIdentifier {
1092 fn into_block_identifier(self) -> BlockIdentifier;
1093}
1094
1095impl IntoBlockIdentifier for BlockIdentifier {
1096 fn into_block_identifier(self) -> BlockIdentifier {
1097 self
1098 }
1099}
1100
1101impl<X: IntoBlockIdentifier + Copy> IntoBlockIdentifier for &X {
1102 fn into_block_identifier(self) -> BlockIdentifier {
1103 (*self).into_block_identifier()
1104 }
1105}
1106
1107impl IntoBlockIdentifier for BlockHash {
1108 fn into_block_identifier(self) -> BlockIdentifier {
1109 BlockIdentifier::Given(self)
1110 }
1111}
1112
1113impl IntoBlockIdentifier for AbsoluteBlockHeight {
1114 fn into_block_identifier(self) -> BlockIdentifier {
1115 BlockIdentifier::AbsoluteHeight(self)
1116 }
1117}
1118
1119impl IntoBlockIdentifier for RelativeBlockHeight {
1120 fn into_block_identifier(self) -> BlockIdentifier {
1121 BlockIdentifier::RelativeHeight(self)
1122 }
1123}
1124
1125impl Client {
1126 pub async fn new<E>(endpoint: E) -> Result<Self, tonic::transport::Error>
1143 where
1144 E: TryInto<tonic::transport::Endpoint>,
1145 E::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
1146 {
1147 let client = generated::queries_client::QueriesClient::connect(endpoint).await?;
1148 Ok(Self { client })
1149 }
1150
1151 pub async fn get_account_info(
1155 &mut self,
1156 acc: &AccountIdentifier,
1157 bi: impl IntoBlockIdentifier,
1158 ) -> endpoints::QueryResult<QueryResponse<AccountInfo>> {
1159 let response = self
1160 .client
1161 .get_account_info((acc, &bi.into_block_identifier()))
1162 .await?;
1163 let block_hash = extract_metadata(&response)?;
1164 let response = AccountInfo::try_from(response.into_inner())?;
1165 Ok(QueryResponse {
1166 block_hash,
1167 response,
1168 })
1169 }
1170
1171 pub async fn get_next_account_sequence_number(
1174 &mut self,
1175 account_address: &AccountAddress,
1176 ) -> endpoints::QueryResult<types::queries::AccountNonceResponse> {
1177 let response = self
1178 .client
1179 .get_next_account_sequence_number(account_address)
1180 .await?;
1181 let response = types::queries::AccountNonceResponse::try_from(response.into_inner())?;
1182 Ok(response)
1183 }
1184
1185 pub async fn get_consensus_info(
1188 &mut self,
1189 ) -> endpoints::QueryResult<types::queries::ConsensusInfo> {
1190 let response = self
1191 .client
1192 .get_consensus_info(generated::Empty::default())
1193 .await?;
1194 let response = types::queries::ConsensusInfo::try_from(response.into_inner())?;
1195 Ok(response)
1196 }
1197
1198 pub async fn get_cryptographic_parameters(
1201 &mut self,
1202 bi: impl IntoBlockIdentifier,
1203 ) -> endpoints::QueryResult<QueryResponse<types::CryptographicParameters>> {
1204 let response = self
1205 .client
1206 .get_cryptographic_parameters(&bi.into_block_identifier())
1207 .await?;
1208 let block_hash = extract_metadata(&response)?;
1209 let response = types::CryptographicParameters::try_from(response.into_inner())?;
1210 Ok(QueryResponse {
1211 block_hash,
1212 response,
1213 })
1214 }
1215
1216 pub async fn get_account_list(
1221 &mut self,
1222 bi: impl IntoBlockIdentifier,
1223 ) -> endpoints::QueryResult<
1224 QueryResponse<impl Stream<Item = Result<AccountAddress, tonic::Status>>>,
1225 > {
1226 let response = self
1227 .client
1228 .get_account_list(&bi.into_block_identifier())
1229 .await?;
1230 let block_hash = extract_metadata(&response)?;
1231 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1232 Ok(QueryResponse {
1233 block_hash,
1234 response: stream,
1235 })
1236 }
1237
1238 pub async fn get_module_list(
1243 &mut self,
1244 bi: impl IntoBlockIdentifier,
1245 ) -> endpoints::QueryResult<
1246 QueryResponse<impl Stream<Item = Result<ModuleReference, tonic::Status>>>,
1247 > {
1248 let response = self
1249 .client
1250 .get_module_list(&bi.into_block_identifier())
1251 .await?;
1252 let block_hash = extract_metadata(&response)?;
1253 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1254 Ok(QueryResponse {
1255 block_hash,
1256 response: stream,
1257 })
1258 }
1259
1260 pub async fn get_module_source(
1264 &mut self,
1265 module_ref: &ModuleReference,
1266 bi: impl IntoBlockIdentifier,
1267 ) -> endpoints::QueryResult<QueryResponse<types::smart_contracts::WasmModule>> {
1268 let response = self
1269 .client
1270 .get_module_source((module_ref, &bi.into_block_identifier()))
1271 .await?;
1272 let block_hash = extract_metadata(&response)?;
1273 let response = types::smart_contracts::WasmModule::try_from(response.into_inner())?;
1274 Ok(QueryResponse {
1275 block_hash,
1276 response,
1277 })
1278 }
1279
1280 pub async fn get_instance_list(
1285 &mut self,
1286 bi: impl IntoBlockIdentifier,
1287 ) -> endpoints::QueryResult<
1288 QueryResponse<impl Stream<Item = Result<ContractAddress, tonic::Status>>>,
1289 > {
1290 let response = self
1291 .client
1292 .get_instance_list(&bi.into_block_identifier())
1293 .await?;
1294 let block_hash = extract_metadata(&response)?;
1295 let stream = response.into_inner().map(|x| x.map(From::from));
1296 Ok(QueryResponse {
1297 block_hash,
1298 response: stream,
1299 })
1300 }
1301
1302 pub async fn get_instance_info(
1306 &mut self,
1307 address: ContractAddress,
1308 bi: impl IntoBlockIdentifier,
1309 ) -> endpoints::QueryResult<QueryResponse<InstanceInfo>> {
1310 let response = self
1311 .client
1312 .get_instance_info((address, &bi.into_block_identifier()))
1313 .await?;
1314 let block_hash = extract_metadata(&response)?;
1315 let response = InstanceInfo::try_from(response.into_inner())?;
1316 Ok(QueryResponse {
1317 block_hash,
1318 response,
1319 })
1320 }
1321
1322 pub async fn get_ancestors(
1326 &mut self,
1327 bi: impl IntoBlockIdentifier,
1328 limit: u64,
1329 ) -> endpoints::QueryResult<QueryResponse<impl Stream<Item = Result<BlockHash, tonic::Status>>>>
1330 {
1331 let response = self
1332 .client
1333 .get_ancestors((&bi.into_block_identifier(), limit))
1334 .await?;
1335 let block_hash = extract_metadata(&response)?;
1336 let stream = response.into_inner().map(|x| x.and_then(TryFrom::try_from));
1337 Ok(QueryResponse {
1338 block_hash,
1339 response: stream,
1340 })
1341 }
1342
1343 pub async fn get_finalized_blocks(
1351 &mut self,
1352 ) -> endpoints::QueryResult<impl Stream<Item = Result<FinalizedBlockInfo, tonic::Status>>> {
1353 let response = self
1354 .client
1355 .get_finalized_blocks(generated::Empty::default())
1356 .await?;
1357 let stream = response.into_inner().map(|x| match x {
1358 Ok(v) => {
1359 let block_hash = v.hash.require().and_then(TryFrom::try_from)?;
1360 let height = v.height.require()?.into();
1361 Ok(FinalizedBlockInfo { block_hash, height })
1362 }
1363 Err(x) => Err(x),
1364 });
1365 Ok(stream)
1366 }
1367
1368 pub async fn get_instance_state(
1374 &mut self,
1375 ca: ContractAddress,
1376 bi: impl IntoBlockIdentifier,
1377 ) -> endpoints::QueryResult<
1378 QueryResponse<impl Stream<Item = Result<(Vec<u8>, Vec<u8>), tonic::Status>>>,
1379 > {
1380 let response = self
1381 .client
1382 .get_instance_state((ca, &bi.into_block_identifier()))
1383 .await?;
1384 let block_hash = extract_metadata(&response)?;
1385 let stream = response.into_inner().map(|x| match x {
1386 Ok(v) => {
1387 let key = v.key;
1388 let value = v.value;
1389 Ok((key, value))
1390 }
1391 Err(x) => Err(x),
1392 });
1393 Ok(QueryResponse {
1394 block_hash,
1395 response: stream,
1396 })
1397 }
1398
1399 pub async fn instance_state_lookup(
1405 &mut self,
1406 ca: ContractAddress,
1407 key: impl Into<Vec<u8>>,
1408 bi: impl IntoBlockIdentifier,
1409 ) -> endpoints::QueryResult<QueryResponse<Vec<u8>>> {
1410 let response = self
1411 .client
1412 .instance_state_lookup((ca, &bi.into_block_identifier(), key))
1413 .await?;
1414 let block_hash = extract_metadata(&response)?;
1415 Ok(QueryResponse {
1416 block_hash,
1417 response: response.into_inner().value,
1418 })
1419 }
1420
1421 pub async fn get_block_item_status(
1425 &mut self,
1426 th: &TransactionHash,
1427 ) -> endpoints::QueryResult<TransactionStatus> {
1428 let response = self.client.get_block_item_status(th).await?;
1429 let response = TransactionStatus::try_from(response.into_inner())?;
1430 Ok(response)
1431 }
1432
1433 pub async fn send_block_item<P: PayloadLike>(
1440 &mut self,
1441 bi: &transactions::BlockItem<P>,
1442 ) -> endpoints::RPCResult<TransactionHash> {
1443 let response = self.client.send_block_item(bi).await?;
1444 let response = TransactionHash::try_from(response.into_inner())?;
1445 Ok(response)
1446 }
1447
1448 pub async fn send_account_transaction<P: PayloadLike>(
1451 &mut self,
1452 at: transactions::AccountTransaction<P>,
1453 ) -> endpoints::RPCResult<TransactionHash> {
1454 self.send_block_item(&at.into()).await
1455 }
1456
1457 pub async fn get_account_transaction_sign_hash(
1470 &mut self,
1471 header: &transactions::TransactionHeader,
1472 payload: &transactions::Payload,
1473 ) -> endpoints::RPCResult<TransactionSignHash> {
1474 let response = self
1475 .client
1476 .get_account_transaction_sign_hash((header, payload))
1477 .await?;
1478 let response = TransactionSignHash::try_from(response.into_inner())?;
1479 Ok(response)
1480 }
1481
1482 pub async fn wait_until_finalized(
1493 &mut self,
1494 hash: &types::hashes::TransactionHash,
1495 ) -> endpoints::QueryResult<(types::hashes::BlockHash, types::BlockItemSummary)> {
1496 let hash = *hash;
1497 let process_response = |response| {
1498 if let types::TransactionStatus::Finalized(blocks) = response {
1499 let mut iter = blocks.into_iter();
1500 if let Some(rv) = iter.next() {
1501 if iter.next().is_some() {
1502 Err(tonic::Status::internal(
1503 "Finalized transaction finalized into multiple blocks. This cannot \
1504 happen.",
1505 )
1506 .into())
1507 } else {
1508 Ok::<_, QueryError>(Some(rv))
1509 }
1510 } else {
1511 Err(tonic::Status::internal(
1512 "Finalized transaction finalized into no blocks. This cannot happen.",
1513 )
1514 .into())
1515 }
1516 } else {
1517 Ok(None)
1518 }
1519 };
1520
1521 match process_response(self.get_block_item_status(&hash).await?)? {
1522 Some(rv) => Ok(rv),
1523 None => {
1524 let mut blocks = self.get_finalized_blocks().await?;
1527 while blocks.next().await.transpose()?.is_some() {
1528 if let Some(rv) = process_response(self.get_block_item_status(&hash).await?)? {
1529 return Ok(rv);
1530 }
1531 }
1532 Err(QueryError::NotFound)
1533 }
1534 }
1535 }
1536
1537 pub async fn invoke_instance(
1541 &mut self,
1542 bi: impl IntoBlockIdentifier,
1543 context: &ContractContext,
1544 ) -> endpoints::QueryResult<QueryResponse<InvokeContractResult>> {
1545 let response = self
1546 .client
1547 .invoke_instance((&bi.into_block_identifier(), context))
1548 .await?;
1549 let block_hash = extract_metadata(&response)?;
1550 let response = InvokeContractResult::try_from(response.into_inner())?;
1551 Ok(QueryResponse {
1552 block_hash,
1553 response,
1554 })
1555 }
1556
1557 pub async fn begin_dry_run(&mut self) -> endpoints::QueryResult<dry_run::DryRun> {
1564 Ok(dry_run::DryRun::new(&mut self.client).await?)
1565 }
1566
1567 pub async fn dry_run(
1571 &mut self,
1572 bi: impl IntoBlockIdentifier,
1573 ) -> dry_run::DryRunResult<(dry_run::DryRun, dry_run::BlockStateLoaded)> {
1574 let mut runner = dry_run::DryRun::new(&mut self.client).await?;
1575 let load_result = runner.load_block_state(bi).await?;
1576 Ok(WithRemainingQuota {
1577 inner: (runner, load_result.inner),
1578 quota_remaining: load_result.quota_remaining,
1579 })
1580 }
1581
1582 pub async fn get_block_info(
1586 &mut self,
1587 bi: impl IntoBlockIdentifier,
1588 ) -> endpoints::QueryResult<QueryResponse<types::queries::BlockInfo>> {
1589 let response = self
1590 .client
1591 .get_block_info(&bi.into_block_identifier())
1592 .await?;
1593 let block_hash = extract_metadata(&response)?;
1594 let response = types::queries::BlockInfo::try_from(response.into_inner())?;
1595 Ok(QueryResponse {
1596 block_hash,
1597 response,
1598 })
1599 }
1600
1601 pub async fn is_payday_block(
1606 &mut self,
1607 bi: impl IntoBlockIdentifier,
1608 ) -> endpoints::QueryResult<QueryResponse<bool>> {
1609 let mut special_events = self.get_block_special_events(bi).await?;
1610 let block_hash = special_events.block_hash;
1611
1612 while let Some(event) = special_events.response.next().await.transpose()? {
1613 let Upward::Known(event) = event else {
1614 continue;
1616 };
1617 let has_payday_event = matches!(
1618 event,
1619 SpecialTransactionOutcome::PaydayPoolReward { .. }
1620 | SpecialTransactionOutcome::PaydayAccountReward { .. }
1621 | SpecialTransactionOutcome::PaydayFoundationReward { .. }
1622 );
1623
1624 if has_payday_event {
1625 return Ok(QueryResponse {
1626 block_hash,
1627 response: true,
1628 });
1629 };
1630 }
1631
1632 Ok(QueryResponse {
1633 block_hash,
1634 response: false,
1635 })
1636 }
1637
1638 pub async fn get_baker_list(
1641 &mut self,
1642 bi: impl IntoBlockIdentifier,
1643 ) -> endpoints::QueryResult<
1644 QueryResponse<impl Stream<Item = Result<types::BakerId, tonic::Status>>>,
1645 > {
1646 let response = self
1647 .client
1648 .get_baker_list(&bi.into_block_identifier())
1649 .await?;
1650 let block_hash = extract_metadata(&response)?;
1651 let stream = response.into_inner().map(|x| x.map(From::from));
1652 Ok(QueryResponse {
1653 block_hash,
1654 response: stream,
1655 })
1656 }
1657
1658 pub async fn get_pool_info(
1662 &mut self,
1663 block_id: impl IntoBlockIdentifier,
1664 baker_id: types::BakerId,
1665 ) -> endpoints::QueryResult<QueryResponse<types::BakerPoolStatus>> {
1666 let response = self
1667 .client
1668 .get_pool_info((&block_id.into_block_identifier(), baker_id))
1669 .await?;
1670 let block_hash = extract_metadata(&response)?;
1671 let response = types::BakerPoolStatus::try_from(response.into_inner())?;
1672 Ok(QueryResponse {
1673 block_hash,
1674 response,
1675 })
1676 }
1677
1678 pub async fn get_passive_delegation_info(
1683 &mut self,
1684 block_id: impl IntoBlockIdentifier,
1685 ) -> endpoints::QueryResult<QueryResponse<types::PassiveDelegationStatus>> {
1686 let response = self
1687 .client
1688 .get_passive_delegation_info(&block_id.into_block_identifier())
1689 .await?;
1690 let block_hash = extract_metadata(&response)?;
1691 let response = types::PassiveDelegationStatus::try_from(response.into_inner())?;
1692 Ok(QueryResponse {
1693 block_hash,
1694 response,
1695 })
1696 }
1697
1698 pub async fn get_blocks_at_height(
1700 &mut self,
1701 blocks_at_height_input: &endpoints::BlocksAtHeightInput,
1702 ) -> endpoints::QueryResult<Vec<BlockHash>> {
1703 let response = self
1704 .client
1705 .get_blocks_at_height(blocks_at_height_input)
1706 .await?;
1707 let blocks = response
1708 .into_inner()
1709 .blocks
1710 .into_iter()
1711 .map(TryFrom::try_from)
1712 .collect::<Result<_, tonic::Status>>()?;
1713 Ok(blocks)
1714 }
1715
1716 pub async fn get_tokenomics_info(
1719 &mut self,
1720 block_id: impl IntoBlockIdentifier,
1721 ) -> endpoints::QueryResult<QueryResponse<types::RewardsOverview>> {
1722 let response = self
1723 .client
1724 .get_tokenomics_info(&block_id.into_block_identifier())
1725 .await?;
1726 let block_hash = extract_metadata(&response)?;
1727 let response = types::RewardsOverview::try_from(response.into_inner())?;
1728 Ok(QueryResponse {
1729 block_hash,
1730 response,
1731 })
1732 }
1733
1734 pub async fn get_pool_delegators(
1748 &mut self,
1749 bi: impl IntoBlockIdentifier,
1750 baker_id: types::BakerId,
1751 ) -> endpoints::QueryResult<
1752 QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1753 > {
1754 let response = self
1755 .client
1756 .get_pool_delegators((&bi.into_block_identifier(), baker_id))
1757 .await?;
1758 let block_hash = extract_metadata(&response)?;
1759 let stream = response.into_inner().map(|result| match result {
1760 Ok(delegator) => delegator.try_into(),
1761 Err(err) => Err(err),
1762 });
1763 Ok(QueryResponse {
1764 block_hash,
1765 response: stream,
1766 })
1767 }
1768
1769 pub async fn get_pool_delegators_reward_period(
1782 &mut self,
1783 bi: impl IntoBlockIdentifier,
1784 baker_id: types::BakerId,
1785 ) -> endpoints::QueryResult<
1786 QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1787 > {
1788 let response = self
1789 .client
1790 .get_pool_delegators_reward_period((&bi.into_block_identifier(), baker_id))
1791 .await?;
1792 let block_hash = extract_metadata(&response)?;
1793 let stream = response.into_inner().map(|result| match result {
1794 Ok(delegator) => delegator.try_into(),
1795 Err(err) => Err(err),
1796 });
1797 Ok(QueryResponse {
1798 block_hash,
1799 response: stream,
1800 })
1801 }
1802
1803 pub async fn get_passive_delegators(
1815 &mut self,
1816 bi: impl IntoBlockIdentifier,
1817 ) -> endpoints::QueryResult<
1818 QueryResponse<impl Stream<Item = Result<types::DelegatorInfo, tonic::Status>>>,
1819 > {
1820 let response = self
1821 .client
1822 .get_passive_delegators(&bi.into_block_identifier())
1823 .await?;
1824 let block_hash = extract_metadata(&response)?;
1825 let stream = response.into_inner().map(|result| match result {
1826 Ok(delegator) => delegator.try_into(),
1827 Err(err) => Err(err),
1828 });
1829 Ok(QueryResponse {
1830 block_hash,
1831 response: stream,
1832 })
1833 }
1834
1835 pub async fn get_passive_delegators_reward_period(
1848 &mut self,
1849 bi: impl IntoBlockIdentifier,
1850 ) -> endpoints::QueryResult<
1851 QueryResponse<impl Stream<Item = Result<types::DelegatorRewardPeriodInfo, tonic::Status>>>,
1852 > {
1853 let response = self
1854 .client
1855 .get_passive_delegators_reward_period(&bi.into_block_identifier())
1856 .await?;
1857 let block_hash = extract_metadata(&response)?;
1858 let stream = response.into_inner().map(|result| match result {
1859 Ok(delegator) => delegator.try_into(),
1860 Err(err) => Err(err),
1861 });
1862 Ok(QueryResponse {
1863 block_hash,
1864 response: stream,
1865 })
1866 }
1867
1868 pub async fn get_branches(&mut self) -> endpoints::QueryResult<types::queries::Branch> {
1875 let response = self
1876 .client
1877 .get_branches(generated::Empty::default())
1878 .await?;
1879 let response = types::queries::Branch::try_from(response.into_inner())?;
1880 Ok(response)
1881 }
1882
1883 pub async fn get_election_info(
1886 &mut self,
1887 bi: impl IntoBlockIdentifier,
1888 ) -> endpoints::QueryResult<QueryResponse<types::BirkParameters>> {
1889 let response = self
1890 .client
1891 .get_election_info(&bi.into_block_identifier())
1892 .await?;
1893 let block_hash = extract_metadata(&response)?;
1894 let response = types::BirkParameters::try_from(response.into_inner())?;
1895 Ok(QueryResponse {
1896 block_hash,
1897 response,
1898 })
1899 }
1900
1901 pub async fn get_identity_providers(
1905 &mut self,
1906 bi: impl IntoBlockIdentifier,
1907 ) -> endpoints::QueryResult<
1908 QueryResponse<
1909 impl Stream<
1910 Item = Result<
1911 crate::id::types::IpInfo<crate::id::constants::IpPairing>,
1912 tonic::Status,
1913 >,
1914 >,
1915 >,
1916 > {
1917 let response = self
1918 .client
1919 .get_identity_providers(&bi.into_block_identifier())
1920 .await?;
1921 let block_hash = extract_metadata(&response)?;
1922 let stream = response.into_inner().map(|result| match result {
1923 Ok(ip_info) => ip_info.try_into(),
1924 Err(err) => Err(err),
1925 });
1926 Ok(QueryResponse {
1927 block_hash,
1928 response: stream,
1929 })
1930 }
1931
1932 pub async fn get_anonymity_revokers(
1936 &mut self,
1937 bi: impl IntoBlockIdentifier,
1938 ) -> endpoints::QueryResult<
1939 QueryResponse<
1940 impl Stream<
1941 Item = Result<
1942 crate::id::types::ArInfo<crate::id::constants::ArCurve>,
1943 tonic::Status,
1944 >,
1945 >,
1946 >,
1947 > {
1948 let response = self
1949 .client
1950 .get_anonymity_revokers(&bi.into_block_identifier())
1951 .await?;
1952 let block_hash = extract_metadata(&response)?;
1953 let stream = response.into_inner().map(|result| match result {
1954 Ok(ar_info) => ar_info.try_into(),
1955 Err(err) => Err(err),
1956 });
1957 Ok(QueryResponse {
1958 block_hash,
1959 response: stream,
1960 })
1961 }
1962
1963 pub async fn get_account_non_finalized_transactions(
1973 &mut self,
1974 account_address: &AccountAddress,
1975 ) -> endpoints::QueryResult<impl Stream<Item = Result<TransactionHash, tonic::Status>>> {
1976 let response = self
1977 .client
1978 .get_account_non_finalized_transactions(account_address)
1979 .await?;
1980 let stream = response.into_inner().map(|result| match result {
1981 Ok(transaction_hash) => transaction_hash.try_into(),
1982 Err(err) => Err(err),
1983 });
1984 Ok(stream)
1985 }
1986
1987 pub async fn get_block_items(
1994 &mut self,
1995 bi: impl IntoBlockIdentifier,
1996 ) -> endpoints::QueryResult<
1997 QueryResponse<impl Stream<Item = Result<Upward<BlockItem<EncodedPayload>>, tonic::Status>>>,
1998 > {
1999 let response = self
2000 .client
2001 .get_block_items(&bi.into_block_identifier())
2002 .await?;
2003 let block_hash = extract_metadata(&response)?;
2004 let stream = response.into_inner().map(|result| match result {
2005 Ok(summary) => summary.try_into(),
2006 Err(err) => Err(err),
2007 });
2008 Ok(QueryResponse {
2009 block_hash,
2010 response: stream,
2011 })
2012 }
2013
2014 pub async fn get_finalized_block_item(
2029 &mut self,
2030 th: TransactionHash,
2031 ) -> endpoints::QueryResult<(
2032 Upward<BlockItem<EncodedPayload>>,
2033 BlockHash,
2034 BlockItemSummary,
2035 )> {
2036 let status = self.get_block_item_status(&th).await?;
2037 let Some((bh, status)) = status.is_finalized() else {
2038 return Err(QueryError::NotFound);
2039 };
2040 let mut response = self
2041 .client
2042 .get_block_items(&bh.into_block_identifier())
2043 .await?
2044 .into_inner();
2045 while let Some(tx) = response.try_next().await? {
2046 let tx_hash = TransactionHash::try_from(tx.hash.clone().require()?)?;
2047 if tx_hash == th {
2048 return Ok((tx.try_into()?, *bh, status.clone()));
2049 }
2050 }
2051 Err(endpoints::QueryError::NotFound)
2052 }
2053
2054 pub async fn shutdown(&mut self) -> endpoints::RPCResult<()> {
2057 self.client.shutdown(generated::Empty::default()).await?;
2058 Ok(())
2059 }
2060
2061 pub async fn peer_connect(&mut self, addr: std::net::SocketAddr) -> endpoints::RPCResult<()> {
2069 let peer_connection = generated::IpSocketAddress {
2070 ip: Some(generated::IpAddress {
2071 value: addr.ip().to_string(),
2072 }),
2073 port: Some(generated::Port {
2074 value: addr.port() as u32,
2075 }),
2076 };
2077 self.client.peer_connect(peer_connection).await?;
2078 Ok(())
2079 }
2080
2081 pub async fn peer_disconnect(
2085 &mut self,
2086 addr: std::net::SocketAddr,
2087 ) -> endpoints::RPCResult<()> {
2088 let peer_connection = generated::IpSocketAddress {
2089 ip: Some(generated::IpAddress {
2090 value: addr.ip().to_string(),
2091 }),
2092 port: Some(generated::Port {
2093 value: addr.port() as u32,
2094 }),
2095 };
2096 self.client.peer_disconnect(peer_connection).await?;
2097 Ok(())
2098 }
2099
2100 pub async fn get_banned_peers(
2102 &mut self,
2103 ) -> endpoints::RPCResult<Vec<super::types::network::BannedPeer>> {
2104 Ok(self
2105 .client
2106 .get_banned_peers(generated::Empty::default())
2107 .await?
2108 .into_inner()
2109 .peers
2110 .into_iter()
2111 .map(super::types::network::BannedPeer::try_from)
2112 .collect::<anyhow::Result<Vec<super::types::network::BannedPeer>>>()?)
2113 }
2114
2115 pub async fn ban_peer(
2119 &mut self,
2120 peer_to_ban: super::types::network::PeerToBan,
2121 ) -> endpoints::RPCResult<()> {
2122 self.client.ban_peer(peer_to_ban).await?;
2123 Ok(())
2124 }
2125
2126 pub async fn unban_peer(
2130 &mut self,
2131 banned_peer: &super::types::network::BannedPeer,
2132 ) -> endpoints::RPCResult<()> {
2133 self.client.unban_peer(banned_peer).await?;
2134 Ok(())
2135 }
2136
2137 pub async fn dump_start(
2148 &mut self,
2149 file: &std::path::Path,
2150 raw: bool,
2151 ) -> endpoints::RPCResult<()> {
2152 let file_str = file.to_str().ok_or_else(|| {
2153 tonic::Status::invalid_argument(
2154 "The provided path cannot is not a valid UTF8 string, so cannot be used.",
2155 )
2156 })?;
2157
2158 self.client
2159 .dump_start(generated::DumpRequest {
2160 file: file_str.to_string(),
2161 raw,
2162 })
2163 .await?;
2164 Ok(())
2165 }
2166
2167 pub async fn dump_stop(&mut self) -> endpoints::RPCResult<()> {
2174 self.client.dump_stop(generated::Empty::default()).await?;
2175 Ok(())
2176 }
2177
2178 pub async fn get_peers_info(&mut self) -> endpoints::RPCResult<types::network::PeersInfo> {
2181 let response = self
2182 .client
2183 .get_peers_info(generated::Empty::default())
2184 .await?;
2185 let peers_info = types::network::PeersInfo::try_from(response.into_inner())?;
2186 Ok(peers_info)
2187 }
2188
2189 pub async fn get_node_info(&mut self) -> endpoints::RPCResult<types::NodeInfo> {
2203 let response = self
2204 .client
2205 .get_node_info(generated::Empty::default())
2206 .await?;
2207 let node_info = types::NodeInfo::try_from(response.into_inner())?;
2208 Ok(node_info)
2209 }
2210
2211 pub async fn get_baker_earliest_win_time(
2219 &mut self,
2220 bid: types::BakerId,
2221 ) -> endpoints::RPCResult<chrono::DateTime<chrono::Utc>> {
2222 let ts = self.client.get_baker_earliest_win_time(bid).await?;
2223 let local_time = ts.into_inner().try_into()?;
2224 Ok(local_time)
2225 }
2226
2227 pub async fn get_block_transaction_events(
2231 &mut self,
2232 bi: impl IntoBlockIdentifier,
2233 ) -> endpoints::QueryResult<
2234 QueryResponse<impl Stream<Item = Result<types::BlockItemSummary, tonic::Status>>>,
2235 > {
2236 let response = self
2237 .client
2238 .get_block_transaction_events(&bi.into_block_identifier())
2239 .await?;
2240 let block_hash = extract_metadata(&response)?;
2241 let stream = response.into_inner().map(|result| match result {
2242 Ok(summary) => summary.try_into(),
2243 Err(err) => Err(err),
2244 });
2245 Ok(QueryResponse {
2246 block_hash,
2247 response: stream,
2248 })
2249 }
2250
2251 pub async fn get_block_special_events(
2258 &mut self,
2259 bi: impl IntoBlockIdentifier,
2260 ) -> endpoints::QueryResult<
2261 QueryResponse<
2262 impl Stream<Item = Result<Upward<types::SpecialTransactionOutcome>, tonic::Status>>,
2263 >,
2264 > {
2265 let response = self
2266 .client
2267 .get_block_special_events(&bi.into_block_identifier())
2268 .await?;
2269 let block_hash = extract_metadata(&response)?;
2270 let stream = response.into_inner().map(|result| match result {
2271 Ok(summary) => summary.try_into(),
2272 Err(err) => Err(err),
2273 });
2274 Ok(QueryResponse {
2275 block_hash,
2276 response: stream,
2277 })
2278 }
2279
2280 pub async fn get_block_pending_updates(
2285 &mut self,
2286 bi: impl IntoBlockIdentifier,
2287 ) -> endpoints::QueryResult<
2288 QueryResponse<impl Stream<Item = Result<types::queries::PendingUpdate, tonic::Status>>>,
2289 > {
2290 let response = self
2291 .client
2292 .get_block_pending_updates(&bi.into_block_identifier())
2293 .await?;
2294 let block_hash = extract_metadata(&response)?;
2295 let stream = response.into_inner().map(|result| match result {
2296 Ok(update) => update.try_into(),
2297 Err(err) => Err(err),
2298 });
2299 Ok(QueryResponse {
2300 block_hash,
2301 response: stream,
2302 })
2303 }
2304
2305 pub async fn get_winning_bakers_epoch(
2316 &mut self,
2317 ei: impl Into<EpochIdentifier>,
2318 ) -> endpoints::QueryResult<impl Stream<Item = Result<types::WinningBaker, tonic::Status>>>
2319 {
2320 let response = self.client.get_winning_bakers_epoch(&ei.into()).await?;
2321 let stream = response.into_inner().map(|result| match result {
2322 Ok(wb) => wb.try_into(),
2323 Err(err) => Err(err),
2324 });
2325 Ok(stream)
2326 }
2327
2328 pub async fn get_first_block_epoch(
2330 &mut self,
2331 ei: impl Into<EpochIdentifier>,
2332 ) -> endpoints::QueryResult<BlockHash> {
2333 let response = self.client.get_first_block_epoch(&ei.into()).await?;
2334 Ok(response.into_inner().try_into()?)
2335 }
2336
2337 pub async fn get_consensus_detailed_status(
2341 &mut self,
2342 genesis_index: Option<GenesisIndex>,
2343 ) -> endpoints::RPCResult<ConsensusDetailedStatus> {
2344 let query = generated::ConsensusDetailedStatusQuery {
2345 genesis_index: genesis_index.map(Into::into),
2346 };
2347 let response = self.client.get_consensus_detailed_status(query).await?;
2348 Ok(response.into_inner().try_into()?)
2349 }
2350
2351 pub async fn get_next_update_sequence_numbers(
2355 &mut self,
2356 block_id: impl IntoBlockIdentifier,
2357 ) -> endpoints::QueryResult<QueryResponse<types::queries::NextUpdateSequenceNumbers>> {
2358 let response = self
2359 .client
2360 .get_next_update_sequence_numbers(&block_id.into_block_identifier())
2361 .await?;
2362 let block_hash = extract_metadata(&response)?;
2363 let response = types::queries::NextUpdateSequenceNumbers::try_from(response.into_inner())?;
2364 Ok(QueryResponse {
2365 block_hash,
2366 response,
2367 })
2368 }
2369
2370 pub async fn get_scheduled_release_accounts(
2375 &mut self,
2376 block_id: impl IntoBlockIdentifier,
2377 ) -> endpoints::QueryResult<
2378 QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2379 > {
2380 let response = self
2381 .client
2382 .get_scheduled_release_accounts(&block_id.into_block_identifier())
2383 .await?;
2384 let block_hash = extract_metadata(&response)?;
2385 let stream = response.into_inner().map(|result| match result {
2386 Ok(pending) => pending.try_into(),
2387 Err(err) => Err(err),
2388 });
2389 Ok(QueryResponse {
2390 block_hash,
2391 response: stream,
2392 })
2393 }
2394
2395 pub async fn get_cooldown_accounts(
2401 &mut self,
2402 block_id: impl IntoBlockIdentifier,
2403 ) -> endpoints::QueryResult<
2404 QueryResponse<impl Stream<Item = Result<AccountPending, tonic::Status>>>,
2405 > {
2406 let response = self
2407 .client
2408 .get_cooldown_accounts(&block_id.into_block_identifier())
2409 .await?;
2410 let block_hash = extract_metadata(&response)?;
2411 let stream = response.into_inner().map(|result| match result {
2412 Ok(pending) => pending.try_into(),
2413 Err(err) => Err(err),
2414 });
2415 Ok(QueryResponse {
2416 block_hash,
2417 response: stream,
2418 })
2419 }
2420
2421 pub async fn get_pre_cooldown_accounts(
2425 &mut self,
2426 block_id: impl IntoBlockIdentifier,
2427 ) -> endpoints::QueryResult<
2428 QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2429 > {
2430 let response = self
2431 .client
2432 .get_pre_cooldown_accounts(&block_id.into_block_identifier())
2433 .await?;
2434 let block_hash = extract_metadata(&response)?;
2435 let stream = response.into_inner().map(|result| match result {
2436 Ok(account) => Ok(account.into()),
2437 Err(err) => Err(err),
2438 });
2439 Ok(QueryResponse {
2440 block_hash,
2441 response: stream,
2442 })
2443 }
2444
2445 pub async fn get_pre_pre_cooldown_accounts(
2449 &mut self,
2450 block_id: impl IntoBlockIdentifier,
2451 ) -> endpoints::QueryResult<
2452 QueryResponse<impl Stream<Item = Result<AccountIndex, tonic::Status>>>,
2453 > {
2454 let response = self
2455 .client
2456 .get_pre_pre_cooldown_accounts(&block_id.into_block_identifier())
2457 .await?;
2458 let block_hash = extract_metadata(&response)?;
2459 let stream = response.into_inner().map(|result| match result {
2460 Ok(account) => Ok(account.into()),
2461 Err(err) => Err(err),
2462 });
2463 Ok(QueryResponse {
2464 block_hash,
2465 response: stream,
2466 })
2467 }
2468
2469 pub async fn get_block_chain_parameters(
2472 &mut self,
2473 block_id: impl IntoBlockIdentifier,
2474 ) -> endpoints::QueryResult<QueryResponse<ChainParameters>> {
2475 let response = self
2476 .client
2477 .get_block_chain_parameters(&block_id.into_block_identifier())
2478 .await?;
2479 let block_hash = extract_metadata(&response)?;
2480 let response = ChainParameters::try_from(response.into_inner())?;
2481 Ok(QueryResponse {
2482 block_hash,
2483 response,
2484 })
2485 }
2486
2487 pub async fn get_block_certificates(
2497 &mut self,
2498 bi: impl IntoBlockIdentifier,
2499 ) -> endpoints::QueryResult<QueryResponse<block_certificates::BlockCertificates>> {
2500 let response = self
2501 .client
2502 .get_block_certificates(&bi.into_block_identifier())
2503 .await?;
2504 let block_hash = extract_metadata(&response)?;
2505 let response = block_certificates::BlockCertificates::try_from(response.into_inner())?;
2506 Ok(QueryResponse {
2507 block_hash,
2508 response,
2509 })
2510 }
2511
2512 pub async fn get_block_finalization_summary(
2519 &mut self,
2520 block_id: impl IntoBlockIdentifier,
2521 ) -> endpoints::QueryResult<QueryResponse<Option<types::FinalizationSummary>>> {
2522 let response = self
2523 .client
2524 .get_block_finalization_summary(&block_id.into_block_identifier())
2525 .await?;
2526 let block_hash = extract_metadata(&response)?;
2527 let response = response.into_inner().try_into()?;
2528 Ok(QueryResponse {
2529 block_hash,
2530 response,
2531 })
2532 }
2533
2534 pub async fn get_finalized_blocks_from(
2539 &mut self,
2540 start_height: AbsoluteBlockHeight,
2541 ) -> endpoints::QueryResult<FinalizedBlocksStream> {
2542 let mut fin_height = self.get_consensus_info().await?.last_finalized_block_height;
2543 let (sender, receiver) = tokio::sync::mpsc::channel(100);
2544 let mut client = self.clone();
2545 let handle = tokio::spawn(async move {
2546 let mut height = start_height;
2547 loop {
2548 if height > fin_height {
2549 fin_height = client
2550 .get_consensus_info()
2551 .await?
2552 .last_finalized_block_height;
2553 if height > fin_height {
2554 break;
2555 }
2556 } else {
2557 let mut bi = client.get_blocks_at_height(&height.into()).await?;
2558 let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2559 let info = FinalizedBlockInfo { block_hash, height };
2560 if sender.send(info).await.is_err() {
2561 return Ok(());
2562 }
2563 height = height.next();
2564 }
2565 }
2566 let mut stream = client.get_finalized_blocks().await?;
2567 while let Some(fbi) = stream.next().await.transpose()? {
2568 while height < fbi.height {
2570 let mut bi = client.get_blocks_at_height(&height.into()).await?;
2571 let block_hash = bi.pop().ok_or(endpoints::QueryError::NotFound)?;
2572 let info = FinalizedBlockInfo { block_hash, height };
2573 if sender.send(info).await.is_err() {
2574 return Ok(());
2575 }
2576 height = height.next();
2577 }
2578 if sender.send(fbi).await.is_err() {
2579 return Ok(());
2580 }
2581 height = height.next();
2582 }
2583 Ok(())
2584 });
2585 Ok(FinalizedBlocksStream { handle, receiver })
2586 }
2587
2588 pub async fn find_account_creation(
2605 &mut self,
2606 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2607 addr: AccountAddress,
2608 ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, AccountInfo)> {
2609 self.find_at_lowest_height(range, |mut client, height| async move {
2610 match client.get_account_info(&addr.into(), &height).await {
2611 Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2612 Err(e) if e.is_not_found() => Ok(None),
2613 Err(e) => Err(e),
2614 }
2615 })
2616 .await
2617 }
2618
2619 pub async fn find_instance_creation(
2636 &mut self,
2637 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2638 addr: ContractAddress,
2639 ) -> QueryResult<(AbsoluteBlockHeight, BlockHash, InstanceInfo)> {
2640 self.find_at_lowest_height(range, |mut client, height| async move {
2641 match client.get_instance_info(addr, &height).await {
2642 Ok(ii) => Ok(Some((height, ii.block_hash, ii.response))),
2643 Err(e) if e.is_not_found() => Ok(None),
2644 Err(e) => Err(e),
2645 }
2646 })
2647 .await
2648 }
2649
2650 pub async fn find_first_finalized_block_no_earlier_than(
2658 &mut self,
2659 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2660 time: chrono::DateTime<chrono::Utc>,
2661 ) -> QueryResult<types::queries::BlockInfo> {
2662 self.find_at_lowest_height(range, move |mut client, height| async move {
2663 let info = client.get_block_info(&height).await?.response;
2664 if info.block_slot_time >= time {
2665 Ok(Some(info))
2666 } else {
2667 Ok(None)
2668 }
2669 })
2670 .await
2671 }
2672
2673 pub async fn find_at_lowest_height<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2692 &mut self,
2693 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2694 test: impl Fn(Self, AbsoluteBlockHeight) -> F,
2695 ) -> QueryResult<A> {
2696 let mut start = match range.start_bound() {
2697 std::ops::Bound::Included(s) => u64::from(*s),
2698 std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2699 std::ops::Bound::Unbounded => 0,
2700 };
2701 let mut end = {
2702 let ci = self.get_consensus_info().await?;
2703 let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2704 match range.end_bound() {
2705 std::ops::Bound::Included(e) => bound(u64::from(*e)),
2706 std::ops::Bound::Excluded(e) => {
2707 bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2708 }
2709 std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2710 }
2711 };
2712 if end < start {
2713 return Err(QueryError::NotFound);
2714 }
2715 let mut last_found = None;
2716 while start < end {
2717 let mid = start + (end - start) / 2;
2718 let ok = test(self.clone(), mid.into()).await?;
2719 if ok.is_some() {
2720 end = mid;
2721 last_found = ok;
2722 } else {
2723 start = mid + 1;
2724 }
2725 }
2726 last_found.ok_or(QueryError::NotFound)
2727 }
2728
2729 #[deprecated(note = "Use [`find_at_lowest_height`](./struct.Client.html#method.\
2730 find_at_lowest_height) instead since it avoids an extra call.")]
2731 pub async fn find_earliest_finalized<A, F: futures::Future<Output = QueryResult<Option<A>>>>(
2732 &mut self,
2733 range: impl std::ops::RangeBounds<AbsoluteBlockHeight>,
2734 test: impl Fn(Self, AbsoluteBlockHeight, BlockHash) -> F,
2735 ) -> QueryResult<A> {
2736 let mut start = match range.start_bound() {
2737 std::ops::Bound::Included(s) => u64::from(*s),
2738 std::ops::Bound::Excluded(e) => u64::from(*e).saturating_add(1),
2739 std::ops::Bound::Unbounded => 0,
2740 };
2741 let mut end = {
2742 let ci = self.get_consensus_info().await?;
2743 let bound = |end: u64| std::cmp::min(end, ci.last_finalized_block_height.into());
2744 match range.end_bound() {
2745 std::ops::Bound::Included(e) => bound(u64::from(*e)),
2746 std::ops::Bound::Excluded(e) => {
2747 bound(u64::from(*e).checked_sub(1).ok_or(QueryError::NotFound)?)
2748 }
2749 std::ops::Bound::Unbounded => u64::from(ci.last_finalized_block_height),
2750 }
2751 };
2752 if end < start {
2753 return Err(QueryError::NotFound);
2754 }
2755 let mut last_found = None;
2756 while start < end {
2757 let mid = start + (end - start) / 2;
2758 let bh = self
2759 .get_blocks_at_height(&AbsoluteBlockHeight::from(mid).into())
2760 .await?[0]; let ok = test(self.clone(), mid.into(), bh).await?;
2762 if ok.is_some() {
2763 end = mid;
2764 last_found = ok;
2765 } else {
2766 start = mid + 1;
2767 }
2768 }
2769 last_found.ok_or(QueryError::NotFound)
2770 }
2771
2772 pub async fn get_bakers_reward_period(
2777 &mut self,
2778 bi: impl IntoBlockIdentifier,
2779 ) -> endpoints::QueryResult<
2780 QueryResponse<impl Stream<Item = Result<types::BakerRewardPeriodInfo, tonic::Status>>>,
2781 > {
2782 let response = self
2783 .client
2784 .get_bakers_reward_period(&bi.into_block_identifier())
2785 .await?;
2786 let block_hash = extract_metadata(&response)?;
2787 let stream = response.into_inner().map(|result| match result {
2788 Ok(baker) => baker.try_into(),
2789 Err(err) => Err(err),
2790 });
2791 Ok(QueryResponse {
2792 block_hash,
2793 response: stream,
2794 })
2795 }
2796
2797 pub async fn get_token_list(
2803 &mut self,
2804 bi: impl IntoBlockIdentifier,
2805 ) -> endpoints::QueryResult<
2806 QueryResponse<impl Stream<Item = Result<protocol_level_tokens::TokenId, tonic::Status>>>,
2807 > {
2808 let response = self
2809 .client
2810 .get_token_list(&bi.into_block_identifier())
2811 .await?;
2812 let block_hash = extract_metadata(&response)?;
2813 let stream = response.into_inner().map(|result| match result {
2814 Ok(token_id) => protocol_level_tokens::TokenId::try_from(token_id),
2815 Err(err) => Err(err),
2816 });
2817 Ok(QueryResponse {
2818 block_hash,
2819 response: stream,
2820 })
2821 }
2822
2823 pub async fn get_token_info(
2829 &mut self,
2830 token_id: protocol_level_tokens::TokenId,
2831 bi: impl IntoBlockIdentifier,
2832 ) -> endpoints::QueryResult<QueryResponse<protocol_level_tokens::TokenInfo>> {
2833 let request = generated::TokenInfoRequest {
2834 block_hash: Some((&bi.into_block_identifier()).into()),
2835 token_id: Some(token_id.into()),
2836 };
2837 let response = self.client.get_token_info(request).await?;
2838 let block_hash = extract_metadata(&response)?;
2839 let response = protocol_level_tokens::TokenInfo::try_from(response.into_inner())?;
2840 Ok(QueryResponse {
2841 block_hash,
2842 response,
2843 })
2844 }
2845}
2846
2847pub struct FinalizedBlocksStream {
2851 handle: tokio::task::JoinHandle<endpoints::QueryResult<()>>,
2852 receiver: tokio::sync::mpsc::Receiver<FinalizedBlockInfo>,
2853}
2854
2855impl Drop for FinalizedBlocksStream {
2858 fn drop(&mut self) {
2859 self.handle.abort();
2860 }
2861}
2862
2863impl FinalizedBlocksStream {
2864 pub async fn next(&mut self) -> Option<FinalizedBlockInfo> {
2869 self.receiver.recv().await
2870 }
2871
2872 pub async fn next_timeout(
2875 &mut self,
2876 duration: std::time::Duration,
2877 ) -> Result<Option<FinalizedBlockInfo>, tokio::time::error::Elapsed> {
2878 tokio::time::timeout(duration, async move { self.next().await }).await
2879 }
2880
2881 pub async fn next_chunk(
2891 &mut self,
2892 n: usize,
2893 ) -> Result<Vec<FinalizedBlockInfo>, Vec<FinalizedBlockInfo>> {
2894 let mut out = Vec::with_capacity(n);
2895 let first = self.receiver.recv().await;
2896 match first {
2897 Some(v) => out.push(v),
2898 None => {
2899 return Err(out);
2900 }
2901 }
2902 for _ in 1..n {
2903 match self.receiver.try_recv() {
2904 Ok(v) => {
2905 out.push(v);
2906 }
2907 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
2908 break;
2909 }
2910 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => return Err(out),
2911 }
2912 }
2913 Ok(out)
2914 }
2915
2916 pub async fn next_chunk_timeout(
2925 &mut self,
2926 n: usize,
2927 duration: std::time::Duration,
2928 ) -> Result<(bool, Vec<FinalizedBlockInfo>), tokio::time::error::Elapsed> {
2929 let mut out = Vec::with_capacity(n);
2930 let first = self.next_timeout(duration).await?;
2931 match first {
2932 Some(v) => out.push(v),
2933 None => return Ok((true, out)),
2934 }
2935 for _ in 1..n {
2936 match self.receiver.try_recv() {
2937 Ok(v) => {
2938 out.push(v);
2939 }
2940 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {
2941 break;
2942 }
2943 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
2944 return Ok((true, out))
2945 }
2946 }
2947 }
2948 Ok((false, out))
2949 }
2950}
2951
2952fn extract_metadata<T>(response: &tonic::Response<T>) -> endpoints::RPCResult<BlockHash> {
2953 match response.metadata().get("blockhash") {
2954 Some(bytes) => {
2955 let bytes = bytes.as_bytes();
2956 if bytes.len() == 64 {
2957 let mut hash = [0u8; 32];
2958 if hex::decode_to_slice(bytes, &mut hash).is_err() {
2959 tonic::Status::unknown("Response does correctly encode the block hash.");
2960 }
2961 Ok(hash.into())
2962 } else {
2963 Err(endpoints::RPCError::CallError(tonic::Status::unknown(
2964 "Response does not include the expected metadata.",
2965 )))
2966 }
2967 }
2968 None => Err(endpoints::RPCError::CallError(tonic::Status::unknown(
2969 "Response does not include the expected metadata.",
2970 ))),
2971 }
2972}
2973
2974pub(crate) trait Require<E> {
2982 type A;
2983 fn require(self) -> Result<Self::A, E>;
2984}
2985
2986impl<A> Require<tonic::Status> for Option<A> {
2987 type A = A;
2988
2989 fn require(self) -> Result<Self::A, tonic::Status> {
2990 match self {
2991 Some(v) => Ok(v),
2992 None => Err(tonic::Status::invalid_argument("missing field in response")),
2993 }
2994 }
2995}
2996
2997#[cfg(test)]
2998mod tests {
2999 use super::*;
3000 #[test]
3001 fn block_ident_from_str() -> anyhow::Result<()> {
3003 let b1 = "best".parse::<BlockIdentifier>()?;
3004 assert_eq!(b1, BlockIdentifier::Best);
3005
3006 let b2 = "lastFinal".parse::<BlockIdentifier>()?;
3007 assert_eq!(b2, BlockIdentifier::LastFinal);
3008
3009 let b3 = "lastfinal".parse::<BlockIdentifier>()?;
3010 assert_eq!(b3, BlockIdentifier::LastFinal);
3011
3012 let b4 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
3013 .parse::<BlockIdentifier>()?;
3014 assert_eq!(
3015 b4,
3016 BlockIdentifier::Given(
3017 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse()?
3018 )
3019 );
3020
3021 let b5 = "@33".parse::<BlockIdentifier>()?;
3022 assert_eq!(b5, BlockIdentifier::AbsoluteHeight(33.into()));
3023
3024 let b6 = "@33/3".parse::<BlockIdentifier>()?;
3025 assert_eq!(
3026 b6,
3027 BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3028 genesis_index: 3.into(),
3029 height: 33.into(),
3030 restrict: false,
3031 })
3032 );
3033
3034 let b7 = "@33/3!".parse::<BlockIdentifier>()?;
3035 assert_eq!(
3036 b7,
3037 BlockIdentifier::RelativeHeight(RelativeBlockHeight {
3038 genesis_index: 3.into(),
3039 height: 33.into(),
3040 restrict: true,
3041 })
3042 );
3043
3044 Ok(())
3045 }
3046}