Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(zebra-checkpoints): make zebra-checkpoints work for zebrad backend #5894

Merged
merged 10 commits into from
Jan 23, 2023
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions zebra-consensus/src/checkpoint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ When updating the lists there is no need to start from the genesis block. The pr

It is easier if `zcash-cli` is in your execution path however you can specify the location of it anywhere in the filesystem with option `--cli`.

By default, `zebra-checkpoints` will use a `zebrad` backend. If the running instance is `zcashd`, please add `-b zcashd` to your command.

To update the checkpoint list, run:

```sh
Expand Down
66 changes: 25 additions & 41 deletions zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use zebra_chain::{
orchard,
parameters::{ConsensusBranchId, Network, NetworkUpgrade},
sapling,
serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
serialization::{SerializationError, ZcashDeserialize},
transaction::{self, SerializedTransaction, Transaction, UnminedTx},
transparent::{self, Address},
};
Expand Down Expand Up @@ -139,9 +139,7 @@ pub trait Rpc {
///
/// With verbosity=1, [`lightwalletd` only reads the `tx` field of the
/// result](https://github.com/zcash/lightwalletd/blob/dfac02093d85fb31fb9a8475b884dd6abca966c7/common/common.go#L152),
/// so we return that.
///
/// Additionally, we return block height, hash and size to be compatible with our `zebra-checkpoints` utility.
/// so we only return that for now.
///
/// `lightwalletd` only requests blocks by height, so we don't support
/// getting blocks by hash. (But we parse the height as a JSON string, not an integer).
Expand Down Expand Up @@ -577,29 +575,29 @@ where
data: None,
})?;

let request = zebra_state::ReadRequest::Block(hash_or_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,
})?;

let block = match response {
zebra_state::ReadResponse::Block(Some(block)) => Ok(block),
zebra_state::ReadResponse::Block(None) => Err(Error {
code: MISSING_BLOCK_ERROR_CODE,
message: "Block not found".to_string(),
data: None,
}),
_ => unreachable!("unmatched response to a block request"),
}?;

if verbosity == 0 {
Ok(GetBlock::Raw(block.into()))
let request = zebra_state::ReadRequest::Block(hash_or_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::ReadResponse::Block(Some(block)) => {
Ok(GetBlock::Raw(block.into()))
}
zebra_state::ReadResponse::Block(None) => Err(Error {
code: MISSING_BLOCK_ERROR_CODE,
message: "Block not found".to_string(),
data: None,
}),
_ => unreachable!("unmatched response to a block request"),
}
} else if verbosity == 1 {
let request = zebra_state::ReadRequest::TransactionIdsForBlock(hash_or_height);
let response = state
Expand All @@ -615,14 +613,7 @@ where
match response {
zebra_state::ReadResponse::TransactionIdsForBlock(Some(tx_ids)) => {
let tx_ids = tx_ids.iter().map(|tx_id| tx_id.encode_hex()).collect();
Ok(GetBlock::Object {
hash: block.hash(),
height: block
.coinbase_height()
.expect("all blocks should have a coinbase height"),
size: block.zcash_serialized_size(),
tx: tx_ids,
})
Ok(GetBlock::Object { tx: tx_ids })
}
zebra_state::ReadResponse::TransactionIdsForBlock(None) => Err(Error {
code: MISSING_BLOCK_ERROR_CODE,
Expand Down Expand Up @@ -1191,13 +1182,6 @@ pub enum GetBlock {
Raw(#[serde(with = "hex")] SerializedBlock),
/// The block object.
Object {
/// Requested block hash.
#[serde(with = "hex")]
hash: block::Hash,
/// Requested block height.
height: Height,
/// Requested block size.
size: usize,
/// List of transaction IDs in block order, hex-encoded.
tx: Vec<String>,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"hash": "0007bc227e1c57a4a70e237cad00e7b7ce565155ab49166bc57397a26d339283",
"height": 1,
"size": 1617,
"tx": [
"851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ source: zebra-rpc/src/methods/tests/snapshot.rs
expression: block
---
{
"hash": "025579869bcf52a989337342f5f57a84f3a28b968f7d6a8307902b065a668d23",
"height": 1,
"size": 1618,
"tx": [
"f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75"
]
Expand Down
7 changes: 1 addition & 6 deletions zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,7 @@ async fn rpc_getblock() {
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.collect(),
height: block
.coinbase_height()
.expect("test block should have coinbase height"),
hash: block.hash(),
size: block.zcash_serialized_size(),
.collect()
}
);
}
Expand Down
5 changes: 1 addition & 4 deletions zebra-rpc/src/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@ pub fn test_block_serialization() {

let expected_tx = GetBlock::Object {
tx: vec!["42".into()],
height: zebra_chain::block::Height(1),
hash: zebra_chain::block::Hash([0; 32]),
size: 1,
};
let expected_json = r#"{"hash":"0000000000000000000000000000000000000000000000000000000000000000","height":1,"size":1,"tx":["42"]}"#;
let expected_json = r#"{"tx":["42"]}"#;
let j = serde_json::to_string(&expected_tx).unwrap();

assert_eq!(j, expected_json);
Expand Down
1 change: 1 addition & 0 deletions zebra-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ hex = "0.4.3"
serde_json = "1.0.91"
tracing-error = "0.2.0"
tracing-subscriber = "0.3.16"
thiserror = "1.0.38"

zebra-node-services = { path = "../zebra-node-services" }
zebra-chain = { path = "../zebra-chain" }
Expand Down
30 changes: 30 additions & 0 deletions zebra-utils/src/bin/zebra-checkpoints/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,40 @@
//! For usage please refer to the program help: `zebra-checkpoints --help`

use structopt::StructOpt;
use thiserror::Error;

use std::str::FromStr;

/// The backend type the zebra-checkpoints utility will use to get data from.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Backend {
Zebrad,
Zcashd,
}

impl FromStr for Backend {
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
type Err = InvalidModeError;

fn from_str(string: &str) -> Result<Self, Self::Err> {
match string.to_lowercase().as_str() {
"zebrad" => Ok(Backend::Zebrad),
"zcashd" => Ok(Backend::Zcashd),
_ => Err(InvalidModeError(string.to_owned())),
}
}
}

#[derive(Debug, Error)]
#[error("Invalid mode: {0}")]
pub struct InvalidModeError(String);

/// zebra-checkpoints arguments
#[derive(Clone, Debug, Eq, PartialEq, StructOpt)]
pub struct Args {
/// Backend type
#[structopt(default_value = "zebrad", short, long)]
pub backend: Backend,

/// Path to zcash-cli command
#[structopt(default_value = "zcash-cli", short, long)]
pub cli: String,
Expand Down
50 changes: 40 additions & 10 deletions zebra-utils/src/bin/zebra-checkpoints/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ use std::process::Stdio;
use std::os::unix::process::ExitStatusExt;

use color_eyre::eyre::{ensure, Result};
use hex::FromHex;
use serde_json::Value;
use structopt::StructOpt;

use zebra_chain::{block, transparent::MIN_TRANSPARENT_COINBASE_MATURITY};
use zebra_chain::{
block, serialization::ZcashDeserializeInto, transparent::MIN_TRANSPARENT_COINBASE_MATURITY,
};
use zebra_node_services::constants::{MAX_CHECKPOINT_BYTE_COUNT, MAX_CHECKPOINT_HEIGHT_GAP};
use zebra_utils::init_tracing;

Expand Down Expand Up @@ -109,19 +112,46 @@ fn main() -> Result<()> {
// unfortunately we need to create a process for each block
let mut cmd = passthrough_cmd();

// get block data
cmd.args(["getblock", &x.to_string(), "1"]);
let output = cmd_output(&mut cmd)?;
let (hash, height, size) = match args::Args::from_args().backend {
args::Backend::Zcashd => {
// get block data from zcashd using verbose=1
cmd.args(["getblock", &x.to_string(), "1"]);
let output = cmd_output(&mut cmd)?;

// parse json
let v: Value = serde_json::from_str(&output)?;
// parse json
let v: Value = serde_json::from_str(&output)?;

// get the values we are interested in
let hash: block::Hash = v["hash"].as_str().unwrap().parse()?;
let height = block::Height(v["height"].as_u64().unwrap() as u32);

let size = v["size"].as_u64().unwrap();

(hash, height, size)
}
args::Backend::Zebrad => {
// get block data from zebrad by deserializing the raw block
cmd.args(["getblock", &x.to_string(), "0"]);
let output = cmd_output(&mut cmd)?;

let block_bytes = <Vec<u8>>::from_hex(output.trim_end_matches('\n'))?;

let block = block_bytes
.zcash_deserialize_into::<block::Block>()
.expect("obtained block should deserialize");

(
block.hash(),
block
.coinbase_height()
.expect("block has always a coinbase height"),
block_bytes.len().try_into()?,
)
}
};

// get the values we are interested in
let hash: block::Hash = v["hash"].as_str().unwrap().parse()?;
let height = block::Height(v["height"].as_u64().unwrap() as u32);
assert!(height <= block::Height::MAX);
assert_eq!(x, height.0);
let size = v["size"].as_u64().unwrap();

// compute
cumulative_bytes += size;
Expand Down