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
32 changes: 27 additions & 5 deletions crates/zakura-consensus/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,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 @@ -1002,11 +1002,33 @@ 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
//
// Use NU7's exact activation height: a later configured upgrade must
// not substitute its height when this network omits NU7.
if cfg!(feature = "nu7-experimental")
&& NetworkUpgrade::Nu7
.activation_height(network)
.is_some_and(|nu7_height| height >= nu7_height)
{
return Err(TransactionError::UnsupportedByNetworkUpgrade(
transaction.version(),
network_upgrade,
));
}

match network_upgrade {
// Supports V4 transactions
//
Expand All @@ -1031,16 +1053,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
102 changes: 101 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,106 @@ async fn v5_with_duplicate_orchard_action() {
}
}

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

let nu7 = Height(2_000_000);
let transaction = test_transactions(&Network::Mainnet)
.map(|(_, transaction)| transaction)
.find(|transaction| matches!(**transaction, Transaction::V4 { .. }))
.expect("the test vectors contain a V4 transaction");
let network = configured_network_with_nu7(Some(nu7));

assert!(
verify_v4_at(
&network,
&transaction,
nu7.previous().expect("NU7 is above the minimum height"),
)
.is_ok(),
"a V4 transaction must be valid below the NU7 activation height",
);

let expected = if cfg!(feature = "nu7-experimental") {
Err(TransactionError::UnsupportedByNetworkUpgrade(
transaction.version(),
NetworkUpgrade::Nu7,
))
} else {
Ok(())
};
assert_eq!(
verify_v4_at(&network, &transaction, nu7),
expected,
"V4 deprecation must match the experimental build at NU7",
);
assert_eq!(
verify_v4_at(
&network,
&transaction,
nu7.next().expect("NU7 is below the maximum height"),
),
expected,
"V4 deprecation must match the experimental build after NU7",
);

let no_nu7 = configured_network_with_nu7(None);
assert!(
verify_v4_at(&no_nu7, &transaction, Height::MAX).is_ok(),
"a network without an exact NU7 activation must keep accepting V4",
);
}

/// Returns a configured network whose latest upgrade is NU6.3 unless `nu7`
/// supplies an exact NU7 activation height.
fn configured_network_with_nu7(nu7: Option<Height>) -> Network {
Parameters::build()
.with_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: nu7.map(|height| height.0),
#[cfg(zcash_unstable = "zfuture")]
zfuture: None,
})
.expect("activation heights are ordered")
.clear_funding_streams()
.to_network()
.expect("the configured network parameters are valid")
}

/// A [`Verifier`] with concrete service types for calling its associated
/// network-upgrade check in tests.
type TestVerifier = Verifier<
MockService<zakura_state::Request, zakura_state::Response, PanicAssertion>,
MockService<mempool::Request, mempool::Response, PanicAssertion>,
>;

/// Runs the V4 network-upgrade check at `height` on `network`.
fn verify_v4_at(
network: &Network,
transaction: &Transaction,
height: Height,
) -> Result<(), TransactionError> {
TestVerifier::verify_v4_transaction_network_upgrade(
transaction,
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
5 changes: 5 additions & 0 deletions docs/changelog/unreleased/959.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Changed

- The experimental NU7 rules now reject version 4 transactions from the exact
NU7 activation height, implementing ZIP 2003 without changing default-build
behavior ([#959](https://github.com/zakura-core/zakura/pull/959)).
Loading