Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ All notable changes to Zebra are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org).

## [Unreleased]

### Added

- Zebra can now serve Zcash light clients directly: a new gRPC server implements
the lightwalletd `CompactTxStreamer` interface, enabled by setting
`rpc.lightwalletd_listen_addr` in the config
([#10953](https://github.com/ZcashFoundation/zebra/pull/10953)).

## [Zebra 6.0.0](https://github.com/ZcashFoundation/zebra/releases/tag/v6.0.0) - 2026-07-10

### Added
Expand Down
9 changes: 9 additions & 0 deletions zebra-rpc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- A new `lightwalletd` module with a tonic gRPC server that implements the
lightwalletd `CompactTxStreamer` interface (`lightwalletd::server::init`),
and a new `rpc.lightwalletd_listen_addr` config field to enable it
([#10953](https://github.com/ZcashFoundation/zebra/pull/10953)).

## [11.1.0] - 2026-07-10

### Changed
Expand Down
53 changes: 44 additions & 9 deletions zebra-rpc/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,62 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}

fn build_or_copy_proto() -> Result<(), Box<dyn std::error::Error>> {
const PROTO_FILE_PATH: &str = "proto/indexer.proto";
// Zebra's indexer API.
build_or_copy_proto_set(
&["proto/indexer.proto"],
&[""],
"indexer_descriptor.bin",
&["indexer_descriptor.bin", "zebra.indexer.rpc.rs"],
true,
)?;

// The lightwalletd `CompactTxStreamer` client API.
//
// `service.proto` imports `compact_formats.proto`, so both are compiled with
// the `proto` directory as the include path.
build_or_copy_proto_set(
&["proto/service.proto", "proto/compact_formats.proto"],
&["proto"],
"lightwalletd_descriptor.bin",
&["lightwalletd_descriptor.bin", "cash.z.wallet.sdk.rpc.rs"],
false,
)?;

Ok(())
}

/// Compiles the given proto files if `protoc` and the proto files are available,
/// copying the generated files into `proto/__generated__/` so they can be checked in.
/// Otherwise, copies the checked-in generated files into `OUT_DIR`.
fn build_or_copy_proto_set(
proto_files: &[&str],
include_dirs: &[&str],
descriptor_name: &str,
file_names: &[&str],
serde_derives: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let out_dir = env::var("OUT_DIR").map(PathBuf::from)?;
let file_names = ["indexer_descriptor.bin", "zebra.indexer.rpc.rs"];

let is_proto_file_available = Path::new(PROTO_FILE_PATH).exists();
let are_proto_files_available = proto_files.iter().all(|path| Path::new(path).exists());
let is_protoc_available = env::var_os("PROTOC")
.map(PathBuf::from)
.or_else(|| which::which("protoc").ok())
.is_some();

if is_proto_file_available && is_protoc_available {
tonic_prost_build::configure()
.type_attribute(".", "#[derive(serde::Deserialize, serde::Serialize)]")
.file_descriptor_set_path(out_dir.join("indexer_descriptor.bin"))
.compile_protos(&[PROTO_FILE_PATH], &[""])?;
if are_proto_files_available && is_protoc_available {
let mut config =
tonic_prost_build::configure().file_descriptor_set_path(out_dir.join(descriptor_name));

if serde_derives {
config = config.type_attribute(".", "#[derive(serde::Deserialize, serde::Serialize)]");
}

config.compile_protos(proto_files, include_dirs)?;

for file_name in file_names {
let out_path = out_dir.join(file_name);
let generated_path = format!("proto/__generated__/{file_name}");
if fs::read_to_string(&out_path).ok() != fs::read_to_string(&generated_path).ok() {
if fs::read(&out_path).ok() != fs::read(&generated_path).ok() {
fs::copy(out_path, generated_path)?;
}
}
Expand Down
Loading
Loading