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
21 changes: 21 additions & 0 deletions crates/zakura-chain/src/parameters/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,27 @@ impl Network {
.collect()
}

/// Returns the first height at which version 4 transactions are invalid.
///
/// [ZIP 2003] deprecates version 4 transactions at NU7. The
/// `nu7-experimental` feature must be enabled. A network that does not
/// activate NU7 keeps accepting them.
///
/// [ZIP 2003]: https://zips.z.cash/zip-2003
pub fn v4_deprecation_height(&self) -> Option<Height> {
if cfg!(feature = "nu7-experimental") {
NetworkUpgrade::Nu7.activation_height(self)
} else {
None
}
}

/// Returns whether version 4 transactions are invalid at `height` on this network.
pub fn is_v4_deprecated(&self, height: Height) -> bool {
self.v4_deprecation_height()
.is_some_and(|deprecation_height| height >= deprecation_height)
}

/// Returns the height at which the soft fork that temporarily disables Orchard
/// actions in transactions activates, if it is configured for this network.
pub fn temporary_orchard_disabling_soft_fork_height(&self) -> Option<Height> {
Expand Down
27 changes: 22 additions & 5 deletions crates/zakura-consensus/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,7 @@ where
let tx = request.transaction();
let nu = request.upgrade(network);

Self::verify_v4_transaction_network_upgrade(&tx, nu)?;
Self::verify_v4_transaction_network_upgrade(&tx, network, request.height(), nu)?;

let sapling_bundle = cached_ffi_transaction.sighasher().sapling_bundle();

Expand All @@ -1010,11 +1010,28 @@ where
.and(Self::verify_sapling_bundle(sapling_bundle, &sighash, tx_id)))
}

/// Verifies if a V4 `transaction` is supported by `network_upgrade`.
/// Verifies if a V4 `transaction` is supported by `network_upgrade` at `height` on
/// `network`.
fn verify_v4_transaction_network_upgrade(
transaction: &Transaction,
network: &Network,
height: block::Height,
network_upgrade: NetworkUpgrade,
) -> Result<(), TransactionError> {
// # Consensus
//
// > [NU7 onward] The transaction version number MUST be 5 or 6.
//
// https://zips.z.cash/zip-2003
//
// ZIP 2003 deprecates V4 transactions at NU7.
if network.is_v4_deprecated(height) {
return Err(TransactionError::UnsupportedByNetworkUpgrade(
transaction.version(),
network_upgrade,
));
}

match network_upgrade {
// Supports V4 transactions
//
Expand All @@ -1039,16 +1056,16 @@ where
| NetworkUpgrade::Nu6
| NetworkUpgrade::Nu6_1
| NetworkUpgrade::Nu6_2
| NetworkUpgrade::Nu6_3 => Ok(()),
| NetworkUpgrade::Nu6_3
| NetworkUpgrade::Nu7 => Ok(()),

#[cfg(zcash_unstable = "zfuture")]
NetworkUpgrade::ZFuture => Ok(()),

// Does not support V4 transactions
NetworkUpgrade::Genesis
| NetworkUpgrade::BeforeOverwinter
| NetworkUpgrade::Overwinter
| NetworkUpgrade::Nu7 => Err(TransactionError::UnsupportedByNetworkUpgrade(
| NetworkUpgrade::Overwinter => Err(TransactionError::UnsupportedByNetworkUpgrade(
transaction.version(),
network_upgrade,
)),
Expand Down
89 changes: 88 additions & 1 deletion crates/zakura-consensus/src/transaction/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use zakura_chain::{ironwood, orchard};

use zakura_node_services::mempool;
use zakura_state::ValidateContextError;
use zakura_test::mock_service::MockService;
use zakura_test::mock_service::{MockService, PanicAssertion};

use crate::{error::TransactionError, primitives, transaction::POLL_MEMPOOL_DELAY, BoxError};

Expand Down Expand Up @@ -4452,6 +4452,93 @@ async fn v5_with_duplicate_orchard_action() {
}
}

/// Checks that ZIP 2003 accepts V4 transactions below NU7 and rejects them from NU7.
#[test]
fn v4_deprecation_boundary() {
let _init_guard = zakura_test::init();

let nu7 = Height(2_000_000);
let tx = test_transactions(&Network::Mainnet)
.map(|(_, tx)| tx)
.find(|tx| matches!(**tx, Transaction::V4 { .. }))
.expect("V4 tx");

let activation_heights = ConfiguredActivationHeights {
before_overwinter: Some(1),
overwinter: Some(2),
sapling: Some(3),
blossom: Some(4),
heartwood: Some(5),
canopy: Some(6),
nu5: Some(7),
nu6: Some(8),
nu6_1: Some(9),
nu6_2: Some(10),
nu6_3: Some(11),
nu7: Some(nu7.0),
};

let at_nu7 = Parameters::build()
.with_activation_heights(activation_heights)
.expect("activation heights are valid")
.clear_funding_streams()
.to_network()
.expect("failed to build configured network");

assert_eq!(
at_nu7.v4_deprecation_height(),
cfg!(feature = "nu7-experimental").then_some(nu7)
);
assert!(
verify_v4_at(&at_nu7, &tx, nu7.previous().expect("height")).is_ok(),
"a V4 transaction must be valid below the deprecation height",
);
if cfg!(feature = "nu7-experimental") {
assert_eq!(
verify_v4_at(&at_nu7, &tx, nu7),
Err(TransactionError::UnsupportedByNetworkUpgrade(
4,
NetworkUpgrade::Nu7
)),
"a V4 transaction must be invalid at the deprecation height",
);
} else {
assert!(
verify_v4_at(&at_nu7, &tx, nu7).is_ok(),
"a default build must keep accepting V4 transactions",
);
}

// A network without NU7 keeps accepting V4 transactions.
let no_nu7 = Parameters::build()
.to_network()
.expect("failed to build configured network");

assert_eq!(no_nu7.v4_deprecation_height(), None);
assert!(!no_nu7.is_v4_deprecated(Height::MAX));
}

/// A [`Verifier`] with concrete service types, so a test can name its associated
/// functions without the compiler inferring the services from a call.
type TestVerifier = Verifier<
MockService<zakura_state::Request, zakura_state::Response, PanicAssertion>,
MockService<mempool::Request, mempool::Response, PanicAssertion>,
>;

/// Runs the V4 network upgrade check for `tx` at `height` on `network`.
fn verify_v4_at(
network: &Network,
tx: &Transaction,
height: Height,
) -> Result<(), TransactionError> {
TestVerifier::verify_v4_transaction_network_upgrade(
tx,
network,
height,
NetworkUpgrade::current(network, height),
)
}

/// Checks the activation boundary of the temporary Orchard-disabling soft fork:
/// it is inactive below the configured height and active at and above it, can be
/// disabled entirely, and Mainnet uses its fixed activation height.
Expand Down
7 changes: 7 additions & 0 deletions docs/changelog/unreleased/856.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Changed

- ZIP 2003 "Disallow version 4 transactions": The off-by-default
`nu7-experimental` feature makes Zakura reject version 4 transactions from
NU7 activation. This deprecates new Sprout transactions without exposing a
separate consensus configuration
([#856](https://github.com/zakura-core/zakura/pull/856)).
Loading