1use std::{
10 collections::{HashMap, HashSet},
11 fmt::Debug,
12 ops::RangeInclusive,
13};
14
15use chrono::Utc;
16use futures::{stream::FuturesOrdered, StreamExt, TryFutureExt};
17use hex::{FromHex, ToHex};
18use hex_data::HexData;
19use indexmap::IndexMap;
20use jsonrpsee::core::{async_trait, RpcResult as Result};
21use jsonrpsee_proc_macros::rpc;
22use jsonrpsee_types::{ErrorCode, ErrorObject};
23use tokio::{
24 sync::{broadcast, watch},
25 task::JoinHandle,
26};
27use tower::{Service, ServiceExt};
28use tracing::Instrument;
29
30use zcash_primitives::consensus::Parameters;
31use zebra_chain::{
32 block::{self, Commitment, Height, SerializedBlock},
33 chain_tip::{ChainTip, NetworkChainTipHeightEstimator},
34 parameters::{ConsensusBranchId, Network, NetworkUpgrade},
35 serialization::{ZcashDeserialize, ZcashSerialize},
36 subtree::NoteCommitmentSubtreeIndex,
37 transaction::{self, SerializedTransaction, Transaction, UnminedTx},
38 transparent::{self, Address},
39 work::{
40 difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty, U256},
41 equihash::Solution,
42 },
43};
44use zebra_consensus::ParameterCheckpoint;
45use zebra_network::address_book_peers::AddressBookPeers;
46use zebra_node_services::mempool;
47use zebra_state::{
48 HashOrHeight, OutputIndex, OutputLocation, ReadRequest, ReadResponse, TransactionLocation,
49};
50
51use crate::{
52 methods::trees::{GetSubtrees, GetTreestate, SubtreeRpcData},
53 queue::Queue,
54 server::{
55 self,
56 error::{MapError, OkOrError},
57 },
58};
59
60pub mod hex_data;
61
62pub mod trees;
64
65pub mod types;
66
67use types::GetRawMempool;
68use types::MempoolObject;
69use types::TransactionObject;
70
71pub mod get_block_template_rpcs;
72
73pub use get_block_template_rpcs::{GetBlockTemplateRpcImpl, GetBlockTemplateRpcServer};
74
75#[cfg(test)]
76mod tests;
77
78#[rpc(server)]
79pub trait Rpc {
81 #[method(name = "getinfo")]
96 async fn get_info(&self) -> Result<GetInfo>;
97
98 #[method(name = "getblockchaininfo")]
109 async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo>;
110
111 #[method(name = "getaddressbalance")]
134 async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance>;
135
136 #[method(name = "sendrawtransaction")]
153 async fn send_raw_transaction(
154 &self,
155 raw_transaction_hex: String,
156 _allow_high_fees: Option<bool>,
157 ) -> Result<SentTransactionHash>;
158
159 #[method(name = "getblock")]
179 async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock>;
180
181 #[method(name = "getblockheader")]
199 async fn get_block_header(
200 &self,
201 hash_or_height: String,
202 verbose: Option<bool>,
203 ) -> Result<GetBlockHeader>;
204
205 #[method(name = "getbestblockhash")]
211 fn get_best_block_hash(&self) -> Result<GetBlockHash>;
212
213 #[method(name = "getbestblockheightandhash")]
219 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash>;
220
221 #[method(name = "getrawmempool")]
231 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool>;
232
233 #[method(name = "z_gettreestate")]
250 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate>;
251
252 #[method(name = "z_getsubtreesbyindex")]
271 async fn z_get_subtrees_by_index(
272 &self,
273 pool: String,
274 start_index: NoteCommitmentSubtreeIndex,
275 limit: Option<NoteCommitmentSubtreeIndex>,
276 ) -> Result<GetSubtrees>;
277
278 #[method(name = "getrawtransaction")]
298 async fn get_raw_transaction(
299 &self,
300 txid: String,
301 verbose: Option<u8>,
302 ) -> Result<GetRawTransaction>;
303
304 #[method(name = "getaddresstxids")]
322 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>>;
323
324 #[method(name = "getaddressutxos")]
339 async fn get_address_utxos(
340 &self,
341 address_strings: AddressStrings,
342 ) -> Result<Vec<GetAddressUtxos>>;
343
344 #[method(name = "stop")]
355 fn stop(&self) -> Result<String>;
356}
357
358#[derive(Clone)]
360pub struct RpcImpl<Mempool, State, Tip, AddressBook>
361where
362 Mempool: Service<
363 mempool::Request,
364 Response = mempool::Response,
365 Error = zebra_node_services::BoxError,
366 > + Clone
367 + Send
368 + Sync
369 + 'static,
370 Mempool::Future: Send,
371 State: Service<
372 zebra_state::ReadRequest,
373 Response = zebra_state::ReadResponse,
374 Error = zebra_state::BoxError,
375 > + Clone
376 + Send
377 + Sync
378 + 'static,
379 State::Future: Send,
380 Tip: ChainTip + Clone + Send + Sync + 'static,
381 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
382{
383 build_version: String,
387
388 user_agent: String,
390
391 network: Network,
393
394 debug_force_finished_sync: bool,
397
398 #[allow(dead_code)]
400 debug_like_zcashd: bool,
401
402 mempool: Mempool,
406
407 state: State,
409
410 latest_chain_tip: Tip,
412
413 queue_sender: broadcast::Sender<UnminedTx>,
417
418 address_book: AddressBook,
420
421 last_warn_error_log_rx: LoggedLastEvent,
423}
424
425pub type LoggedLastEvent = watch::Receiver<Option<(String, tracing::Level, chrono::DateTime<Utc>)>>;
427
428impl<Mempool, State, Tip, AddressBook> Debug for RpcImpl<Mempool, State, Tip, AddressBook>
429where
430 Mempool: Service<
431 mempool::Request,
432 Response = mempool::Response,
433 Error = zebra_node_services::BoxError,
434 > + Clone
435 + Send
436 + Sync
437 + 'static,
438 Mempool::Future: Send,
439 State: Service<
440 zebra_state::ReadRequest,
441 Response = zebra_state::ReadResponse,
442 Error = zebra_state::BoxError,
443 > + Clone
444 + Send
445 + Sync
446 + 'static,
447 State::Future: Send,
448 Tip: ChainTip + Clone + Send + Sync + 'static,
449 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
450{
451 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452 f.debug_struct("RpcImpl")
454 .field("build_version", &self.build_version)
455 .field("user_agent", &self.user_agent)
456 .field("network", &self.network)
457 .field("debug_force_finished_sync", &self.debug_force_finished_sync)
458 .field("debug_like_zcashd", &self.debug_like_zcashd)
459 .finish()
460 }
461}
462
463impl<Mempool, State, Tip, AddressBook> RpcImpl<Mempool, State, Tip, AddressBook>
464where
465 Mempool: Service<
466 mempool::Request,
467 Response = mempool::Response,
468 Error = zebra_node_services::BoxError,
469 > + Clone
470 + Send
471 + Sync
472 + 'static,
473 Mempool::Future: Send,
474 State: Service<
475 zebra_state::ReadRequest,
476 Response = zebra_state::ReadResponse,
477 Error = zebra_state::BoxError,
478 > + Clone
479 + Send
480 + Sync
481 + 'static,
482 State::Future: Send,
483 Tip: ChainTip + Clone + Send + Sync + 'static,
484 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
485{
486 #[allow(clippy::too_many_arguments)]
491 pub fn new<VersionString, UserAgentString>(
492 build_version: VersionString,
493 user_agent: UserAgentString,
494 network: Network,
495 debug_force_finished_sync: bool,
496 debug_like_zcashd: bool,
497 mempool: Mempool,
498 state: State,
499 latest_chain_tip: Tip,
500 address_book: AddressBook,
501 last_warn_error_log_rx: LoggedLastEvent,
502 ) -> (Self, JoinHandle<()>)
503 where
504 VersionString: ToString + Clone + Send + 'static,
505 UserAgentString: ToString + Clone + Send + 'static,
506 {
507 let (runner, queue_sender) = Queue::start();
508
509 let mut build_version = build_version.to_string();
510 let user_agent = user_agent.to_string();
511
512 if !build_version.is_empty() && !build_version.starts_with('v') {
514 build_version.insert(0, 'v');
515 }
516
517 let rpc_impl = RpcImpl {
518 build_version,
519 user_agent,
520 network: network.clone(),
521 debug_force_finished_sync,
522 debug_like_zcashd,
523 mempool: mempool.clone(),
524 state: state.clone(),
525 latest_chain_tip: latest_chain_tip.clone(),
526 queue_sender,
527 address_book,
528 last_warn_error_log_rx,
529 };
530
531 let rpc_tx_queue_task_handle = tokio::spawn(
533 runner
534 .run(mempool, state, latest_chain_tip, network)
535 .in_current_span(),
536 );
537
538 (rpc_impl, rpc_tx_queue_task_handle)
539 }
540}
541
542#[async_trait]
543impl<Mempool, State, Tip, AddressBook> RpcServer for RpcImpl<Mempool, State, Tip, AddressBook>
544where
545 Mempool: Service<
546 mempool::Request,
547 Response = mempool::Response,
548 Error = zebra_node_services::BoxError,
549 > + Clone
550 + Send
551 + Sync
552 + 'static,
553 Mempool::Future: Send,
554 State: Service<
555 zebra_state::ReadRequest,
556 Response = zebra_state::ReadResponse,
557 Error = zebra_state::BoxError,
558 > + Clone
559 + Send
560 + Sync
561 + 'static,
562 State::Future: Send,
563 Tip: ChainTip + Clone + Send + Sync + 'static,
564 AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
565{
566 async fn get_info(&self) -> Result<GetInfo> {
567 let version = GetInfo::version(&self.build_version).expect("invalid version string");
568
569 let connections = self.address_book.recently_live_peers(Utc::now()).len();
570
571 let last_error_recorded = self.last_warn_error_log_rx.borrow().clone();
572 let (last_error_log, _level, last_error_log_time) = last_error_recorded.unwrap_or((
573 GetInfo::default().errors,
574 tracing::Level::INFO,
575 Utc::now(),
576 ));
577
578 let tip_height = self
579 .latest_chain_tip
580 .best_tip_height()
581 .unwrap_or(Height::MIN);
582 let testnet = self.network.is_a_test_network();
583
584 let pay_tx_fee = 0.0;
590
591 let relay_fee = zebra_chain::transaction::zip317::MIN_MEMPOOL_TX_FEE_RATE as f64
592 / (zebra_chain::amount::COIN as f64);
593 let difficulty = chain_tip_difficulty(self.network.clone(), self.state.clone(), true)
594 .await
595 .expect("should always be Ok when `should_use_default` is true");
596
597 let response = GetInfo {
598 version,
599 build: self.build_version.clone(),
600 subversion: self.user_agent.clone(),
601 protocol_version: zebra_network::constants::CURRENT_NETWORK_PROTOCOL_VERSION.0,
602 blocks: tip_height.0,
603 connections,
604 proxy: None,
605 difficulty,
606 testnet,
607 pay_tx_fee,
608 relay_fee,
609 errors: last_error_log,
610 errors_timestamp: last_error_log_time.to_string(),
611 };
612
613 Ok(response)
614 }
615
616 #[allow(clippy::unwrap_in_result)]
617 async fn get_blockchain_info(&self) -> Result<GetBlockChainInfo> {
618 let debug_force_finished_sync = self.debug_force_finished_sync;
619 let network = &self.network;
620
621 let (usage_info_rsp, tip_pool_values_rsp, chain_tip_difficulty) = {
622 use zebra_state::ReadRequest::*;
623 let state_call = |request| self.state.clone().oneshot(request);
624 tokio::join!(
625 state_call(UsageInfo),
626 state_call(TipPoolValues),
627 chain_tip_difficulty(network.clone(), self.state.clone(), true)
628 )
629 };
630
631 let (size_on_disk, (tip_height, tip_hash), value_balance, difficulty) = {
632 use zebra_state::ReadResponse::*;
633
634 let UsageInfo(size_on_disk) = usage_info_rsp.map_misc_error()? else {
635 unreachable!("unmatched response to a TipPoolValues request")
636 };
637
638 let (tip, value_balance) = match tip_pool_values_rsp {
639 Ok(TipPoolValues {
640 tip_height,
641 tip_hash,
642 value_balance,
643 }) => ((tip_height, tip_hash), value_balance),
644 Ok(_) => unreachable!("unmatched response to a TipPoolValues request"),
645 Err(_) => ((Height::MIN, network.genesis_hash()), Default::default()),
646 };
647
648 let difficulty = chain_tip_difficulty
649 .expect("should always be Ok when `should_use_default` is true");
650
651 (size_on_disk, tip, value_balance, difficulty)
652 };
653
654 let now = Utc::now();
655 let (estimated_height, verification_progress) = self
656 .latest_chain_tip
657 .best_tip_height_and_block_time()
658 .map(|(tip_height, tip_block_time)| {
659 let height =
660 NetworkChainTipHeightEstimator::new(tip_block_time, tip_height, network)
661 .estimate_height_at(now);
662
663 let height =
667 if tip_block_time > now || height < tip_height || debug_force_finished_sync {
668 tip_height
669 } else {
670 height
671 };
672
673 (height, f64::from(tip_height.0) / f64::from(height.0))
674 })
675 .unwrap_or((Height::MIN, 0.0));
677
678 let mut upgrades = IndexMap::new();
682 for (activation_height, network_upgrade) in network.full_activation_list() {
683 if let Some(branch_id) = network_upgrade.branch_id() {
688 let status = if tip_height >= activation_height {
690 NetworkUpgradeStatus::Active
691 } else {
692 NetworkUpgradeStatus::Pending
693 };
694
695 let upgrade = NetworkUpgradeInfo {
696 name: network_upgrade,
697 activation_height,
698 status,
699 };
700 upgrades.insert(ConsensusBranchIdHex(branch_id), upgrade);
701 }
702 }
703
704 let next_block_height =
706 (tip_height + 1).expect("valid chain tips are a lot less than Height::MAX");
707 let consensus = TipConsensusBranch {
708 chain_tip: ConsensusBranchIdHex(
709 NetworkUpgrade::current(network, tip_height)
710 .branch_id()
711 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
712 ),
713 next_block: ConsensusBranchIdHex(
714 NetworkUpgrade::current(network, next_block_height)
715 .branch_id()
716 .unwrap_or(ConsensusBranchId::RPC_MISSING_ID),
717 ),
718 };
719
720 let response = GetBlockChainInfo {
721 chain: network.bip70_network_name(),
722 blocks: tip_height,
723 best_block_hash: tip_hash,
724 estimated_height,
725 chain_supply: types::Balance::chain_supply(value_balance),
726 value_pools: types::Balance::value_pools(value_balance),
727 upgrades,
728 consensus,
729 headers: tip_height,
730 difficulty,
731 verification_progress,
732 chain_work: 0,
734 pruned: false,
735 size_on_disk,
736 commitments: 0,
738 };
739
740 Ok(response)
741 }
742
743 async fn get_address_balance(&self, address_strings: AddressStrings) -> Result<AddressBalance> {
744 let state = self.state.clone();
745
746 let valid_addresses = address_strings.valid_addresses()?;
747
748 let request = zebra_state::ReadRequest::AddressBalance(valid_addresses);
749 let response = state.oneshot(request).await.map_misc_error()?;
750
751 match response {
752 zebra_state::ReadResponse::AddressBalance(balance) => Ok(AddressBalance {
753 balance: u64::from(balance),
754 }),
755 _ => unreachable!("Unexpected response from state service: {response:?}"),
756 }
757 }
758
759 async fn send_raw_transaction(
761 &self,
762 raw_transaction_hex: String,
763 _allow_high_fees: Option<bool>,
764 ) -> Result<SentTransactionHash> {
765 let mempool = self.mempool.clone();
766 let queue_sender = self.queue_sender.clone();
767
768 let raw_transaction_bytes = Vec::from_hex(raw_transaction_hex)
771 .map_error(server::error::LegacyCode::Deserialization)?;
772 let raw_transaction = Transaction::zcash_deserialize(&*raw_transaction_bytes)
773 .map_error(server::error::LegacyCode::Deserialization)?;
774
775 let transaction_hash = raw_transaction.hash();
776
777 let unmined_transaction = UnminedTx::from(raw_transaction.clone());
779 let _ = queue_sender.send(unmined_transaction);
780
781 let transaction_parameter = mempool::Gossip::Tx(raw_transaction.into());
782 let request = mempool::Request::Queue(vec![transaction_parameter]);
783
784 let response = mempool.oneshot(request).await.map_misc_error()?;
785
786 let mut queue_results = match response {
787 mempool::Response::Queued(results) => results,
788 _ => unreachable!("incorrect response variant from mempool service"),
789 };
790
791 assert_eq!(
792 queue_results.len(),
793 1,
794 "mempool service returned more results than expected"
795 );
796
797 let queue_result = queue_results
798 .pop()
799 .expect("there should be exactly one item in Vec")
800 .inspect_err(|err| tracing::debug!("sent transaction to mempool: {:?}", &err))
801 .map_misc_error()?
802 .await
803 .map_misc_error()?;
804
805 tracing::debug!("sent transaction to mempool: {:?}", &queue_result);
806
807 queue_result
808 .map(|_| SentTransactionHash(transaction_hash))
809 .map_error(server::error::LegacyCode::Verify)
816 }
817
818 async fn get_block(&self, hash_or_height: String, verbosity: Option<u8>) -> Result<GetBlock> {
827 let mut state = self.state.clone();
828 let verbosity = verbosity.unwrap_or(1);
829 let network = self.network.clone();
830 let original_hash_or_height = hash_or_height.clone();
831
832 let get_block_header_future = if matches!(verbosity, 1 | 2) {
834 Some(self.get_block_header(original_hash_or_height.clone(), Some(true)))
835 } else {
836 None
837 };
838
839 let hash_or_height =
840 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
841 .map_error(server::error::LegacyCode::InvalidParameter)?;
844
845 if verbosity == 0 {
846 let request = zebra_state::ReadRequest::Block(hash_or_height);
847 let response = state
848 .ready()
849 .and_then(|service| service.call(request))
850 .await
851 .map_misc_error()?;
852
853 match response {
854 zebra_state::ReadResponse::Block(Some(block)) => Ok(GetBlock::Raw(block.into())),
855 zebra_state::ReadResponse::Block(None) => {
856 Err("Block not found").map_error(server::error::LegacyCode::InvalidParameter)
857 }
858 _ => unreachable!("unmatched response to a block request"),
859 }
860 } else if let Some(get_block_header_future) = get_block_header_future {
861 let get_block_header_result: Result<GetBlockHeader> = get_block_header_future.await;
862
863 let GetBlockHeader::Object(block_header) = get_block_header_result? else {
864 panic!("must return Object")
865 };
866
867 let GetBlockHeaderObject {
868 hash,
869 confirmations,
870 height,
871 version,
872 merkle_root,
873 block_commitments,
874 final_sapling_root,
875 sapling_tree_size,
876 time,
877 nonce,
878 solution,
879 bits,
880 difficulty,
881 previous_block_hash,
882 next_block_hash,
883 } = *block_header;
884
885 let transactions_request = match verbosity {
886 1 => zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height),
887 2 => zebra_state::ReadRequest::BlockAndSize(hash_or_height),
888 _other => panic!("get_block_header_fut should be none"),
889 };
890
891 let hash_or_height = hash.0.into();
896 let requests = vec![
897 transactions_request,
905 zebra_state::ReadRequest::OrchardTree(hash_or_height),
907 ];
908
909 let mut futs = FuturesOrdered::new();
910
911 for request in requests {
912 futs.push_back(state.clone().oneshot(request));
913 }
914
915 let tx_ids_response = futs.next().await.expect("`futs` should not be empty");
916 let (tx, size): (Vec<_>, Option<usize>) = match tx_ids_response.map_misc_error()? {
917 zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => (
918 tx_ids
919 .ok_or_misc_error("block not found")?
920 .iter()
921 .map(|tx_id| GetBlockTransaction::Hash(*tx_id))
922 .collect(),
923 None,
924 ),
925 zebra_state::ReadResponse::BlockAndSize(block_and_size) => {
926 let (block, size) = block_and_size.ok_or_misc_error("Block not found")?;
927 let block_time = block.header.time;
928 let transactions =
929 block
930 .transactions
931 .iter()
932 .map(|tx| {
933 GetBlockTransaction::Object(Box::new(
934 TransactionObject::from_transaction(
935 tx.clone(),
936 Some(height),
937 Some(confirmations.try_into().expect(
938 "should be less than max block height, i32::MAX",
939 )),
940 &network,
941 Some(block_time),
942 ),
943 ))
944 })
945 .collect();
946 (transactions, Some(size))
947 }
948 _ => unreachable!("unmatched response to a transaction_ids_for_block request"),
949 };
950
951 let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
952 let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
953 orchard_tree_response.map_misc_error()?
954 else {
955 unreachable!("unmatched response to a OrchardTree request");
956 };
957
958 let nu5_activation = NetworkUpgrade::Nu5.activation_height(&network);
959
960 let orchard_tree = orchard_tree.ok_or_misc_error("missing Orchard tree")?;
962
963 let final_orchard_root = match nu5_activation {
964 Some(activation_height) if height >= activation_height => {
965 Some(orchard_tree.root().into())
966 }
967 _other => None,
968 };
969
970 let sapling = SaplingTrees {
971 size: sapling_tree_size,
972 };
973
974 let orchard_tree_size = orchard_tree.count();
975 let orchard = OrchardTrees {
976 size: orchard_tree_size,
977 };
978
979 let trees = GetBlockTrees { sapling, orchard };
980
981 Ok(GetBlock::Object {
982 hash,
983 confirmations,
984 height: Some(height),
985 version: Some(version),
986 merkle_root: Some(merkle_root),
987 time: Some(time),
988 nonce: Some(nonce),
989 solution: Some(solution),
990 bits: Some(bits),
991 difficulty: Some(difficulty),
992 tx,
993 trees,
994 size: size.map(|size| size as i64),
995 block_commitments: Some(block_commitments),
996 final_sapling_root: Some(final_sapling_root),
997 final_orchard_root,
998 previous_block_hash: Some(previous_block_hash),
999 next_block_hash,
1000 })
1001 } else {
1002 Err("invalid verbosity value").map_error(server::error::LegacyCode::InvalidParameter)
1003 }
1004 }
1005
1006 async fn get_block_header(
1007 &self,
1008 hash_or_height: String,
1009 verbose: Option<bool>,
1010 ) -> Result<GetBlockHeader> {
1011 let state = self.state.clone();
1012 let verbose = verbose.unwrap_or(true);
1013 let network = self.network.clone();
1014
1015 let hash_or_height =
1016 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1017 .map_error(server::error::LegacyCode::InvalidParameter)?;
1020 let zebra_state::ReadResponse::BlockHeader {
1021 header,
1022 hash,
1023 height,
1024 next_block_hash,
1025 } = state
1026 .clone()
1027 .oneshot(zebra_state::ReadRequest::BlockHeader(hash_or_height))
1028 .await
1029 .map_err(|_| "block height not in best chain")
1030 .map_error(
1031 if hash_or_height.hash().is_some() {
1036 server::error::LegacyCode::InvalidAddressOrKey
1037 } else {
1038 server::error::LegacyCode::InvalidParameter
1039 },
1040 )?
1041 else {
1042 panic!("unexpected response to BlockHeader request")
1043 };
1044
1045 let response = if !verbose {
1046 GetBlockHeader::Raw(HexData(header.zcash_serialize_to_vec().map_misc_error()?))
1047 } else {
1048 let zebra_state::ReadResponse::SaplingTree(sapling_tree) = state
1049 .clone()
1050 .oneshot(zebra_state::ReadRequest::SaplingTree(hash_or_height))
1051 .await
1052 .map_misc_error()?
1053 else {
1054 panic!("unexpected response to SaplingTree request")
1055 };
1056
1057 let sapling_tree = sapling_tree.ok_or_misc_error("missing Sapling tree")?;
1059
1060 let zebra_state::ReadResponse::Depth(depth) = state
1061 .clone()
1062 .oneshot(zebra_state::ReadRequest::Depth(hash))
1063 .await
1064 .map_misc_error()?
1065 else {
1066 panic!("unexpected response to SaplingTree request")
1067 };
1068
1069 const NOT_IN_BEST_CHAIN_CONFIRMATIONS: i64 = -1;
1072
1073 let confirmations = depth
1076 .map(|depth| i64::from(depth) + 1)
1077 .unwrap_or(NOT_IN_BEST_CHAIN_CONFIRMATIONS);
1078
1079 let mut nonce = *header.nonce;
1080 nonce.reverse();
1081
1082 let sapling_activation = NetworkUpgrade::Sapling.activation_height(&network);
1083 let sapling_tree_size = sapling_tree.count();
1084 let final_sapling_root: [u8; 32] =
1085 if sapling_activation.is_some() && height >= sapling_activation.unwrap() {
1086 let mut root: [u8; 32] = sapling_tree.root().into();
1087 root.reverse();
1088 root
1089 } else {
1090 [0; 32]
1091 };
1092
1093 let difficulty = header.difficulty_threshold.relative_to_network(&network);
1094
1095 let block_commitments = match header.commitment(&network, height).expect(
1096 "Unexpected failure while parsing the blockcommitments field in get_block_header",
1097 ) {
1098 Commitment::PreSaplingReserved(bytes) => bytes,
1099 Commitment::FinalSaplingRoot(_) => final_sapling_root,
1100 Commitment::ChainHistoryActivationReserved => [0; 32],
1101 Commitment::ChainHistoryRoot(root) => root.bytes_in_display_order(),
1102 Commitment::ChainHistoryBlockTxAuthCommitment(hash) => {
1103 hash.bytes_in_display_order()
1104 }
1105 };
1106
1107 let block_header = GetBlockHeaderObject {
1108 hash: GetBlockHash(hash),
1109 confirmations,
1110 height,
1111 version: header.version,
1112 merkle_root: header.merkle_root,
1113 block_commitments,
1114 final_sapling_root,
1115 sapling_tree_size,
1116 time: header.time.timestamp(),
1117 nonce,
1118 solution: header.solution,
1119 bits: header.difficulty_threshold,
1120 difficulty,
1121 previous_block_hash: GetBlockHash(header.previous_block_hash),
1122 next_block_hash: next_block_hash.map(GetBlockHash),
1123 };
1124
1125 GetBlockHeader::Object(Box::new(block_header))
1126 };
1127
1128 Ok(response)
1129 }
1130
1131 fn get_best_block_hash(&self) -> Result<GetBlockHash> {
1132 self.latest_chain_tip
1133 .best_tip_hash()
1134 .map(GetBlockHash)
1135 .ok_or_misc_error("No blocks in state")
1136 }
1137
1138 fn get_best_block_height_and_hash(&self) -> Result<GetBlockHeightAndHash> {
1139 self.latest_chain_tip
1140 .best_tip_height_and_hash()
1141 .map(|(height, hash)| GetBlockHeightAndHash { height, hash })
1142 .ok_or_misc_error("No blocks in state")
1143 }
1144
1145 async fn get_raw_mempool(&self, verbose: Option<bool>) -> Result<GetRawMempool> {
1146 #[allow(unused)]
1147 let verbose = verbose.unwrap_or(false);
1148
1149 use zebra_chain::block::MAX_BLOCK_BYTES;
1150
1151 let should_use_zcashd_order = self.debug_like_zcashd;
1153
1154 let mut mempool = self.mempool.clone();
1155
1156 let request = if should_use_zcashd_order || verbose {
1157 mempool::Request::FullTransactions
1158 } else {
1159 mempool::Request::TransactionIds
1160 };
1161
1162 let response = mempool
1164 .ready()
1165 .and_then(|service| service.call(request))
1166 .await
1167 .map_misc_error()?;
1168
1169 match response {
1170 mempool::Response::FullTransactions {
1171 mut transactions,
1172 transaction_dependencies,
1173 last_seen_tip_hash: _,
1174 } => {
1175 if verbose {
1176 let map = transactions
1177 .iter()
1178 .map(|unmined_tx| {
1179 (
1180 unmined_tx.transaction.id.mined_id().encode_hex(),
1181 MempoolObject::from_verified_unmined_tx(
1182 unmined_tx,
1183 &transactions,
1184 &transaction_dependencies,
1185 ),
1186 )
1187 })
1188 .collect::<HashMap<_, _>>();
1189 Ok(GetRawMempool::Verbose(map))
1190 } else {
1191 transactions.sort_by_cached_key(|tx| {
1196 std::cmp::Reverse((
1199 i64::from(tx.miner_fee) as u128 * MAX_BLOCK_BYTES as u128
1200 / tx.transaction.size as u128,
1201 tx.transaction.id.mined_id(),
1203 ))
1204 });
1205 let tx_ids: Vec<String> = transactions
1206 .iter()
1207 .map(|unmined_tx| unmined_tx.transaction.id.mined_id().encode_hex())
1208 .collect();
1209
1210 Ok(GetRawMempool::TxIds(tx_ids))
1211 }
1212 }
1213
1214 mempool::Response::TransactionIds(unmined_transaction_ids) => {
1215 let mut tx_ids: Vec<String> = unmined_transaction_ids
1216 .iter()
1217 .map(|id| id.mined_id().encode_hex())
1218 .collect();
1219
1220 tx_ids.sort();
1222
1223 Ok(GetRawMempool::TxIds(tx_ids))
1224 }
1225
1226 _ => unreachable!("unmatched response to a transactionids request"),
1227 }
1228 }
1229
1230 async fn get_raw_transaction(
1231 &self,
1232 txid: String,
1233 verbose: Option<u8>,
1234 ) -> Result<GetRawTransaction> {
1235 let mut state = self.state.clone();
1236 let mut mempool = self.mempool.clone();
1237 let verbose = verbose.unwrap_or(0) != 0;
1238
1239 let txid = transaction::Hash::from_hex(txid)
1242 .map_error(server::error::LegacyCode::InvalidAddressOrKey)?;
1243
1244 match mempool
1246 .ready()
1247 .and_then(|service| {
1248 service.call(mempool::Request::TransactionsByMinedId([txid].into()))
1249 })
1250 .await
1251 .map_misc_error()?
1252 {
1253 mempool::Response::Transactions(txns) => {
1254 if let Some(tx) = txns.first() {
1255 return Ok(if verbose {
1256 GetRawTransaction::Object(Box::new(TransactionObject::from_transaction(
1257 tx.transaction.clone(),
1258 None,
1259 None,
1260 &self.network,
1261 None,
1262 )))
1263 } else {
1264 let hex = tx.transaction.clone().into();
1265 GetRawTransaction::Raw(hex)
1266 });
1267 }
1268 }
1269
1270 _ => unreachable!("unmatched response to a `TransactionsByMinedId` request"),
1271 };
1272
1273 match state
1275 .ready()
1276 .and_then(|service| service.call(zebra_state::ReadRequest::Transaction(txid)))
1277 .await
1278 .map_misc_error()?
1279 {
1280 zebra_state::ReadResponse::Transaction(Some(tx)) => Ok(if verbose {
1281 GetRawTransaction::Object(Box::new(TransactionObject::from_transaction(
1282 tx.tx.clone(),
1283 Some(tx.height),
1284 Some(tx.confirmations),
1285 &self.network,
1286 Some(tx.block_time),
1289 )))
1290 } else {
1291 let hex = tx.tx.into();
1292 GetRawTransaction::Raw(hex)
1293 }),
1294
1295 zebra_state::ReadResponse::Transaction(None) => {
1296 Err("No such mempool or main chain transaction")
1297 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1298 }
1299
1300 _ => unreachable!("unmatched response to a `Transaction` read request"),
1301 }
1302 }
1303
1304 async fn z_get_treestate(&self, hash_or_height: String) -> Result<GetTreestate> {
1308 let mut state = self.state.clone();
1309 let network = self.network.clone();
1310
1311 let hash_or_height =
1312 HashOrHeight::new(&hash_or_height, self.latest_chain_tip.best_tip_height())
1313 .map_error(server::error::LegacyCode::InvalidParameter)?;
1316
1317 let block = match state
1326 .ready()
1327 .and_then(|service| service.call(zebra_state::ReadRequest::Block(hash_or_height)))
1328 .await
1329 .map_misc_error()?
1330 {
1331 zebra_state::ReadResponse::Block(Some(block)) => block,
1332 zebra_state::ReadResponse::Block(None) => {
1333 return Err("the requested block is not in the main chain")
1336 .map_error(server::error::LegacyCode::InvalidParameter);
1337 }
1338 _ => unreachable!("unmatched response to a block request"),
1339 };
1340
1341 let hash = hash_or_height
1342 .hash_or_else(|_| Some(block.hash()))
1343 .expect("block hash");
1344
1345 let height = hash_or_height
1346 .height_or_else(|_| block.coinbase_height())
1347 .expect("verified blocks have a coinbase height");
1348
1349 let time = u32::try_from(block.header.time.timestamp())
1350 .expect("Timestamps of valid blocks always fit into u32.");
1351
1352 let sapling_nu = zcash_primitives::consensus::NetworkUpgrade::Sapling;
1353 let sapling = if network.is_nu_active(sapling_nu, height.into()) {
1354 match state
1355 .ready()
1356 .and_then(|service| {
1357 service.call(zebra_state::ReadRequest::SaplingTree(hash.into()))
1358 })
1359 .await
1360 .map_misc_error()?
1361 {
1362 zebra_state::ReadResponse::SaplingTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1363 _ => unreachable!("unmatched response to a Sapling tree request"),
1364 }
1365 } else {
1366 None
1367 };
1368
1369 let orchard_nu = zcash_primitives::consensus::NetworkUpgrade::Nu5;
1370 let orchard = if network.is_nu_active(orchard_nu, height.into()) {
1371 match state
1372 .ready()
1373 .and_then(|service| {
1374 service.call(zebra_state::ReadRequest::OrchardTree(hash.into()))
1375 })
1376 .await
1377 .map_misc_error()?
1378 {
1379 zebra_state::ReadResponse::OrchardTree(tree) => tree.map(|t| t.to_rpc_bytes()),
1380 _ => unreachable!("unmatched response to an Orchard tree request"),
1381 }
1382 } else {
1383 None
1384 };
1385
1386 Ok(GetTreestate::from_parts(
1387 hash, height, time, sapling, orchard,
1388 ))
1389 }
1390
1391 async fn z_get_subtrees_by_index(
1392 &self,
1393 pool: String,
1394 start_index: NoteCommitmentSubtreeIndex,
1395 limit: Option<NoteCommitmentSubtreeIndex>,
1396 ) -> Result<GetSubtrees> {
1397 let mut state = self.state.clone();
1398
1399 const POOL_LIST: &[&str] = &["sapling", "orchard"];
1400
1401 if pool == "sapling" {
1402 let request = zebra_state::ReadRequest::SaplingSubtrees { start_index, limit };
1403 let response = state
1404 .ready()
1405 .and_then(|service| service.call(request))
1406 .await
1407 .map_misc_error()?;
1408
1409 let subtrees = match response {
1410 zebra_state::ReadResponse::SaplingSubtrees(subtrees) => subtrees,
1411 _ => unreachable!("unmatched response to a subtrees request"),
1412 };
1413
1414 let subtrees = subtrees
1415 .values()
1416 .map(|subtree| SubtreeRpcData {
1417 root: subtree.root.encode_hex(),
1418 end_height: subtree.end_height,
1419 })
1420 .collect();
1421
1422 Ok(GetSubtrees {
1423 pool,
1424 start_index,
1425 subtrees,
1426 })
1427 } else if pool == "orchard" {
1428 let request = zebra_state::ReadRequest::OrchardSubtrees { start_index, limit };
1429 let response = state
1430 .ready()
1431 .and_then(|service| service.call(request))
1432 .await
1433 .map_misc_error()?;
1434
1435 let subtrees = match response {
1436 zebra_state::ReadResponse::OrchardSubtrees(subtrees) => subtrees,
1437 _ => unreachable!("unmatched response to a subtrees request"),
1438 };
1439
1440 let subtrees = subtrees
1441 .values()
1442 .map(|subtree| SubtreeRpcData {
1443 root: subtree.root.encode_hex(),
1444 end_height: subtree.end_height,
1445 })
1446 .collect();
1447
1448 Ok(GetSubtrees {
1449 pool,
1450 start_index,
1451 subtrees,
1452 })
1453 } else {
1454 Err(ErrorObject::owned(
1455 server::error::LegacyCode::Misc.into(),
1456 format!("invalid pool name, must be one of: {:?}", POOL_LIST).as_str(),
1457 None::<()>,
1458 ))
1459 }
1460 }
1461
1462 async fn get_address_tx_ids(&self, request: GetAddressTxIdsRequest) -> Result<Vec<String>> {
1463 let mut state = self.state.clone();
1464 let latest_chain_tip = self.latest_chain_tip.clone();
1465
1466 let height_range = build_height_range(
1467 request.start,
1468 request.end,
1469 best_chain_tip_height(&latest_chain_tip)?,
1470 )?;
1471
1472 let valid_addresses = AddressStrings {
1473 addresses: request.addresses,
1474 }
1475 .valid_addresses()?;
1476
1477 let request = zebra_state::ReadRequest::TransactionIdsByAddresses {
1478 addresses: valid_addresses,
1479 height_range,
1480 };
1481 let response = state
1482 .ready()
1483 .and_then(|service| service.call(request))
1484 .await
1485 .map_misc_error()?;
1486
1487 let hashes = match response {
1488 zebra_state::ReadResponse::AddressesTransactionIds(hashes) => {
1489 let mut last_tx_location = TransactionLocation::from_usize(Height(0), 0);
1490
1491 hashes
1492 .iter()
1493 .map(|(tx_loc, tx_id)| {
1494 assert!(
1496 *tx_loc > last_tx_location,
1497 "Transactions were not in chain order:\n\
1498 {tx_loc:?} {tx_id:?} was after:\n\
1499 {last_tx_location:?}",
1500 );
1501
1502 last_tx_location = *tx_loc;
1503
1504 tx_id.to_string()
1505 })
1506 .collect()
1507 }
1508 _ => unreachable!("unmatched response to a TransactionsByAddresses request"),
1509 };
1510
1511 Ok(hashes)
1512 }
1513
1514 async fn get_address_utxos(
1515 &self,
1516 address_strings: AddressStrings,
1517 ) -> Result<Vec<GetAddressUtxos>> {
1518 let mut state = self.state.clone();
1519 let mut response_utxos = vec![];
1520
1521 let valid_addresses = address_strings.valid_addresses()?;
1522
1523 let request = zebra_state::ReadRequest::UtxosByAddresses(valid_addresses);
1525 let response = state
1526 .ready()
1527 .and_then(|service| service.call(request))
1528 .await
1529 .map_misc_error()?;
1530 let utxos = match response {
1531 zebra_state::ReadResponse::AddressUtxos(utxos) => utxos,
1532 _ => unreachable!("unmatched response to a UtxosByAddresses request"),
1533 };
1534
1535 let mut last_output_location = OutputLocation::from_usize(Height(0), 0, 0);
1536
1537 for utxo_data in utxos.utxos() {
1538 let address = utxo_data.0;
1539 let txid = *utxo_data.1;
1540 let height = utxo_data.2.height();
1541 let output_index = utxo_data.2.output_index();
1542 let script = utxo_data.3.lock_script.clone();
1543 let satoshis = u64::from(utxo_data.3.value);
1544
1545 let output_location = *utxo_data.2;
1546 assert!(
1548 output_location > last_output_location,
1549 "UTXOs were not in chain order:\n\
1550 {output_location:?} {address:?} {txid:?} was after:\n\
1551 {last_output_location:?}",
1552 );
1553
1554 let entry = GetAddressUtxos {
1555 address,
1556 txid,
1557 output_index,
1558 script,
1559 satoshis,
1560 height,
1561 };
1562 response_utxos.push(entry);
1563
1564 last_output_location = output_location;
1565 }
1566
1567 Ok(response_utxos)
1568 }
1569
1570 fn stop(&self) -> Result<String> {
1571 #[cfg(not(target_os = "windows"))]
1572 if self.network.is_regtest() {
1573 match nix::sys::signal::raise(nix::sys::signal::SIGINT) {
1574 Ok(_) => Ok("Zebra server stopping".to_string()),
1575 Err(error) => Err(ErrorObject::owned(
1576 ErrorCode::InternalError.code(),
1577 format!("Failed to shut down: {}", error).as_str(),
1578 None::<()>,
1579 )),
1580 }
1581 } else {
1582 Err(ErrorObject::borrowed(
1583 ErrorCode::MethodNotFound.code(),
1584 "stop is only available on regtest networks",
1585 None,
1586 ))
1587 }
1588 #[cfg(target_os = "windows")]
1589 Err(ErrorObject::borrowed(
1590 ErrorCode::MethodNotFound.code(),
1591 "stop is not available in windows targets",
1592 None,
1593 ))
1594 }
1595}
1596
1597pub fn best_chain_tip_height<Tip>(latest_chain_tip: &Tip) -> Result<Height>
1600where
1601 Tip: ChainTip + Clone + Send + Sync + 'static,
1602{
1603 latest_chain_tip
1604 .best_tip_height()
1605 .ok_or_misc_error("No blocks in state")
1606}
1607
1608#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
1612pub struct GetInfo {
1613 version: u64,
1615
1616 build: String,
1618
1619 subversion: String,
1621
1622 #[serde(rename = "protocolversion")]
1624 protocol_version: u32,
1625
1626 blocks: u32,
1628
1629 connections: usize,
1631
1632 #[serde(skip_serializing_if = "Option::is_none")]
1634 proxy: Option<String>,
1635
1636 difficulty: f64,
1638
1639 testnet: bool,
1641
1642 #[serde(rename = "paytxfee")]
1644 pay_tx_fee: f64,
1645
1646 #[serde(rename = "relayfee")]
1648 relay_fee: f64,
1649
1650 errors: String,
1652
1653 #[serde(rename = "errorstimestamp")]
1655 errors_timestamp: String,
1656}
1657
1658impl Default for GetInfo {
1659 fn default() -> Self {
1660 GetInfo {
1661 version: 0,
1662 build: "some build version".to_string(),
1663 subversion: "some subversion".to_string(),
1664 protocol_version: 0,
1665 blocks: 0,
1666 connections: 0,
1667 proxy: None,
1668 difficulty: 0.0,
1669 testnet: false,
1670 pay_tx_fee: 0.0,
1671 relay_fee: 0.0,
1672 errors: "no errors".to_string(),
1673 errors_timestamp: "no errors timestamp".to_string(),
1674 }
1675 }
1676}
1677
1678impl GetInfo {
1679 #[allow(clippy::too_many_arguments)]
1681 pub fn from_parts(
1682 version: u64,
1683 build: String,
1684 subversion: String,
1685 protocol_version: u32,
1686 blocks: u32,
1687 connections: usize,
1688 proxy: Option<String>,
1689 difficulty: f64,
1690 testnet: bool,
1691 pay_tx_fee: f64,
1692 relay_fee: f64,
1693 errors: String,
1694 errors_timestamp: String,
1695 ) -> Self {
1696 Self {
1697 version,
1698 build,
1699 subversion,
1700 protocol_version,
1701 blocks,
1702 connections,
1703 proxy,
1704 difficulty,
1705 testnet,
1706 pay_tx_fee,
1707 relay_fee,
1708 errors,
1709 errors_timestamp,
1710 }
1711 }
1712
1713 pub fn into_parts(
1715 self,
1716 ) -> (
1717 u64,
1718 String,
1719 String,
1720 u32,
1721 u32,
1722 usize,
1723 Option<String>,
1724 f64,
1725 bool,
1726 f64,
1727 f64,
1728 String,
1729 String,
1730 ) {
1731 (
1732 self.version,
1733 self.build,
1734 self.subversion,
1735 self.protocol_version,
1736 self.blocks,
1737 self.connections,
1738 self.proxy,
1739 self.difficulty,
1740 self.testnet,
1741 self.pay_tx_fee,
1742 self.relay_fee,
1743 self.errors,
1744 self.errors_timestamp,
1745 )
1746 }
1747
1748 pub fn version(build_string: &str) -> Option<u64> {
1750 let semver_version = semver::Version::parse(build_string.strip_prefix('v')?).ok()?;
1751 let build_number = semver_version
1752 .build
1753 .as_str()
1754 .split('.')
1755 .next()
1756 .and_then(|num_str| num_str.parse::<u64>().ok())
1757 .unwrap_or_default();
1758
1759 let version_number = 1_000_000 * semver_version.major
1761 + 10_000 * semver_version.minor
1762 + 100 * semver_version.patch
1763 + build_number;
1764
1765 Some(version_number)
1766 }
1767}
1768
1769#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
1773pub struct GetBlockChainInfo {
1774 chain: String,
1776
1777 blocks: Height,
1779
1780 headers: Height,
1783
1784 difficulty: f64,
1786
1787 #[serde(rename = "verificationprogress")]
1789 verification_progress: f64,
1790
1791 #[serde(rename = "chainwork")]
1793 chain_work: u64,
1794
1795 pruned: bool,
1797
1798 size_on_disk: u64,
1800
1801 commitments: u64,
1803
1804 #[serde(rename = "bestblockhash", with = "hex")]
1806 best_block_hash: block::Hash,
1807
1808 #[serde(rename = "estimatedheight")]
1812 estimated_height: Height,
1813
1814 #[serde(rename = "chainSupply")]
1816 chain_supply: types::Balance,
1817
1818 #[serde(rename = "valuePools")]
1820 value_pools: [types::Balance; 5],
1821
1822 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
1824
1825 consensus: TipConsensusBranch,
1827}
1828
1829impl Default for GetBlockChainInfo {
1830 fn default() -> Self {
1831 GetBlockChainInfo {
1832 chain: "main".to_string(),
1833 blocks: Height(1),
1834 best_block_hash: block::Hash([0; 32]),
1835 estimated_height: Height(1),
1836 chain_supply: types::Balance::chain_supply(Default::default()),
1837 value_pools: types::Balance::zero_pools(),
1838 upgrades: IndexMap::new(),
1839 consensus: TipConsensusBranch {
1840 chain_tip: ConsensusBranchIdHex(ConsensusBranchId::default()),
1841 next_block: ConsensusBranchIdHex(ConsensusBranchId::default()),
1842 },
1843 headers: Height(1),
1844 difficulty: 0.0,
1845 verification_progress: 0.0,
1846 chain_work: 0,
1847 pruned: false,
1848 size_on_disk: 0,
1849 commitments: 0,
1850 }
1851 }
1852}
1853
1854impl GetBlockChainInfo {
1855 #[allow(clippy::too_many_arguments)]
1857 pub fn new(
1858 chain: String,
1859 blocks: Height,
1860 best_block_hash: block::Hash,
1861 estimated_height: Height,
1862 chain_supply: types::Balance,
1863 value_pools: [types::Balance; 5],
1864 upgrades: IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo>,
1865 consensus: TipConsensusBranch,
1866 headers: Height,
1867 difficulty: f64,
1868 verification_progress: f64,
1869 chain_work: u64,
1870 pruned: bool,
1871 size_on_disk: u64,
1872 commitments: u64,
1873 ) -> Self {
1874 Self {
1875 chain,
1876 blocks,
1877 best_block_hash,
1878 estimated_height,
1879 chain_supply,
1880 value_pools,
1881 upgrades,
1882 consensus,
1883 headers,
1884 difficulty,
1885 verification_progress,
1886 chain_work,
1887 pruned,
1888 size_on_disk,
1889 commitments,
1890 }
1891 }
1892
1893 pub fn chain(&self) -> String {
1895 self.chain.clone()
1896 }
1897
1898 pub fn blocks(&self) -> Height {
1900 self.blocks
1901 }
1902
1903 pub fn best_block_hash(&self) -> &block::Hash {
1905 &self.best_block_hash
1906 }
1907
1908 pub fn estimated_height(&self) -> Height {
1914 self.estimated_height
1915 }
1916
1917 pub fn value_pools(&self) -> &[types::Balance; 5] {
1919 &self.value_pools
1920 }
1921
1922 pub fn upgrades(&self) -> &IndexMap<ConsensusBranchIdHex, NetworkUpgradeInfo> {
1924 &self.upgrades
1925 }
1926
1927 pub fn consensus(&self) -> &TipConsensusBranch {
1929 &self.consensus
1930 }
1931}
1932
1933#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize)]
1938pub struct AddressStrings {
1939 addresses: Vec<String>,
1941}
1942
1943impl AddressStrings {
1944 #[cfg(test)]
1946 pub fn new(addresses: Vec<String>) -> AddressStrings {
1947 AddressStrings { addresses }
1948 }
1949
1950 pub fn new_valid(addresses: Vec<String>) -> Result<AddressStrings> {
1952 let address_strings = Self { addresses };
1953 address_strings.clone().valid_addresses()?;
1954 Ok(address_strings)
1955 }
1956
1957 pub fn valid_addresses(self) -> Result<HashSet<Address>> {
1961 let valid_addresses: HashSet<Address> = self
1964 .addresses
1965 .into_iter()
1966 .map(|address| {
1967 address
1968 .parse()
1969 .map_error(server::error::LegacyCode::InvalidAddressOrKey)
1970 })
1971 .collect::<Result<_>>()?;
1972
1973 Ok(valid_addresses)
1974 }
1975
1976 pub fn valid_address_strings(self) -> Result<Vec<String>> {
1980 self.clone().valid_addresses()?;
1981 Ok(self.addresses)
1982 }
1983}
1984
1985#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize)]
1987pub struct AddressBalance {
1988 pub balance: u64,
1990}
1991
1992#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
1994pub struct ConsensusBranchIdHex(#[serde(with = "hex")] ConsensusBranchId);
1995
1996impl ConsensusBranchIdHex {
1997 pub fn new(consensus_branch_id: u32) -> Self {
1999 ConsensusBranchIdHex(consensus_branch_id.into())
2000 }
2001
2002 pub fn inner(&self) -> u32 {
2004 self.0.into()
2005 }
2006}
2007
2008#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
2010pub struct NetworkUpgradeInfo {
2011 name: NetworkUpgrade,
2015
2016 #[serde(rename = "activationheight")]
2018 activation_height: Height,
2019
2020 status: NetworkUpgradeStatus,
2022}
2023
2024impl NetworkUpgradeInfo {
2025 pub fn from_parts(
2027 name: NetworkUpgrade,
2028 activation_height: Height,
2029 status: NetworkUpgradeStatus,
2030 ) -> Self {
2031 Self {
2032 name,
2033 activation_height,
2034 status,
2035 }
2036 }
2037
2038 pub fn into_parts(self) -> (NetworkUpgrade, Height, NetworkUpgradeStatus) {
2040 (self.name, self.activation_height, self.status)
2041 }
2042}
2043
2044#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
2046pub enum NetworkUpgradeStatus {
2047 #[serde(rename = "active")]
2052 Active,
2053
2054 #[serde(rename = "disabled")]
2056 Disabled,
2057
2058 #[serde(rename = "pending")]
2060 Pending,
2061}
2062
2063#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
2067pub struct TipConsensusBranch {
2068 #[serde(rename = "chaintip")]
2070 chain_tip: ConsensusBranchIdHex,
2071
2072 #[serde(rename = "nextblock")]
2074 next_block: ConsensusBranchIdHex,
2075}
2076
2077impl TipConsensusBranch {
2078 pub fn from_parts(chain_tip: u32, next_block: u32) -> Self {
2080 Self {
2081 chain_tip: ConsensusBranchIdHex::new(chain_tip),
2082 next_block: ConsensusBranchIdHex::new(next_block),
2083 }
2084 }
2085
2086 pub fn into_parts(self) -> (u32, u32) {
2088 (self.chain_tip.inner(), self.next_block.inner())
2089 }
2090}
2091
2092#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
2098pub struct SentTransactionHash(#[serde(with = "hex")] transaction::Hash);
2099
2100impl Default for SentTransactionHash {
2101 fn default() -> Self {
2102 Self(transaction::Hash::from([0; 32]))
2103 }
2104}
2105
2106impl SentTransactionHash {
2107 pub fn new(hash: transaction::Hash) -> Self {
2109 SentTransactionHash(hash)
2110 }
2111
2112 pub fn inner(&self) -> transaction::Hash {
2114 self.0
2115 }
2116}
2117
2118#[derive(Clone, Debug, PartialEq, serde::Serialize)]
2122#[serde(untagged)]
2123#[allow(clippy::large_enum_variant)] pub enum GetBlock {
2125 Raw(#[serde(with = "hex")] SerializedBlock),
2127 Object {
2129 hash: GetBlockHash,
2131
2132 confirmations: i64,
2135
2136 #[serde(skip_serializing_if = "Option::is_none")]
2138 size: Option<i64>,
2139
2140 #[serde(skip_serializing_if = "Option::is_none")]
2142 height: Option<Height>,
2143
2144 #[serde(skip_serializing_if = "Option::is_none")]
2146 version: Option<u32>,
2147
2148 #[serde(with = "opthex", rename = "merkleroot")]
2150 #[serde(skip_serializing_if = "Option::is_none")]
2151 merkle_root: Option<block::merkle::Root>,
2152
2153 #[serde(with = "opthex", rename = "blockcommitments")]
2156 #[serde(skip_serializing_if = "Option::is_none")]
2157 block_commitments: Option<[u8; 32]>,
2158
2159 #[serde(with = "opthex", rename = "finalsaplingroot")]
2163 #[serde(skip_serializing_if = "Option::is_none")]
2164 final_sapling_root: Option<[u8; 32]>,
2165
2166 #[serde(with = "opthex", rename = "finalorchardroot")]
2168 #[serde(skip_serializing_if = "Option::is_none")]
2169 final_orchard_root: Option<[u8; 32]>,
2170
2171 tx: Vec<GetBlockTransaction>,
2176
2177 #[serde(skip_serializing_if = "Option::is_none")]
2179 time: Option<i64>,
2180
2181 #[serde(with = "opthex")]
2183 #[serde(skip_serializing_if = "Option::is_none")]
2184 nonce: Option<[u8; 32]>,
2185
2186 #[serde(with = "opthex")]
2189 #[serde(skip_serializing_if = "Option::is_none")]
2190 solution: Option<Solution>,
2191
2192 #[serde(with = "opthex")]
2194 #[serde(skip_serializing_if = "Option::is_none")]
2195 bits: Option<CompactDifficulty>,
2196
2197 #[serde(skip_serializing_if = "Option::is_none")]
2200 difficulty: Option<f64>,
2201
2202 trees: GetBlockTrees,
2209
2210 #[serde(rename = "previousblockhash", skip_serializing_if = "Option::is_none")]
2212 previous_block_hash: Option<GetBlockHash>,
2213
2214 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
2216 next_block_hash: Option<GetBlockHash>,
2217 },
2218}
2219
2220impl Default for GetBlock {
2221 fn default() -> Self {
2222 GetBlock::Object {
2223 hash: GetBlockHash::default(),
2224 confirmations: 0,
2225 height: None,
2226 time: None,
2227 tx: Vec::new(),
2228 trees: GetBlockTrees::default(),
2229 size: None,
2230 version: None,
2231 merkle_root: None,
2232 block_commitments: None,
2233 final_sapling_root: None,
2234 final_orchard_root: None,
2235 nonce: None,
2236 bits: None,
2237 difficulty: None,
2238 previous_block_hash: None,
2239 next_block_hash: None,
2240 solution: None,
2241 }
2242 }
2243}
2244
2245#[derive(Clone, Debug, PartialEq, serde::Serialize)]
2246#[serde(untagged)]
2247pub enum GetBlockTransaction {
2250 Hash(#[serde(with = "hex")] transaction::Hash),
2252 Object(Box<TransactionObject>),
2254}
2255
2256#[derive(Clone, Debug, PartialEq, serde::Serialize)]
2260#[serde(untagged)]
2261pub enum GetBlockHeader {
2262 Raw(hex_data::HexData),
2264
2265 Object(Box<GetBlockHeaderObject>),
2267}
2268
2269#[derive(Clone, Debug, PartialEq, serde::Serialize)]
2270pub struct GetBlockHeaderObject {
2274 pub hash: GetBlockHash,
2276
2277 pub confirmations: i64,
2280
2281 pub height: Height,
2283
2284 pub version: u32,
2286
2287 #[serde(with = "hex", rename = "merkleroot")]
2289 pub merkle_root: block::merkle::Root,
2290
2291 #[serde(with = "hex", rename = "blockcommitments")]
2294 pub block_commitments: [u8; 32],
2295
2296 #[serde(with = "hex", rename = "finalsaplingroot")]
2298 pub final_sapling_root: [u8; 32],
2299
2300 #[serde(skip)]
2303 pub sapling_tree_size: u64,
2304
2305 pub time: i64,
2307
2308 #[serde(with = "hex")]
2310 pub nonce: [u8; 32],
2311
2312 #[serde(with = "hex")]
2314 pub solution: Solution,
2315
2316 #[serde(with = "hex")]
2318 pub bits: CompactDifficulty,
2319
2320 pub difficulty: f64,
2323
2324 #[serde(rename = "previousblockhash")]
2326 pub previous_block_hash: GetBlockHash,
2327
2328 #[serde(rename = "nextblockhash", skip_serializing_if = "Option::is_none")]
2330 pub next_block_hash: Option<GetBlockHash>,
2331}
2332
2333impl Default for GetBlockHeader {
2334 fn default() -> Self {
2335 GetBlockHeader::Object(Box::default())
2336 }
2337}
2338
2339impl Default for GetBlockHeaderObject {
2340 fn default() -> Self {
2341 let difficulty: ExpandedDifficulty = zebra_chain::work::difficulty::U256::one().into();
2342
2343 GetBlockHeaderObject {
2344 hash: GetBlockHash::default(),
2345 confirmations: 0,
2346 height: Height::MIN,
2347 version: 4,
2348 merkle_root: block::merkle::Root([0; 32]),
2349 block_commitments: Default::default(),
2350 final_sapling_root: Default::default(),
2351 sapling_tree_size: Default::default(),
2352 time: 0,
2353 nonce: [0; 32],
2354 solution: Solution::for_proposal(),
2355 bits: difficulty.to_compact(),
2356 difficulty: 1.0,
2357 previous_block_hash: Default::default(),
2358 next_block_hash: Default::default(),
2359 }
2360 }
2361}
2362
2363#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
2369#[serde(transparent)]
2370pub struct GetBlockHash(#[serde(with = "hex")] pub block::Hash);
2371
2372#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
2374pub struct GetBlockHeightAndHash {
2375 pub height: block::Height,
2377 pub hash: block::Hash,
2379}
2380
2381impl Default for GetBlockHeightAndHash {
2382 fn default() -> Self {
2383 Self {
2384 height: block::Height::MIN,
2385 hash: block::Hash([0; 32]),
2386 }
2387 }
2388}
2389
2390impl Default for GetBlockHash {
2391 fn default() -> Self {
2392 GetBlockHash(block::Hash([0; 32]))
2393 }
2394}
2395
2396#[derive(Clone, Debug, PartialEq, serde::Serialize)]
2400#[serde(untagged)]
2401pub enum GetRawTransaction {
2402 Raw(#[serde(with = "hex")] SerializedTransaction),
2404 Object(Box<TransactionObject>),
2406}
2407
2408impl Default for GetRawTransaction {
2409 fn default() -> Self {
2410 Self::Object(Box::default())
2411 }
2412}
2413
2414#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
2418pub struct GetAddressUtxos {
2419 address: transparent::Address,
2421
2422 #[serde(with = "hex")]
2424 txid: transaction::Hash,
2425
2426 #[serde(rename = "outputIndex")]
2428 output_index: OutputIndex,
2429
2430 #[serde(with = "hex")]
2432 script: transparent::Script,
2433
2434 satoshis: u64,
2436
2437 height: Height,
2441}
2442
2443impl Default for GetAddressUtxos {
2444 fn default() -> Self {
2445 Self {
2446 address: transparent::Address::from_pub_key_hash(
2447 zebra_chain::parameters::NetworkKind::default(),
2448 [0u8; 20],
2449 ),
2450 txid: transaction::Hash::from([0; 32]),
2451 output_index: OutputIndex::from_u64(0),
2452 script: transparent::Script::new(&[0u8; 10]),
2453 satoshis: u64::default(),
2454 height: Height(0),
2455 }
2456 }
2457}
2458
2459impl GetAddressUtxos {
2460 pub fn from_parts(
2462 address: transparent::Address,
2463 txid: transaction::Hash,
2464 output_index: OutputIndex,
2465 script: transparent::Script,
2466 satoshis: u64,
2467 height: Height,
2468 ) -> Self {
2469 GetAddressUtxos {
2470 address,
2471 txid,
2472 output_index,
2473 script,
2474 satoshis,
2475 height,
2476 }
2477 }
2478
2479 pub fn into_parts(
2481 &self,
2482 ) -> (
2483 transparent::Address,
2484 transaction::Hash,
2485 OutputIndex,
2486 transparent::Script,
2487 u64,
2488 Height,
2489 ) {
2490 (
2491 self.address.clone(),
2492 self.txid,
2493 self.output_index,
2494 self.script.clone(),
2495 self.satoshis,
2496 self.height,
2497 )
2498 }
2499}
2500
2501#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize)]
2505pub struct GetAddressTxIdsRequest {
2506 addresses: Vec<String>,
2508 start: Option<u32>,
2510 end: Option<u32>,
2512}
2513
2514impl GetAddressTxIdsRequest {
2515 pub fn from_parts(addresses: Vec<String>, start: u32, end: u32) -> Self {
2517 GetAddressTxIdsRequest {
2518 addresses,
2519 start: Some(start),
2520 end: Some(end),
2521 }
2522 }
2523 pub fn into_parts(&self) -> (Vec<String>, u32, u32) {
2525 (
2526 self.addresses.clone(),
2527 self.start.unwrap_or(0),
2528 self.end.unwrap_or(0),
2529 )
2530 }
2531}
2532
2533#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
2535pub struct GetBlockTrees {
2536 #[serde(skip_serializing_if = "SaplingTrees::is_empty")]
2537 sapling: SaplingTrees,
2538 #[serde(skip_serializing_if = "OrchardTrees::is_empty")]
2539 orchard: OrchardTrees,
2540}
2541
2542impl Default for GetBlockTrees {
2543 fn default() -> Self {
2544 GetBlockTrees {
2545 sapling: SaplingTrees { size: 0 },
2546 orchard: OrchardTrees { size: 0 },
2547 }
2548 }
2549}
2550
2551impl GetBlockTrees {
2552 pub fn new(sapling: u64, orchard: u64) -> Self {
2554 GetBlockTrees {
2555 sapling: SaplingTrees { size: sapling },
2556 orchard: OrchardTrees { size: orchard },
2557 }
2558 }
2559
2560 pub fn sapling(self) -> u64 {
2562 self.sapling.size
2563 }
2564
2565 pub fn orchard(self) -> u64 {
2567 self.orchard.size
2568 }
2569}
2570
2571#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
2573pub struct SaplingTrees {
2574 size: u64,
2575}
2576
2577impl SaplingTrees {
2578 fn is_empty(&self) -> bool {
2579 self.size == 0
2580 }
2581}
2582
2583#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
2585pub struct OrchardTrees {
2586 size: u64,
2587}
2588
2589impl OrchardTrees {
2590 fn is_empty(&self) -> bool {
2591 self.size == 0
2592 }
2593}
2594
2595fn build_height_range(
2611 start: Option<u32>,
2612 end: Option<u32>,
2613 chain_height: Height,
2614) -> Result<RangeInclusive<Height>> {
2615 let start = Height(start.unwrap_or(0)).min(chain_height);
2618
2619 let end = match end {
2621 Some(0) | None => chain_height,
2622 Some(val) => Height(val).min(chain_height),
2623 };
2624
2625 if start > end {
2626 return Err(ErrorObject::owned(
2627 ErrorCode::InvalidParams.code(),
2628 format!("start {start:?} must be less than or equal to end {end:?}"),
2629 None::<()>,
2630 ));
2631 }
2632
2633 Ok(start..=end)
2634}
2635
2636#[allow(dead_code)]
2644pub fn height_from_signed_int(index: i32, tip_height: Height) -> Result<Height> {
2645 if index >= 0 {
2646 let height = index.try_into().expect("Positive i32 always fits in u32");
2647 if height > tip_height.0 {
2648 return Err(ErrorObject::borrowed(
2649 ErrorCode::InvalidParams.code(),
2650 "Provided index is greater than the current tip",
2651 None,
2652 ));
2653 }
2654 Ok(Height(height))
2655 } else {
2656 let height = i32::try_from(tip_height.0)
2658 .expect("tip height fits in i32, because Height::MAX fits in i32")
2659 .checked_add(index + 1);
2660
2661 let sanitized_height = match height {
2662 None => {
2663 return Err(ErrorObject::borrowed(
2664 ErrorCode::InvalidParams.code(),
2665 "Provided index is not valid",
2666 None,
2667 ))
2668 }
2669 Some(h) => {
2670 if h < 0 {
2671 return Err(ErrorObject::borrowed(
2672 ErrorCode::InvalidParams.code(),
2673 "Provided negative index ends up with a negative height",
2674 None,
2675 ));
2676 }
2677 let h: u32 = h.try_into().expect("Positive i32 always fits in u32");
2678 if h > tip_height.0 {
2679 return Err(ErrorObject::borrowed(
2680 ErrorCode::InvalidParams.code(),
2681 "Provided index is greater than the current tip",
2682 None,
2683 ));
2684 }
2685
2686 h
2687 }
2688 };
2689
2690 Ok(Height(sanitized_height))
2691 }
2692}
2693
2694mod opthex {
2696 use hex::ToHex;
2697 use serde::Serializer;
2698
2699 pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
2700 where
2701 S: Serializer,
2702 T: ToHex,
2703 {
2704 match data {
2705 Some(data) => {
2706 let s = data.encode_hex::<String>();
2707 serializer.serialize_str(&s)
2708 }
2709 None => serializer.serialize_none(),
2710 }
2711 }
2712}
2713pub async fn chain_tip_difficulty<State>(
2715 network: Network,
2716 mut state: State,
2717 should_use_default: bool,
2718) -> Result<f64>
2719where
2720 State: Service<
2721 zebra_state::ReadRequest,
2722 Response = zebra_state::ReadResponse,
2723 Error = zebra_state::BoxError,
2724 > + Clone
2725 + Send
2726 + Sync
2727 + 'static,
2728 State::Future: Send,
2729{
2730 let request = ReadRequest::ChainInfo;
2731
2732 let response = state
2738 .ready()
2739 .and_then(|service| service.call(request))
2740 .await;
2741
2742 let response = match (should_use_default, response) {
2743 (_, Ok(res)) => res,
2744 (true, Err(_)) => {
2745 return Ok((U256::from(network.target_difficulty_limit()) >> 128).as_u128() as f64)
2746 }
2747 (false, Err(error)) => return Err(ErrorObject::owned(0, error.to_string(), None::<()>)),
2748 };
2749
2750 let chain_info = match response {
2751 ReadResponse::ChainInfo(info) => info,
2752 _ => unreachable!("unmatched response to a chain info request"),
2753 };
2754
2755 let pow_limit: U256 = network.target_difficulty_limit().into();
2778 let Some(difficulty) = chain_info.expected_difficulty.to_expanded() else {
2779 return Ok(0.0);
2780 };
2781
2782 let pow_limit = pow_limit >> 128;
2784 let difficulty = U256::from(difficulty) >> 128;
2785
2786 let pow_limit = pow_limit.as_u128() as f64;
2789 let difficulty = difficulty.as_u128() as f64;
2790
2791 Ok(pow_limit / difficulty)
2793}