diff --git a/packages/zaino-state/build.rs b/packages/zaino-state/build.rs index d194481d3..40efa6911 100644 --- a/packages/zaino-state/build.rs +++ b/packages/zaino-state/build.rs @@ -41,9 +41,5 @@ fn main() -> io::Result<()> { let build_user = whoami::username(); println!("cargo:rustc-env=BUILD_USER={build_user}"); - // Set the version from Cargo.toml - let version = env::var("CARGO_PKG_VERSION").expect("Failed to get version from Cargo.toml"); - println!("cargo:rustc-env=VERSION={version}"); - Ok(()) } diff --git a/packages/zaino-state/src/backends/fetch.rs b/packages/zaino-state/src/backends/fetch.rs index 1efcc71fb..86f942c16 100644 --- a/packages/zaino-state/src/backends/fetch.rs +++ b/packages/zaino-state/src/backends/fetch.rs @@ -118,26 +118,26 @@ impl ZcashService for FetchService { type Config = FetchServiceConfig; /// Initializes a new FetchService instance and starts sync process. - #[instrument(name = "FetchService::spawn", skip(config), fields(network = %config.network))] + #[instrument(name = "FetchService::spawn", skip(config), fields(network = %config.common.network))] async fn spawn(config: FetchServiceConfig) -> Result { info!( - rpc_address = %config.validator_rpc_address, - network = %config.network, + rpc_address = %config.common.validator_rpc_address, + network = %config.common.network, "Launching Fetch Service" ); let fetcher = JsonRpSeeConnector::new_from_config_parts( - &config.validator_rpc_address, - config.validator_rpc_user.clone(), - config.validator_rpc_password.clone(), - config.validator_cookie_path.clone(), + &config.common.validator_rpc_address, + config.common.validator_rpc_user.clone(), + config.common.validator_rpc_password.clone(), + config.common.validator_cookie_path.clone(), ) .await?; let zebra_build_data = fetcher.get_info().await?; let data = ServiceMetadata::new( - get_build_info(), - config.network.to_zebra_network(), + get_build_info(config.common.indexer_version.clone()), + config.common.network.to_zebra_network(), zebra_build_data.build, zebra_build_data.subversion, ); @@ -235,7 +235,7 @@ impl FetchServiceSubscriber { /// Returns the network type running. #[allow(deprecated)] pub fn network(&self) -> zaino_common::Network { - self.config.network + self.config.common.network } } @@ -1011,8 +1011,8 @@ impl LightWalletIndexer for FetchServiceSubscriber { let end = validated_request.end() as u32; let fetch_service_clone = self.clone(); - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); let snapshot = fetch_service_clone .indexer .snapshot_nonfinalized_state() @@ -1143,8 +1143,8 @@ impl LightWalletIndexer for FetchServiceSubscriber { let end = validated_request.end() as u32; let fetch_service_clone = self.clone(); - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); let snapshot = fetch_service_clone .indexer .snapshot_nonfinalized_state() @@ -1331,8 +1331,8 @@ impl LightWalletIndexer for FetchServiceSubscriber { let chain_height = self.chain_height().await?; let txids = self.get_taddress_txids_helper(request).await?; let fetch_service_clone = self.clone(); - let service_timeout = self.config.service.timeout; - let (transmitter, receiver) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (transmitter, receiver) = mpsc::channel(self.config.common.service.channel_size as usize); tokio::spawn(async move { let timeout = timeout( time::Duration::from_secs((service_timeout * 4) as u64), @@ -1403,9 +1403,9 @@ impl LightWalletIndexer for FetchServiceSubscriber { mut request: AddressStream, ) -> Result { let fetch_service_clone = self.clone(); - let service_timeout = self.config.service.timeout; + let service_timeout = self.config.common.service.timeout; let (channel_tx, mut channel_rx) = - mpsc::channel::(self.config.service.channel_size as usize); + mpsc::channel::(self.config.common.service.channel_size as usize); let fetcher_task_handle = tokio::spawn(async move { let fetcher_timeout = timeout( time::Duration::from_secs((service_timeout * 4) as u64), @@ -1537,8 +1537,8 @@ impl LightWalletIndexer for FetchServiceSubscriber { } let mempool = self.indexer.clone(); - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); tokio::spawn(async move { let timeout = timeout( @@ -1637,8 +1637,8 @@ impl LightWalletIndexer for FetchServiceSubscriber { #[allow(deprecated)] async fn get_mempool_stream(&self) -> Result { let indexer = self.indexer.clone(); - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); let snapshot = indexer.snapshot_nonfinalized_state().await?; tokio::spawn(async move { let timeout = timeout( @@ -1733,7 +1733,7 @@ impl LightWalletIndexer for FetchServiceSubscriber { .await? .into_parts(); Ok(TreeState { - network: self.config.network.to_zebra_network().bip70_network_name(), + network: self.config.common.network.to_zebra_network().bip70_network_name(), height: height.0 as u64, hash: hash.to_string(), time, @@ -1755,8 +1755,8 @@ impl LightWalletIndexer for FetchServiceSubscriber { #[allow(deprecated)] fn timeout_channel_size(&self) -> (u32, u32) { ( - self.config.service.timeout, - self.config.service.channel_size, + self.config.common.service.timeout, + self.config.common.service.channel_size, ) } @@ -1825,8 +1825,8 @@ impl LightWalletIndexer for FetchServiceSubscriber { ) -> Result { let taddrs = GetAddressBalanceRequest::new(request.addresses); let utxos = self.z_get_address_utxos(taddrs).await?; - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); tokio::spawn(async move { let timeout = timeout( time::Duration::from_secs((service_timeout * 4) as u64), @@ -1944,6 +1944,7 @@ impl LightWalletIndexer for FetchServiceSubscriber { zcashd_subversion: self.data.zebra_subversion(), donation_address: self .config + .common .donation_address .as_ref() .map(DonationAddress::encode) diff --git a/packages/zaino-state/src/backends/state.rs b/packages/zaino-state/src/backends/state.rs index 2fedecc81..7e6e38721 100644 --- a/packages/zaino-state/src/backends/state.rs +++ b/packages/zaino-state/src/backends/state.rs @@ -179,27 +179,27 @@ impl ZcashService for StateService { type Config = StateServiceConfig; /// Initializes a new StateService instance and starts sync process. - #[instrument(name = "StateService::spawn", skip(config), fields(network = %config.network))] + #[instrument(name = "StateService::spawn", skip(config), fields(network = %config.common.network))] async fn spawn(config: StateServiceConfig) -> Result { info!( - rpc_address = %config.validator_rpc_address, - network = %config.network, + rpc_address = %config.common.validator_rpc_address, + network = %config.common.network, "Spawning State Service" ); let rpc_client = JsonRpSeeConnector::new_from_config_parts( - &config.validator_rpc_address, - config.validator_rpc_user.clone(), - config.validator_rpc_password.clone(), - config.validator_cookie_path.clone(), + &config.common.validator_rpc_address, + config.common.validator_rpc_user.clone(), + config.common.validator_rpc_password.clone(), + config.common.validator_cookie_path.clone(), ) .await?; let zebra_build_data = rpc_client.get_info().await?; let data = ServiceMetadata::new( - get_build_info(), - config.network.to_zebra_network(), + get_build_info(config.common.indexer_version.clone()), + config.common.network.to_zebra_network(), zebra_build_data.build, zebra_build_data.subversion, ); @@ -212,7 +212,7 @@ impl ZcashService for StateService { let (mut read_state_service, _latest_chain_tip, chain_tip_change, sync_task_handle) = init_read_state_with_syncer( config.validator_state_config.clone(), - &config.network.to_zebra_network(), + &config.common.network.to_zebra_network(), config.validator_grpc_address, ) .await??; @@ -251,7 +251,7 @@ impl ZcashService for StateService { let mempool_source = ValidatorConnector::State(crate::chain_index::source::State { read_state_service: read_state_service.clone(), mempool_fetcher: rpc_client.clone(), - network: config.network, + network: config.common.network, }); let mempool = Mempool::spawn(mempool_source, None).await?; @@ -260,7 +260,7 @@ impl ZcashService for StateService { ValidatorConnector::State(State { read_state_service: read_state_service.clone(), mempool_fetcher: rpc_client.clone(), - network: config.network, + network: config.common.network, }), config.clone().into(), ) @@ -558,8 +558,8 @@ impl StateServiceSubscriber { let end = validated_request.end() as u32; let state_service_clone = self.clone(); - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); let snapshot = state_service_clone .indexer .snapshot_nonfinalized_state() @@ -950,7 +950,7 @@ impl StateServiceSubscriber { /// Returns the network type running. #[allow(deprecated)] pub fn network(&self) -> zaino_common::Network { - self.config.network + self.config.common.network } /// Returns the median time of the last 11 blocks. @@ -1114,7 +1114,7 @@ impl ZcashIndexer for StateServiceSubscriber { async fn get_difficulty(&self) -> Result { chain_tip_difficulty( - self.config.network.to_zebra_network(), + self.config.common.network.to_zebra_network(), self.read_state_service.clone(), false, ) @@ -1172,7 +1172,7 @@ impl ZcashIndexer for StateServiceSubscriber { let now = Utc::now(); let zebra_estimated_height = - NetworkChainTipHeightEstimator::new(header.time, height, &self.config.network.into()) + NetworkChainTipHeightEstimator::new(header.time, height, &self.config.common.network.into()) .estimate_height_at(now); let estimated_height = if header.time > now || zebra_estimated_height < height { height @@ -1182,6 +1182,7 @@ impl ZcashIndexer for StateServiceSubscriber { let upgrades = IndexMap::from_iter( self.config + .common .network .to_zebra_network() .full_activation_list() @@ -1217,14 +1218,14 @@ impl ZcashIndexer for StateServiceSubscriber { (height + 1).expect("valid chain tips are a lot less than Height::MAX"); let consensus = TipConsensusBranch::from_parts( ConsensusBranchIdHex::new( - NetworkUpgrade::current(&self.config.network.into(), height) + NetworkUpgrade::current(&self.config.common.network.into(), height) .branch_id() .unwrap_or(ConsensusBranchId::RPC_MISSING_ID) .into(), ) .inner(), ConsensusBranchIdHex::new( - NetworkUpgrade::current(&self.config.network.into(), next_block_height) + NetworkUpgrade::current(&self.config.common.network.into(), next_block_height) .branch_id() .unwrap_or(ConsensusBranchId::RPC_MISSING_ID) .into(), @@ -1234,7 +1235,7 @@ impl ZcashIndexer for StateServiceSubscriber { // TODO: Remove unwrap() let difficulty = chain_tip_difficulty( - self.config.network.to_zebra_network(), + self.config.common.network.to_zebra_network(), self.read_state_service.clone(), false, ) @@ -1244,7 +1245,7 @@ impl ZcashIndexer for StateServiceSubscriber { let verification_progress = f64::from(height.0) / f64::from(zebra_estimated_height.0); Ok(GetBlockchainInfoResponse::new( - self.config.network.to_zebra_network().bip70_network_name(), + self.config.common.network.to_zebra_network().bip70_network_name(), height, hash, estimated_height, @@ -1612,7 +1613,7 @@ impl ZcashIndexer for StateServiceSubscriber { }; let address = match address.convert_if_network::
( - match self.config.network.to_zebra_network().kind() { + match self.config.common.network.to_zebra_network().kind() { NetworkKind::Mainnet => NetworkType::Main, NetworkKind::Testnet => NetworkType::Test, NetworkKind::Regtest => NetworkType::Regtest, @@ -1649,7 +1650,7 @@ impl ZcashIndexer for StateServiceSubscriber { }; let converted_address = match parsed_address.convert_if_network::
( - match self.config.network.to_zebra_network().kind() { + match self.config.common.network.to_zebra_network().kind() { NetworkKind::Mainnet => NetworkType::Main, NetworkKind::Testnet => NetworkType::Test, NetworkKind::Regtest => NetworkType::Regtest, @@ -1807,7 +1808,7 @@ impl ZcashIndexer for StateServiceSubscriber { parsed_tx.into(), None, // best_chain_height Some(0), // confirmations - &self.config.network.into(), // network + &self.config.common.network.into(), // network None, // block_time None, // block_hash Some(false), // in_best_chain @@ -1850,7 +1851,7 @@ impl ZcashIndexer for StateServiceSubscriber { tx.tx.clone(), best_chain_height, Some(tx.confirmations), - &self.config.network.into(), + &self.config.common.network.into(), Some(tx.block_time), Some(zebra_chain::block::Hash::from_bytes(compact_block.hash)), Some(best_chain_height.is_some()), @@ -2189,8 +2190,8 @@ impl LightWalletIndexer for StateServiceSubscriber { ) -> Result { let txids = self.get_taddress_txids_helper(request).await?; let chain_height = self.chain_height().await?; - let (transmitter, receiver) = mpsc::channel(self.config.service.channel_size as usize); - let service_timeout = self.config.service.timeout; + let (transmitter, receiver) = mpsc::channel(self.config.common.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; let service_clone = self.clone(); tokio::spawn(async move { let timeout = timeout( @@ -2263,9 +2264,9 @@ impl LightWalletIndexer for StateServiceSubscriber { mut request: AddressStream, ) -> Result { let fetch_service_clone = self.clone(); - let service_timeout = self.config.service.timeout; + let service_timeout = self.config.common.service.timeout; let (channel_tx, mut channel_rx) = - mpsc::channel::(self.config.service.channel_size as usize); + mpsc::channel::(self.config.common.service.channel_size as usize); let fetcher_task_handle = tokio::spawn(async move { let fetcher_timeout = timeout( time::Duration::from_secs((service_timeout * 4) as u64), @@ -2405,8 +2406,8 @@ impl LightWalletIndexer for StateServiceSubscriber { }; let mempool = self.mempool.clone(); - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); tokio::spawn(async move { let timeout = timeout( time::Duration::from_secs((service_timeout * 4) as u64), @@ -2495,8 +2496,8 @@ impl LightWalletIndexer for StateServiceSubscriber { /// there are mempool transactions. It will close the returned stream when a new block is mined. async fn get_mempool_stream(&self) -> Result { let mut mempool = self.mempool.clone(); - let service_timeout = self.config.service.timeout; - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let service_timeout = self.config.common.service.timeout; + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); let snapshot = self.indexer.snapshot_nonfinalized_state().await?; let Some(non_finalized_snapshot) = snapshot.get_nfs_snapshot() else { // TODO: This probably shouldn't be an error. @@ -2590,7 +2591,7 @@ impl LightWalletIndexer for StateServiceSubscriber { .await? .into_parts(); Ok(TreeState { - network: self.config.network.to_zebra_network().bip70_network_name(), + network: self.config.common.network.to_zebra_network().bip70_network_name(), height: height.0 as u64, hash: hash.to_string(), time, @@ -2611,8 +2612,8 @@ impl LightWalletIndexer for StateServiceSubscriber { fn timeout_channel_size(&self) -> (u32, u32) { ( - self.config.service.timeout, - self.config.service.channel_size, + self.config.common.service.timeout, + self.config.common.service.channel_size, ) } @@ -2657,7 +2658,7 @@ impl LightWalletIndexer for StateServiceSubscriber { .and_then(|service| service.call(ReadRequest::UtxosByAddresses(address_set))) .await?; let utxos = expected_read_response!(address_utxos_response, AddressUtxos); - let (channel_tx, channel_rx) = mpsc::channel(self.config.service.channel_size as usize); + let (channel_tx, channel_rx) = mpsc::channel(self.config.common.service.channel_size as usize); tokio::spawn(async move { for utxo in utxos .utxos() @@ -2739,6 +2740,7 @@ impl LightWalletIndexer for StateServiceSubscriber { zcashd_subversion: self.data.zebra_subversion(), donation_address: self .config + .common .donation_address .as_ref() .map(DonationAddress::encode) diff --git a/packages/zaino-state/src/config.rs b/packages/zaino-state/src/config.rs index 15c1ca304..f057aba3e 100644 --- a/packages/zaino-state/src/config.rs +++ b/packages/zaino-state/src/config.rs @@ -69,19 +69,15 @@ pub enum BackendConfig { Fetch(FetchServiceConfig), } -/// Holds config data for [crate::StateService]. +/// Configuration shared by every backend variant. +/// +/// Carries the validator-RPC connection bits plus the runtime indexer +/// settings that are independent of how blockchain data is fetched. #[derive(Debug, Clone)] -// #[deprecated] -pub struct StateServiceConfig { - /// Zebra [`zebra_state::ReadStateService`] config data - pub validator_state_config: zebra_state::Config, +pub struct CommonBackendConfig { /// Validator JsonRPC address (supports hostname:port or ip:port format). pub validator_rpc_address: String, - /// Validator gRPC address (requires ip:port format for Zebra state sync). - pub validator_grpc_address: std::net::SocketAddr, - /// Validator cookie auth. - pub validator_cookie_auth: bool, - /// Enable validator rpc cookie authentification with Some: Path to the validator cookie file. + /// Enable validator rpc cookie authentication with Some: path to the validator cookie file. pub validator_cookie_path: Option, /// Validator JsonRPC user. pub validator_rpc_user: String, @@ -95,13 +91,34 @@ pub struct StateServiceConfig { pub network: Network, /// Zcash donation UA address pub donation_address: Option, + /// Version of the indexer binary embedding this service. + /// + /// Reported on the wire via `LightdInfo.version`. Defaults to this + /// crate's `CARGO_PKG_VERSION` when constructed via the parent + /// service's `new`; the embedding binary should overwrite it with + /// its own `CARGO_PKG_VERSION` so the wire reflects the deployed + /// indexer rather than the library crate. + pub indexer_version: String, +} + +/// Holds config data for [crate::StateService]. +#[derive(Debug, Clone)] +// #[deprecated] +pub struct StateServiceConfig { + /// Settings shared with [`FetchServiceConfig`]. + pub common: CommonBackendConfig, + /// Zebra [`zebra_state::ReadStateService`] config data + pub validator_state_config: zebra_state::Config, + /// Validator gRPC address (requires ip:port format for Zebra state sync). + pub validator_grpc_address: std::net::SocketAddr, + /// Validator cookie auth. + pub validator_cookie_auth: bool, } #[allow(deprecated)] impl StateServiceConfig { /// Returns a new instance of [`StateServiceConfig`]. #[allow(clippy::too_many_arguments)] - // TODO: replace with struct-literal init only? pub fn new( validator_state_config: zebra_state::Config, validator_rpc_address: String, @@ -120,17 +137,20 @@ impl StateServiceConfig { network.to_zebra_network().full_activation_list() ); StateServiceConfig { + common: CommonBackendConfig { + validator_rpc_address, + validator_cookie_path, + validator_rpc_user: validator_rpc_user.unwrap_or("xxxxxx".to_string()), + validator_rpc_password: validator_rpc_password.unwrap_or("xxxxxx".to_string()), + service, + storage, + network, + donation_address, + indexer_version: env!("CARGO_PKG_VERSION").to_string(), + }, validator_state_config, - validator_rpc_address, validator_grpc_address, validator_cookie_auth, - validator_cookie_path, - validator_rpc_user: validator_rpc_user.unwrap_or("xxxxxx".to_string()), - validator_rpc_password: validator_rpc_password.unwrap_or("xxxxxx".to_string()), - service, - storage, - network, - donation_address, } } } @@ -139,22 +159,8 @@ impl StateServiceConfig { #[derive(Debug, Clone)] #[deprecated] pub struct FetchServiceConfig { - /// Validator JsonRPC address (supports hostname:port or ip:port format). - pub validator_rpc_address: String, - /// Enable validator rpc cookie authentification with Some: path to the validator cookie file. - pub validator_cookie_path: Option, - /// Validator JsonRPC user. - pub validator_rpc_user: String, - /// Validator JsonRPC password. - pub validator_rpc_password: String, - /// Service-level configuration (timeout, channel size) - pub service: ServiceConfig, - /// Storage configuration (cache and database) - pub storage: StorageConfig, - /// Network type. - pub network: Network, - /// Zcash donation UA address - pub donation_address: Option, + /// Settings shared with [`StateServiceConfig`]. + pub common: CommonBackendConfig, } #[allow(deprecated)] @@ -172,14 +178,17 @@ impl FetchServiceConfig { donation_address: Option, ) -> Self { FetchServiceConfig { - validator_rpc_address, - validator_cookie_path, - validator_rpc_user: validator_rpc_user.unwrap_or("xxxxxx".to_string()), - validator_rpc_password: validator_rpc_password.unwrap_or("xxxxxx".to_string()), - service, - storage, - network, - donation_address, + common: CommonBackendConfig { + validator_rpc_address, + validator_cookie_path, + validator_rpc_user: validator_rpc_user.unwrap_or("xxxxxx".to_string()), + validator_rpc_password: validator_rpc_password.unwrap_or("xxxxxx".to_string()), + service, + storage, + network, + donation_address, + indexer_version: env!("CARGO_PKG_VERSION").to_string(), + }, } } } @@ -208,9 +217,8 @@ impl BlockCacheConfig { } } -#[allow(deprecated)] -impl From for BlockCacheConfig { - fn from(value: StateServiceConfig) -> Self { +impl From for BlockCacheConfig { + fn from(value: CommonBackendConfig) -> Self { Self { storage: value.storage, // TODO: update zaino configs to include db version. @@ -220,15 +228,17 @@ impl From for BlockCacheConfig { } } +#[allow(deprecated)] +impl From for BlockCacheConfig { + fn from(value: StateServiceConfig) -> Self { + value.common.into() + } +} + #[allow(deprecated)] impl From for BlockCacheConfig { fn from(value: FetchServiceConfig) -> Self { - Self { - storage: value.storage, - // TODO: update zaino configs to include db version. - db_version: 1, - network: value.network, - } + value.common.into() } } diff --git a/packages/zaino-state/src/lib.rs b/packages/zaino-state/src/lib.rs index 8fd67c9b4..e63d9bd7b 100644 --- a/packages/zaino-state/src/lib.rs +++ b/packages/zaino-state/src/lib.rs @@ -64,8 +64,8 @@ pub(crate) mod config; #[allow(deprecated)] pub use config::{ - BackendConfig, BackendType, BlockCacheConfig, DonationAddress, FetchServiceConfig, - StateServiceConfig, + BackendConfig, BackendType, BlockCacheConfig, CommonBackendConfig, DonationAddress, + FetchServiceConfig, StateServiceConfig, }; pub(crate) mod error; diff --git a/packages/zaino-state/src/utils.rs b/packages/zaino-state/src/utils.rs index 70c935f48..46f11fada 100644 --- a/packages/zaino-state/src/utils.rs +++ b/packages/zaino-state/src/utils.rs @@ -53,13 +53,17 @@ impl fmt::Display for BuildInfo { } /// Returns build info for Zingo-Indexer. -pub(crate) fn get_build_info() -> BuildInfo { +/// +/// `version` is the version of the deployed indexer binary (e.g. `zainod`), +/// supplied by the caller. Library crates do not know which binary embeds +/// them, so each binary passes its own `CARGO_PKG_VERSION`. +pub(crate) fn get_build_info(version: String) -> BuildInfo { BuildInfo { commit_hash: env!("GIT_COMMIT").to_string(), branch: env!("BRANCH").to_string(), build_date: env!("BUILD_DATE").to_string(), build_user: env!("BUILD_USER").to_string(), - version: env!("VERSION").to_string(), + version, } } @@ -113,3 +117,17 @@ impl fmt::Display for ServiceMetadata { writeln!(f, "Zebra Subversion: {}", self.zebra_subversion) } } + +#[cfg(test)] +mod tests { + use super::*; + + /// Regression test for issue #1057: the version flowed onto the wire via + /// `LightdInfo.version` must come from the caller-supplied string (the + /// embedding binary's `CARGO_PKG_VERSION`), not from this library crate. + #[test] + fn get_build_info_uses_caller_supplied_version() { + let build_info = get_build_info("9.9.9-test".to_string()); + assert_eq!(build_info.version(), "9.9.9-test"); + } +} diff --git a/packages/zainod/src/config.rs b/packages/zainod/src/config.rs index 4ca8018c8..51703aa51 100644 --- a/packages/zainod/src/config.rs +++ b/packages/zainod/src/config.rs @@ -16,7 +16,9 @@ use zaino_common::{ }; use zaino_serve::server::config::{GrpcServerConfig, JsonRpcServerConfig}; #[allow(deprecated)] -use zaino_state::{BackendType, DonationAddress, FetchServiceConfig, StateServiceConfig}; +use zaino_state::{ + BackendType, CommonBackendConfig, DonationAddress, FetchServiceConfig, StateServiceConfig, +}; /// Header for generated configuration files. pub const GENERATED_CONFIG_HEADER: &str = r#"# Zaino Configuration @@ -349,35 +351,22 @@ impl TryFrom for StateServiceConfig { )) })?; + let validator_state_config = zebra_state::Config { + cache_dir: cfg.zebra_db_path.clone(), + ephemeral: false, + delete_old_database: true, + debug_stop_at_height: None, + debug_validity_check_interval: None, + should_backup_non_finalized_state: true, + debug_skip_non_finalized_state_backup_task: false, + }; + let validator_cookie_auth = cfg.validator_settings.validator_cookie_path.is_some(); + Ok(StateServiceConfig { - validator_state_config: zebra_state::Config { - cache_dir: cfg.zebra_db_path.clone(), - ephemeral: false, - delete_old_database: true, - debug_stop_at_height: None, - debug_validity_check_interval: None, - should_backup_non_finalized_state: true, - debug_skip_non_finalized_state_backup_task: false, - }, - validator_rpc_address: cfg - .validator_settings - .validator_jsonrpc_listen_address - .clone(), + common: build_common(cfg), + validator_state_config, validator_grpc_address, - validator_cookie_auth: cfg.validator_settings.validator_cookie_path.is_some(), - validator_cookie_path: cfg.validator_settings.validator_cookie_path, - validator_rpc_user: cfg - .validator_settings - .validator_user - .unwrap_or_else(|| "xxxxxx".to_string()), - validator_rpc_password: cfg - .validator_settings - .validator_password - .unwrap_or_else(|| "xxxxxx".to_string()), - service: cfg.service, - storage: cfg.storage, - network: cfg.network, - donation_address: cfg.donation_address, + validator_cookie_auth, }) } } @@ -388,24 +377,31 @@ impl TryFrom for FetchServiceConfig { fn try_from(cfg: ZainodConfig) -> Result { Ok(FetchServiceConfig { - validator_rpc_address: cfg.validator_settings.validator_jsonrpc_listen_address, - validator_cookie_path: cfg.validator_settings.validator_cookie_path, - validator_rpc_user: cfg - .validator_settings - .validator_user - .unwrap_or_else(|| "xxxxxx".to_string()), - validator_rpc_password: cfg - .validator_settings - .validator_password - .unwrap_or_else(|| "xxxxxx".to_string()), - service: cfg.service, - storage: cfg.storage, - network: cfg.network, - donation_address: cfg.donation_address, + common: build_common(cfg), }) } } +fn build_common(cfg: ZainodConfig) -> CommonBackendConfig { + CommonBackendConfig { + validator_rpc_address: cfg.validator_settings.validator_jsonrpc_listen_address, + validator_cookie_path: cfg.validator_settings.validator_cookie_path, + validator_rpc_user: cfg + .validator_settings + .validator_user + .unwrap_or_else(|| "xxxxxx".to_string()), + validator_rpc_password: cfg + .validator_settings + .validator_password + .unwrap_or_else(|| "xxxxxx".to_string()), + service: cfg.service, + storage: cfg.storage, + network: cfg.network, + donation_address: cfg.donation_address, + indexer_version: env!("CARGO_PKG_VERSION").to_string(), + } +} + #[cfg(test)] mod tests { use super::*; @@ -1040,4 +1036,53 @@ listen_address = "127.0.0.1:8137" let path = create_test_config_file(&dir, content, "invalid_donation.toml"); assert!(load_config(&path).is_err()); } + + /// `LightdInfo.version` (issue #1057) is sourced from + /// `*ServiceConfig.indexer_version`. This must be set to `zainod`'s + /// `CARGO_PKG_VERSION` at the boundary so the wire reflects the + /// deployed binary, not zaino-state's library version. + #[test] + #[allow(deprecated)] + fn indexer_version_is_zainod_pkg_version() { + let _guard = EnvGuard::new(); + + let cfg = ZainodConfig::default(); + + let state_cfg = StateServiceConfig::try_from(cfg.clone()) + .expect("StateServiceConfig conversion should succeed for default ZainodConfig"); + assert_eq!(state_cfg.common.indexer_version, env!("CARGO_PKG_VERSION")); + + let fetch_cfg = FetchServiceConfig::try_from(cfg) + .expect("FetchServiceConfig conversion should succeed for default ZainodConfig"); + assert_eq!(fetch_cfg.common.indexer_version, env!("CARGO_PKG_VERSION")); + } + + /// `StateServiceConfig::try_from` and `FetchServiceConfig::try_from` + /// share a single `build_common` helper, so the two backends can + /// never quietly disagree on the common payload they hand to a + /// service. Locks that property in across every field: a future + /// hand-rolled divergence (e.g. one path stops applying the + /// missing-credentials sentinel, or a new common field gets + /// populated on only one side) makes this fail. Pretty-Debug + /// equality is used because not every constituent of + /// `CommonBackendConfig` derives `PartialEq`, and a single + /// stringified compare future-proofs the test against fields added + /// later. + #[test] + #[allow(deprecated)] + fn state_and_fetch_common_payloads_agree() { + let _guard = EnvGuard::new(); + + let cfg = ZainodConfig::default(); + + let state_cfg = StateServiceConfig::try_from(cfg.clone()) + .expect("StateServiceConfig conversion should succeed for default ZainodConfig"); + let fetch_cfg = FetchServiceConfig::try_from(cfg) + .expect("FetchServiceConfig conversion should succeed for default ZainodConfig"); + + assert_eq!( + format!("{:#?}", state_cfg.common), + format!("{:#?}", fetch_cfg.common), + ); + } }