use std::{error::Error, sync::Arc};
use crate::chain_index::{
types::{BlockHash, TransactionHash},
ShieldedPool,
};
use crate::SendFut;
use futures::TryFutureExt as _;
use incrementalmerkletree::frontier::CommitmentTree;
use tower::{Service, ServiceExt as _};
use zaino_fetch::jsonrpsee::{
connector::{JsonRpSeeConnector, RpcRequestError},
response::{
address_deltas::{GetAddressDeltasParams, GetAddressDeltasResponse},
block_header::GetBlockHeader,
block_subsidy::GetBlockSubsidy,
mining_info::GetMiningInfoWire,
peer_info::GetPeerInfo,
GetBlockError, GetBlockResponse, GetNetworkSolPsResponse, GetSpentInfoRequest,
GetSpentInfoResponse, GetTransactionResponse, GetTreestateResponse, GetTxOutResponse,
},
};
use zcash_primitives::merkle_tree::{read_commitment_tree, write_commitment_tree};
use zebra_chain::{
block::TryIntoHeight, serialization::ZcashDeserialize, subtree::NoteCommitmentSubtreeIndex,
};
use zebra_rpc::{
client::{GetAddressBalanceRequest, GetAddressTxIdsRequest},
methods::{
AddressBalance, GetAddressUtxos, GetBlockchainInfoResponse, GetInfo, SentTransactionHash,
},
};
use zebra_state::{HashOrHeight, ReadRequest, ReadResponse, ReadStateService};
#[cfg(test)]
pub(crate) mod mockchain_source;
pub mod validator_connector;
pub use validator_connector::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PoolTreestate {
pub final_root: Option<Vec<u8>>,
pub final_state: Vec<u8>,
}
pub(crate) type TreestateBytes = (
Option<PoolTreestate>,
Option<PoolTreestate>,
Option<PoolTreestate>,
);
pub(crate) type ShieldedTreeRoots = (
Option<(zebra_chain::sapling::tree::Root, u64)>,
Option<(zebra_chain::orchard::tree::Root, u64)>,
Option<(zebra_chain::orchard::tree::Root, u64)>,
);
pub(crate) type NonfinalizedBlockReceiver =
tokio::sync::mpsc::Receiver<(zebra_chain::block::Hash, Arc<zebra_chain::block::Block>)>;
pub trait BlockchainSource: Clone + Send + Sync + 'static {
fn get_block(
&self,
id: HashOrHeight,
) -> impl SendFut<BlockchainSourceResult<Option<Arc<zebra_chain::block::Block>>>>;
fn get_block_verbose(
&self,
hash_or_height: HashOrHeight,
verbosity: Option<u8>,
) -> impl SendFut<BlockchainSourceResult<zebra_rpc::methods::GetBlock>>;
fn get_block_header(
&self,
hash: String,
verbose: bool,
) -> impl SendFut<BlockchainSourceResult<GetBlockHeader>>;
fn get_block_deltas(
&self,
hash: String,
) -> impl SendFut<BlockchainSourceResult<zaino_fetch::jsonrpsee::response::block_deltas::BlockDeltas>>;
fn get_transaction(
&self,
txid: TransactionHash,
) -> impl SendFut<
BlockchainSourceResult<
Option<(
Arc<zebra_chain::transaction::Transaction>,
GetTransactionLocation,
)>,
>,
>;
fn get_mempool_txids(
&self,
) -> impl SendFut<BlockchainSourceResult<Option<Vec<zebra_chain::transaction::Hash>>>>;
fn get_best_block_hash(
&self,
) -> impl SendFut<BlockchainSourceResult<Option<zebra_chain::block::Hash>>>;
fn get_best_block_height(
&self,
) -> impl SendFut<BlockchainSourceResult<Option<zebra_chain::block::Height>>>;
fn get_difficulty(&self) -> impl SendFut<BlockchainSourceResult<f64>>;
fn chain_tip_change(&self) -> Option<zebra_state::ChainTipChange> {
None
}
fn get_blockchain_info(
&self,
) -> impl SendFut<BlockchainSourceResult<GetBlockchainInfoResponse>>;
fn get_info(&self) -> impl SendFut<BlockchainSourceResult<GetInfo>>;
fn get_peer_info(&self) -> impl SendFut<BlockchainSourceResult<GetPeerInfo>>;
fn get_chain_tips(
&self,
) -> impl SendFut<
BlockchainSourceResult<zaino_fetch::jsonrpsee::response::chain_tips::GetChainTipsResponse>,
>;
fn get_block_subsidy(
&self,
height: u32,
) -> impl SendFut<BlockchainSourceResult<GetBlockSubsidy>>;
fn get_mining_info(&self) -> impl SendFut<BlockchainSourceResult<GetMiningInfoWire>>;
fn get_tx_out(
&self,
txid: String,
n: u32,
include_mempool: Option<bool>,
) -> impl SendFut<BlockchainSourceResult<GetTxOutResponse>>;
fn get_spent_info(
&self,
request: GetSpentInfoRequest,
) -> impl SendFut<BlockchainSourceResult<GetSpentInfoResponse>>;
fn get_network_sol_ps(
&self,
blocks: Option<i32>,
height: Option<i32>,
) -> impl SendFut<BlockchainSourceResult<GetNetworkSolPsResponse>>;
fn send_raw_transaction(
&self,
raw_transaction_hex: String,
) -> impl SendFut<BlockchainSourceResult<SentTransactionHash>>;
fn get_treestate_by_id(
&self,
hash_or_height: String,
) -> impl SendFut<BlockchainSourceResult<zebra_rpc::client::GetTreestateResponse>>;
fn get_treestate(&self, id: BlockHash) -> impl SendFut<BlockchainSourceResult<TreestateBytes>>;
fn get_subtree_roots(
&self,
pool: ShieldedPool,
start_index: u16,
max_entries: Option<u16>,
) -> impl SendFut<BlockchainSourceResult<Vec<([u8; 32], u32)>>>;
fn get_commitment_tree_roots(
&self,
id: BlockHash,
) -> impl SendFut<BlockchainSourceResult<ShieldedTreeRoots>>;
fn get_address_deltas(
&self,
params: GetAddressDeltasParams,
) -> impl SendFut<BlockchainSourceResult<GetAddressDeltasResponse>>;
fn get_address_balance(
&self,
address_strings: GetAddressBalanceRequest,
) -> impl SendFut<BlockchainSourceResult<AddressBalance>>;
fn get_address_txids(
&self,
request: GetAddressTxIdsRequest,
) -> impl SendFut<BlockchainSourceResult<Vec<TransactionHash>>>;
fn get_address_utxos(
&self,
address_strings: GetAddressBalanceRequest,
) -> impl SendFut<BlockchainSourceResult<Vec<GetAddressUtxos>>>;
fn nonfinalized_listener(
&self,
) -> impl SendFut<Result<Option<NonfinalizedBlockReceiver>, Box<dyn Error + Send + Sync>>>;
fn subscribe_to_blocks_received(&self) -> Option<tokio::sync::watch::Receiver<()>> {
None
}
fn shutdown(&self) {}
}
pub(super) async fn wait_or_source_change(
change_rx: Option<&mut tokio::sync::watch::Receiver<()>>,
duration: std::time::Duration,
) {
match change_rx {
Some(rx) => tokio::select! {
_ = tokio::time::sleep(duration) => {}
_ = rx.changed() => {}
},
None => tokio::time::sleep(duration).await,
}
}
#[derive(Debug, thiserror::Error)]
pub enum BlockchainSourceError {
#[error("critical error in backing block source: {0}")]
Unrecoverable(String),
#[error("critical error in backing block source: {message}")]
UnrecoverableWithSource {
message: String,
#[source]
source: Box<dyn std::error::Error + Send + Sync + 'static>,
},
}
impl BlockchainSourceError {
pub(crate) fn unrecoverable(
error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
) -> Self {
let source = error.into();
Self::UnrecoverableWithSource {
message: source.to_string(),
source,
}
}
pub(crate) fn unrecoverable_context(
context: impl std::fmt::Display,
error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
) -> Self {
let source = error.into();
Self::UnrecoverableWithSource {
message: format!("{context}: {source}"),
source,
}
}
}
#[derive(thiserror::Error, Debug)]
#[error("data from validator invalid: {0}")]
pub struct InvalidData(String);
pub(crate) type BlockchainSourceResult<T> = Result<T, BlockchainSourceError>;
#[derive(Debug, Clone)]
pub enum GetTransactionLocation {
BestChain(zebra_chain::block::Height),
NonbestChain,
Mempool,
}