test(rpc): Add Rust tests for lightwalletd sync from Zebra (#4177)
* Make the lightwalletd integration test take a test type * Configure lightwalletd tests based on the test type * Remove obsolete kill_on_error() in the lightwalletd test * Refactor to simplify the test function * Move LightwalletdTestType to the lightwalletd module * Create a test function that runs the full lightwalletd test suite * Actually use the cached Zebra state * Add checks for the new integration test modes * Populate the lightwalletd state dir in the FullSyncFromGenesis test * Fix up state handling, fail earlier if state is invalid * Adjust timeouts and regex escapes * Make state requirements for each test stricter * Move configs to the top of the test function * Allow unexpected lightwalletd cached state in some tests * Speed up tests slightly by removing an intermittent log check * Move timeout selection into test type methods * Move failure messages into test type methods * Turn a function argument into an enum field * Check lightwalletd state directly, rather than Zebra RPC results * Update gRPC tests for function argument changes * Remove duplicate env var constant and redundant code
This commit is contained in:
parent
23ff00b246
commit
59bdab17bf
|
|
@ -35,12 +35,7 @@ use zebra_chain::{
|
||||||
use zebra_network::constants::PORT_IN_USE_ERROR;
|
use zebra_network::constants::PORT_IN_USE_ERROR;
|
||||||
use zebra_state::constants::LOCK_FILE_ERROR;
|
use zebra_state::constants::LOCK_FILE_ERROR;
|
||||||
|
|
||||||
use zebra_test::{
|
use zebra_test::{args, command::ContextFrom, net::random_known_port, prelude::*};
|
||||||
args,
|
|
||||||
command::{ContextFrom, NO_MATCHES_REGEX_ITER},
|
|
||||||
net::random_known_port,
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
|
|
@ -49,11 +44,12 @@ use common::{
|
||||||
config::{default_test_config, persistent_test_config, testdir},
|
config::{default_test_config, persistent_test_config, testdir},
|
||||||
launch::{
|
launch::{
|
||||||
spawn_zebrad_for_rpc_without_initial_peers, ZebradTestDirExt, BETWEEN_NODES_DELAY,
|
spawn_zebrad_for_rpc_without_initial_peers, ZebradTestDirExt, BETWEEN_NODES_DELAY,
|
||||||
LAUNCH_DELAY, LIGHTWALLETD_DELAY,
|
LAUNCH_DELAY,
|
||||||
},
|
},
|
||||||
lightwalletd::{
|
lightwalletd::{
|
||||||
random_known_rpc_port_config, zebra_skip_lightwalletd_tests, LightWalletdTestDirExt,
|
random_known_rpc_port_config, zebra_skip_lightwalletd_tests, LightWalletdTestDirExt,
|
||||||
LIGHTWALLETD_TEST_TIMEOUT,
|
LightwalletdTestType::{self, *},
|
||||||
|
LIGHTWALLETD_DATA_DIR_VAR,
|
||||||
},
|
},
|
||||||
sync::{
|
sync::{
|
||||||
create_cached_database_height, sync_until, MempoolBehavior, LARGE_CHECKPOINT_TEST_HEIGHT,
|
create_cached_database_height, sync_until, MempoolBehavior, LARGE_CHECKPOINT_TEST_HEIGHT,
|
||||||
|
|
@ -997,124 +993,7 @@ async fn rpc_endpoint() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Failure log messages for any process, from the OS or shell.
|
/// Make sure `lightwalletd` works with Zebra, when both their states are empty.
|
||||||
///
|
|
||||||
/// These messages show that the child process has failed.
|
|
||||||
/// So when we see them in the logs, we make the test fail.
|
|
||||||
const PROCESS_FAILURE_MESSAGES: &[&str] = &[
|
|
||||||
// Linux
|
|
||||||
"Aborted",
|
|
||||||
// macOS / BSDs
|
|
||||||
"Abort trap",
|
|
||||||
// TODO: add other OS or C library errors?
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Failure log messages from Zebra.
|
|
||||||
///
|
|
||||||
/// These `zebrad` messages show that the `lightwalletd` integration test has failed.
|
|
||||||
/// So when we see them in the logs, we make the test fail.
|
|
||||||
const ZEBRA_FAILURE_MESSAGES: &[&str] = &[
|
|
||||||
// Rust-specific panics
|
|
||||||
"The application panicked",
|
|
||||||
// RPC port errors
|
|
||||||
"Unable to start RPC server",
|
|
||||||
// TODO: disable if this actually happens during test zebrad shutdown
|
|
||||||
"Stopping RPC endpoint",
|
|
||||||
// Missing RPCs in zebrad logs (this log is from PR #3860)
|
|
||||||
//
|
|
||||||
// TODO: temporarily disable until enough RPCs are implemented, if needed
|
|
||||||
"Received unrecognized RPC request",
|
|
||||||
// RPC argument errors: parsing and data
|
|
||||||
//
|
|
||||||
// These logs are produced by jsonrpc_core inside Zebra,
|
|
||||||
// but it doesn't log them yet.
|
|
||||||
//
|
|
||||||
// TODO: log these errors in Zebra, and check for them in the Zebra logs?
|
|
||||||
"Invalid params",
|
|
||||||
"Method not found",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Failure log messages from lightwalletd.
|
|
||||||
///
|
|
||||||
/// These `lightwalletd` messages show that the `lightwalletd` integration test has failed.
|
|
||||||
/// So when we see them in the logs, we make the test fail.
|
|
||||||
const LIGHTWALLETD_FAILURE_MESSAGES: &[&str] = &[
|
|
||||||
// Go-specific panics
|
|
||||||
"panic:",
|
|
||||||
// Missing RPCs in lightwalletd logs
|
|
||||||
// TODO: temporarily disable until enough RPCs are implemented, if needed
|
|
||||||
"unable to issue RPC call",
|
|
||||||
// RPC response errors: parsing and data
|
|
||||||
//
|
|
||||||
// jsonrpc_core error messages from Zebra,
|
|
||||||
// received by lightwalletd and written to its logs
|
|
||||||
"Invalid params",
|
|
||||||
"Method not found",
|
|
||||||
// Early termination
|
|
||||||
//
|
|
||||||
// TODO: temporarily disable until enough RPCs are implemented, if needed
|
|
||||||
"Lightwalletd died with a Fatal error",
|
|
||||||
// Go json package error messages:
|
|
||||||
"json: cannot unmarshal",
|
|
||||||
"into Go value of type",
|
|
||||||
// lightwalletd custom RPC error messages from:
|
|
||||||
// https://github.com/adityapk00/lightwalletd/blob/master/common/common.go
|
|
||||||
"block requested is newer than latest block",
|
|
||||||
"Cache add failed",
|
|
||||||
"error decoding",
|
|
||||||
"error marshaling",
|
|
||||||
"error parsing JSON",
|
|
||||||
"error reading JSON response",
|
|
||||||
"error with",
|
|
||||||
// We expect these errors when lightwalletd reaches the end of the zebrad cached state
|
|
||||||
// "error requesting block: 0: Block not found",
|
|
||||||
// "error zcashd getblock rpc",
|
|
||||||
"received overlong message",
|
|
||||||
"received unexpected height block",
|
|
||||||
"Reorg exceeded max",
|
|
||||||
"unable to issue RPC call",
|
|
||||||
// Missing fields for each specific RPC
|
|
||||||
//
|
|
||||||
// get_block_chain_info
|
|
||||||
//
|
|
||||||
// invalid sapling height
|
|
||||||
"Got sapling height 0",
|
|
||||||
// missing BIP70 chain name, should be "main" or "test"
|
|
||||||
" chain ",
|
|
||||||
// missing branchID, should be 8 hex digits
|
|
||||||
" branchID \"",
|
|
||||||
// get_block
|
|
||||||
//
|
|
||||||
// a block error other than "-8: Block not found"
|
|
||||||
"error requesting block",
|
|
||||||
// a missing block with an incorrect error code
|
|
||||||
"Block not found",
|
|
||||||
//
|
|
||||||
// TODO: complete this list for each RPC with fields, if that RPC generates logs
|
|
||||||
// get_info - doesn't generate logs
|
|
||||||
// get_raw_transaction - might not generate logs
|
|
||||||
// z_get_tree_state
|
|
||||||
// get_address_txids
|
|
||||||
// get_address_balance
|
|
||||||
// get_address_utxos
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Ignored failure logs for lightwalletd.
|
|
||||||
/// These regexes override the [`LIGHTWALLETD_FAILURE_MESSAGES`].
|
|
||||||
///
|
|
||||||
/// These `lightwalletd` messages look like failure messages, but they are actually ok.
|
|
||||||
/// So when we see them in the logs, we make the test continue.
|
|
||||||
const LIGHTWALLETD_IGNORE_MESSAGES: &[&str] = &[
|
|
||||||
// Exceptions to lightwalletd custom RPC error messages:
|
|
||||||
//
|
|
||||||
// This log matches the "error with" RPC error message,
|
|
||||||
// but we expect Zebra to start with an empty state.
|
|
||||||
//
|
|
||||||
// TODO: this exception should not be used for the cached state tests (#3511)
|
|
||||||
r#"No Chain tip available yet","level":"warning","msg":"error with getblockchaininfo rpc, retrying"#,
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Launch `zebrad` with an RPC port, and make sure `lightwalletd` works with Zebra.
|
|
||||||
///
|
///
|
||||||
/// This test only runs when the `ZEBRA_TEST_LIGHTWALLETD` env var is set.
|
/// This test only runs when the `ZEBRA_TEST_LIGHTWALLETD` env var is set.
|
||||||
///
|
///
|
||||||
|
|
@ -1122,6 +1001,72 @@ const LIGHTWALLETD_IGNORE_MESSAGES: &[&str] = &[
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
fn lightwalletd_integration() -> Result<()> {
|
fn lightwalletd_integration() -> Result<()> {
|
||||||
|
lightwalletd_integration_test(LaunchWithEmptyState)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make sure `lightwalletd` can sync from Zebra, in update sync mode.
|
||||||
|
///
|
||||||
|
/// If is set, runs a quick sync, then a full sync.
|
||||||
|
/// If `LIGHTWALLETD_DATA_DIR` is not set, just runs a full sync.
|
||||||
|
///
|
||||||
|
/// This test only runs when the `ZEBRA_TEST_LIGHTWALLETD`,
|
||||||
|
/// `ZEBRA_CACHED_STATE_DIR`, and `LIGHTWALLETD_DATA_DIR` env vars are set.
|
||||||
|
///
|
||||||
|
/// This test doesn't work on Windows, so it is always skipped on that platform.
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn lightwalletd_update_sync() -> Result<()> {
|
||||||
|
lightwalletd_integration_test(UpdateCachedState)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make sure `lightwalletd` can fully sync from genesis using Zebra.
|
||||||
|
///
|
||||||
|
/// This test only runs when the `ZEBRA_TEST_LIGHTWALLETD` and
|
||||||
|
/// `ZEBRA_CACHED_STATE_DIR` env vars are set.
|
||||||
|
///
|
||||||
|
/// This test doesn't work on Windows, so it is always skipped on that platform.
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn lightwalletd_full_sync() -> Result<()> {
|
||||||
|
lightwalletd_integration_test(FullSyncFromGenesis {
|
||||||
|
allow_lightwalletd_cached_state: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make sure `lightwalletd` can sync from Zebra, in all available modes.
|
||||||
|
///
|
||||||
|
/// Runs the tests in this order:
|
||||||
|
/// - launch lightwalletd with empty states,
|
||||||
|
/// - if `ZEBRA_CACHED_STATE_DIR` and `LIGHTWALLETD_DATA_DIR` are set: run a quick update sync,
|
||||||
|
/// - if `ZEBRA_CACHED_STATE_DIR` is set: run a full sync.
|
||||||
|
///
|
||||||
|
/// These tests don't work on Windows, so they are always skipped on that platform.
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn lightwalletd_test_suite() -> Result<()> {
|
||||||
|
lightwalletd_integration_test(LaunchWithEmptyState)?;
|
||||||
|
|
||||||
|
// Only runs when ZEBRA_CACHED_STATE_DIR is set.
|
||||||
|
// When manually running the test suite, allow cached state in the full sync test.
|
||||||
|
lightwalletd_integration_test(FullSyncFromGenesis {
|
||||||
|
allow_lightwalletd_cached_state: true,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Only runs when LIGHTWALLETD_DATA_DIR and ZEBRA_CACHED_STATE_DIR are set
|
||||||
|
lightwalletd_integration_test(UpdateCachedState)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a lightwalletd integration test with a configuration for `test_type`.
|
||||||
|
///
|
||||||
|
/// Set `allow_cached_state_for_full_sync` to speed up manual full sync tests.
|
||||||
|
///
|
||||||
|
/// The random ports in this test can cause [rare port conflicts.](#Note on port conflict)
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn lightwalletd_integration_test(test_type: LightwalletdTestType) -> Result<()> {
|
||||||
zebra_test::init();
|
zebra_test::init();
|
||||||
|
|
||||||
// Skip the test unless the user specifically asked for it
|
// Skip the test unless the user specifically asked for it
|
||||||
|
|
@ -1129,29 +1074,62 @@ fn lightwalletd_integration() -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch zebrad
|
// Get the zebrad and lightwalletd configs
|
||||||
|
|
||||||
// Write a configuration that has RPC listen_addr set
|
// Handle the Zebra state directory based on the test type:
|
||||||
// [Note on port conflict](#Note on port conflict)
|
// - LaunchWithEmptyState: ignore the state directory
|
||||||
let mut config = random_known_rpc_port_config()?;
|
// - FullSyncFromGenesis & UpdateCachedState:
|
||||||
|
// skip the test if it is not available, timeout if it is not populated
|
||||||
|
|
||||||
let zdir = testdir()?.with_config(&mut config)?;
|
// Write a configuration that has RPC listen_addr set.
|
||||||
let mut zebrad = zdir
|
// If the state path env var is set, use it in the config.
|
||||||
.spawn_child(args!["start"])?
|
let config = if let Some(config) = test_type.zebrad_config() {
|
||||||
.with_timeout(LAUNCH_DELAY)
|
config?
|
||||||
.with_failure_regex_iter(
|
} else {
|
||||||
// TODO: replace with a function that returns the full list and correct return type
|
return Ok(());
|
||||||
ZEBRA_FAILURE_MESSAGES
|
};
|
||||||
.iter()
|
|
||||||
.chain(PROCESS_FAILURE_MESSAGES)
|
// Handle the lightwalletd state directory based on the test type:
|
||||||
.cloned(),
|
// - LaunchWithEmptyState: ignore the state directory
|
||||||
NO_MATCHES_REGEX_ITER.iter().cloned(),
|
// - FullSyncFromGenesis: use it if available, timeout if it is already populated
|
||||||
|
// - UpdateCachedState: skip the test if it is not available, timeout if it is not populated
|
||||||
|
let lightwalletd_state_path = test_type.lightwalletd_state_path();
|
||||||
|
|
||||||
|
if test_type.needs_lightwalletd_cached_state() && lightwalletd_state_path.is_none() {
|
||||||
|
tracing::info!(
|
||||||
|
"skipped {test_type:?} lightwalletd test, \
|
||||||
|
set the {LIGHTWALLETD_DATA_DIR_VAR:?} environment variable to run the test",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(?test_type, "running lightwalletd & zebrad integration test");
|
||||||
|
|
||||||
|
// Get the lists of process failure logs
|
||||||
|
let (zebrad_failure_messages, zebrad_ignore_messages) = test_type.zebrad_failure_messages();
|
||||||
|
|
||||||
|
let (lightwalletd_failure_messages, lightwalletd_ignore_messages) =
|
||||||
|
test_type.lightwalletd_failure_messages();
|
||||||
|
|
||||||
|
// Launch zebrad
|
||||||
|
let zdir = testdir()?.with_exact_config(&config)?;
|
||||||
|
let mut zebrad = zdir
|
||||||
|
.spawn_child(args!["start"])?
|
||||||
|
.with_timeout(test_type.zebrad_timeout())
|
||||||
|
.with_failure_regex_iter(zebrad_failure_messages, zebrad_ignore_messages);
|
||||||
|
|
||||||
|
if test_type.needs_zebra_cached_state() {
|
||||||
|
zebrad.expect_stdout_line_matches(r"loaded Zebra state cache tip=.*Height\([0-9]{7}\)")?;
|
||||||
|
} else {
|
||||||
|
// Timeout the test if we're somehow accidentally using a cached state
|
||||||
|
zebrad.expect_stdout_line_matches("loaded Zebra state cache tip=None")?;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait until `zebrad` has opened the RPC endpoint
|
// Wait until `zebrad` has opened the RPC endpoint
|
||||||
zebrad.expect_stdout_line_matches(
|
zebrad.expect_stdout_line_matches(regex::escape(
|
||||||
format!("Opened RPC endpoint at {}", config.rpc.listen_addr.unwrap()).as_str(),
|
format!("Opened RPC endpoint at {}", config.rpc.listen_addr.unwrap()).as_str(),
|
||||||
)?;
|
))?;
|
||||||
|
|
||||||
// Launch lightwalletd
|
// Launch lightwalletd
|
||||||
|
|
||||||
|
|
@ -1160,37 +1138,40 @@ fn lightwalletd_integration() -> Result<()> {
|
||||||
let ldir = ldir.with_lightwalletd_config(config.rpc.listen_addr.unwrap())?;
|
let ldir = ldir.with_lightwalletd_config(config.rpc.listen_addr.unwrap())?;
|
||||||
|
|
||||||
// Launch the lightwalletd process
|
// Launch the lightwalletd process
|
||||||
let result = ldir.spawn_lightwalletd_child(args![]);
|
let lightwalletd = if test_type == LaunchWithEmptyState {
|
||||||
let (lightwalletd, zebrad) = zebrad.kill_on_error(result)?;
|
ldir.spawn_lightwalletd_child(None, args![])?
|
||||||
|
} else {
|
||||||
|
ldir.spawn_lightwalletd_child(lightwalletd_state_path, args![])?
|
||||||
|
};
|
||||||
|
|
||||||
let mut lightwalletd = lightwalletd
|
let mut lightwalletd = lightwalletd
|
||||||
.with_timeout(LIGHTWALLETD_DELAY)
|
.with_timeout(test_type.lightwalletd_timeout())
|
||||||
.with_failure_regex_iter(
|
.with_failure_regex_iter(lightwalletd_failure_messages, lightwalletd_ignore_messages);
|
||||||
// TODO: replace with a function that returns the full list and correct return type
|
|
||||||
LIGHTWALLETD_FAILURE_MESSAGES
|
|
||||||
.iter()
|
|
||||||
.chain(PROCESS_FAILURE_MESSAGES)
|
|
||||||
.cloned(),
|
|
||||||
// TODO: some exceptions do not apply to the cached state tests (#3511)
|
|
||||||
LIGHTWALLETD_IGNORE_MESSAGES.iter().cloned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wait until `lightwalletd` has launched
|
// Wait until `lightwalletd` has launched
|
||||||
let result = lightwalletd.expect_stdout_line_matches("Starting gRPC server");
|
lightwalletd.expect_stdout_line_matches(regex::escape("Starting gRPC server"))?;
|
||||||
let (_, zebrad) = zebrad.kill_on_error(result)?;
|
|
||||||
|
|
||||||
// Check that `lightwalletd` is calling the expected Zebra RPCs
|
// Check that `lightwalletd` is calling the expected Zebra RPCs
|
||||||
|
|
||||||
// getblockchaininfo
|
// getblockchaininfo
|
||||||
//
|
if test_type.needs_zebra_cached_state() {
|
||||||
// TODO: update branchID when we're using cached state (#3511)
|
lightwalletd.expect_stdout_line_matches(
|
||||||
// add "Waiting for zcashd height to reach Sapling activation height"
|
"Got sapling height 419200 block height [0-9]{7} chain main branchID e9ff75a6",
|
||||||
let result = lightwalletd.expect_stdout_line_matches(
|
)?;
|
||||||
"Got sapling height 419200 block height [0-9]+ chain main branchID 00000000",
|
} else {
|
||||||
);
|
// Timeout the test if we're somehow accidentally using a cached state in our temp dir
|
||||||
let (_, zebrad) = zebrad.kill_on_error(result)?;
|
lightwalletd.expect_stdout_line_matches(
|
||||||
|
"Got sapling height 419200 block height [0-9]{1,6} chain main branchID 00000000",
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
let result = lightwalletd.expect_stdout_line_matches("Found 0 blocks in cache");
|
if test_type.needs_lightwalletd_cached_state() {
|
||||||
let (_, zebrad) = zebrad.kill_on_error(result)?;
|
// TODO: expect `[0-9]{7}` when we're using the tip cached state (#4155)
|
||||||
|
lightwalletd.expect_stdout_line_matches("Found [0-9]{6,7} blocks in cache")?;
|
||||||
|
} else if !test_type.allow_lightwalletd_cached_state() {
|
||||||
|
// Timeout the test if we're somehow accidentally using a cached state in our temp dir
|
||||||
|
lightwalletd.expect_stdout_line_matches("Found 0 blocks in cache")?;
|
||||||
|
}
|
||||||
|
|
||||||
// getblock with the first Sapling block in Zebra's state
|
// getblock with the first Sapling block in Zebra's state
|
||||||
//
|
//
|
||||||
|
|
@ -1199,34 +1180,44 @@ fn lightwalletd_integration() -> Result<()> {
|
||||||
//
|
//
|
||||||
// The log also depends on what is in Zebra's state:
|
// The log also depends on what is in Zebra's state:
|
||||||
//
|
//
|
||||||
|
// # Cached Zebra State
|
||||||
|
//
|
||||||
|
// lightwalletd ingests blocks into its cache.
|
||||||
|
//
|
||||||
// # Empty Zebra State
|
// # Empty Zebra State
|
||||||
//
|
//
|
||||||
// lightwalletd tries to download the Sapling activation block, but it's not in the state.
|
// lightwalletd tries to download the Sapling activation block, but it's not in the state.
|
||||||
//
|
//
|
||||||
// Until the Sapling activation block has been downloaded, lightwalletd will log Zebra's RPC error:
|
// Until the Sapling activation block has been downloaded,
|
||||||
// "error requesting block: 0: Block not found"
|
// lightwalletd will keep retrying getblock.
|
||||||
// We also get a similar log when lightwalletd reaches the end of Zebra's cache.
|
if test_type.needs_zebra_cached_state() {
|
||||||
//
|
lightwalletd.expect_stdout_line_matches(regex::escape("Ingestor adding block to cache"))?;
|
||||||
// # Cached Zebra State
|
} else {
|
||||||
//
|
lightwalletd.expect_stdout_line_matches(regex::escape(
|
||||||
// After the first successful getblock call, lightwalletd will log:
|
"Waiting for zcashd height to reach Sapling activation height (419200)",
|
||||||
// "Block hash changed, clearing mempool clients"
|
))?;
|
||||||
// But we can't check for that, because it can come before or after the Ingestor log.
|
}
|
||||||
//
|
|
||||||
// TODO: expect Ingestor log when we're using cached state (#3511)
|
|
||||||
// "Ingestor adding block to cache"
|
|
||||||
let result = lightwalletd.expect_stdout_line_matches(regex::escape(
|
|
||||||
"Waiting for zcashd height to reach Sapling activation height (419200)",
|
|
||||||
));
|
|
||||||
let (_, zebrad) = zebrad.kill_on_error(result)?;
|
|
||||||
|
|
||||||
// (next RPC)
|
if matches!(test_type, UpdateCachedState | FullSyncFromGenesis { .. }) {
|
||||||
//
|
// Wait for Zebra to sync its cached state to the chain tip
|
||||||
// TODO: add extra checks when we add new Zebra RPCs
|
zebrad.expect_stdout_line_matches(regex::escape("sync_percent=100"))?;
|
||||||
|
|
||||||
|
// Wait for lightwalletd to sync to Zebra's tip
|
||||||
|
lightwalletd.expect_stdout_line_matches(regex::escape("Ingestor waiting for block"))?;
|
||||||
|
|
||||||
|
// Check Zebra is still at the tip (also clears and prints Zebra's logs)
|
||||||
|
zebrad.expect_stdout_line_matches(regex::escape("sync_percent=100"))?;
|
||||||
|
|
||||||
|
// lightwalletd doesn't log anything when we've reached the tip.
|
||||||
|
// But when it gets near the tip, it starts using the mempool.
|
||||||
|
lightwalletd.expect_stdout_line_matches(regex::escape(
|
||||||
|
"Block hash changed, clearing mempool clients",
|
||||||
|
))?;
|
||||||
|
lightwalletd.expect_stdout_line_matches(regex::escape("Adding new mempool txid"))?;
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup both processes
|
// Cleanup both processes
|
||||||
let result = lightwalletd.kill();
|
lightwalletd.kill()?;
|
||||||
let (_, mut zebrad) = zebrad.kill_on_error(result)?;
|
|
||||||
zebrad.kill()?;
|
zebrad.kill()?;
|
||||||
|
|
||||||
let lightwalletd_output = lightwalletd.wait_with_output()?.assert_failure()?;
|
let lightwalletd_output = lightwalletd.wait_with_output()?.assert_failure()?;
|
||||||
|
|
@ -1482,26 +1473,27 @@ where
|
||||||
async fn fully_synced_rpc_test() -> Result<()> {
|
async fn fully_synced_rpc_test() -> Result<()> {
|
||||||
zebra_test::init();
|
zebra_test::init();
|
||||||
|
|
||||||
// TODO: reuse code from https://github.com/ZcashFoundation/zebra/pull/4177/
|
// We're only using cached Zebra state here, so this test type is the most similar
|
||||||
// to get the cached_state_path
|
let test_type = LightwalletdTestType::FullSyncFromGenesis {
|
||||||
const CACHED_STATE_PATH_VAR: &str = "ZEBRA_CACHED_STATE_PATH";
|
allow_lightwalletd_cached_state: false,
|
||||||
let cached_state_path = match env::var_os(CACHED_STATE_PATH_VAR) {
|
|
||||||
Some(argument) => PathBuf::from(argument),
|
|
||||||
None => {
|
|
||||||
tracing::info!(
|
|
||||||
"skipped send transactions using lightwalletd test, \
|
|
||||||
set the {CACHED_STATE_PATH_VAR:?} environment variable to run the test",
|
|
||||||
);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle the Zebra state directory
|
||||||
|
let cached_state_path = test_type.zebrad_state_path();
|
||||||
|
|
||||||
|
if cached_state_path.is_none() {
|
||||||
|
tracing::info!("skipping fully synced zebrad RPC test");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::info!("running fully synced zebrad RPC test");
|
||||||
|
|
||||||
let network = Network::Mainnet;
|
let network = Network::Mainnet;
|
||||||
|
|
||||||
let (_zebrad, zebra_rpc_address) = spawn_zebrad_for_rpc_without_initial_peers(
|
let (_zebrad, zebra_rpc_address) = spawn_zebrad_for_rpc_without_initial_peers(
|
||||||
network,
|
network,
|
||||||
cached_state_path,
|
cached_state_path.unwrap(),
|
||||||
LIGHTWALLETD_TEST_TIMEOUT,
|
test_type.zebrad_timeout(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Make a getblock test that works only on synced node (high block number).
|
// Make a getblock test that works only on synced node (high block number).
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
//! Failure messages logged by test child processes.
|
||||||
|
//!
|
||||||
|
//! # Warning
|
||||||
|
//!
|
||||||
|
//! Test functions in this file will not be run.
|
||||||
|
//! This file is only for test library code.
|
||||||
|
|
||||||
|
/// Failure log messages for any process, from the OS or shell.
|
||||||
|
///
|
||||||
|
/// These messages show that the child process has failed.
|
||||||
|
/// So when we see them in the logs, we make the test fail.
|
||||||
|
pub const PROCESS_FAILURE_MESSAGES: &[&str] = &[
|
||||||
|
// Linux
|
||||||
|
"Aborted",
|
||||||
|
// macOS / BSDs
|
||||||
|
"Abort trap",
|
||||||
|
// TODO: add other OS or C library errors?
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Failure log messages from Zebra.
|
||||||
|
///
|
||||||
|
/// These `zebrad` messages show that the `lightwalletd` integration test has failed.
|
||||||
|
/// So when we see them in the logs, we make the test fail.
|
||||||
|
pub const ZEBRA_FAILURE_MESSAGES: &[&str] = &[
|
||||||
|
// Rust-specific panics
|
||||||
|
"The application panicked",
|
||||||
|
// RPC port errors
|
||||||
|
"Unable to start RPC server",
|
||||||
|
// TODO: disable if this actually happens during test zebrad shutdown
|
||||||
|
"Stopping RPC endpoint",
|
||||||
|
// Missing RPCs in zebrad logs (this log is from PR #3860)
|
||||||
|
//
|
||||||
|
// TODO: temporarily disable until enough RPCs are implemented, if needed
|
||||||
|
"Received unrecognized RPC request",
|
||||||
|
// RPC argument errors: parsing and data
|
||||||
|
//
|
||||||
|
// These logs are produced by jsonrpc_core inside Zebra,
|
||||||
|
// but it doesn't log them yet.
|
||||||
|
//
|
||||||
|
// TODO: log these errors in Zebra, and check for them in the Zebra logs?
|
||||||
|
"Invalid params",
|
||||||
|
"Method not found",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Failure log messages from lightwalletd.
|
||||||
|
///
|
||||||
|
/// These `lightwalletd` messages show that the `lightwalletd` integration test has failed.
|
||||||
|
/// So when we see them in the logs, we make the test fail.
|
||||||
|
pub const LIGHTWALLETD_FAILURE_MESSAGES: &[&str] = &[
|
||||||
|
// Go-specific panics
|
||||||
|
"panic:",
|
||||||
|
// Missing RPCs in lightwalletd logs
|
||||||
|
// TODO: temporarily disable until enough RPCs are implemented, if needed
|
||||||
|
"unable to issue RPC call",
|
||||||
|
// RPC response errors: parsing and data
|
||||||
|
//
|
||||||
|
// jsonrpc_core error messages from Zebra,
|
||||||
|
// received by lightwalletd and written to its logs
|
||||||
|
"Invalid params",
|
||||||
|
"Method not found",
|
||||||
|
// Early termination
|
||||||
|
//
|
||||||
|
// TODO: temporarily disable until enough RPCs are implemented, if needed
|
||||||
|
"Lightwalletd died with a Fatal error",
|
||||||
|
// Go json package error messages:
|
||||||
|
"json: cannot unmarshal",
|
||||||
|
"into Go value of type",
|
||||||
|
// lightwalletd custom RPC error messages from:
|
||||||
|
// https://github.com/adityapk00/lightwalletd/blob/master/common/common.go
|
||||||
|
"block requested is newer than latest block",
|
||||||
|
"Cache add failed",
|
||||||
|
"error decoding",
|
||||||
|
"error marshaling",
|
||||||
|
"error parsing JSON",
|
||||||
|
"error reading JSON response",
|
||||||
|
"error with",
|
||||||
|
// We expect these errors when lightwalletd reaches the end of the zebrad cached state
|
||||||
|
// "error requesting block: 0: Block not found",
|
||||||
|
// "error zcashd getblock rpc",
|
||||||
|
"received overlong message",
|
||||||
|
"received unexpected height block",
|
||||||
|
"Reorg exceeded max",
|
||||||
|
"unable to issue RPC call",
|
||||||
|
// Missing fields for each specific RPC
|
||||||
|
//
|
||||||
|
// get_block_chain_info
|
||||||
|
//
|
||||||
|
// invalid sapling height
|
||||||
|
"Got sapling height 0",
|
||||||
|
// missing BIP70 chain name, should be "main" or "test"
|
||||||
|
" chain ",
|
||||||
|
// missing branchID, should be 8 hex digits
|
||||||
|
" branchID \"",
|
||||||
|
// get_block
|
||||||
|
//
|
||||||
|
// a block error other than "-8: Block not found"
|
||||||
|
"error requesting block",
|
||||||
|
// a missing block with an incorrect error code
|
||||||
|
"Block not found",
|
||||||
|
//
|
||||||
|
// TODO: complete this list for each RPC with fields, if that RPC generates logs
|
||||||
|
// get_info - doesn't generate logs
|
||||||
|
// get_raw_transaction - might not generate logs
|
||||||
|
// z_get_tree_state
|
||||||
|
// get_address_txids
|
||||||
|
// get_address_balance
|
||||||
|
// get_address_utxos
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Ignored failure logs for lightwalletd.
|
||||||
|
/// These regexes override the [`LIGHTWALLETD_FAILURE_MESSAGES`].
|
||||||
|
///
|
||||||
|
/// These `lightwalletd` messages look like failure messages, but they are actually ok.
|
||||||
|
/// So when we see them in the logs, we make the test continue.
|
||||||
|
pub const LIGHTWALLETD_EMPTY_ZEBRA_STATE_IGNORE_MESSAGES: &[&str] = &[
|
||||||
|
// Exceptions to lightwalletd custom RPC error messages:
|
||||||
|
//
|
||||||
|
// This log matches the "error with" RPC error message,
|
||||||
|
// but we expect Zebra to start with an empty state.
|
||||||
|
r#"No Chain tip available yet","level":"warning","msg":"error with getblockchaininfo rpc, retrying"#,
|
||||||
|
];
|
||||||
|
|
@ -23,9 +23,9 @@ use zebra_test::{
|
||||||
};
|
};
|
||||||
use zebrad::config::ZebradConfig;
|
use zebrad::config::ZebradConfig;
|
||||||
|
|
||||||
use crate::{
|
use crate::common::{
|
||||||
common::lightwalletd::random_known_rpc_port_config, PROCESS_FAILURE_MESSAGES,
|
failure_messages::{PROCESS_FAILURE_MESSAGES, ZEBRA_FAILURE_MESSAGES},
|
||||||
ZEBRA_FAILURE_MESSAGES,
|
lightwalletd::random_known_rpc_port_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// After we launch `zebrad`, wait this long for the command to start up,
|
/// After we launch `zebrad`, wait this long for the command to start up,
|
||||||
|
|
@ -42,10 +42,20 @@ pub const LAUNCH_DELAY: Duration = Duration::from_secs(15);
|
||||||
/// it is using for its RPCs.
|
/// it is using for its RPCs.
|
||||||
pub const LIGHTWALLETD_DELAY: Duration = Duration::from_secs(60);
|
pub const LIGHTWALLETD_DELAY: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
/// The amount of time we wait between launching two
|
/// The amount of time we wait between launching two conflicting nodes.
|
||||||
/// conflicting nodes.
|
|
||||||
pub const BETWEEN_NODES_DELAY: Duration = Duration::from_secs(2);
|
pub const BETWEEN_NODES_DELAY: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
|
/// The amount of time we wait for lightwalletd to update to the tip.
|
||||||
|
///
|
||||||
|
/// The cached tip can be a few days old, and Zebra needs time to activate its mempool.
|
||||||
|
pub const LIGHTWALLETD_UPDATE_TIP_DELAY: Duration = Duration::from_secs(10 * 60);
|
||||||
|
|
||||||
|
/// The amount of time we wait for lightwalletd to do a full sync to the tip.
|
||||||
|
///
|
||||||
|
/// `lightwalletd` takes about half an hour to fully sync,
|
||||||
|
/// and Zebra needs time to activate its mempool.
|
||||||
|
pub const LIGHTWALLETD_FULL_SYNC_TIP_DELAY: Duration = Duration::from_secs(60 * 60);
|
||||||
|
|
||||||
/// Extension trait for methods on `tempfile::TempDir` for using it as a test
|
/// Extension trait for methods on `tempfile::TempDir` for using it as a test
|
||||||
/// directory for `zebrad`.
|
/// directory for `zebrad`.
|
||||||
pub trait ZebradTestDirExt
|
pub trait ZebradTestDirExt
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,34 @@
|
||||||
//! Test functions in this file will not be run.
|
//! Test functions in this file will not be run.
|
||||||
//! This file is only for test library code.
|
//! This file is only for test library code.
|
||||||
|
|
||||||
use std::{env, net::SocketAddr, path::Path, time::Duration};
|
use std::{
|
||||||
|
env,
|
||||||
|
net::SocketAddr,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use zebra_test::{
|
use zebra_test::{
|
||||||
command::{Arguments, TestChild, TestDirExt},
|
command::{Arguments, TestChild, TestDirExt, NO_MATCHES_REGEX_ITER},
|
||||||
net::random_known_port,
|
net::random_known_port,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use zebrad::config::ZebradConfig;
|
use zebrad::config::ZebradConfig;
|
||||||
|
|
||||||
use super::{config::default_test_config, launch::ZebradTestDirExt};
|
use super::{
|
||||||
|
cached_state::ZEBRA_CACHED_STATE_DIR_VAR,
|
||||||
|
config::default_test_config,
|
||||||
|
failure_messages::{
|
||||||
|
LIGHTWALLETD_EMPTY_ZEBRA_STATE_IGNORE_MESSAGES, LIGHTWALLETD_FAILURE_MESSAGES,
|
||||||
|
PROCESS_FAILURE_MESSAGES, ZEBRA_FAILURE_MESSAGES,
|
||||||
|
},
|
||||||
|
launch::{
|
||||||
|
ZebradTestDirExt, LIGHTWALLETD_DELAY, LIGHTWALLETD_FULL_SYNC_TIP_DELAY,
|
||||||
|
LIGHTWALLETD_UPDATE_TIP_DELAY,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use LightwalletdTestType::*;
|
||||||
|
|
||||||
pub mod send_transaction_test;
|
pub mod send_transaction_test;
|
||||||
pub mod wallet_grpc;
|
pub mod wallet_grpc;
|
||||||
|
|
@ -28,7 +46,16 @@ pub mod wallet_grpc;
|
||||||
///
|
///
|
||||||
/// This environmental variable is used to enable the lightwalletd tests.
|
/// This environmental variable is used to enable the lightwalletd tests.
|
||||||
/// But the network tests are *disabled* by their environmental variables.
|
/// But the network tests are *disabled* by their environmental variables.
|
||||||
const ZEBRA_TEST_LIGHTWALLETD: &str = "ZEBRA_TEST_LIGHTWALLETD";
|
pub const ZEBRA_TEST_LIGHTWALLETD: &str = "ZEBRA_TEST_LIGHTWALLETD";
|
||||||
|
|
||||||
|
/// Optional environment variable with the cached state for lightwalletd.
|
||||||
|
///
|
||||||
|
/// Required for [`LightwalletdTestType::UpdateCachedState`],
|
||||||
|
/// so we can test lightwalletd RPC integration with a populated state.
|
||||||
|
///
|
||||||
|
/// Can also be used to speed up the [`sending_transactions_using_lightwalletd`] test,
|
||||||
|
/// by skipping the lightwalletd initial sync.
|
||||||
|
pub const LIGHTWALLETD_DATA_DIR_VAR: &str = "LIGHTWALLETD_DATA_DIR";
|
||||||
|
|
||||||
/// The maximum time that a `lightwalletd` integration test is expected to run.
|
/// The maximum time that a `lightwalletd` integration test is expected to run.
|
||||||
pub const LIGHTWALLETD_TEST_TIMEOUT: Duration = Duration::from_secs(60 * 60);
|
pub const LIGHTWALLETD_TEST_TIMEOUT: Duration = Duration::from_secs(60 * 60);
|
||||||
|
|
@ -75,16 +102,20 @@ pub trait LightWalletdTestDirExt: ZebradTestDirExt
|
||||||
where
|
where
|
||||||
Self: AsRef<Path> + Sized,
|
Self: AsRef<Path> + Sized,
|
||||||
{
|
{
|
||||||
/// Spawn `lightwalletd` with `args` as a child process in this test directory,
|
/// Spawn `lightwalletd` with `lightwalletd_state_path`, and `extra_args`,
|
||||||
/// potentially taking ownership of the tempdir for the duration of the
|
/// as a child process in this test directory,
|
||||||
/// child process.
|
/// potentially taking ownership of the tempdir for the duration of the child process.
|
||||||
///
|
///
|
||||||
/// By default, launch a working test instance with logging, and avoid port conflicts.
|
/// By default, launch a working test instance with logging, and avoid port conflicts.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If there is no lightwalletd config in the test directory.
|
/// If there is no lightwalletd config in the test directory.
|
||||||
fn spawn_lightwalletd_child(self, extra_args: Arguments) -> Result<TestChild<Self>>;
|
fn spawn_lightwalletd_child(
|
||||||
|
self,
|
||||||
|
lightwalletd_state_path: impl Into<Option<PathBuf>>,
|
||||||
|
extra_args: Arguments,
|
||||||
|
) -> Result<TestChild<Self>>;
|
||||||
|
|
||||||
/// Create a config file and use it for all subsequently spawned `lightwalletd` processes.
|
/// Create a config file and use it for all subsequently spawned `lightwalletd` processes.
|
||||||
/// Returns an error if the config already exists.
|
/// Returns an error if the config already exists.
|
||||||
|
|
@ -98,9 +129,13 @@ impl<T> LightWalletdTestDirExt for T
|
||||||
where
|
where
|
||||||
Self: TestDirExt + AsRef<Path> + Sized,
|
Self: TestDirExt + AsRef<Path> + Sized,
|
||||||
{
|
{
|
||||||
fn spawn_lightwalletd_child(self, extra_args: Arguments) -> Result<TestChild<Self>> {
|
fn spawn_lightwalletd_child(
|
||||||
let dir = self.as_ref().to_owned();
|
self,
|
||||||
let default_config_path = dir.join("lightwalletd-zcash.conf");
|
lightwalletd_state_path: impl Into<Option<PathBuf>>,
|
||||||
|
extra_args: Arguments,
|
||||||
|
) -> Result<TestChild<Self>> {
|
||||||
|
let test_dir = self.as_ref().to_owned();
|
||||||
|
let default_config_path = test_dir.join("lightwalletd-zcash.conf");
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
default_config_path.exists(),
|
default_config_path.exists(),
|
||||||
|
|
@ -119,9 +154,24 @@ where
|
||||||
args.set_parameter("--zcash-conf-path", zcash_conf_path);
|
args.set_parameter("--zcash-conf-path", zcash_conf_path);
|
||||||
|
|
||||||
// the lightwalletd cache directory
|
// the lightwalletd cache directory
|
||||||
//
|
if let Some(lightwalletd_state_path) = lightwalletd_state_path.into() {
|
||||||
// TODO: create a sub-directory for lightwalletd
|
args.set_parameter(
|
||||||
args.set_parameter("--data-dir", dir.to_str().expect("Path is valid Unicode"));
|
"--data-dir",
|
||||||
|
lightwalletd_state_path
|
||||||
|
.to_str()
|
||||||
|
.expect("path is valid Unicode"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let empty_state_path = test_dir.join("lightwalletd_state");
|
||||||
|
|
||||||
|
std::fs::create_dir(&empty_state_path)
|
||||||
|
.expect("unexpected failure creating lightwalletd state sub-directory");
|
||||||
|
|
||||||
|
args.set_parameter(
|
||||||
|
"--data-dir",
|
||||||
|
empty_state_path.to_str().expect("path is valid Unicode"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// log to standard output
|
// log to standard output
|
||||||
//
|
//
|
||||||
|
|
@ -163,3 +213,177 @@ where
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of lightwalletd integration test that we're running.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum LightwalletdTestType {
|
||||||
|
/// Launch with an empty Zebra and lightwalletd state.
|
||||||
|
LaunchWithEmptyState,
|
||||||
|
|
||||||
|
/// Do a full sync from an empty lightwalletd state.
|
||||||
|
///
|
||||||
|
/// This test requires a cached Zebra state.
|
||||||
|
FullSyncFromGenesis {
|
||||||
|
/// Should the test allow a cached lightwalletd state?
|
||||||
|
///
|
||||||
|
/// If `false`, the test fails if the lightwalletd state is populated.
|
||||||
|
allow_lightwalletd_cached_state: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Sync to tip from a lightwalletd cached state.
|
||||||
|
///
|
||||||
|
/// This test requires a cached Zebra and lightwalletd state.
|
||||||
|
UpdateCachedState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightwalletdTestType {
|
||||||
|
/// Does this test need a Zebra cached state?
|
||||||
|
pub fn needs_zebra_cached_state(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
LaunchWithEmptyState => false,
|
||||||
|
FullSyncFromGenesis { .. } | UpdateCachedState => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does this test need a lightwalletd cached state?
|
||||||
|
pub fn needs_lightwalletd_cached_state(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
LaunchWithEmptyState | FullSyncFromGenesis { .. } => false,
|
||||||
|
UpdateCachedState => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does this test allow a lightwalletd cached state, even if it is not required?
|
||||||
|
pub fn allow_lightwalletd_cached_state(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
LaunchWithEmptyState => false,
|
||||||
|
FullSyncFromGenesis {
|
||||||
|
allow_lightwalletd_cached_state,
|
||||||
|
} => *allow_lightwalletd_cached_state,
|
||||||
|
UpdateCachedState => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the Zebra state path for this test, if set.
|
||||||
|
pub fn zebrad_state_path(&self) -> Option<PathBuf> {
|
||||||
|
match env::var_os(ZEBRA_CACHED_STATE_DIR_VAR) {
|
||||||
|
Some(path) => Some(path.into()),
|
||||||
|
None => {
|
||||||
|
tracing::info!(
|
||||||
|
"skipped {self:?} lightwalletd test, \
|
||||||
|
set the {ZEBRA_CACHED_STATE_DIR_VAR:?} environment variable to run the test",
|
||||||
|
);
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a Zebra config for this test.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the test should be skipped,
|
||||||
|
/// and `Some(Err(_))` if the config could not be created.
|
||||||
|
pub fn zebrad_config(&self) -> Option<Result<ZebradConfig>> {
|
||||||
|
if !self.needs_zebra_cached_state() {
|
||||||
|
return Some(random_known_rpc_port_config());
|
||||||
|
}
|
||||||
|
|
||||||
|
let zebra_state_path = self.zebrad_state_path()?;
|
||||||
|
|
||||||
|
let mut config = match random_known_rpc_port_config() {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(error) => return Some(Err(error)),
|
||||||
|
};
|
||||||
|
|
||||||
|
config.sync.lookahead_limit = zebrad::components::sync::DEFAULT_LOOKAHEAD_LIMIT;
|
||||||
|
|
||||||
|
config.state.ephemeral = false;
|
||||||
|
config.state.cache_dir = zebra_state_path;
|
||||||
|
|
||||||
|
Some(Ok(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the lightwalletd state path for this test, if set.
|
||||||
|
pub fn lightwalletd_state_path(&self) -> Option<PathBuf> {
|
||||||
|
env::var_os(LIGHTWALLETD_DATA_DIR_VAR).map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `zebrad` timeout for this test type.
|
||||||
|
pub fn zebrad_timeout(&self) -> Duration {
|
||||||
|
match self {
|
||||||
|
LaunchWithEmptyState => LIGHTWALLETD_DELAY,
|
||||||
|
FullSyncFromGenesis { .. } | UpdateCachedState => LIGHTWALLETD_UPDATE_TIP_DELAY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `lightwalletd` timeout for this test type.
|
||||||
|
pub fn lightwalletd_timeout(&self) -> Duration {
|
||||||
|
match self {
|
||||||
|
LaunchWithEmptyState => LIGHTWALLETD_DELAY,
|
||||||
|
UpdateCachedState => LIGHTWALLETD_UPDATE_TIP_DELAY,
|
||||||
|
FullSyncFromGenesis { .. } => LIGHTWALLETD_FULL_SYNC_TIP_DELAY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns Zebra log regexes that indicate the tests have failed,
|
||||||
|
/// and regexes of any failures that should be ignored.
|
||||||
|
pub fn zebrad_failure_messages(&self) -> (Vec<String>, Vec<String>) {
|
||||||
|
let mut zebrad_failure_messages: Vec<String> = ZEBRA_FAILURE_MESSAGES
|
||||||
|
.iter()
|
||||||
|
.chain(PROCESS_FAILURE_MESSAGES)
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if self.needs_zebra_cached_state() {
|
||||||
|
// Fail if we need a cached Zebra state, but it's empty
|
||||||
|
zebrad_failure_messages.push("loaded Zebra state cache tip=None".to_string());
|
||||||
|
}
|
||||||
|
if *self == LaunchWithEmptyState {
|
||||||
|
// Fail if we need an empty Zebra state, but it has blocks
|
||||||
|
zebrad_failure_messages
|
||||||
|
.push(r"loaded Zebra state cache tip=.*Height\([1-9][0-9]*\)".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let zebrad_ignore_messages = Vec::new();
|
||||||
|
|
||||||
|
(zebrad_failure_messages, zebrad_ignore_messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `lightwalletd` log regexes that indicate the tests have failed,
|
||||||
|
/// and regexes of any failures that should be ignored.
|
||||||
|
pub fn lightwalletd_failure_messages(&self) -> (Vec<String>, Vec<String>) {
|
||||||
|
let mut lightwalletd_failure_messages: Vec<String> = LIGHTWALLETD_FAILURE_MESSAGES
|
||||||
|
.iter()
|
||||||
|
.chain(PROCESS_FAILURE_MESSAGES)
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Zebra state failures
|
||||||
|
if self.needs_zebra_cached_state() {
|
||||||
|
// Fail if we need a cached Zebra state, but it's empty
|
||||||
|
lightwalletd_failure_messages.push("No Chain tip available yet".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// lightwalletd state failures
|
||||||
|
if self.needs_lightwalletd_cached_state() {
|
||||||
|
// Fail if we need a cached lightwalletd state, but it isn't near the tip
|
||||||
|
//
|
||||||
|
// TODO: fail on `[0-9]{1,6}` when we're using the tip cached state (#4155)
|
||||||
|
lightwalletd_failure_messages.push("Found [0-9]{1,5} blocks in cache".to_string());
|
||||||
|
}
|
||||||
|
if !self.allow_lightwalletd_cached_state() {
|
||||||
|
// Fail if we need an empty lightwalletd state, but it has blocks
|
||||||
|
lightwalletd_failure_messages.push("Found [1-9][0-9]* blocks in cache".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let lightwalletd_ignore_messages = if *self == LaunchWithEmptyState {
|
||||||
|
LIGHTWALLETD_EMPTY_ZEBRA_STATE_IGNORE_MESSAGES.iter()
|
||||||
|
} else {
|
||||||
|
NO_MATCHES_REGEX_ITER.iter()
|
||||||
|
}
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
(lightwalletd_failure_messages, lightwalletd_ignore_messages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,12 @@ use tempfile::TempDir;
|
||||||
|
|
||||||
use zebra_test::{args, net::random_known_port, prelude::*};
|
use zebra_test::{args, net::random_known_port, prelude::*};
|
||||||
|
|
||||||
use crate::{
|
use crate::common::{config::testdir, lightwalletd::LightWalletdTestDirExt};
|
||||||
common::{
|
|
||||||
config::testdir,
|
use super::LightwalletdTestType;
|
||||||
lightwalletd::{LightWalletdTestDirExt, LIGHTWALLETD_TEST_TIMEOUT},
|
|
||||||
},
|
|
||||||
LIGHTWALLETD_FAILURE_MESSAGES, LIGHTWALLETD_IGNORE_MESSAGES, PROCESS_FAILURE_MESSAGES,
|
|
||||||
};
|
|
||||||
|
|
||||||
tonic::include_proto!("cash.z.wallet.sdk.rpc");
|
tonic::include_proto!("cash.z.wallet.sdk.rpc");
|
||||||
|
|
||||||
/// Optional environment variable with the cached state for lightwalletd.
|
|
||||||
///
|
|
||||||
/// Can be used to speed up the [`sending_transactions_using_lightwalletd`] test, by allowing the
|
|
||||||
/// test to reuse the cached lightwalletd synchronization data.
|
|
||||||
const LIGHTWALLETD_DATA_DIR_VAR: &str = "LIGHTWALLETD_DATA_DIR";
|
|
||||||
|
|
||||||
/// Type alias for the RPC client to communicate with a lightwalletd instance.
|
/// Type alias for the RPC client to communicate with a lightwalletd instance.
|
||||||
pub type LightwalletdRpcClient =
|
pub type LightwalletdRpcClient =
|
||||||
compact_tx_streamer_client::CompactTxStreamerClient<tonic::transport::Channel>;
|
compact_tx_streamer_client::CompactTxStreamerClient<tonic::transport::Channel>;
|
||||||
|
|
@ -32,29 +22,23 @@ pub type LightwalletdRpcClient =
|
||||||
pub fn spawn_lightwalletd_with_rpc_server(
|
pub fn spawn_lightwalletd_with_rpc_server(
|
||||||
zebrad_rpc_address: SocketAddr,
|
zebrad_rpc_address: SocketAddr,
|
||||||
) -> Result<(TestChild<TempDir>, u16)> {
|
) -> Result<(TestChild<TempDir>, u16)> {
|
||||||
|
// We're using cached Zebra state here, so this test type is the most similar
|
||||||
|
let test_type = LightwalletdTestType::UpdateCachedState;
|
||||||
|
|
||||||
let lightwalletd_dir = testdir()?.with_lightwalletd_config(zebrad_rpc_address)?;
|
let lightwalletd_dir = testdir()?.with_lightwalletd_config(zebrad_rpc_address)?;
|
||||||
|
|
||||||
let lightwalletd_rpc_port = random_known_port();
|
let lightwalletd_rpc_port = random_known_port();
|
||||||
let lightwalletd_rpc_address = format!("127.0.0.1:{lightwalletd_rpc_port}");
|
let lightwalletd_rpc_address = format!("127.0.0.1:{lightwalletd_rpc_port}");
|
||||||
|
|
||||||
let mut arguments = args!["--grpc-bind-addr": lightwalletd_rpc_address];
|
let arguments = args!["--grpc-bind-addr": lightwalletd_rpc_address];
|
||||||
|
|
||||||
if let Ok(data_dir) = env::var(LIGHTWALLETD_DATA_DIR_VAR) {
|
let (lightwalletd_failure_messages, lightwalletd_ignore_messages) =
|
||||||
arguments.set_parameter("--data-dir", data_dir);
|
test_type.lightwalletd_failure_messages();
|
||||||
}
|
|
||||||
|
|
||||||
let mut lightwalletd = lightwalletd_dir
|
let mut lightwalletd = lightwalletd_dir
|
||||||
.spawn_lightwalletd_child(arguments)?
|
.spawn_lightwalletd_child(test_type.lightwalletd_state_path(), arguments)?
|
||||||
.with_timeout(LIGHTWALLETD_TEST_TIMEOUT)
|
.with_timeout(test_type.lightwalletd_timeout())
|
||||||
.with_failure_regex_iter(
|
.with_failure_regex_iter(lightwalletd_failure_messages, lightwalletd_ignore_messages);
|
||||||
// TODO: replace with a function that returns the full list and correct return type
|
|
||||||
LIGHTWALLETD_FAILURE_MESSAGES
|
|
||||||
.iter()
|
|
||||||
.chain(PROCESS_FAILURE_MESSAGES)
|
|
||||||
.cloned(),
|
|
||||||
// TODO: some exceptions do not apply to the cached state tests (#3511)
|
|
||||||
LIGHTWALLETD_IGNORE_MESSAGES.iter().cloned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
lightwalletd.expect_stdout_line_matches("Starting gRPC server")?;
|
lightwalletd.expect_stdout_line_matches("Starting gRPC server")?;
|
||||||
lightwalletd.expect_stdout_line_matches("Waiting for block")?;
|
lightwalletd.expect_stdout_line_matches("Waiting for block")?;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
pub mod cached_state;
|
pub mod cached_state;
|
||||||
pub mod check;
|
pub mod check;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod failure_messages;
|
||||||
pub mod launch;
|
pub mod launch;
|
||||||
pub mod lightwalletd;
|
pub mod lightwalletd;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue