Test `z_getsubtreesbyindex` using a lightwalletd gRPC request (#7521)

* Add lightwalletd's protobuf types

* Don't explicitly derive `Eq` for enums

I got bitten by this bug: https://github.com/tokio-rs/prost/issues/332
when I added the enum `ShieldedProtocol` to the file `service.proto`.
The problem is that `prost` implicitly derives `Eq` for enums, so
deriving it explicitly via `type_attribute` causes a conflict. Lukily,
there is another method `message_attribute` that operates only on
messages and not enums.

* Test the `z_getsubtreesbyindex` RPC

* Fix a typo

* Add test vectors
This commit is contained in:
Marek 2023-09-13 01:59:56 +02:00 committed by GitHub
parent 70d34a6d4b
commit dc6aa708d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 2 deletions

View File

@ -1795,6 +1795,12 @@ dependencies = [
"serde",
]
[[package]]
name = "hex-literal"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "hmac"
version = "0.12.1"
@ -5814,6 +5820,7 @@ dependencies = [
"dirs",
"futures",
"hex",
"hex-literal",
"howudoin",
"humantime-serde",
"hyper",

View File

@ -231,6 +231,7 @@ tonic-build = { version = "0.10.0", optional = true }
[dev-dependencies]
abscissa_core = { version = "0.7.0", features = ["testing"] }
hex = "0.4.3"
hex-literal = "0.4.1"
jsonrpc-core = "18.0.0"
once_cell = "1.18.0"
regex = "1.9.5"

View File

@ -46,7 +46,7 @@ fn main() {
// The lightwalletd gRPC types don't use floats or complex collections,
// so we can derive `Eq` as well as the default generated `PartialEq` derive.
// This fixes `clippy::derive_partial_eq_without_eq` warnings.
.type_attribute(".", "#[derive(Eq)]")
.message_attribute(".", "#[derive(Eq)]")
.compile(
&["tests/common/lightwalletd/proto/service.proto"],
&["tests/common/lightwalletd/proto"],

View File

@ -117,6 +117,27 @@ message TreeState {
string tree = 5; // sapling commitment tree state
}
enum ShieldedProtocol {
sapling = 0;
orchard = 1;
}
// Request type for `GetSubtreeRoots`
message GetSubtreeRootsArg {
uint32 startIndex = 1; // Index identifying where to start returning subtree roots.
ShieldedProtocol shieldedProtocol = 2; // Shielded protocol to return subtree roots for.
uint32 maxEntries = 3; // Maximum number of entries to return, or 0 for all entries.
}
// Response type for `GetSubtreeRoots`.
// The actual response contains a stream of `SubtreeRoot`s.
message SubtreeRoot {
bytes rootHash = 2; // The 32-byte Merkle root of the subtree.
bytes completingBlockHash = 3; // The hash of the block that completed this subtree.
uint64 completingBlockHeight = 4; // The height of the block that completed this subtree in the main chain.
}
// Results are sorted by height, which makes it easy to issue another
// request that picks up from where the previous left off.
message GetAddressUtxosArg {
@ -175,6 +196,10 @@ service CompactTxStreamer {
// The block can be specified by either height or hash.
rpc GetTreeState(BlockID) returns (TreeState) {}
// Returns a stream of information about roots of subtrees of the Sapling
// and Orchard note commitment trees.
rpc GetSubtreeRoots(GetSubtreeRootsArg) returns (stream SubtreeRoot) {}
rpc GetAddressUtxos(GetAddressUtxosArg) returns (GetAddressUtxosReplyList) {}
rpc GetAddressUtxosStream(GetAddressUtxosArg) returns (stream GetAddressUtxosReply) {}

View File

@ -35,6 +35,7 @@
//! purposes.
use color_eyre::eyre::Result;
use hex_literal::hex;
use zebra_chain::{
block::Block,
@ -50,7 +51,8 @@ use crate::common::{
sync::wait_for_zebrad_and_lightwalletd_sync,
wallet_grpc::{
connect_to_lightwalletd, Address, AddressList, BlockId, BlockRange, ChainSpec, Empty,
GetAddressUtxosArg, TransparentAddressBlockFilter, TxFilter,
GetAddressUtxosArg, GetSubtreeRootsArg, ShieldedProtocol,
TransparentAddressBlockFilter, TxFilter,
},
},
test_type::TestType::UpdateCachedState,
@ -337,6 +339,78 @@ pub async fn run() -> Result<()> {
*zebra_test::vectors::SAPLING_TREESTATE_MAINNET_419201_STRING
);
// Call `z_getsubtreesbyindex` separately for
// ... Sapling.
let mut subtrees = rpc_client
.get_subtree_roots(GetSubtreeRootsArg {
start_index: 0u32,
shielded_protocol: ShieldedProtocol::Sapling.into(),
max_entries: 2u32,
})
.await?
.into_inner();
let mut counter = 0;
while let Some(subtree) = subtrees.message().await? {
match counter {
0 => {
assert_eq!(
subtree.root_hash,
hex!("754bb593ea42d231a7ddf367640f09bbf59dc00f2c1d2003cc340e0c016b5b13")
);
assert_eq!(subtree.completing_block_height, 558822u64);
}
1 => {
assert_eq!(
subtree.root_hash,
hex!("03654c3eacbb9b93e122cf6d77b606eae29610f4f38a477985368197fd68e02d")
);
assert_eq!(subtree.completing_block_height, 670209u64);
}
_ => {
panic!("The response from the `z_getsubtreesbyindex` RPC contains a wrong number of Sapling subtrees.")
}
}
counter += 1;
}
assert_eq!(counter, 2);
// ... Orchard.
let mut subtrees = rpc_client
.get_subtree_roots(GetSubtreeRootsArg {
start_index: 0u32,
shielded_protocol: ShieldedProtocol::Orchard.into(),
max_entries: 2u32,
})
.await?
.into_inner();
let mut counter = 0;
while let Some(subtree) = subtrees.message().await? {
match counter {
0 => {
assert_eq!(
subtree.root_hash,
hex!("d4e323b3ae0cabfb6be4087fec8c66d9a9bbfc354bf1d9588b6620448182063b")
);
assert_eq!(subtree.completing_block_height, 1707429u64);
}
1 => {
assert_eq!(
subtree.root_hash,
hex!("8c47d0ca43f323ac573ee57c90af4ced484682827248ca5f3eead95eb6415a14")
);
assert_eq!(subtree.completing_block_height, 1708132u64);
}
_ => {
panic!("The response from the `z_getsubtreesbyindex` RPC contains a wrong number of Orchard subtrees.")
}
}
counter += 1;
}
assert_eq!(counter, 2);
// Call `GetAddressUtxos` with the ZF funding stream address that will always have utxos
let utxos = rpc_client
.get_address_utxos(GetAddressUtxosArg {