Skip to content
Merged
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
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ jobs:
name: Repo guards
# GitHub-hosted runner so it runs on pull requests. Runs the workbench
# repo guards: the rustc-pin check (Dockerfile.deterministic's pallet-rust
# tag == rust-toolchain.toml channel) and the no-OpenSSL-apt check, plus the
# crate's own fmt/clippy/tests.
# tag == rust-toolchain.toml channel), the no-OpenSSL-apt check, and the
# duplicate-Rust-logic check, plus the crate's own fmt/clippy/tests.
runs-on: ubuntu-latest
steps:
- name: Checkout repository
Expand All @@ -80,6 +80,9 @@ jobs:
- name: Check no OpenSSL apt packages
run: cargo run -q --manifest-path tools/workbench/Cargo.toml --bin check-no-openssl-apt

- name: Check for duplicate Rust logic
run: cargo run -q --manifest-path tools/workbench/Cargo.toml --bin check-code-duplication

cargo-deny:
name: cargo-deny
# Enforces deny.toml — including the openssl / openssl-sys / boring* crate
Expand Down
2 changes: 2 additions & 0 deletions live-tests/clientless/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ zaino-fetch = { workspace = true }
zaino-testutils = { workspace = true }
# The lib's z_validate helper calls `ZcashIndexer::z_validate_address`.
zaino-state = { workspace = true, features = ["test_dependencies"] }
# The lib's get_block_header oracle helper matches on `zebra_rpc::methods::GetBlock`.
zebra-rpc = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
Expand Down
35 changes: 35 additions & 0 deletions live-tests/clientless/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,41 @@
//!
//! This crate also exposes test-vectors.

/// Assert that `oracle` and `subject` return the same `getblockheader` response for
/// the block at `height`: look the block up on the oracle (verbosity 1) to learn its
/// hash, then compare the two servers' non-verbose header responses for that hash.
/// Shared body of the per-backend `get_block_header` oracle tests.
#[allow(deprecated)]
pub async fn assert_get_block_header_matches<Oracle, Subject>(
oracle: &Oracle,
subject: &Subject,
height: u32,
) where
Oracle: zaino_state::ZcashIndexer,
Subject: zaino_state::ZcashIndexer,
{
let block = oracle
.z_get_block(height.to_string(), Some(1))
.await
.unwrap();

let block_hash = match block {
zebra_rpc::methods::GetBlock::Object(block) => block.hash(),
zebra_rpc::methods::GetBlock::Raw(_) => panic!("Expected block object"),
};

let oracle_header = oracle
.get_block_header(block_hash.to_string(), false)
.await
.unwrap();

let subject_header = subject
.get_block_header(block_hash.to_string(), false)
.await
.unwrap();
assert_eq!(oracle_header, subject_header);
}

