diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 85ec5c2dc..e34e1ba3c 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -14,6 +14,8 @@ use tower::{buffer::Buffer, Service, ServiceExt}; use zebra_chain::{ block::{self, SerializedBlock}, + chain_tip::ChainTip, + parameters::Network, serialization::{SerializationError, ZcashDeserialize}, transaction::{self, Transaction}, }; @@ -104,7 +106,7 @@ pub trait Rpc { } /// RPC method implementations. -pub struct RpcImpl +pub struct RpcImpl where Mempool: Service, State: Service< @@ -112,16 +114,27 @@ where Response = zebra_state::Response, Error = zebra_state::BoxError, >, + Tip: ChainTip, { /// Zebra's application version. app_version: String, + /// A handle to the mempool service. mempool: Buffer, + /// A handle to the state service. state: State, + + /// Allows efficient access to the best tip of the blockchain. + #[allow(dead_code)] + latest_chain_tip: Tip, + + /// The configured network for this RPC service. + #[allow(dead_code)] + network: Network, } -impl RpcImpl +impl RpcImpl where Mempool: Service, State: Service< @@ -129,22 +142,30 @@ where Response = zebra_state::Response, Error = zebra_state::BoxError, >, + Tip: ChainTip + Send + Sync, { /// Create a new instance of the RPC handler. - pub fn new( - app_version: String, + pub fn new( + app_version: Version, mempool: Buffer, state: State, - ) -> Self { + latest_chain_tip: Tip, + network: Network, + ) -> Self + where + Version: ToString, + { RpcImpl { - app_version, + app_version: app_version.to_string(), mempool, state, + latest_chain_tip, + network, } } } -impl Rpc for RpcImpl +impl Rpc for RpcImpl where Mempool: tower::Service + 'static, @@ -158,6 +179,7 @@ where + Sync + 'static, State::Future: Send, + Tip: ChainTip + Send + Sync + 'static, { fn get_info(&self) -> Result { let response = GetInfo { @@ -170,6 +192,8 @@ where fn get_blockchain_info(&self) -> Result { // TODO: dummy output data, fix in the context of #3143 + // use self.latest_chain_tip.estimate_network_chain_tip_height() + // to estimate the current block height on the network let response = GetBlockChainInfo { chain: "TODO: main".to_string(), }; diff --git a/zebra-rpc/src/methods/tests/prop.rs b/zebra-rpc/src/methods/tests/prop.rs index 71304a7b6..0311e754b 100644 --- a/zebra-rpc/src/methods/tests/prop.rs +++ b/zebra-rpc/src/methods/tests/prop.rs @@ -1,3 +1,5 @@ +//! Randomised property tests for RPC methods. + use std::collections::HashSet; use hex::ToHex; @@ -7,6 +9,8 @@ use thiserror::Error; use tower::buffer::Buffer; use zebra_chain::{ + chain_tip::NoChainTip, + parameters::Network::*, serialization::{ZcashDeserialize, ZcashSerialize}, transaction::{Transaction, UnminedTx, UnminedTxId}, }; @@ -27,9 +31,11 @@ proptest! { let mut mempool = MockService::build().for_prop_tests(); let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests(); let rpc = RpcImpl::new( - "RPC test".to_owned(), + "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, ); let hash = SentTransactionHash(transaction.hash()); @@ -73,9 +79,11 @@ proptest! { let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests(); let rpc = RpcImpl::new( - "RPC test".to_owned(), + "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, ); let transaction_bytes = transaction @@ -124,9 +132,11 @@ proptest! { let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests(); let rpc = RpcImpl::new( - "RPC test".to_owned(), + "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, ); let transaction_bytes = transaction @@ -183,9 +193,11 @@ proptest! { let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests(); let rpc = RpcImpl::new( - "RPC test".to_owned(), + "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, ); let send_task = tokio::spawn(rpc.send_raw_transaction(non_hex_string)); @@ -231,9 +243,11 @@ proptest! { let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests(); let rpc = RpcImpl::new( - "RPC test".to_owned(), + "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, ); let send_task = tokio::spawn(rpc.send_raw_transaction(hex::encode(random_bytes))); @@ -278,9 +292,11 @@ proptest! { let mut state: MockService<_, _, _, BoxError> = MockService::build().for_prop_tests(); let rpc = RpcImpl::new( - "RPC test".to_owned(), + "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, ); let call_task = tokio::spawn(rpc.get_raw_mempool()); diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index e75d26e5a..d75ed6e19 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -4,7 +4,12 @@ use std::sync::Arc; use tower::buffer::Buffer; -use zebra_chain::{block::Block, parameters::Network, serialization::ZcashDeserializeInto}; +use zebra_chain::{ + block::Block, + chain_tip::NoChainTip, + parameters::Network::{self, *}, + serialization::ZcashDeserializeInto, +}; use zebra_network::constants::USER_AGENT; use zebra_node_services::BoxError; @@ -23,16 +28,18 @@ async fn rpc_getinfo() { let mut state: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); let rpc = RpcImpl::new( - "Zebra version test".to_string(), + "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, ); let get_info = rpc.get_info().expect("We should have a GetInfo struct"); // make sure there is a `build` field in the response, // and that is equal to the provided string. - assert_eq!(get_info.build, "Zebra version test"); + assert_eq!(get_info.build, "RPC test"); // make sure there is a `subversion` field, // and that is equal to the Zebra user agent. @@ -57,11 +64,13 @@ async fn rpc_getblock() { let state = zebra_state::populated_state(blocks.clone(), Network::Mainnet).await; // Init RPC - let rpc = RpcImpl { - app_version: "Zebra version test".to_string(), - mempool: Buffer::new(mempool.clone(), 1), + let rpc = RpcImpl::new( + "RPC test", + Buffer::new(mempool.clone(), 1), state, - }; + NoChainTip, + Mainnet, + ); // Make calls and check response for (i, block) in blocks.into_iter().enumerate() { @@ -84,11 +93,13 @@ async fn rpc_getblock_error() { let mut state: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); // Init RPC - let rpc = RpcImpl { - app_version: "Zebra version test".to_string(), - mempool: Buffer::new(mempool.clone(), 1), - state: Buffer::new(state.clone(), 1), - }; + let rpc = RpcImpl::new( + "RPC test", + Buffer::new(mempool.clone(), 1), + Buffer::new(state.clone(), 1), + NoChainTip, + Mainnet, + ); // Make sure we get an error if Zebra can't parse the block height. assert!(rpc @@ -123,11 +134,13 @@ async fn rpc_getbestblockhash() { let state = zebra_state::populated_state(blocks.clone(), Network::Mainnet).await; // Init RPC - let rpc = RpcImpl { - app_version: "Zebra version test".to_string(), - mempool: Buffer::new(mempool.clone(), 1), + let rpc = RpcImpl::new( + "RPC test", + Buffer::new(mempool.clone(), 1), state, - }; + NoChainTip, + Mainnet, + ); // Get the tip hash using RPC method `get_best_block_hash` let get_best_block_hash = rpc diff --git a/zebra-rpc/src/server.rs b/zebra-rpc/src/server.rs index 6be8807fd..c97b42f3d 100644 --- a/zebra-rpc/src/server.rs +++ b/zebra-rpc/src/server.rs @@ -13,6 +13,7 @@ use tower::{buffer::Buffer, Service}; use tracing::*; use tracing_futures::Instrument; +use zebra_chain::{chain_tip::ChainTip, parameters::Network}; use zebra_node_services::{mempool, BoxError}; use crate::{ @@ -29,13 +30,16 @@ pub struct RpcServer; impl RpcServer { /// Start a new RPC server endpoint - pub fn spawn( + pub fn spawn( config: Config, - app_version: String, + app_version: Version, mempool: Buffer, state: State, + latest_chain_tip: Tip, + network: Network, ) -> tokio::task::JoinHandle<()> where + Version: ToString, Mempool: tower::Service + 'static, Mempool::Future: Send, @@ -48,12 +52,13 @@ impl RpcServer { + Sync + 'static, State::Future: Send, + Tip: ChainTip + Send + Sync + 'static, { if let Some(listen_addr) = config.listen_addr { info!("Trying to open RPC endpoint at {}...", listen_addr,); // Initialize the rpc methods with the zebra version - let rpc_impl = RpcImpl::new(app_version, mempool, state); + let rpc_impl = RpcImpl::new(app_version, mempool, state, latest_chain_tip, network); // Create handler compatible with V1 and V2 RPC protocols let mut io = diff --git a/zebrad/src/commands/start.rs b/zebrad/src/commands/start.rs index d6f118bd4..01e3d0937 100644 --- a/zebrad/src/commands/start.rs +++ b/zebrad/src/commands/start.rs @@ -162,9 +162,11 @@ impl StartCmd { // Launch RPC server let rpc_task_handle = RpcServer::spawn( config.rpc, - app_version().to_string(), + app_version(), mempool.clone(), read_only_state_service, + latest_chain_tip.clone(), + config.network.network, ); let setup_data = InboundSetupData {