Skip to content

Commit 5098bdc

Browse files
authored
refactor(consensus)!: split transaction verifier into BlockVerifier and MempoolVerifier (#11095)
2 parents 485d859 + 6e5a797 commit 5098bdc

17 files changed

Lines changed: 1173 additions & 1307 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org).
77

88
## [Unreleased]
99

10+
### Breaking Changes
11+
12+
- Updated zebrad's mempool transaction downloader for zebra-consensus's transaction-verifier API
13+
split: the removed `transaction::Request::Mempool`/`transaction::Response::Mempool` enum
14+
variants are replaced by dedicated `transaction::MempoolRequest`/`transaction::MempoolResponse`
15+
types. Internal-only; no user-facing or operator-facing behavior change. See zebra-consensus's
16+
changelog for the underlying API split
17+
([#11095](https://github.com/ZcashFoundation/zebra/pull/11095)).
18+
- New `getdeprecationinfo` RPC returning the block height and estimated time at which this
19+
release will halt for end of support, in zcashd's `end_of_service` format. The `end_of_service`
20+
object is only present on Mainnet, where end of support is enforced
21+
([#11097](https://github.com/ZcashFoundation/zebra/pull/11097)).
22+
1023
### Added
1124

1225
- Added `seeder.zec.rocks` and `seeder.testnet.zec.rocks` as default DNS seeders

zebra-chain/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717

1818
- `ValueBalance::total`, which returns the sum of all value pool balances
1919

20+
### Fixed
21+
22+
- Comments in `zebra-chain/src/transaction/tests/vectors.rs`
23+
2024
## [11.3.0] - 2026-07-27
2125

2226
### Added

zebra-chain/src/transaction/tests/vectors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ fn deserialize_large_transaction() {
262262
/// An empty transaction v5, with no Orchard, Sapling, or Transparent data
263263
///
264264
/// empty transaction are invalid, but Zebra only checks this rule in
265-
/// zebra_consensus::transaction::Verifier
265+
/// zebra_consensus::transaction::check::has_inputs_and_outputs
266266
#[test]
267267
fn empty_v5_round_trip() {
268268
let _init_guard = zebra_test::init();
@@ -286,7 +286,7 @@ fn empty_v5_round_trip() {
286286
/// An empty transaction v4, with no Sapling, Sprout, or Transparent data
287287
///
288288
/// empty transaction are invalid, but Zebra only checks this rule in
289-
/// zebra_consensus::transaction::Verifier
289+
/// zebra_consensus::transaction::check::has_inputs_and_outputs
290290
#[test]
291291
fn empty_v4_round_trip() {
292292
let _init_guard = zebra_test::init();

zebra-consensus/CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Breaking Changes
11+
12+
- Split the unified transaction verifier API into separate
13+
`transaction::BlockTxVerifier` and `transaction::MempoolTxVerifier`
14+
services with dedicated request/response types.
15+
- Removed the unified verifier API:
16+
- `transaction::Verifier`
17+
- `transaction::Request`
18+
- `transaction::Response`
19+
- `transaction::BlockRequest::transaction_hash` must now be the hash of the request's
20+
`transaction`. It is used to build `BlockResponse::tx_id` instead of re-hashing the
21+
transaction, so a mismatched value yields a response identifying a different transaction.
22+
A debug assertion checks this in test and debug builds.
23+
- The second value returned by `router::init` and `router::init_test` is now a
24+
`transaction::MempoolTxVerifier` service and can no longer verify block
25+
transactions. Callers verifying transactions as part of block verification
26+
should construct a `transaction::BlockTxVerifier` directly.
27+
([#11095](https://github.com/ZcashFoundation/zebra/pull/11095)).
28+
29+
### Added
30+
31+
- `transaction::BlockTxVerifier` and `transaction::MempoolTxVerifier`.
32+
- `transaction::BlockRequest`, `transaction::BlockResponse`,
33+
`transaction::MempoolRequest`, and `transaction::MempoolResponse`.
34+
1035
## [14.0.1] - 2026-07-27
1136

1237
### Changed

zebra-consensus/src/block.rs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
//! verification, where it may be accepted or rejected.
99
1010
use std::{
11-
collections::HashSet,
1211
future::Future,
1312
pin::Pin,
1413
sync::Arc,
@@ -26,7 +25,7 @@ use zebra_chain::{
2625
amount::Amount,
2726
block,
2827
parameters::{subsidy::SubsidyError, Network},
29-
transaction, transparent,
28+
transparent,
3029
work::equihash,
3130
};
3231
use zebra_state as zs;
@@ -163,7 +162,10 @@ impl<S, V> SemanticBlockVerifier<S, V>
163162
where
164163
S: Service<zs::Request, Response = zs::Response, Error = BoxError> + Send + Clone + 'static,
165164
S::Future: Send + 'static,
166-
V: Service<tx::Request, Response = tx::Response, Error = BoxError> + Send + Clone + 'static,
165+
V: Service<tx::BlockRequest, Response = tx::BlockResponse, Error = BoxError>
166+
+ Send
167+
+ Clone
168+
+ 'static,
167169
V::Future: Send + 'static,
168170
{
169171
/// Creates a new SemanticBlockVerifier
@@ -180,7 +182,10 @@ impl<S, V> Service<Request> for SemanticBlockVerifier<S, V>
180182
where
181183
S: Service<zs::Request, Response = zs::Response, Error = BoxError> + Send + Clone + 'static,
182184
S::Future: Send + 'static,
183-
V: Service<tx::Request, Response = tx::Response, Error = BoxError> + Send + Clone + 'static,
185+
V: Service<tx::BlockRequest, Response = tx::BlockResponse, Error = BoxError>
186+
+ Send
187+
+ Clone
188+
+ 'static,
184189
V::Future: Send + 'static,
185190
{
186191
type Response = block::Hash;
@@ -285,20 +290,16 @@ where
285290
&transaction_hashes,
286291
));
287292

288-
let known_outpoint_hashes: Arc<HashSet<transaction::Hash>> =
289-
Arc::new(known_utxos.keys().map(|outpoint| outpoint.hash).collect());
290-
291293
for (&transaction_hash, transaction) in
292294
transaction_hashes.iter().zip(block.transactions.iter())
293295
{
294296
let rsp = transaction_verifier
295297
.ready()
296298
.await
297299
.expect("transaction verifier is always ready")
298-
.call(tx::Request::Block {
300+
.call(tx::BlockRequest {
299301
transaction_hash,
300302
transaction: transaction.clone(),
301-
known_outpoint_hashes: known_outpoint_hashes.clone(),
302303
known_utxos: known_utxos.clone(),
303304
height,
304305
time: block.header.time,
@@ -320,16 +321,11 @@ where
320321
.map_err(Into::into)
321322
.map_err(VerifyBlockError::Transaction)?;
322323

323-
assert!(
324-
matches!(response, tx::Response::Block { .. }),
325-
"unexpected response from transaction verifier: {response:?}"
326-
);
327-
328-
sigops += response.sigops();
324+
sigops += response.sigops;
329325

330326
// Coinbase transactions consume the miner fee,
331327
// so they don't add any value to the block's total miner fee.
332-
if let Some(miner_fee) = response.miner_fee() {
328+
if let Some(miner_fee) = response.miner_fee {
333329
block_miner_fees += miner_fee;
334330
}
335331
}

zebra-consensus/src/block/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ async fn check_transcripts() -> Result<(), Report> {
139139
let network = Network::Mainnet;
140140
let state_service = zebra_state::init_test(&network).await;
141141

142-
let transaction = transaction::Verifier::new_for_tests(&network, state_service.clone());
142+
let transaction = transaction::BlockTxVerifier::new(&network, state_service.clone());
143143
let transaction = Buffer::new(BoxService::new(transaction), 1);
144144
let block_verifier = Buffer::new(
145145
SemanticBlockVerifier::new(&network, state_service.clone(), transaction),

zebra-consensus/src/router.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ struct BlockVerifierRouter<S, V>
7272
where
7373
S: Service<zs::Request, Response = zs::Response, Error = BoxError> + Send + Clone + 'static,
7474
S::Future: Send + 'static,
75-
V: Service<transaction::Request, Response = transaction::Response, Error = BoxError>
75+
V: Service<transaction::BlockRequest, Response = transaction::BlockResponse, Error = BoxError>
7676
+ Send
7777
+ Clone
7878
+ 'static,
@@ -157,7 +157,7 @@ impl<S, V> Service<Request> for BlockVerifierRouter<S, V>
157157
where
158158
S: Service<zs::Request, Response = zs::Response, Error = BoxError> + Send + Clone + 'static,
159159
S::Future: Send + 'static,
160-
V: Service<transaction::Request, Response = transaction::Response, Error = BoxError>
160+
V: Service<transaction::BlockRequest, Response = transaction::BlockResponse, Error = BoxError>
161161
+ Send
162162
+ Clone
163163
+ 'static,
@@ -219,7 +219,7 @@ where
219219

220220
/// Initialize block and transaction verification services.
221221
///
222-
/// Returns a block verifier, transaction verifier,
222+
/// Returns a block verifier, mempool transaction verifier,
223223
/// a [`BackgroundTaskHandles`] with the state checkpoint verify task,
224224
/// and the maximum configured checkpoint verification height.
225225
///
@@ -251,8 +251,8 @@ pub async fn init<S, Mempool>(
251251
) -> (
252252
Buffer<BoxService<Request, block::Hash, RouterError>, Request>,
253253
Buffer<
254-
BoxService<transaction::Request, transaction::Response, TransactionError>,
255-
transaction::Request,
254+
BoxService<transaction::MempoolRequest, transaction::MempoolResponse, TransactionError>,
255+
transaction::MempoolRequest,
256256
>,
257257
BackgroundTaskHandles,
258258
Height,
@@ -351,8 +351,13 @@ where
351351

352352
// transaction verification
353353

354-
let transaction = transaction::Verifier::new(network, state_service.clone(), mempool);
355-
let transaction = Buffer::new(BoxService::new(transaction), VERIFIER_BUFFER_BOUND);
354+
let block_transaction = transaction::BlockTxVerifier::new(network, state_service.clone());
355+
let block_transaction = Buffer::new(BoxService::new(block_transaction), VERIFIER_BUFFER_BOUND);
356+
357+
let mempool_transaction =
358+
transaction::MempoolTxVerifier::new(network, state_service.clone(), mempool);
359+
let mempool_transaction =
360+
Buffer::new(BoxService::new(mempool_transaction), VERIFIER_BUFFER_BOUND);
356361

357362
// block verification
358363
let (list, max_checkpoint_height) = init_checkpoint_list(config, network);
@@ -374,7 +379,8 @@ where
374379
"initializing block verifier router"
375380
);
376381

377-
let block = SemanticBlockVerifier::new(network, state_service.clone(), transaction.clone());
382+
let block =
383+
SemanticBlockVerifier::new(network, state_service.clone(), block_transaction.clone());
378384
let checkpoint = CheckpointVerifier::from_checkpoint_list(list, network, tip, state_service);
379385
let router = BlockVerifierRouter {
380386
checkpoint,
@@ -388,7 +394,12 @@ where
388394
state_checkpoint_verify_handle,
389395
};
390396

391-
(router, transaction, task_handles, max_checkpoint_height)
397+
(
398+
router,
399+
mempool_transaction,
400+
task_handles,
401+
max_checkpoint_height,
402+
)
392403
}
393404

394405
/// Parses the checkpoint list for `network` and `config`.
@@ -427,8 +438,8 @@ pub async fn init_test<S>(
427438
) -> (
428439
Buffer<BoxService<Request, block::Hash, RouterError>, Request>,
429440
Buffer<
430-
BoxService<transaction::Request, transaction::Response, TransactionError>,
431-
transaction::Request,
441+
BoxService<transaction::MempoolRequest, transaction::MempoolResponse, TransactionError>,
442+
transaction::MempoolRequest,
432443
>,
433444
BackgroundTaskHandles,
434445
Height,

0 commit comments

Comments
 (0)