pub mod rpc {
pub mod json_rpc {
pub const VALID_P2PKH_ADDRESS: &str = "tmVqEASZxBNKFTbmASZikGa5fPLkd68iJyx";
Expand Down
43 changes: 29 additions & 14 deletions live-tests/clientless/tests/compact_block_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,42 @@ async fn unfiltered_compact_blocks_match_chain_metadata_zebrad() {
test_manager.close().await;
}

/// Class-1 (consensus) predicate: in the NU5-through-NU6.2 era, a shielded
/// (orchard-receiver) miner's coinbase carries the reward as Orchard actions.
/// The shielded pool a miner's coinbase routes the reward to: Orchard in the
/// NU5-through-NU6.2 era (transaction v5), Ironwood from NU6.3 (v6).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CoinbaseRewardPool {
Orchard,
Ironwood,
}

/// Shared body of the class-1 (consensus) coinbase predicates: the first
/// transaction is a coinbase of the era's version, carries the reward as at least
/// one action in `pool`, and has an empty sapling component and an empty
/// other-shielded-pool component.
///
/// The action count uses `>= 1` rather than the padded exact count so the predicate
/// does not couple to the Orchard bundle-padding rule.
fn is_valid_orchard_coinbase(block: &zebra_chain::block::Block) -> bool {
fn is_valid_shielded_coinbase(block: &zebra_chain::block::Block, pool: CoinbaseRewardPool) -> bool {
let Some(coinbase) = block.transactions.first() else {
return false;
};
let orchard_actions = coinbase.orchard_actions().count();
let ironwood_actions = coinbase.ironwood_actions().count();
let (version, rewarded_pool_actions, other_pool_actions) = match pool {
CoinbaseRewardPool::Orchard => (5, orchard_actions, ironwood_actions),
CoinbaseRewardPool::Ironwood => (6, ironwood_actions, orchard_actions),
};
coinbase.is_coinbase()
&& coinbase.version() == 5
&& coinbase.version() == version
&& coinbase.sapling_outputs().count() == 0
&& coinbase.orchard_actions().count() >= 1
&& coinbase.ironwood_actions().count() == 0
&& rewarded_pool_actions >= 1
&& other_pool_actions == 0
}

/// Class-1 (consensus) predicate: in the NU5-through-NU6.2 era, a shielded
/// (orchard-receiver) miner's coinbase carries the reward as Orchard actions.
fn is_valid_orchard_coinbase(block: &zebra_chain::block::Block) -> bool {
is_valid_shielded_coinbase(block, CoinbaseRewardPool::Orchard)
}

/// Class-1 (consensus) predicate: from NU6.3 the same miner's coinbase must have an
Expand All @@ -182,14 +204,7 @@ fn is_valid_orchard_coinbase(block: &zebra_chain::block::Block) -> bool {
/// builder off-by-one vs missing routing vs a zaino pool-swap). This predicate over
/// raw validator blocks is that issue's disambiguator.
fn is_valid_ironwood_coinbase(block: &zebra_chain::block::Block) -> bool {
let Some(coinbase) = block.transactions.first() else {
return false;
};
coinbase.is_coinbase()
&& coinbase.version() == 6
&& coinbase.sapling_outputs().count() == 0
&& coinbase.orchard_actions().count() == 0
&& coinbase.ironwood_actions().count() >= 1
is_valid_shielded_coinbase(block, CoinbaseRewardPool::Ironwood)
}

/// One-line coinbase summary for assertion messages, so a predicate failure names the
Expand Down
30 changes: 6 additions & 24 deletions live-tests/clientless/tests/json_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,6 @@ mod zcashd {

pub(crate) mod zcash_indexer {
use zaino_state::LightWalletIndexer;
use zebra_rpc::methods::GetBlock;

use super::*;

Expand Down Expand Up @@ -453,29 +452,12 @@ mod zcashd {
&services.zaino_subscriber,
&services.zcashd_subscriber,
async |i| {
let block = services
.zcashd_subscriber
.z_get_block(i.to_string(), Some(1))
.await
.unwrap();

let block_hash = match block {
GetBlock::Object(block) => block.hash(),
GetBlock::Raw(_) => panic!("Expected block object"),
};

let zcashd_get_block_header = services
.zcashd_subscriber
.get_block_header(block_hash.to_string(), false)
.await
.unwrap();

let zainod_block_header_response = services
.zaino_subscriber
.get_block_header(block_hash.to_string(), false)
.await
.unwrap();
assert_eq!(zcashd_get_block_header, zainod_block_header_response);
clientless::assert_get_block_header_matches(
&services.zcashd_subscriber,
&services.zaino_subscriber,
i,
)
.await;
},
)
.await;
Expand Down
33 changes: 6 additions & 27 deletions live-tests/clientless/tests/state_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,6 @@ mod zebra {
pub(crate) mod lightwallet_indexer {
use futures::StreamExt as _;
use zaino_proto::proto::service::{BlockId, BlockRange, GetSubtreeRootsArg};
use zebra_rpc::methods::GetBlock;

use super::*;

Expand Down Expand Up @@ -712,32 +711,12 @@ mod zebra {
&services.fetch_subscriber,
&services.state_subscriber,
async |i| {
let block = services
.fetch_subscriber
.z_get_block(i.to_string(), Some(1))
.await
.unwrap();

let block_hash = match block {
GetBlock::Object(block) => block.hash(),
GetBlock::Raw(_) => panic!("Expected block object"),
};

let fetch_service_get_block_header = services
.fetch_subscriber
.get_block_header(block_hash.to_string(), false)
.await
.unwrap();

let state_service_block_header_response = services
.state_subscriber
.get_block_header(block_hash.to_string(), false)
.await
.unwrap();
assert_eq!(
fetch_service_get_block_header,
state_service_block_header_response
);
clientless::assert_get_block_header_matches(
&services.fetch_subscriber,
&services.state_subscriber,
i,
)
.await;
},
)
.await;
Expand Down
Loading
Loading