tests(rpc): Add wallet grpc tests (#4253)
* add a test that call all the lightwalletd grpc methods * fix some docs * use ZF address in tests for balance and utxos Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
2e643c8631
commit
25712f1935
|
@ -1006,7 +1006,7 @@ fn lightwalletd_integration() -> Result<()> {
|
||||||
|
|
||||||
/// Make sure `lightwalletd` can sync from Zebra, in update sync mode.
|
/// 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 set, runs a quick sync, then a full sync.
|
||||||
/// If `LIGHTWALLETD_DATA_DIR` is not set, just runs a full sync.
|
/// If `LIGHTWALLETD_DATA_DIR` is not set, just runs a full sync.
|
||||||
///
|
///
|
||||||
/// This test only runs when the `ZEBRA_TEST_LIGHTWALLETD`,
|
/// This test only runs when the `ZEBRA_TEST_LIGHTWALLETD`,
|
||||||
|
@ -1531,3 +1531,11 @@ async fn fully_synced_rpc_test() -> Result<()> {
|
||||||
async fn sending_transactions_using_lightwalletd() -> Result<()> {
|
async fn sending_transactions_using_lightwalletd() -> Result<()> {
|
||||||
common::lightwalletd::send_transaction_test::run().await
|
common::lightwalletd::send_transaction_test::run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test all the rpc methods a wallet connected to lightwalletd can call.
|
||||||
|
///
|
||||||
|
/// See [`common::lightwalletd::wallet_grpc_test`] for more information.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn lightwalletd_wallet_grpc_tests() -> Result<()> {
|
||||||
|
common::lightwalletd::wallet_grpc_test::run().await
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ use LightwalletdTestType::*;
|
||||||
|
|
||||||
pub mod send_transaction_test;
|
pub mod send_transaction_test;
|
||||||
pub mod wallet_grpc;
|
pub mod wallet_grpc;
|
||||||
|
pub mod wallet_grpc_test;
|
||||||
|
|
||||||
/// The name of the env var that enables Zebra lightwalletd integration tests.
|
/// The name of the env var that enables Zebra lightwalletd integration tests.
|
||||||
/// These tests need a `lightwalletd` binary in the test machine's path.
|
/// These tests need a `lightwalletd` binary in the test machine's path.
|
||||||
|
|
|
@ -73,7 +73,7 @@ pub async fn run() -> Result<()> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (_lightwalletd, lightwalletd_rpc_port) =
|
let (_lightwalletd, lightwalletd_rpc_port) =
|
||||||
spawn_lightwalletd_with_rpc_server(zebra_rpc_address)?;
|
spawn_lightwalletd_with_rpc_server(zebra_rpc_address, true)?;
|
||||||
|
|
||||||
let mut rpc_client = connect_to_lightwalletd(lightwalletd_rpc_port).await?;
|
let mut rpc_client = connect_to_lightwalletd(lightwalletd_rpc_port).await?;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub type LightwalletdRpcClient =
|
||||||
/// Returns the lightwalletd instance and the port number that it is listening for RPC connections.
|
/// Returns the lightwalletd instance and the port number that it is listening for RPC connections.
|
||||||
pub fn spawn_lightwalletd_with_rpc_server(
|
pub fn spawn_lightwalletd_with_rpc_server(
|
||||||
zebrad_rpc_address: SocketAddr,
|
zebrad_rpc_address: SocketAddr,
|
||||||
|
wait_for_blocks: bool,
|
||||||
) -> Result<(TestChild<TempDir>, u16)> {
|
) -> Result<(TestChild<TempDir>, u16)> {
|
||||||
// We're using cached Zebra state here, so this test type is the most similar
|
// We're using cached Zebra state here, so this test type is the most similar
|
||||||
let test_type = LightwalletdTestType::UpdateCachedState;
|
let test_type = LightwalletdTestType::UpdateCachedState;
|
||||||
|
@ -41,8 +42,9 @@ pub fn spawn_lightwalletd_with_rpc_server(
|
||||||
.with_failure_regex_iter(lightwalletd_failure_messages, lightwalletd_ignore_messages);
|
.with_failure_regex_iter(lightwalletd_failure_messages, lightwalletd_ignore_messages);
|
||||||
|
|
||||||
lightwalletd.expect_stdout_line_matches("Starting gRPC server")?;
|
lightwalletd.expect_stdout_line_matches("Starting gRPC server")?;
|
||||||
|
if wait_for_blocks {
|
||||||
lightwalletd.expect_stdout_line_matches("Waiting for block")?;
|
lightwalletd.expect_stdout_line_matches("Waiting for block")?;
|
||||||
|
}
|
||||||
Ok((lightwalletd, lightwalletd_rpc_port))
|
Ok((lightwalletd, lightwalletd_rpc_port))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
//! Test all grpc calls a wallet connected to a lightwalletd instance backed by zebra can do.
|
||||||
|
//!
|
||||||
|
//! This test requires a cached chain state that is partially synchronized, i.e., it should be a
|
||||||
|
//! few blocks below the network chain tip height. It also requires a lightwalletd data dir in sync
|
||||||
|
//! with the cached chain state.
|
||||||
|
//!
|
||||||
|
//! Current coverage of all available rpc methods according to `CompactTxStreamer`:
|
||||||
|
//!
|
||||||
|
//! - `GetLatestBlock`: Covered.
|
||||||
|
//! - `GetBlock`: Covered.
|
||||||
|
//! - `GetBlockRange`: Covered.
|
||||||
|
//!
|
||||||
|
//! - `GetTransaction`: Covered.
|
||||||
|
//! - `SendTransaction`: Not covered and it will never will, it has its own test.
|
||||||
|
//!
|
||||||
|
//! - `GetTaddressTxids`: Not covered, need #4216 to be fixed first.
|
||||||
|
//! - `GetTaddressBalance`: Covered.
|
||||||
|
//! - `GetTaddressBalanceStream`: Not covered.
|
||||||
|
//!
|
||||||
|
//! - `GetMempoolTx`: Not covered.
|
||||||
|
//! - `GetMempoolStream`: Not covered.
|
||||||
|
//!
|
||||||
|
//! - `GetTreeState`: Not covered, Need #3990
|
||||||
|
//!
|
||||||
|
//! - `GetAddressUtxos` -= Covered.
|
||||||
|
//! - `GetAddressUtxosStream`: Not covered.
|
||||||
|
//!
|
||||||
|
//! - `GetLightdInfo`: Covered.
|
||||||
|
//! - `Ping`: Not covered and it will never will, ping is only used for testing purposes.
|
||||||
|
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
|
use zebra_chain::{
|
||||||
|
block::Block, parameters::Network, parameters::NetworkUpgrade::Canopy,
|
||||||
|
serialization::ZcashDeserializeInto,
|
||||||
|
};
|
||||||
|
|
||||||
|
use zebra_network::constants::USER_AGENT;
|
||||||
|
|
||||||
|
use crate::common::{
|
||||||
|
launch::spawn_zebrad_for_rpc_without_initial_peers,
|
||||||
|
lightwalletd::{
|
||||||
|
wallet_grpc::{
|
||||||
|
connect_to_lightwalletd, spawn_lightwalletd_with_rpc_server, AddressList, BlockId,
|
||||||
|
BlockRange, ChainSpec, Empty, GetAddressUtxosArg, TxFilter,
|
||||||
|
},
|
||||||
|
zebra_skip_lightwalletd_tests,
|
||||||
|
LightwalletdTestType::UpdateCachedState,
|
||||||
|
LIGHTWALLETD_DATA_DIR_VAR, LIGHTWALLETD_TEST_TIMEOUT, ZEBRA_CACHED_STATE_DIR_VAR,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The test entry point.
|
||||||
|
pub async fn run() -> Result<()> {
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
// Skip the test unless the user specifically asked for it
|
||||||
|
if zebra_skip_lightwalletd_tests() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want a zebra state dir and a lightwalletd data dir in place,
|
||||||
|
// so `UpdateCachedState` can be used as our test type
|
||||||
|
let test_type = UpdateCachedState;
|
||||||
|
|
||||||
|
// Require to have a `ZEBRA_CACHED_STATE_DIR` in place
|
||||||
|
let zebrad_state_path = test_type.zebrad_state_path();
|
||||||
|
if zebrad_state_path.is_none() {
|
||||||
|
tracing::info!(
|
||||||
|
"skipped {test_type:?} lightwalletd test, \
|
||||||
|
set the {ZEBRA_CACHED_STATE_DIR_VAR:?} environment variable to run the test",
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require to have a `LIGHTWALLETD_DATA_DIR` in place
|
||||||
|
let lightwalletd_state_path = test_type.lightwalletd_state_path();
|
||||||
|
if 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(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is only for the mainnet
|
||||||
|
let network = Network::Mainnet;
|
||||||
|
|
||||||
|
// Launch zebra using a predefined zebrad state path
|
||||||
|
let (_zebrad, zebra_rpc_address) = spawn_zebrad_for_rpc_without_initial_peers(
|
||||||
|
network,
|
||||||
|
zebrad_state_path.unwrap(),
|
||||||
|
LIGHTWALLETD_TEST_TIMEOUT,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Launch lightwalletd
|
||||||
|
let (_lightwalletd, lightwalletd_rpc_port) =
|
||||||
|
spawn_lightwalletd_with_rpc_server(zebra_rpc_address, false)?;
|
||||||
|
|
||||||
|
// Give lightwalletd a few seconds to open its grpc port before connecting to it
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
||||||
|
|
||||||
|
// Connect to the lightwalletd instance
|
||||||
|
let mut rpc_client = connect_to_lightwalletd(lightwalletd_rpc_port).await?;
|
||||||
|
|
||||||
|
// End of the setup and start the tests
|
||||||
|
|
||||||
|
// Call `GetLatestBlock`
|
||||||
|
let block_tip = rpc_client
|
||||||
|
.get_latest_block(ChainSpec {})
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// As we are using a pretty much synchronized blockchain, we can assume the tip is above the Canopy network upgrade
|
||||||
|
assert!(block_tip.height > Canopy.activation_height(network).unwrap().0 as u64);
|
||||||
|
|
||||||
|
// Call `GetBlock` with block 1 height
|
||||||
|
let block_one = rpc_client
|
||||||
|
.get_block(BlockId {
|
||||||
|
height: 1,
|
||||||
|
hash: vec![],
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// Make sure we got block 1 back
|
||||||
|
assert_eq!(block_one.height, 1);
|
||||||
|
|
||||||
|
// Call `GetBlockRange` with the range starting at block 1 up to block 10
|
||||||
|
let mut block_range = rpc_client
|
||||||
|
.get_block_range(BlockRange {
|
||||||
|
start: Some(BlockId {
|
||||||
|
height: 1,
|
||||||
|
hash: vec![],
|
||||||
|
}),
|
||||||
|
end: Some(BlockId {
|
||||||
|
height: 10,
|
||||||
|
hash: vec![],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// Make sure the returned Stream of blocks is what we expect
|
||||||
|
let mut counter = 0;
|
||||||
|
while let Some(block) = block_range.message().await? {
|
||||||
|
counter += 1;
|
||||||
|
assert_eq!(block.height, counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first transction of the first block in the mainnet
|
||||||
|
let hash = zebra_test::vectors::BLOCK_MAINNET_1_BYTES
|
||||||
|
.zcash_deserialize_into::<Block>()
|
||||||
|
.expect("block should deserialize")
|
||||||
|
.transactions[0]
|
||||||
|
.hash()
|
||||||
|
.0
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
// Call `GetTransaction` with the transaction hash
|
||||||
|
let transaction = rpc_client
|
||||||
|
.get_transaction(TxFilter {
|
||||||
|
block: None,
|
||||||
|
index: 0,
|
||||||
|
hash,
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// Check the height of transactions is 1 as expected
|
||||||
|
assert_eq!(transaction.height, 1);
|
||||||
|
|
||||||
|
// TODO: Add after #4216
|
||||||
|
// Currently, this call fails with:
|
||||||
|
// 0: status: Unknown, message: "-32602: Invalid params: invalid length 1, expected a tuple of size 3.", details: [], metadata: MetadataMap { headers: {"content-type": "application/grpc"} }
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Call `GetTaddressTxids` with a founders reward address that we know exists and have transactions in the first
|
||||||
|
// few blocks of the mainnet
|
||||||
|
let transactions = rpc_client.get_taddress_txids(TransparentAddressBlockFilter {
|
||||||
|
address: "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd".to_string(),
|
||||||
|
range: Some(BlockRange {
|
||||||
|
start: Some(BlockId {
|
||||||
|
height: 1,
|
||||||
|
hash: vec![],
|
||||||
|
}),
|
||||||
|
end: Some(BlockId {
|
||||||
|
height: 10,
|
||||||
|
hash: vec![],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}).await?.into_inner();
|
||||||
|
|
||||||
|
dbg!(transactions);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Call `GetTaddressBalance` with the ZF funding stream address
|
||||||
|
let balance = rpc_client
|
||||||
|
.get_taddress_balance(AddressList {
|
||||||
|
addresses: vec!["t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1".to_string()],
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// With ZFND or Major Grants funding stream address, the balance will always be greater than zero,
|
||||||
|
// because new coins are created in each block
|
||||||
|
assert!(balance.value_zat > 0);
|
||||||
|
|
||||||
|
// TODO: Create call and check for `GetTaddressBalanceStream`
|
||||||
|
|
||||||
|
// TODO: Create call and checks for `GetMempoolTx` and `GetMempoolTxStream`?
|
||||||
|
|
||||||
|
// TODO: Activate after #3990 is merged
|
||||||
|
// Currently, this call fails as the method is not available
|
||||||
|
/*
|
||||||
|
// Call `GetTreeState` for block 1.
|
||||||
|
let tree_state = rpc_client.get_tree_state(BlockId {
|
||||||
|
height: 1,
|
||||||
|
hash: vec![]
|
||||||
|
}).await?.into_inner();
|
||||||
|
|
||||||
|
dbg!(tree_state);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Call `GetAddressUtxos` with the ZF funding stream address that will always have utxos
|
||||||
|
let utxos = rpc_client
|
||||||
|
.get_address_utxos(GetAddressUtxosArg {
|
||||||
|
addresses: vec!["t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1".to_string()],
|
||||||
|
start_height: 1,
|
||||||
|
max_entries: 1,
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// As we requested one entry we should get a response of length 1
|
||||||
|
assert_eq!(utxos.address_utxos.len(), 1);
|
||||||
|
|
||||||
|
// TODO: Create call and check for `GetAddressUtxosStream`
|
||||||
|
|
||||||
|
// Call `GetLightdInfo`
|
||||||
|
let lightd_info = rpc_client.get_lightd_info(Empty {}).await?.into_inner();
|
||||||
|
|
||||||
|
// Make sure the subversion field is zebra the user agent
|
||||||
|
assert_eq!(lightd_info.zcashd_subversion, USER_AGENT);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue