diff --git a/Cargo.lock b/Cargo.lock index b5b37916..5b6ab965 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -554,9 +554,9 @@ checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "bytemuck" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" +checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead" [[package]] name = "byteorder" @@ -747,9 +747,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6ec7641ff3474b7593009c809db602c414cd97c7d47a78ed004162b74ff96c" +checksum = "8ebf286c900a6d5867aeff75cfee3192857bb7f24b547d4f0df2ed6baa812c90" dependencies = [ "backtrace", "color-spantrace 0.2.0", @@ -1233,9 +1233,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" +checksum = "eed12bbf7b5312f8da1c2722bc06d8c6b12c2d86a7fb35a194c7f3e6fc2bbe39" dependencies = [ "signature", ] @@ -1351,9 +1351,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc225d8f637923fe585089fcf03e705c222131232d2c1fb622e84ecf725d0eb8" +checksum = "9289ed2c0440a6536e65119725cf91fc2c6b5e513bfd2e36e1134d7cca6ca12f" dependencies = [ "indenter", "once_cell", @@ -1616,9 +1616,9 @@ checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" [[package]] name = "git2" -version = "0.13.25" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" +checksum = "6e7d3b96ec1fcaa8431cf04a4f1ef5caafe58d5cf7bcc31f09c1626adddb0ffe" dependencies = [ "bitflags", "libc", @@ -1963,9 +1963,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c0c443f6dceb3a1cb7607c87501aa91e4b9c976044f725c2a74ca2152c91a4" +checksum = "30a7e1911532a662f6b08b68f884080850f2fd9544963c3ab23a5af42bda1eac" dependencies = [ "console", "once_cell", @@ -1987,9 +1987,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" [[package]] name = "itertools" @@ -2134,9 +2134,9 @@ checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] name = "libgit2-sys" -version = "0.12.26+1.3.0" +version = "0.13.1+1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494" +checksum = "43e598aa7a4faedf1ea1b4608f582b06f0f40211eec551b7ef36019ae3f62def" dependencies = [ "cc", "libc", @@ -2187,9 +2187,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" +checksum = "df2bf61678a0a521c3f7daf815d2e6717d85a272a7dcd02c9768272b32bd1e2a" dependencies = [ "cc", "libc", @@ -2399,9 +2399,9 @@ dependencies = [ [[package]] name = "minreq" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086dde2aacc3ce84b680e76ef4a60d77e75cb8109e5a508cc7ea81a86d65fd0d" +checksum = "4c785bc6027fd359756e538541c8624012ba3776d3d3fe123885643092ed4132" dependencies = [ "lazy_static", "log", @@ -3355,9 +3355,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" dependencies = [ "bitflags", ] @@ -3447,9 +3447,9 @@ dependencies = [ [[package]] name = "retain_mut" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51dd4445360338dab5116712bee1388dc727991d51969558a8882ab552e6db30" +checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086" [[package]] name = "retry-error" @@ -3459,9 +3459,9 @@ checksum = "5f0cb6e2859e29280664e192b37e0a698cef381fec81783f9efa3e5b0ffbaf8f" [[package]] name = "rgb" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1" +checksum = "e74fdc210d8f24a7dbfedc13b04ba5764f5232754ccebfdf5fff1bad791ccbc6" dependencies = [ "bytemuck", ] @@ -4006,9 +4006,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "sketches-ddsketch" @@ -4206,9 +4206,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] @@ -4841,7 +4841,7 @@ dependencies = [ name = "tower-batch" version = "0.2.21" dependencies = [ - "color-eyre 0.6.0", + "color-eyre 0.6.1", "ed25519-zebra", "futures", "futures-core", @@ -5614,7 +5614,7 @@ dependencies = [ "bs58", "byteorder", "chrono", - "color-eyre 0.6.0", + "color-eyre 0.6.1", "criterion", "displaydoc", "ed25519-zebra", @@ -5666,7 +5666,7 @@ dependencies = [ "blake2b_simd 1.0.0", "bls12_381", "chrono", - "color-eyre 0.6.0", + "color-eyre 0.6.1", "dirs", "displaydoc", "futures", @@ -5762,6 +5762,7 @@ dependencies = [ "zebra-chain", "zebra-network", "zebra-node-services", + "zebra-state", "zebra-test", ] @@ -5784,7 +5785,7 @@ version = "1.0.0-beta.5" dependencies = [ "bincode", "chrono", - "color-eyre 0.6.0", + "color-eyre 0.6.1", "dirs", "displaydoc", "futures", @@ -5841,7 +5842,7 @@ dependencies = [ name = "zebra-utils" version = "1.0.0-beta.5" dependencies = [ - "color-eyre 0.6.0", + "color-eyre 0.6.1", "hex", "serde_json", "structopt", diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index 18d60606..351328f6 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -22,7 +22,7 @@ pub use commitment::{ pub use hash::Hash; pub use header::{BlockTimeError, CountedHeader, Header}; pub use height::Height; -pub use serialize::MAX_BLOCK_BYTES; +pub use serialize::{SerializedBlock, MAX_BLOCK_BYTES}; #[cfg(any(test, feature = "proptest-impl"))] pub use arbitrary::LedgerState; diff --git a/zebra-chain/src/block/serialize.rs b/zebra-chain/src/block/serialize.rs index edae4824..d5b6b6e7 100644 --- a/zebra-chain/src/block/serialize.rs +++ b/zebra-chain/src/block/serialize.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, io}; +use std::{borrow::Borrow, convert::TryInto, io}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use chrono::{TimeZone, Utc}; @@ -135,3 +135,30 @@ impl ZcashDeserialize for Block { }) } } + +/// A serialized block. +/// +/// Stores bytes that are guaranteed to be deserializable into a [`Block`]. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct SerializedBlock { + bytes: Vec, +} + +/// Build a [`SerializedBlock`] by serializing a block. +impl> From for SerializedBlock { + fn from(block: B) -> Self { + SerializedBlock { + bytes: block + .borrow() + .zcash_serialize_to_vec() + .expect("Writing to a `Vec` should never fail"), + } + } +} + +/// Access the serialized bytes of a [`SerializedBlock`]. +impl AsRef<[u8]> for SerializedBlock { + fn as_ref(&self) -> &[u8] { + self.bytes.as_ref() + } +} diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index 04e1c955..35519bc4 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -9,9 +9,9 @@ edition = "2021" [dependencies] zebra-chain = { path = "../zebra-chain" } -zebra-node-services = { path = "../zebra-node-services" } - zebra-network = { path = "../zebra-network" } +zebra-node-services = { path = "../zebra-node-services" } +zebra-state = { path = "../zebra-state" } futures = "0.3.21" diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 2932ce68..914f1d24 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -6,14 +6,15 @@ //! Some parts of the `zcashd` RPC documentation are outdated. //! So this implementation follows the `lightwalletd` client implementation. -use futures::FutureExt; +use futures::{FutureExt, TryFutureExt}; use hex::FromHex; use jsonrpc_core::{self, BoxFuture, Error, ErrorCode, Result}; use jsonrpc_derive::rpc; -use tower::{buffer::Buffer, ServiceExt}; +use tower::{buffer::Buffer, Service, ServiceExt}; use zebra_chain::{ - serialization::ZcashDeserialize, + block::SerializedBlock, + serialization::{SerializationError, ZcashDeserialize}, transaction::{self, Transaction}, }; use zebra_network::constants::USER_AGENT; @@ -72,38 +73,82 @@ pub trait Rpc { &self, raw_transaction_hex: String, ) -> BoxFuture>; + + /// getblock + /// + /// Returns requested block by height, encoded as hex. + /// + /// zcashd reference: + /// + /// Result: + /// { + /// "data": String, // The block encoded as hex + /// } + /// + /// Note 1: We only expose the `data` field as lightwalletd uses the non-verbose + /// mode for all getblock calls: + /// + /// Note 2: `lightwalletd` only requests blocks by height, so we don't support + /// getting blocks by hash. + /// + /// Note 3: The `verbosity` parameter is ignored but required in the call. + #[rpc(name = "getblock")] + fn get_block(&self, height: String, verbosity: u8) -> BoxFuture>; } /// RPC method implementations. -pub struct RpcImpl +pub struct RpcImpl where - Mempool: tower::Service, + Mempool: Service, + State: Service< + zebra_state::Request, + Response = zebra_state::Response, + Error = zebra_state::BoxError, + >, { /// Zebra's application version. app_version: String, - /// A handle to the mempool service. mempool: Buffer, + /// A handle to the state service. + state: Buffer, } -impl RpcImpl +impl RpcImpl where - Mempool: tower::Service, + Mempool: Service, + State: Service< + zebra_state::Request, + Response = zebra_state::Response, + Error = zebra_state::BoxError, + > + 'static, + State::Future: Send, { /// Create a new instance of the RPC handler. - pub fn new(app_version: String, mempool: Buffer) -> Self { + pub fn new( + app_version: String, + mempool: Buffer, + state: Buffer, + ) -> Self { RpcImpl { app_version, mempool, + state, } } } -impl Rpc for RpcImpl +impl Rpc for RpcImpl where Mempool: tower::Service + 'static, Mempool::Future: Send, + State: Service< + zebra_state::Request, + Response = zebra_state::Response, + Error = zebra_state::BoxError, + > + 'static, + State::Future: Send, { fn get_info(&self) -> Result { let response = GetInfo { @@ -169,6 +214,40 @@ where } .boxed() } + + fn get_block(&self, height: String, _verbosity: u8) -> BoxFuture> { + let mut state = self.state.clone(); + + async move { + let height = height.parse().map_err(|error: SerializationError| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + let request = zebra_state::Request::Block(zebra_state::HashOrHeight::Height(height)); + let response = state + .ready() + .and_then(|service| service.call(request)) + .await + .map_err(|error| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + match response { + zebra_state::Response::Block(Some(block)) => Ok(GetBlock(block.into())), + zebra_state::Response::Block(None) => Err(Error { + code: ErrorCode::ServerError(0), + message: "Block not found".to_string(), + data: None, + }), + _ => unreachable!("unmatched response to a block request"), + } + } + .boxed() + } } #[derive(serde::Serialize, serde::Deserialize)] @@ -190,3 +269,7 @@ pub struct GetBlockChainInfo { /// /// A JSON string with the transaction hash in hexadecimal. pub struct SentTransactionHash(#[serde(with = "hex")] transaction::Hash); + +#[derive(serde::Serialize)] +/// Response to a `getblock` RPC request. +pub struct GetBlock(#[serde(with = "hex")] SerializedBlock); diff --git a/zebra-rpc/src/methods/tests/prop.rs b/zebra-rpc/src/methods/tests/prop.rs index f67c6cb1..67b7f5e1 100644 --- a/zebra-rpc/src/methods/tests/prop.rs +++ b/zebra-rpc/src/methods/tests/prop.rs @@ -20,7 +20,8 @@ proptest! { runtime.block_on(async move { let mut mempool = MockService::build().for_prop_tests(); - let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1)); + let mut state = MockService::build().for_prop_tests(); + let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1)); let hash = SentTransactionHash(transaction.hash()); let transaction_bytes = transaction @@ -39,6 +40,8 @@ proptest! { .await? .respond(response); + state.expect_no_requests().await?; + let result = send_task .await .expect("Sending raw transactions should not panic"); @@ -58,7 +61,9 @@ proptest! { runtime.block_on(async move { let mut mempool = MockService::build().for_prop_tests(); - let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1)); + let mut state = MockService::build().for_prop_tests(); + + let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1)); let transaction_bytes = transaction .zcash_serialize_to_vec() @@ -75,6 +80,9 @@ proptest! { .await? .respond(Err(DummyError)); + state.expect_no_requests().await?; + + let result = send_task .await .expect("Sending raw transactions should not panic"); @@ -101,7 +109,9 @@ proptest! { runtime.block_on(async move { let mut mempool = MockService::build().for_prop_tests(); - let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1)); + let mut state = MockService::build().for_prop_tests(); + + let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1)); let transaction_bytes = transaction .zcash_serialize_to_vec() @@ -119,6 +129,8 @@ proptest! { .await? .respond(response); + state.expect_no_requests().await?; + let result = send_task .await .expect("Sending raw transactions should not panic"); @@ -152,11 +164,14 @@ proptest! { runtime.block_on(async move { let mut mempool = MockService::build().for_prop_tests(); - let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1)); + let mut state = MockService::build().for_prop_tests(); + + let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1)); let send_task = tokio::spawn(rpc.send_raw_transaction(non_hex_string)); mempool.expect_no_requests().await?; + state.expect_no_requests().await?; let result = send_task .await @@ -193,11 +208,14 @@ proptest! { runtime.block_on(async move { let mut mempool = MockService::build().for_prop_tests(); - let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1)); + let mut state = MockService::build().for_prop_tests(); + + let rpc = RpcImpl::new("RPC test".to_owned(), Buffer::new(mempool.clone(), 1), Buffer::new(state.clone(), 1)); let send_task = tokio::spawn(rpc.send_raw_transaction(hex::encode(random_bytes))); mempool.expect_no_requests().await?; + state.expect_no_requests().await?; let result = send_task .await diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index 0689fb08..3c52c90d 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -1,7 +1,10 @@ //! Fixed test vectors for RPC methods. +use std::sync::Arc; + use tower::buffer::Buffer; +use zebra_chain::{block::Block, parameters::Network, serialization::ZcashDeserializeInto}; use zebra_network::constants::USER_AGENT; use zebra_node_services::BoxError; use zebra_test::mock_service::MockService; @@ -13,10 +16,12 @@ async fn rpc_getinfo() { zebra_test::init(); let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + let mut state = MockService::build().for_unit_tests(); let rpc = RpcImpl::new( "Zebra version test".to_string(), Buffer::new(mempool.clone(), 1), + Buffer::new(state.clone(), 1), ); let get_info = rpc.get_info().expect("We should have a GetInfo struct"); @@ -30,4 +35,66 @@ async fn rpc_getinfo() { assert_eq!(get_info.subversion, USER_AGENT); mempool.expect_no_requests().await; + state.expect_no_requests().await; +} + +#[tokio::test] +async fn rpc_getblock() { + zebra_test::init(); + + // Number of blocks to populate state with + const NUMBER_OF_BLOCKS: u32 = 10; + + // Put the first `NUMBER_OF_BLOCKS` blocks in a vector + let blocks: Vec> = zebra_test::vectors::MAINNET_BLOCKS + .range(0..=NUMBER_OF_BLOCKS) + .map(|(_, block_bytes)| block_bytes.zcash_deserialize_into::>().unwrap()) + .collect(); + + let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + // Create a populated state service + 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), + state, + }; + + // Make calls and check response + for (i, block) in blocks.into_iter().enumerate() { + let get_block = rpc + .get_block(i.to_string(), 0u8) + .await + .expect("We should have a GetBlock struct"); + + assert_eq!(get_block.0, block.into()); + } + + mempool.expect_no_requests().await; +} + +#[tokio::test] +async fn rpc_getblock_error() { + zebra_test::init(); + + let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + let mut state = 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), + }; + + // Make sure we get an error if Zebra can't parse the block height. + assert!(rpc + .get_block("not parsable as height".to_string(), 0u8) + .await + .is_err()); + + mempool.expect_no_requests().await; + state.expect_no_requests().await; } diff --git a/zebra-rpc/src/server.rs b/zebra-rpc/src/server.rs index e7bed4d2..01d27111 100644 --- a/zebra-rpc/src/server.rs +++ b/zebra-rpc/src/server.rs @@ -6,7 +6,7 @@ use jsonrpc_core; use jsonrpc_http_server::ServerBuilder; -use tower::buffer::Buffer; +use tower::{buffer::Buffer, Service}; use tracing::*; use tracing_futures::Instrument; @@ -26,21 +26,28 @@ pub struct RpcServer; impl RpcServer { /// Start a new RPC server endpoint - pub fn spawn( + pub fn spawn( config: Config, app_version: String, mempool: Buffer, + state: Buffer, ) -> tokio::task::JoinHandle<()> where Mempool: tower::Service + 'static, Mempool::Future: Send, + State: Service< + zebra_state::Request, + Response = zebra_state::Response, + Error = zebra_state::BoxError, + > + 'static, + State::Future: Send, { 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); + let rpc_impl = RpcImpl::new(app_version, mempool, state); // Create handler compatible with V1 and V2 RPC protocols let mut io = diff --git a/zebra-state/src/lib.rs b/zebra-state/src/lib.rs index dd0b1685..7c882ee6 100644 --- a/zebra-state/src/lib.rs +++ b/zebra-state/src/lib.rs @@ -41,7 +41,7 @@ pub use service::{ #[cfg(any(test, feature = "proptest-impl"))] pub use service::{ chain_tip::{ChainTipBlock, ChainTipSender}, - init_test, + init_test, populated_state, }; pub(crate) use request::ContextuallyValidBlock; diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 60a8793c..e14c3a05 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -7,9 +7,9 @@ use std::{ time::{Duration, Instant}, }; -use futures::future::FutureExt; +use futures::{future::FutureExt, stream::FuturesUnordered}; use tokio::sync::oneshot; -use tower::{util::BoxService, Service}; +use tower::{util::BoxService, Service, ServiceExt}; use tracing::instrument; #[cfg(any(test, feature = "proptest-impl"))] @@ -843,6 +843,33 @@ pub fn init_test(network: Network) -> Buffer>, + network: Network, +) -> Buffer, Request> { + let requests = blocks + .into_iter() + .map(|block| Request::CommitFinalizedBlock(block.into())); + + let mut state = init_test(network); + + let mut responses = FuturesUnordered::new(); + + for request in requests { + let rsp = state.ready().await.unwrap().call(request); + responses.push(rsp); + } + + use futures::StreamExt; + while let Some(rsp) = responses.next().await { + rsp.expect("blocks should commit just fine"); + } + + state +} + /// Check if zebra is following a legacy chain and return an error if so. fn legacy_chain_check( nu5_activation_height: block::Height, diff --git a/zebra-state/src/service/tests.rs b/zebra-state/src/service/tests.rs index c73d0d82..cb71579f 100644 --- a/zebra-state/src/service/tests.rs +++ b/zebra-state/src/service/tests.rs @@ -1,7 +1,6 @@ use std::{convert::TryInto, env, sync::Arc}; -use futures::stream::FuturesUnordered; -use tower::{buffer::Buffer, util::BoxService, Service, ServiceExt}; +use tower::{buffer::Buffer, util::BoxService}; use zebra_chain::{ block::{self, Block, CountedHeader}, @@ -17,38 +16,13 @@ use zebra_test::{prelude::*, transcript::Transcript}; use crate::{ arbitrary::Prepare, constants, init_test, - service::{chain_tip::TipAction, StateService}, + service::{chain_tip::TipAction, populated_state, StateService}, tests::setup::{partial_nu5_chain_strategy, transaction_v4_from_coinbase}, BoxError, Config, FinalizedBlock, PreparedBlock, Request, Response, }; const LAST_BLOCK_HEIGHT: u32 = 10; -async fn populated_state( - blocks: impl IntoIterator>, -) -> Buffer, Request> { - let requests = blocks - .into_iter() - .map(|block| Request::CommitFinalizedBlock(block.into())); - - let network = Network::Mainnet; - let mut state = init_test(network); - - let mut responses = FuturesUnordered::new(); - - for request in requests { - let rsp = state.ready().await.unwrap().call(request); - responses.push(rsp); - } - - use futures::StreamExt; - while let Some(rsp) = responses.next().await { - rsp.expect("blocks should commit just fine"); - } - - state -} - async fn test_populated_state_responds_correctly( mut state: Buffer, Request>, ) -> Result<()> { @@ -222,7 +196,7 @@ async fn test_populated_state_responds_correctly( #[tokio::main] async fn populate_and_check(blocks: Vec>) -> Result<()> { - let state = populated_state(blocks).await; + let state = populated_state(blocks, Network::Mainnet).await; test_populated_state_responds_correctly(state).await?; Ok(()) } diff --git a/zebrad/src/commands/start.rs b/zebrad/src/commands/start.rs index 35476a06..b79726d3 100644 --- a/zebrad/src/commands/start.rs +++ b/zebrad/src/commands/start.rs @@ -155,8 +155,12 @@ impl StartCmd { .service(mempool); // Launch RPC server - let rpc_task_handle = - RpcServer::spawn(config.rpc, app_version().to_string(), mempool.clone()); + let rpc_task_handle = RpcServer::spawn( + config.rpc, + app_version().to_string(), + mempool.clone(), + state.clone(), + ); let setup_data = InboundSetupData { address_book, diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index aac81a08..6372e845 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -1693,7 +1693,7 @@ fn lightwalletd_integration() -> Result<()> { // zcash/lightwalletd calls getbestblockhash here, but // adityapk00/lightwalletd calls getblock let result = - lightwalletd.expect_stdout_line_matches("Method not found.*error zcashd getblock rpc"); + lightwalletd.expect_stdout_line_matches("Block hash changed, clearing mempool clients"); let (_, zebrad) = zebrad.kill_on_error(result)?; // zcash/lightwalletd exits with a fatal error here.