From b33885cb25d144bc681d96287407a6251976f768 Mon Sep 17 00:00:00 2001 From: c-node Date: Fri, 28 Aug 2026 17:12:34 -0400 Subject: [PATCH 01/15] feat: add NuTachyon and V7 transactions --- Cargo.lock | 12 +- Cargo.toml | 2 +- crates/zakura-chain/src/block/arbitrary.rs | 4 + crates/zakura-chain/src/block/commitment.rs | 6 +- crates/zakura-chain/src/history_tree.rs | 4 +- crates/zakura-chain/src/local_genesis.rs | 1 + .../src/parameters/network/testnet.rs | 9 +- .../src/parameters/network/tests/vectors.rs | 20 ++ .../src/parameters/network_upgrade.rs | 6 +- crates/zakura-chain/src/parameters/tests.rs | 7 +- .../src/parameters/transaction.rs | 3 + .../src/primitives/zcash_history.rs | 3 +- crates/zakura-chain/src/transaction.rs | 197 +++++++++++++++--- .../zakura-chain/src/transaction/arbitrary.rs | 4 +- crates/zakura-chain/src/transaction/hash.rs | 10 +- .../zakura-chain/src/transaction/serialize.rs | 86 +++++++- .../src/transaction/tests/vectors.rs | 77 +++++++ crates/zakura-chain/src/transaction/txid.rs | 8 +- .../zakura-chain/src/transaction/unmined.rs | 10 +- .../zakura-consensus/src/primitives/halo2.rs | 2 +- crates/zakura-consensus/src/transaction.rs | 55 +++-- .../src/transaction/tests/prop.rs | 22 +- crates/zakura-network/src/constants.rs | 9 +- .../src/protocol/external/types.rs | 25 ++- .../src/zakura/block_sync/tests.rs | 2 +- .../src/zakura/testkit/cluster.rs | 1 + .../src/zakura/testkit/mock_blocksync.rs | 2 +- .../src/service/finalized_state/tests/prop.rs | 15 ++ .../service/finalized_state/tests/rollback.rs | 1 + .../finalized_state/vct_fast_sync_fixture.rs | 1 + .../src/service/non_finalized_state/chain.rs | 16 ++ .../write/tests/full_state_coherence.rs | 1 + .../write/vct_authentication_sweep/tests.rs | 4 +- crates/zakura-state/src/tests.rs | 2 +- .../components/mempool/storage/tests/prop.rs | 10 +- docs/changelog/unreleased/795.md | 4 + 36 files changed, 540 insertions(+), 101 deletions(-) create mode 100644 docs/changelog/unreleased/795.md diff --git a/Cargo.lock b/Cargo.lock index 9a3a70ae66..d902b6aaa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1669,8 +1669,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d42773cb15447644d170be20231a3268600e0c4cea8987d013b93ac973d3cf7" +source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" dependencies = [ "blake2b_simd", ] @@ -8443,8 +8442,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a854b28c07dba372f4410ea8ad62b4bf7d5c2bf8be32fc4b31bc0db6521a975" +source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" dependencies = [ "bech32", "bs58", @@ -8492,8 +8490,7 @@ dependencies = [ [[package]] name = "zcash_protocol" version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314329b91ec4bbb517441840e47d0b2029bf0b946f086980c96c889c2d92dc5d" +source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" dependencies = [ "corez", "document-features", @@ -8531,8 +8528,7 @@ dependencies = [ [[package]] name = "zcash_transparent" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "547c012778bae17f58007731af074d638aa146ab0ecfc120adebf23d049aff6c" +source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" dependencies = [ "bip32", "bs58", diff --git a/Cargo.toml b/Cargo.toml index ae1eb27491..172b08767f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -380,7 +380,7 @@ lto = "off" # The linter should ignore these expected config flags/values unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(tokio_unstable)', # Used by tokio-console - 'cfg(zcash_unstable, values("zfuture", "nu6.1", "nu7"))', # Used in Zakura and librustzcash + 'cfg(zcash_unstable, values("zfuture", "nu6.1", "nu7", "nutachyon"))', # Used in Zakura and librustzcash ] } # High-risk code diff --git a/crates/zakura-chain/src/block/arbitrary.rs b/crates/zakura-chain/src/block/arbitrary.rs index 9a29304b42..c26a1d844d 100644 --- a/crates/zakura-chain/src/block/arbitrary.rs +++ b/crates/zakura-chain/src/block/arbitrary.rs @@ -608,6 +608,10 @@ where Transaction::V6 { sapling_shielded_data, .. + } + | Transaction::V7 { + sapling_shielded_data, + .. } => *sapling_shielded_data = None, Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {} } diff --git a/crates/zakura-chain/src/block/commitment.rs b/crates/zakura-chain/src/block/commitment.rs index 4141e6d37e..90b721b256 100644 --- a/crates/zakura-chain/src/block/commitment.rs +++ b/crates/zakura-chain/src/block/commitment.rs @@ -139,9 +139,9 @@ impl Commitment { } } (Heartwood | Canopy, _) => Ok(ChainHistoryRoot(ChainHistoryMmrRootHash(bytes))), - (Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7, _) => Ok(ChainHistoryBlockTxAuthCommitment( - ChainHistoryBlockTxAuthCommitmentHash(bytes), - )), + (Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7 | NuTachyon, _) => Ok( + ChainHistoryBlockTxAuthCommitment(ChainHistoryBlockTxAuthCommitmentHash(bytes)), + ), #[cfg(zcash_unstable = "zfuture")] (ZFuture, _) => Ok(ChainHistoryBlockTxAuthCommitment( diff --git a/crates/zakura-chain/src/history_tree.rs b/crates/zakura-chain/src/history_tree.rs index 71ab524507..cf161aa981 100644 --- a/crates/zakura-chain/src/history_tree.rs +++ b/crates/zakura-chain/src/history_tree.rs @@ -132,7 +132,7 @@ impl NonEmptyHistoryTree { )?; InnerHistoryTree::OrchardOnward(tree) } - NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 => { + NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 | NetworkUpgrade::NuTachyon => { let tree = Tree::::new_from_cache( network, network_upgrade, @@ -226,7 +226,7 @@ impl NonEmptyHistoryTree { )?; (InnerHistoryTree::OrchardOnward(tree), entry) } - NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 => { + NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 | NetworkUpgrade::NuTachyon => { let (tree, entry) = Tree::::new_from_parts(network, parts)?; (InnerHistoryTree::IronwoodOnward(tree), entry) } diff --git a/crates/zakura-chain/src/local_genesis.rs b/crates/zakura-chain/src/local_genesis.rs index c699a3d30c..2afbac5337 100644 --- a/crates/zakura-chain/src/local_genesis.rs +++ b/crates/zakura-chain/src/local_genesis.rs @@ -439,6 +439,7 @@ fn configured_activation_heights( nu6_2: (latest_network_upgrade >= Nu6_2).then_some(activation_height), nu6_3: (latest_network_upgrade >= Nu6_3).then_some(activation_height), nu7: (latest_network_upgrade >= Nu7).then_some(activation_height), + nu_tachyon: (latest_network_upgrade >= NuTachyon).then_some(activation_height), }) } diff --git a/crates/zakura-chain/src/parameters/network/testnet.rs b/crates/zakura-chain/src/parameters/network/testnet.rs index 09c8146b56..65f5d493dc 100644 --- a/crates/zakura-chain/src/parameters/network/testnet.rs +++ b/crates/zakura-chain/src/parameters/network/testnet.rs @@ -194,6 +194,7 @@ impl From<&BTreeMap> for ConfiguredActivationHeights { NetworkUpgrade::Nu6_2 => &mut configured_activation_heights.nu6_2, NetworkUpgrade::Nu6_3 => &mut configured_activation_heights.nu6_3, NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7, + NetworkUpgrade::NuTachyon => &mut configured_activation_heights.nu_tachyon, #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => &mut configured_activation_heights.zfuture, NetworkUpgrade::Genesis => continue, @@ -514,6 +515,8 @@ pub struct ConfiguredActivationHeights { /// Activation height for `NU7` network upgrade. #[serde(rename = "NU7")] pub nu7: Option, + /// Activation height for `NuTachyon` network upgrade. + pub nu_tachyon: Option, /// Activation height for `ZFuture` network upgrade. #[serde(rename = "ZFuture")] #[cfg(zcash_unstable = "zfuture")] @@ -537,6 +540,7 @@ impl ConfiguredActivationHeights { nu6_2, nu6_3, nu7, + nu_tachyon, #[cfg(zcash_unstable = "zfuture")] zfuture, } = self; @@ -560,6 +564,7 @@ impl ConfiguredActivationHeights { nu6_2, nu6_3, nu7, + nu_tachyon, #[cfg(zcash_unstable = "zfuture")] zfuture, } @@ -757,6 +762,7 @@ impl ParametersBuilder { nu6_2, nu6_3, nu7, + nu_tachyon, #[cfg(zcash_unstable = "zfuture")] zfuture, }: ConfiguredActivationHeights, @@ -785,7 +791,8 @@ impl ParametersBuilder { .chain(nu6_1.into_iter().map(|h| (h, Nu6_1))) .chain(nu6_2.into_iter().map(|h| (h, Nu6_2))) .chain(nu6_3.into_iter().map(|h| (h, Nu6_3))) - .chain(nu7.into_iter().map(|h| (h, Nu7))); + .chain(nu7.into_iter().map(|h| (h, Nu7))) + .chain(nu_tachyon.into_iter().map(|h| (h, NuTachyon))); #[cfg(zcash_unstable = "zfuture")] let activation_heights = diff --git a/crates/zakura-chain/src/parameters/network/tests/vectors.rs b/crates/zakura-chain/src/parameters/network/tests/vectors.rs index 5cfb2d9e11..ff97809283 100644 --- a/crates/zakura-chain/src/parameters/network/tests/vectors.rs +++ b/crates/zakura-chain/src/parameters/network/tests/vectors.rs @@ -222,10 +222,12 @@ fn bip70_network_names_are_stable_for_every_network_kind() { fn activates_network_upgrades_correctly() { let expected_nu6_3_activation_height = 1; let expected_nu7_activation_height = 2; + let expected_nu_tachyon_activation_height = 3; let network = testnet::Parameters::build() .with_activation_heights(ConfiguredActivationHeights { nu6_3: Some(expected_nu6_3_activation_height), nu7: Some(expected_nu7_activation_height), + nu_tachyon: Some(expected_nu_tachyon_activation_height), ..Default::default() }) .expect("failed to set activation heights") @@ -246,6 +248,7 @@ fn activates_network_upgrades_correctly() { for nu in NetworkUpgrade::iter().skip(1) { let expected_activation_height = match nu { NetworkUpgrade::Nu7 => expected_nu7_activation_height, + NetworkUpgrade::NuTachyon => expected_nu_tachyon_activation_height, _ => expected_nu6_3_activation_height, }; @@ -340,6 +343,23 @@ fn configured_nu6_3_activation_preserves_upgrade_order() { "an overwritten NU6.3 activation must remain implicit at the NU7 height" ); + let nu_tachyon_network = testnet::Parameters::build() + .with_activation_heights(ConfiguredActivationHeights { + nu7: Some(activation_height.0), + nu_tachyon: Some(activation_height.0), + ..Default::default() + }) + .expect("same-height upgrades are valid") + .clear_funding_streams() + .to_network() + .expect("valid configured network"); + + assert_eq!( + NetworkUpgrade::current(&nu_tachyon_network, activation_height), + NetworkUpgrade::NuTachyon, + "NuTachyon must overwrite NU7 when both activate at the same height" + ); + let out_of_order = testnet::Parameters::build() .with_activation_heights(ConfiguredActivationHeights { nu6_2: Some(activation_height.0 + 1), diff --git a/crates/zakura-chain/src/parameters/network_upgrade.rs b/crates/zakura-chain/src/parameters/network_upgrade.rs index 8fb7601f3b..696c87c6ad 100644 --- a/crates/zakura-chain/src/parameters/network_upgrade.rs +++ b/crates/zakura-chain/src/parameters/network_upgrade.rs @@ -67,6 +67,8 @@ pub enum NetworkUpgrade { /// The Zcash protocol after the NU7 upgrade. #[serde(rename = "NU7")] Nu7, + /// The Zcash protocol after the NuTachyon upgrade. + NuTachyon, #[cfg(zcash_unstable = "zfuture")] ZFuture, @@ -241,6 +243,7 @@ pub(crate) const CONSENSUS_BRANCH_IDS: &[(NetworkUpgrade, ConsensusBranchId)] = // TODO: set below to (Nu7, ConsensusBranchId(0x77190ad8)), once the same value is set in librustzcash #[cfg(any(test, feature = "zakura-test"))] (Nu7, ConsensusBranchId(0xfffffffe)), + (NuTachyon, ConsensusBranchId(0xfffffffc)), #[cfg(zcash_unstable = "zfuture")] (ZFuture, ConsensusBranchId(0xfffffffd)), ]; @@ -402,7 +405,7 @@ impl NetworkUpgrade { pub fn target_spacing(&self) -> Duration { let spacing_seconds = match self { Genesis | BeforeOverwinter | Overwinter | Sapling => PRE_BLOSSOM_POW_TARGET_SPACING, - Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7 => { + Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7 | NuTachyon => { POST_BLOSSOM_POW_TARGET_SPACING.into() } @@ -531,6 +534,7 @@ impl From for NetworkUpgrade { zcash_protocol::consensus::NetworkUpgrade::Nu6_3 => Self::Nu6_3, #[cfg(zcash_unstable = "nu7")] zcash_protocol::consensus::NetworkUpgrade::Nu7 => Self::Nu7, + zcash_protocol::consensus::NetworkUpgrade::NuTachyon => Self::NuTachyon, #[cfg(zcash_unstable = "zfuture")] zcash_protocol::consensus::NetworkUpgrade::ZFuture => Self::ZFuture, } diff --git a/crates/zakura-chain/src/parameters/tests.rs b/crates/zakura-chain/src/parameters/tests.rs index 6d5ec15a65..d620c2d088 100644 --- a/crates/zakura-chain/src/parameters/tests.rs +++ b/crates/zakura-chain/src/parameters/tests.rs @@ -274,6 +274,7 @@ const NETWORK_UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ #[cfg(any(test, feature = "zakura-test"))] Nu6_3, Nu7, + NuTachyon, ]; #[test] @@ -289,7 +290,7 @@ fn full_activation_list_contains_all_upgrades() { let network = Network::Mainnet; let full_list = network.full_activation_list(); - // NU7 is unscheduled on Mainnet (no activation height committed), so it is absent from the - // full activation list even though it is always present in the iter. - assert_eq!(full_list.len(), NetworkUpgrade::iter().count() - 1); + // NU7 and NuTachyon are unscheduled on Mainnet, so they are absent from the full activation + // list even though they are always present in the iterator. + assert_eq!(full_list.len(), NetworkUpgrade::iter().count() - 2); } diff --git a/crates/zakura-chain/src/parameters/transaction.rs b/crates/zakura-chain/src/parameters/transaction.rs index b3342d380a..7f93db8a6f 100644 --- a/crates/zakura-chain/src/parameters/transaction.rs +++ b/crates/zakura-chain/src/parameters/transaction.rs @@ -14,3 +14,6 @@ pub const TX_V5_VERSION_GROUP_ID: u32 = 0x26A7_270A; /// The version group ID for version 6 transactions. pub const TX_V6_VERSION_GROUP_ID: u32 = 0xD884_B698; + +/// The placeholder version group ID for version 7 transactions ("tach" in ASCII). +pub const TX_V7_VERSION_GROUP_ID: u32 = 0x7461_6368; diff --git a/crates/zakura-chain/src/primitives/zcash_history.rs b/crates/zakura-chain/src/primitives/zcash_history.rs index bc0d32c503..b075354861 100644 --- a/crates/zakura-chain/src/primitives/zcash_history.rs +++ b/crates/zakura-chain/src/primitives/zcash_history.rs @@ -422,7 +422,8 @@ impl Version for zcash_history::V1 { | NetworkUpgrade::Nu6_1 | NetworkUpgrade::Nu6_2 | NetworkUpgrade::Nu6_3 - | NetworkUpgrade::Nu7 => {} + | NetworkUpgrade::Nu7 + | NetworkUpgrade::NuTachyon => {} #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => {} }; diff --git a/crates/zakura-chain/src/transaction.rs b/crates/zakura-chain/src/transaction.rs index 53d518a6d3..e20d53025f 100644 --- a/crates/zakura-chain/src/transaction.rs +++ b/crates/zakura-chain/src/transaction.rs @@ -38,7 +38,7 @@ pub use unmined::{ }; use zcash_protocol::consensus; -use crate::parameters::TX_V6_VERSION_GROUP_ID; +use crate::parameters::{TX_V6_VERSION_GROUP_ID, TX_V7_VERSION_GROUP_ID}; use crate::{ amount::{Amount, Error as AmountError, NegativeAllowed, NonNegative}, block, ironwood, @@ -169,6 +169,29 @@ pub enum Transaction { /// The Ironwood data for this transaction, if any. ironwood_shielded_data: Option, }, + /// A `version = 7` transaction enabled by NuTachyon. + /// + /// V7 initially has the same fields and behavior as V6. + V7 { + /// The Network Upgrade for this transaction. + /// + /// Derived from the ConsensusBranchId field. + network_upgrade: NetworkUpgrade, + /// The earliest time or block height that this transaction can be added to the chain. + lock_time: LockTime, + /// The latest block height that this transaction can be added to the chain. + expiry_height: block::Height, + /// The transparent inputs to the transaction. + inputs: Vec, + /// The transparent outputs from the transaction. + outputs: Vec, + /// The sapling shielded data for this transaction, if any. + sapling_shielded_data: Option>, + /// The orchard data for this transaction, if any. + orchard_shielded_data: Option, + /// The Ironwood data for this transaction, if any. + ironwood_shielded_data: Option, + }, } impl AttributedMemorySize for Transaction { @@ -240,6 +263,14 @@ impl AttributedMemorySize for Transaction { orchard_shielded_data, ironwood_shielded_data, .. + } + | V7 { + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + ironwood_shielded_data, + .. } => ( inputs, outputs, @@ -388,7 +419,7 @@ impl Transaction { | Transaction::V3 { .. } | Transaction::V4 { .. } => None, Transaction::V5 { .. } => Some(AuthDigest::from(self)), - Transaction::V6 { .. } => Some(AuthDigest::from(self)), + Transaction::V6 { .. } | Transaction::V7 { .. } => Some(AuthDigest::from(self)), } } @@ -408,7 +439,7 @@ impl Transaction { crate::primitives::zcash_primitives::txid_and_auth_digest(self); (txid, Some(auth_digest)) } - Transaction::V6 { .. } => { + Transaction::V6 { .. } | Transaction::V7 { .. } => { let (txid, auth_digest) = crate::primitives::zcash_primitives::txid_and_auth_digest(self); (txid, Some(auth_digest)) @@ -523,7 +554,7 @@ impl Transaction { match self { Transaction::V1 { .. } | Transaction::V2 { .. } => false, Transaction::V3 { .. } | Transaction::V4 { .. } | Transaction::V5 { .. } => true, - Transaction::V6 { .. } => true, + Transaction::V6 { .. } | Transaction::V7 { .. } => true, } } @@ -546,6 +577,7 @@ impl Transaction { Transaction::V4 { .. } => 4, Transaction::V5 { .. } => 5, Transaction::V6 { .. } => 6, + Transaction::V7 { .. } => 7, } } @@ -557,7 +589,7 @@ impl Transaction { | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } | Transaction::V5 { lock_time, .. } => *lock_time, - Transaction::V6 { lock_time, .. } => *lock_time, + Transaction::V6 { lock_time, .. } | Transaction::V7 { lock_time, .. } => *lock_time, }; // `zcashd` checks that the block height is greater than the lock height. @@ -605,7 +637,7 @@ impl Transaction { | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } | Transaction::V5 { lock_time, .. } => *lock_time, - Transaction::V6 { lock_time, .. } => *lock_time, + Transaction::V6 { lock_time, .. } | Transaction::V7 { lock_time, .. } => *lock_time, }; let mut lock_time_bytes = Vec::new(); lock_time @@ -642,14 +674,16 @@ impl Transaction { block::Height(0) => None, block::Height(expiry_height) => Some(block::Height(*expiry_height)), }, - Transaction::V6 { expiry_height, .. } => match expiry_height { - // # Consensus - // - // > No limit: To set no limit on transactions (so that they do not expire), nExpiryHeight should be set to 0. - // https://zips.z.cash/zip-0203#specification - block::Height(0) => None, - block::Height(expiry_height) => Some(block::Height(*expiry_height)), - }, + Transaction::V6 { expiry_height, .. } | Transaction::V7 { expiry_height, .. } => { + match expiry_height { + // # Consensus + // + // > No limit: To set no limit on transactions (so that they do not expire), nExpiryHeight should be set to 0. + // https://zips.z.cash/zip-0203#specification + block::Height(0) => None, + block::Height(expiry_height) => Some(block::Height(*expiry_height)), + } + } } } @@ -668,6 +702,9 @@ impl Transaction { } => Some(*network_upgrade), Transaction::V6 { network_upgrade, .. + } + | Transaction::V7 { + network_upgrade, .. } => Some(*network_upgrade), } } @@ -682,7 +719,7 @@ impl Transaction { Transaction::V3 { ref inputs, .. } => inputs, Transaction::V4 { ref inputs, .. } => inputs, Transaction::V5 { ref inputs, .. } => inputs, - Transaction::V6 { ref inputs, .. } => inputs, + Transaction::V6 { ref inputs, .. } | Transaction::V7 { ref inputs, .. } => inputs, } } @@ -701,7 +738,7 @@ impl Transaction { Transaction::V3 { ref outputs, .. } => outputs, Transaction::V4 { ref outputs, .. } => outputs, Transaction::V5 { ref outputs, .. } => outputs, - Transaction::V6 { ref outputs, .. } => outputs, + Transaction::V6 { ref outputs, .. } | Transaction::V7 { ref outputs, .. } => outputs, } } @@ -750,7 +787,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -786,7 +823,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -822,7 +859,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => 0, - Transaction::V6 { .. } => 0, + Transaction::V6 { .. } | Transaction::V7 { .. } => 0, } } @@ -862,7 +899,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -899,7 +936,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => None, - Transaction::V6 { .. } => None, + Transaction::V6 { .. } | Transaction::V7 { .. } => None, } } @@ -908,7 +945,7 @@ impl Transaction { match self { // No JoinSplits Transaction::V1 { .. } | Transaction::V5 { .. } => false, - Transaction::V6 { .. } => false, + Transaction::V6 { .. } | Transaction::V7 { .. } => false, // JoinSplits-on-BCTV14 Transaction::V2 { joinsplit_data, .. } | Transaction::V3 { joinsplit_data, .. } => { @@ -956,7 +993,7 @@ impl Transaction { } | Transaction::V1 { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -973,6 +1010,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data, .. + } + | Transaction::V7 { + sapling_shielded_data, + .. } => sapling_shielded_data.as_ref(), _ => None, } @@ -997,6 +1038,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => Box::new(sapling_shielded_data.anchors()), // No Spends @@ -1014,6 +1059,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. + } + | Transaction::V7 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -1043,6 +1092,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => Box::new(sapling_shielded_data.spends_per_anchor()), // No Spends @@ -1060,6 +1113,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. + } + | Transaction::V7 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -1079,6 +1136,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => Box::new(sapling_shielded_data.outputs()), // No Outputs @@ -1096,6 +1157,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. + } + | Transaction::V7 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -1117,6 +1182,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => Box::new(sapling_shielded_data.nullifiers()), // No Spends @@ -1134,6 +1203,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. + } + | Transaction::V7 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -1157,6 +1230,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => Box::new(sapling_shielded_data.note_commitments()), // No Spends @@ -1174,6 +1251,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. + } + | Transaction::V7 { + sapling_shielded_data: None, + .. } => Box::new(std::iter::empty()), } } @@ -1193,6 +1274,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data, .. + } + | Transaction::V7 { + sapling_shielded_data, + .. } => sapling_shielded_data.is_some(), } } @@ -1218,6 +1303,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => sapling_shielded_data.point_encodings_are_valid(), _ => true, } @@ -1237,6 +1326,10 @@ impl Transaction { Transaction::V6 { orchard_shielded_data, .. + } + | Transaction::V7 { + orchard_shielded_data, + .. } => orchard_shielded_data.as_ref(), // No Orchard shielded data @@ -1293,6 +1386,10 @@ impl Transaction { Transaction::V6 { ironwood_shielded_data, .. + } + | Transaction::V7 { + ironwood_shielded_data, + .. } => ironwood_shielded_data.as_ref(), Transaction::V1 { .. } @@ -1451,7 +1548,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -1499,7 +1596,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -1541,7 +1638,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(iter::empty()), - Transaction::V6 { .. } => Box::new(iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(iter::empty()), }; joinsplit_value_balances.map(ValueBalance::from_sprout_amount) @@ -1586,6 +1683,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => sapling_shielded_data.value_balance, Transaction::V1 { .. } @@ -1602,6 +1703,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. + } + | Transaction::V7 { + sapling_shielded_data: None, + .. } => Amount::zero(), }; @@ -1625,6 +1730,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => Some(sapling_shielded_data.binding_sig), _ => None, } @@ -1829,6 +1938,7 @@ impl Transaction { Transaction::V4 { .. } => Some(SAPLING_VERSION_GROUP_ID), Transaction::V5 { .. } => Some(TX_V5_VERSION_GROUP_ID), Transaction::V6 { .. } => Some(TX_V6_VERSION_GROUP_ID), + Transaction::V7 { .. } => Some(TX_V7_VERSION_GROUP_ID), } } } @@ -1858,6 +1968,10 @@ impl Transaction { Transaction::V6 { ref mut network_upgrade, .. + } + | Transaction::V7 { + ref mut network_upgrade, + .. } => { *network_upgrade = nu; Ok(()) @@ -1890,6 +2004,10 @@ impl Transaction { Transaction::V6 { ref mut expiry_height, .. + } + | Transaction::V7 { + ref mut expiry_height, + .. } => expiry_height, } } @@ -1902,7 +2020,9 @@ impl Transaction { Transaction::V3 { ref mut inputs, .. } => inputs, Transaction::V4 { ref mut inputs, .. } => inputs, Transaction::V5 { ref mut inputs, .. } => inputs, - Transaction::V6 { ref mut inputs, .. } => inputs, + Transaction::V6 { ref mut inputs, .. } | Transaction::V7 { ref mut inputs, .. } => { + inputs + } } } @@ -1932,6 +2052,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. + } + | Transaction::V7 { + sapling_shielded_data: Some(sapling_shielded_data), + .. } => Some(&mut sapling_shielded_data.value_balance), Transaction::V1 { .. } | Transaction::V2 { .. } @@ -1947,6 +2071,10 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. + } + | Transaction::V7 { + sapling_shielded_data: None, + .. } => None, } } @@ -1996,7 +2124,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -2045,7 +2173,7 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -2067,6 +2195,10 @@ impl Transaction { Transaction::V6 { orchard_shielded_data: Some(orchard_shielded_data), .. + } + | Transaction::V7 { + orchard_shielded_data: Some(orchard_shielded_data), + .. } => Some(orchard_shielded_data), Transaction::V1 { .. } @@ -2080,6 +2212,10 @@ impl Transaction { Transaction::V6 { orchard_shielded_data: None, .. + } + | Transaction::V7 { + orchard_shielded_data: None, + .. } => None, } } @@ -2104,6 +2240,9 @@ impl Transaction { } => outputs, Transaction::V6 { ref mut outputs, .. + } + | Transaction::V7 { + ref mut outputs, .. } => outputs, } } diff --git a/crates/zakura-chain/src/transaction/arbitrary.rs b/crates/zakura-chain/src/transaction/arbitrary.rs index ec3a8b0b8f..5b931e140e 100644 --- a/crates/zakura-chain/src/transaction/arbitrary.rs +++ b/crates/zakura-chain/src/transaction/arbitrary.rs @@ -805,7 +805,7 @@ impl Arbitrary for Transaction { Self::v5_strategy(ledger_state) ] .boxed(), - NetworkUpgrade::Nu7 => Self::v5_strategy(ledger_state), + NetworkUpgrade::Nu7 | NetworkUpgrade::NuTachyon => Self::v5_strategy(ledger_state), #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => Self::v5_strategy(ledger_state), @@ -955,6 +955,7 @@ pub fn transaction_to_fake_v5( }, v5 @ V5 { .. } => v5.clone(), v6 @ V6 { .. } => v6.clone(), + v7 @ V7 { .. } => v7.clone(), } } @@ -1040,6 +1041,7 @@ pub fn v5_transactions<'b>( | Transaction::V4 { .. } => None, ref tx @ Transaction::V5 { .. } => Some(tx.clone()), ref tx @ Transaction::V6 { .. } => Some(tx.clone()), + ref tx @ Transaction::V7 { .. } => Some(tx.clone()), }) } diff --git a/crates/zakura-chain/src/transaction/hash.rs b/crates/zakura-chain/src/transaction/hash.rs index 75fe43fea5..259fe812d1 100644 --- a/crates/zakura-chain/src/transaction/hash.rs +++ b/crates/zakura-chain/src/transaction/hash.rs @@ -6,10 +6,10 @@ //! * [`WtxId`]: a 64-byte witnessed transaction ID, which uniquely identifies unmined transactions //! (transactions that are sent by wallets or stored in node mempools). //! -//! Transaction versions 5 and 6 use both these unique identifiers: -//! * [`struct@Hash`] uniquely identifies the effects of a v5 or v6 transaction (spends and outputs), +//! Transaction versions 5 and later use both these unique identifiers: +//! * [`struct@Hash`] uniquely identifies the effects of a v5+ transaction (spends and outputs), //! so it uniquely identifies the transaction's data after it has been mined into a block; -//! * [`WtxId`] uniquely identifies the effects and authorizing data of a v5 or v6 transaction +//! * [`WtxId`] uniquely identifies the effects and authorizing data of a v5+ transaction //! (signatures, proofs, and scripts), so it uniquely identifies the transaction's data //! outside a block. (For example, transactions produced by Zcash wallets, or in node mempools.) //! @@ -42,7 +42,7 @@ use crate::serialization::{ use super::{txid::TxIdBuilder, AuthDigest, Transaction}; -/// A transaction ID, which uniquely identifies mined v5 and v6 transactions, +/// A transaction ID, which uniquely identifies mined v5+ transactions, /// and all v1-v4 transactions. /// /// Note: Zebra displays transaction and block hashes in big-endian byte-order, @@ -195,7 +195,7 @@ impl ZcashDeserialize for Hash { } } -/// A witnessed transaction ID, which uniquely identifies unmined v5 and v6 transactions. +/// A witnessed transaction ID, which uniquely identifies unmined v5+ transactions. /// /// Witnessed transaction IDs are not used for transaction versions 1-4. /// diff --git a/crates/zakura-chain/src/transaction/serialize.rs b/crates/zakura-chain/src/transaction/serialize.rs index 0e5f9ad582..4b61cc4c37 100644 --- a/crates/zakura-chain/src/transaction/serialize.rs +++ b/crates/zakura-chain/src/transaction/serialize.rs @@ -21,7 +21,7 @@ use crate::{ }, }; -use crate::parameters::TX_V6_VERSION_GROUP_ID; +use crate::parameters::{TX_V6_VERSION_GROUP_ID, TX_V7_VERSION_GROUP_ID}; use super::*; use crate::sapling; @@ -199,7 +199,7 @@ impl ZcashDeserialize for Option> { } } -/// Deserialize V5/V6 Sapling shielded data with an optional early coinbase +/// Deserialize V5+ Sapling shielded data with an optional early coinbase /// rejection. /// /// When `is_coinbase` is true, a non-zero `nSpendsSapling` count is rejected @@ -875,6 +875,45 @@ impl ZcashSerialize for Transaction { ALLOW_CROSS_ADDRESS_BIT, )?; } + Transaction::V7 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + ironwood_shielded_data, + } => { + if *network_upgrade < NetworkUpgrade::NuTachyon { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "v7 transaction must have a NuTachyon or later consensus branch ID", + )); + } + + writer.write_u32::(TX_V7_VERSION_GROUP_ID)?; + writer.write_u32::(u32::from( + network_upgrade + .branch_id() + .expect("valid transactions must have a network upgrade with a branch id"), + ))?; + lock_time.zcash_serialize(&mut writer)?; + writer.write_u32::(expiry_height.0)?; + inputs.zcash_serialize(&mut writer)?; + outputs.zcash_serialize(&mut writer)?; + sapling_shielded_data.zcash_serialize(&mut writer)?; + serialize_optional_orchard_shielded_data_with_flags( + orchard_shielded_data, + &mut writer, + !ALLOW_CROSS_ADDRESS_BIT, + )?; + serialize_optional_orchard_shielded_data_with_flags( + ironwood_shielded_data, + &mut writer, + ALLOW_CROSS_ADDRESS_BIT, + )?; + } } Ok(()) } @@ -1235,6 +1274,49 @@ impl ZcashDeserialize for Transaction { ironwood_shielded_data, }) } + (7, true) => { + let id = limited_reader.read_u32::()?; + if id != TX_V7_VERSION_GROUP_ID { + return Err(SerializationError::Parse("expected TX_V7_VERSION_GROUP_ID")); + } + + let network_upgrade = + NetworkUpgrade::try_from(limited_reader.read_u32::()?)?; + if network_upgrade < NetworkUpgrade::NuTachyon { + return Err(SerializationError::Parse( + "v7 transaction must have a NuTachyon or later consensus branch ID", + )); + } + + let lock_time = LockTime::zcash_deserialize(&mut limited_reader)?; + let expiry_height = block::Height(limited_reader.read_u32::()?); + let inputs: Vec = Vec::zcash_deserialize(&mut limited_reader)?; + let outputs = Vec::zcash_deserialize(&mut limited_reader)?; + + let is_coinbase = inputs.len() == 1 + && matches!(inputs.first(), Some(transparent::Input::Coinbase { .. })); + let sapling_shielded_data = + deserialize_v5_sapling_shielded_data(&mut limited_reader, is_coinbase)?; + let orchard_shielded_data = deserialize_orchard_shielded_data_with_flags( + &mut limited_reader, + !ALLOW_CROSS_ADDRESS_BIT, + )?; + let ironwood_shielded_data = deserialize_orchard_shielded_data_with_flags( + &mut limited_reader, + ALLOW_CROSS_ADDRESS_BIT, + )?; + + Ok(Transaction::V7 { + network_upgrade, + lock_time, + expiry_height, + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + ironwood_shielded_data, + }) + } (_, _) => Err(SerializationError::Parse("bad tx header")), } } diff --git a/crates/zakura-chain/src/transaction/tests/vectors.rs b/crates/zakura-chain/src/transaction/tests/vectors.rs index 53e6407a2b..8553ada55c 100644 --- a/crates/zakura-chain/src/transaction/tests/vectors.rs +++ b/crates/zakura-chain/src/transaction/tests/vectors.rs @@ -1721,6 +1721,10 @@ fn binding_signatures() { Transaction::V6 { sapling_shielded_data, .. + } + | Transaction::V7 { + sapling_shielded_data, + .. } => { if let Some(sapling_shielded_data) = sapling_shielded_data { // V6 txs have the outputs spent by their transparent inputs hashed into @@ -1825,6 +1829,79 @@ fn v6_version_group_id_matches_librustzcash_and_wire_format() { )); } +#[test] +fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { + use crate::parameters::TX_V7_VERSION_GROUP_ID; + + let _init_guard = zakura_test::init(); + + assert_eq!( + TX_V7_VERSION_GROUP_ID, + zcash_protocol::constants::V7_VERSION_GROUP_ID, + ); + assert_eq!( + TX_V7_VERSION_GROUP_ID, + zcash_primitives::transaction::TxVersion::V7.version_group_id(), + ); + + let empty_v7 = |network_upgrade| Transaction::V7 { + network_upgrade, + lock_time: LockTime::unlocked(), + expiry_height: Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + }; + + empty_v7(NetworkUpgrade::Nu6_3) + .zcash_serialize_to_vec() + .expect_err("V7 must not serialize before NuTachyon"); + + let tx = empty_v7(NetworkUpgrade::NuTachyon); + let tx_bytes = tx + .zcash_serialize_to_vec() + .expect("an empty NuTachyon V7 transaction has a valid wire encoding"); + assert_eq!(&tx_bytes[0..4], &[0x07, 0x00, 0x00, 0x80]); + assert_eq!(&tx_bytes[4..8], &TX_V7_VERSION_GROUP_ID.to_le_bytes()); + assert_eq!(&tx_bytes[8..12], &0xffff_fffcu32.to_le_bytes()); + assert_eq!(tx.version_group_id(), Some(TX_V7_VERSION_GROUP_ID)); + + let decoded = Transaction::zcash_deserialize(&tx_bytes[..]) + .expect("Zakura must deserialize its NuTachyon V7 encoding"); + assert_eq!(decoded, tx); + let librustzcash_tx = tx + .to_librustzcash(NetworkUpgrade::NuTachyon) + .expect("librustzcash must accept Zakura's NuTachyon V7 encoding"); + assert_eq!(tx.hash().0, *librustzcash_tx.txid().as_ref()); + let librustzcash_auth_digest: [u8; 32] = librustzcash_tx + .auth_commitment() + .as_ref() + .try_into() + .expect("the authorizing-data digest is 32 bytes"); + assert_eq!( + tx.auth_digest() + .expect("V7 transactions have an authorizing-data digest") + .0, + librustzcash_auth_digest, + ); + + let mut pre_nu_tachyon = tx_bytes; + let nu6_3_branch_id = u32::from( + NetworkUpgrade::Nu6_3 + .branch_id() + .expect("NU6.3 has a branch ID"), + ); + pre_nu_tachyon[8..12].copy_from_slice(&nu6_3_branch_id.to_le_bytes()); + let error = Transaction::zcash_deserialize(&pre_nu_tachyon[..]) + .expect_err("V7 must not deserialize with a pre-NuTachyon branch ID"); + assert!(matches!( + error, + SerializationError::Parse(message) if message.contains("NuTachyon") + )); +} + #[test] fn v6_transactions_accept_nu6_3_and_later_branch_ids() { use crate::parameters::TX_V6_VERSION_GROUP_ID; diff --git a/crates/zakura-chain/src/transaction/txid.rs b/crates/zakura-chain/src/transaction/txid.rs index 1b7033dae4..6876f4c4e3 100644 --- a/crates/zakura-chain/src/transaction/txid.rs +++ b/crates/zakura-chain/src/transaction/txid.rs @@ -6,7 +6,7 @@ use crate::serialization::{sha256d, ZcashSerialize}; /// A Transaction ID builder. It computes the transaction ID by hashing /// different parts of the transaction, depending on the transaction version. -/// For V5 and V6 transactions, it follows [ZIP-244] and [ZIP-225]. +/// For V5 and later transactions, it follows [ZIP-244] and [ZIP-225]. /// /// [ZIP-244]: https://zips.z.cash/zip-0244 /// [ZIP-225]: https://zips.z.cash/zip-0225 @@ -28,7 +28,7 @@ impl<'a> TxIdBuilder<'a> { | Transaction::V3 { .. } | Transaction::V4 { .. } => self.txid_v1_to_v4(), Transaction::V5 { .. } => self.txid_v5(), - Transaction::V6 { .. } => self.txid_v6(), + Transaction::V6 { .. } | Transaction::V7 { .. } => self.txid_v6(), } } @@ -40,7 +40,7 @@ impl<'a> TxIdBuilder<'a> { Some(Hash(hash_writer.finish())) } - /// Compute the Transaction ID for a V5 or V6 transaction. + /// Compute the Transaction ID for a V5 or later transaction. /// In this case it's the hash of a tree of hashes of specific parts of the /// transaction, as specified in ZIP-244 and ZIP-225. fn txid_v5(self) -> Option { @@ -56,7 +56,7 @@ impl<'a> TxIdBuilder<'a> { Some(Hash(*self.trans.to_librustzcash(nu).ok()?.txid().as_ref())) } - /// Passthrough to the ZIP-244 txid path for V6 transactions. + /// Passthrough to the ZIP-244 txid path for V6 and V7 transactions. fn txid_v6(self) -> Option { self.txid_v5() } diff --git a/crates/zakura-chain/src/transaction/unmined.rs b/crates/zakura-chain/src/transaction/unmined.rs index a899963484..28fa07b923 100644 --- a/crates/zakura-chain/src/transaction/unmined.rs +++ b/crates/zakura-chain/src/transaction/unmined.rs @@ -1,8 +1,8 @@ //! Unmined Zcash transaction identifiers and transactions. //! -//! Transaction versions 5 and 6 are uniquely identified by [`WtxId`] when +//! Transaction versions 5 and later are uniquely identified by [`WtxId`] when //! unmined, and [`struct@Hash`] in the blockchain. The effects of a v5 or -//! v6 transaction (spends and outputs) are uniquely identified by the same +//! v5+ transaction (spends and outputs) are uniquely identified by the same //! [`struct@Hash`] in both cases. //! //! Transaction versions 1-4 are uniquely identified by legacy @@ -98,8 +98,8 @@ pub enum UnminedTxId { /// A witnessed unmined transaction identifier. /// - /// Used to uniquely identify unmined version 5 and 6 transactions. - /// (After v5 and v6 transactions are mined, they can be uniquely identified + /// Used to uniquely identify unmined version 5+ transactions. + /// (After v5+ transactions are mined, they can be uniquely identified /// using only the [`struct@Hash`] in their `WtxId.id`.) /// /// For more details, see [`WtxId`]. @@ -141,7 +141,7 @@ impl From<&Transaction> for UnminedTxId { match transaction { V1 { .. } | V2 { .. } | V3 { .. } | V4 { .. } => Legacy(transaction.into()), V5 { .. } => Witnessed(transaction.into()), - V6 { .. } => Witnessed(transaction.into()), + V6 { .. } | V7 { .. } => Witnessed(transaction.into()), } } } diff --git a/crates/zakura-consensus/src/primitives/halo2.rs b/crates/zakura-consensus/src/primitives/halo2.rs index 6d2094a4ac..65c5db0e20 100644 --- a/crates/zakura-consensus/src/primitives/halo2.rs +++ b/crates/zakura-consensus/src/primitives/halo2.rs @@ -402,7 +402,7 @@ fn lazy_verifier_for(network_upgrade: NetworkUpgrade) -> &'static Lazy &VERIFIER_NU6_3_ONWARD, + Nu6_3 | Nu7 | NuTachyon => &VERIFIER_NU6_3_ONWARD, // `ZFuture` is post-NU6.3 and inherits the NU6.3 circuit. Keep the // cfg-gated arm explicit so a future upgrade cannot silently fall back diff --git a/crates/zakura-consensus/src/transaction.rs b/crates/zakura-consensus/src/transaction.rs index 14bc9f4db7..ec58f149b8 100644 --- a/crates/zakura-consensus/src/transaction.rs +++ b/crates/zakura-consensus/src/transaction.rs @@ -612,15 +612,15 @@ where cached_ffi_transaction.clone(), wtx_id.expect("a v5 transaction has a witnessed transaction ID"), )?, - Transaction::V6 { - .. - } => Self::verify_v6_transaction( - &req, - &network, - script_verifier, - cached_ffi_transaction.clone(), - wtx_id.expect("a v6 transaction has a witnessed transaction ID"), - )?, + Transaction::V6 { .. } | Transaction::V7 { .. } => { + Self::verify_v6_or_v7_transaction( + &req, + &network, + script_verifier, + cached_ffi_transaction.clone(), + wtx_id.expect("a v6 or v7 transaction has a witnessed transaction ID"), + )? + } }; if let Some(unmined_tx) = mempool_transaction { @@ -994,7 +994,8 @@ where NetworkUpgrade::Genesis | NetworkUpgrade::BeforeOverwinter | NetworkUpgrade::Overwinter - | NetworkUpgrade::Nu7 => Err(TransactionError::UnsupportedByNetworkUpgrade( + | NetworkUpgrade::Nu7 + | NetworkUpgrade::NuTachyon => Err(TransactionError::UnsupportedByNetworkUpgrade( transaction.version(), network_upgrade, )), @@ -1080,7 +1081,8 @@ where | NetworkUpgrade::Nu6_1 | NetworkUpgrade::Nu6_2 | NetworkUpgrade::Nu6_3 - | NetworkUpgrade::Nu7 => Ok(()), + | NetworkUpgrade::Nu7 + | NetworkUpgrade::NuTachyon => Ok(()), #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => Ok(()), @@ -1099,8 +1101,8 @@ where } } - /// Verify a V6 transaction. - fn verify_v6_transaction( + /// Verify a V6 or V7 transaction. + fn verify_v6_or_v7_transaction( request: &Request, network: &Network, script_verifier: script::Verifier, @@ -1110,7 +1112,15 @@ where let transaction = request.transaction(); let nu = request.upgrade(network); - Self::verify_v6_transaction_network_upgrade(&transaction, nu)?; + match transaction.as_ref() { + Transaction::V6 { .. } => { + Self::verify_v6_transaction_network_upgrade(&transaction, nu)? + } + Transaction::V7 { .. } => { + Self::verify_v7_transaction_network_upgrade(&transaction, nu)? + } + _ => unreachable!("V6/V7 verification is only called for V6 or V7 transactions"), + } let sapling_bundle = cached_ffi_transaction.sighasher().sapling_bundle(); let orchard_bundle = cached_ffi_transaction.sighasher().orchard_bundle(); @@ -1159,6 +1169,21 @@ where Ok(()) } + /// Verifies if a V7 `transaction` is supported by `network_upgrade`. + fn verify_v7_transaction_network_upgrade( + transaction: &Transaction, + network_upgrade: NetworkUpgrade, + ) -> Result<(), TransactionError> { + if network_upgrade < NetworkUpgrade::NuTachyon { + return Err(TransactionError::UnsupportedByNetworkUpgrade( + transaction.version(), + network_upgrade, + )); + } + + Ok(()) + } + /// Verifies if a transaction's transparent inputs are valid using the provided /// `script_verifier` and `cached_ffi_transaction`. /// @@ -1346,7 +1371,7 @@ where /// /// `wtx_id` identifies the transaction containing `bundle`; the cache adds /// the bundle's value pool to distinguish the Orchard and Ironwood slots in - /// a v6 transaction. + /// a v6 or v7 transaction. fn verify_orchard_bundle( bundle: Option<::orchard::bundle::Bundle<::orchard::bundle::Authorized, ZatBalance>>, sighash: &SigHash, diff --git a/crates/zakura-consensus/src/transaction/tests/prop.rs b/crates/zakura-consensus/src/transaction/tests/prop.rs index a2dc56bed9..3ec7dc80e5 100644 --- a/crates/zakura-consensus/src/transaction/tests/prop.rs +++ b/crates/zakura-consensus/src/transaction/tests/prop.rs @@ -331,6 +331,16 @@ fn mock_transparent_transaction( ironwood_shielded_data: None, network_upgrade, }, + 7 => Transaction::V7 { + inputs, + outputs, + lock_time, + expiry_height, + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + network_upgrade, + }, invalid_version => unreachable!("invalid transaction version: {}", invalid_version), }; @@ -360,6 +370,7 @@ fn sanitize_transaction_version( Nu5 | Nu6 | Nu6_1 | Nu6_2 => (4, 5), Nu6_3 => (4, 6), Nu7 => (5, 5), + NuTachyon => (5, 7), #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => (5, u8::MAX), @@ -372,12 +383,13 @@ fn sanitize_transaction_version( } #[test] -fn sanitize_transaction_version_handles_v6_at_nu6_3() { +fn sanitize_transaction_version_handles_v6_and_v7_upgrades() { let network = zakura_chain::parameters::testnet::Parameters::build() .with_activation_heights( zakura_chain::parameters::testnet::ConfiguredActivationHeights { nu6_3: Some(1), nu7: Some(2), + nu_tachyon: Some(3), ..Default::default() }, ) @@ -402,6 +414,14 @@ fn sanitize_transaction_version_handles_v6_at_nu6_3() { sanitize_transaction_version(&network, 6, block::Height(2)), (5, NetworkUpgrade::Nu7) ); + assert_eq!( + sanitize_transaction_version(&network, 6, block::Height(3)), + (6, NetworkUpgrade::NuTachyon) + ); + assert_eq!( + sanitize_transaction_version(&network, 7, block::Height(3)), + (7, NetworkUpgrade::NuTachyon) + ); } /// Create multiple mock transparent transfers. diff --git a/crates/zakura-network/src/constants.rs b/crates/zakura-network/src/constants.rs index e77a0a2697..3d81dfdb38 100644 --- a/crates/zakura-network/src/constants.rs +++ b/crates/zakura-network/src/constants.rs @@ -342,12 +342,9 @@ pub const MAX_ADDRS_IN_ADDRESS_BOOK: usize = /// messages from each of our peers. pub const TIMESTAMP_TRUNCATION_SECONDS: u32 = 30 * 60; -// TODO: The NU7 protocol version is provisional. Update this constant and the mapping in -// `Version::min_specified_for_upgrade` once NU7's deployment ZIP is published. -// Next upgrade values, uncomment on activation: -// const CURRENT_NETWORK_PROTOCOL_VERSION_VALUE: u32 = 170_170; // NU7 Testnet. -// const CURRENT_NETWORK_PROTOCOL_VERSION_VALUE: u32 = 170_180; // NU7 Mainnet. -const CURRENT_NETWORK_PROTOCOL_VERSION_VALUE: u32 = 170_160; // NU6.3 (Mainnet + Testnet). +// TODO: The NU7 and NuTachyon protocol versions are provisional. Update this constant and the +// mapping in `Version::min_specified_for_upgrade` when their deployment ZIPs are published. +const CURRENT_NETWORK_PROTOCOL_VERSION_VALUE: u32 = 170_190; // NuTachyon (Mainnet + Testnet). /// The Zcash network protocol version implemented by this crate, and advertised /// during connection setup. diff --git a/crates/zakura-network/src/protocol/external/types.rs b/crates/zakura-network/src/protocol/external/types.rs index c61b70704e..5077ce03b7 100644 --- a/crates/zakura-network/src/protocol/external/types.rs +++ b/crates/zakura-network/src/protocol/external/types.rs @@ -120,14 +120,18 @@ impl Version { 170_150 } (Mainnet, Nu6_2) => 170_150, - // TODO: these NU6.3 (Ironwood) and Nu7 protocol versions are provisional, bumped above - // Nu6_2's 170_150. Update them when the real values are specified. + // TODO: these NU6.3, NU7, and NuTachyon protocol versions are provisional, bumped + // above Nu6_2's 170_150. Update them when the real values are specified. (Testnet(params), Nu6_3) if params.is_default_testnet() || params.is_regtest() => { 170_160 } (Mainnet, Nu6_3) => 170_160, (Testnet(params), Nu7) if params.is_default_testnet() || params.is_regtest() => 170_170, (Mainnet, Nu7) => 170_180, + (Testnet(params), NuTachyon) if params.is_default_testnet() || params.is_regtest() => { + 170_190 + } + (Mainnet, NuTachyon) => 170_190, // It should be fine to reject peers with earlier network protocol versions on custom testnets for now. (Testnet(_), _) => CURRENT_NETWORK_PROTOCOL_VERSION.0, @@ -220,7 +224,10 @@ mod test { let highest_network_upgrade = NetworkUpgrade::current(network, block::Height::MAX); assert!( - matches!(highest_network_upgrade, Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7), + matches!( + highest_network_upgrade, + Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7 | NuTachyon + ), "expected coverage of all network upgrades: \ add the new network upgrade to the list in this test" ); @@ -238,6 +245,7 @@ mod test { Nu6_2, Nu6_3, Nu7, + NuTachyon, ] { let height = network_upgrade.activation_height(network); if let Some(height) = height { @@ -250,19 +258,22 @@ mod test { } #[test] - fn nu63_protocol_versions_match_current_version() { + fn nu_tachyon_protocol_versions_match_current_version() { let _init_guard = zakura_test::init(); assert_eq!( - Version::min_specified_for_upgrade(&Mainnet, Nu6_3), + Version::min_specified_for_upgrade(&Mainnet, NuTachyon), CURRENT_NETWORK_PROTOCOL_VERSION ); assert_eq!( - Version::min_specified_for_upgrade(&Network::new_default_testnet(), Nu6_3), + Version::min_specified_for_upgrade(&Network::new_default_testnet(), NuTachyon), CURRENT_NETWORK_PROTOCOL_VERSION ); assert_eq!( - Version::min_specified_for_upgrade(&Network::new_regtest(Default::default()), Nu6_3), + Version::min_specified_for_upgrade( + &Network::new_regtest(Default::default()), + NuTachyon + ), CURRENT_NETWORK_PROTOCOL_VERSION ); } diff --git a/crates/zakura-network/src/zakura/block_sync/tests.rs b/crates/zakura-network/src/zakura/block_sync/tests.rs index ee083034c8..f2b9830de0 100644 --- a/crates/zakura-network/src/zakura/block_sync/tests.rs +++ b/crates/zakura-network/src/zakura/block_sync/tests.rs @@ -107,7 +107,7 @@ fn fake_block_at_height(template: &Arc, height: block::Height) -> | Transaction::V3 { inputs, .. } | Transaction::V4 { inputs, .. } | Transaction::V5 { inputs, .. } => &mut inputs[0], - Transaction::V6 { inputs, .. } => &mut inputs[0], + Transaction::V6 { inputs, .. } | Transaction::V7 { inputs, .. } => &mut inputs[0], }; match input { transparent::Input::Coinbase { diff --git a/crates/zakura-network/src/zakura/testkit/cluster.rs b/crates/zakura-network/src/zakura/testkit/cluster.rs index 4c4a02b63b..ebb37d1b8d 100644 --- a/crates/zakura-network/src/zakura/testkit/cluster.rs +++ b/crates/zakura-network/src/zakura/testkit/cluster.rs @@ -925,6 +925,7 @@ mod tests { nu6_2: None, nu6_3: None, nu7: None, + nu_tachyon: None, #[cfg(zcash_unstable = "zfuture")] zfuture: None, }) diff --git a/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs b/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs index 1adc319c8f..5bf61b7709 100644 --- a/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs +++ b/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs @@ -738,7 +738,7 @@ fn synthetic_block_at_height( | Transaction::V3 { inputs, .. } | Transaction::V4 { inputs, .. } | Transaction::V5 { inputs, .. } => &mut inputs[0], - Transaction::V6 { inputs, .. } => &mut inputs[0], + Transaction::V6 { inputs, .. } | Transaction::V7 { inputs, .. } => &mut inputs[0], }; match input { transparent::Input::Coinbase { diff --git a/crates/zakura-state/src/service/finalized_state/tests/prop.rs b/crates/zakura-state/src/service/finalized_state/tests/prop.rs index 672212b220..4d4aadb418 100644 --- a/crates/zakura-state/src/service/finalized_state/tests/prop.rs +++ b/crates/zakura-state/src/service/finalized_state/tests/prop.rs @@ -315,6 +315,7 @@ fn vct_generated_final_frontier_bytes_are_node_loader_compatible() -> Result<()> nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -430,6 +431,7 @@ fn all_upgrades_and_wrong_commitments_with_fake_activation_heights() -> Result<( nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -551,6 +553,7 @@ fn vct_fast_path_matches_legacy_and_rejects_wrong_roots() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -773,6 +776,7 @@ fn vct_frozen_frontier_hole_refuses_instead_of_recomputing() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -878,6 +882,7 @@ fn vct_retryable_root_miss_keeps_checkpoint_response_pending() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -979,6 +984,7 @@ fn vct_untrusted_source_defers_unverifiable_tip_root_until_successor() -> Result nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -1224,6 +1230,7 @@ fn vct_untrusted_source_bad_root_replacement_commits_same_height() -> Result<()> nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -1377,6 +1384,7 @@ fn vct_frozen_frontier_survives_reopen() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -1554,6 +1562,7 @@ fn vct_fast_sync_handoff_marks_database_and_resumes() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -2089,6 +2098,7 @@ fn vct_mode_switches_continue_from_safe_boundaries() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -2294,6 +2304,7 @@ fn vct_dedup_skips_redundant_check_and_guards_stale_cache() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -2566,6 +2577,7 @@ fn vct_clear_prevalidation_cache_disarms_skip_then_dedup_resumes() -> Result<()> nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -2674,6 +2686,7 @@ fn vct_db_produced_payload_round_trips_to_byte_identical_state() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -3071,6 +3084,7 @@ fn vct_untrusted_fixture_drives_byte_identical_state() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() @@ -3245,6 +3259,7 @@ fn vct_read_service_serves_or_refuses_absent_band_treestates() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("failed to set activation heights") .extend_funding_streams() diff --git a/crates/zakura-state/src/service/finalized_state/tests/rollback.rs b/crates/zakura-state/src/service/finalized_state/tests/rollback.rs index 61e39ae285..c15916126b 100644 --- a/crates/zakura-state/src/service/finalized_state/tests/rollback.rs +++ b/crates/zakura-state/src/service/finalized_state/tests/rollback.rs @@ -870,6 +870,7 @@ fn modern_rollback_network() -> Network { nu6_2: Some(10), nu6_3: Some(11), nu7: Some(12), + nu_tachyon: None, }) .expect("configured activation heights are valid") .extend_funding_streams() diff --git a/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs b/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs index d038c853c9..f67a945995 100644 --- a/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs +++ b/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs @@ -125,6 +125,7 @@ impl VctFastSyncedChain { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + nu_tachyon: None, }) .expect("the fixture activation heights are ordered") .extend_funding_streams() diff --git a/crates/zakura-state/src/service/non_finalized_state/chain.rs b/crates/zakura-state/src/service/non_finalized_state/chain.rs index 71c2722b1c..1021251f36 100644 --- a/crates/zakura-state/src/service/non_finalized_state/chain.rs +++ b/crates/zakura-state/src/service/non_finalized_state/chain.rs @@ -1854,6 +1854,14 @@ impl Chain { orchard_shielded_data, ironwood_shielded_data, .. + } + | V7 { + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + ironwood_shielded_data, + .. } => ( inputs, outputs, @@ -2063,6 +2071,14 @@ impl UpdateWith for Chain { orchard_shielded_data, ironwood_shielded_data, .. + } + | V7 { + inputs, + outputs, + sapling_shielded_data, + orchard_shielded_data, + ironwood_shielded_data, + .. } => ( inputs, outputs, diff --git a/crates/zakura-state/src/service/write/tests/full_state_coherence.rs b/crates/zakura-state/src/service/write/tests/full_state_coherence.rs index f6775a0759..3403fff0b1 100644 --- a/crates/zakura-state/src/service/write/tests/full_state_coherence.rs +++ b/crates/zakura-state/src/service/write/tests/full_state_coherence.rs @@ -176,6 +176,7 @@ fn generated_nu5_graph_matches_full_state_before_finalization() { nu6_2: Some(120), nu6_3: Some(130), nu7: Some(140), + nu_tachyon: None, }) .expect("the compressed activation schedule is ordered") .with_disable_pow(true) diff --git a/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs b/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs index 0ff4ed55ec..d5d4e2512a 100644 --- a/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs +++ b/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs @@ -108,6 +108,7 @@ fn parameters(checkpoint_blocks: Option<&[Arc]>) -> Network { nu6_2: Some(NU6_2), nu6_3: Some(NU6_3), nu7: Some(NU7), + nu_tachyon: None, }) .expect("the compressed activation schedule is ordered") .with_disable_pow(true) @@ -192,7 +193,8 @@ fn generate_chain(network: &Network) -> Vec> { | NetworkUpgrade::Nu6_1 | NetworkUpgrade::Nu6_2 | NetworkUpgrade::Nu6_3 - | NetworkUpgrade::Nu7 => Transaction::V5 { + | NetworkUpgrade::Nu7 + | NetworkUpgrade::NuTachyon => Transaction::V5 { network_upgrade: upgrade, lock_time: LockTime::unlocked(), expiry_height: height, diff --git a/crates/zakura-state/src/tests.rs b/crates/zakura-state/src/tests.rs index 4be00ad1f2..c11b963d06 100644 --- a/crates/zakura-state/src/tests.rs +++ b/crates/zakura-state/src/tests.rs @@ -36,7 +36,7 @@ impl FakeChainHelper for Arc { Transaction::V3 { inputs, .. } => &mut inputs[0], Transaction::V4 { inputs, .. } => &mut inputs[0], Transaction::V5 { inputs, .. } => &mut inputs[0], - Transaction::V6 { inputs, .. } => &mut inputs[0], + Transaction::V6 { inputs, .. } | Transaction::V7 { inputs, .. } => &mut inputs[0], }; match input { diff --git a/crates/zakurad/src/components/mempool/storage/tests/prop.rs b/crates/zakurad/src/components/mempool/storage/tests/prop.rs index d1423b1762..d88864324c 100644 --- a/crates/zakurad/src/components/mempool/storage/tests/prop.rs +++ b/crates/zakurad/src/components/mempool/storage/tests/prop.rs @@ -660,7 +660,7 @@ impl SpendConflictTestInput { // No JoinSplits Transaction::V1 { .. } | Transaction::V5 { .. } => {} - Transaction::V6 { .. } => {} + Transaction::V6 { .. } | Transaction::V7 { .. } => {} } } } @@ -734,6 +734,10 @@ impl SpendConflictTestInput { Transaction::V6 { sapling_shielded_data, .. + } + | Transaction::V7 { + sapling_shielded_data, + .. } => { Self::remove_sapling_transfers_with_conflicts(sapling_shielded_data, &conflicts) } @@ -812,6 +816,10 @@ impl SpendConflictTestInput { Transaction::V6 { orchard_shielded_data, .. + } + | Transaction::V7 { + orchard_shielded_data, + .. } => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts), // No Spends diff --git a/docs/changelog/unreleased/795.md b/docs/changelog/unreleased/795.md new file mode 100644 index 0000000000..0e62bc953b --- /dev/null +++ b/docs/changelog/unreleased/795.md @@ -0,0 +1,4 @@ +## Added + +- Added preliminary NuTachyon network-upgrade and V7 transaction support + ([#795](https://github.com/zakura-core/zakura/pull/795)). From 6c1601a5727bceb56a4c438b3cdfe25d0ba28e64 Mon Sep 17 00:00:00 2001 From: c-node Date: Fri, 28 Aug 2026 19:35:02 -0400 Subject: [PATCH 02/15] build: adopt upstream NuTachyon support --- .cargo/config.toml | 6 + .github/workflows/semver-checks.yml | 2 +- .github/workflows/test-crates.yml | 4 +- Cargo.lock | 1207 ++++++++++------- Cargo.toml | 8 +- .../src/primitives/zcash_history.rs | 2 +- .../primitives/zcash_history/tests/vectors.rs | 2 +- crates/zakura-state/src/constants.rs | 2 +- .../finalized_state/disk_format/chain.rs | 70 +- .../disk_format/chain/tests.rs | 50 +- .../disk_format/header_chain.rs | 2 +- .../finalized_state/disk_format/upgrade.rs | 9 +- crates/zakura-utils/coverage | 2 +- 13 files changed, 837 insertions(+), 529 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 65635d8134..4781cd1929 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,6 +4,12 @@ xtask = "run --package xtask --" [build] +# An explicit RUSTFLAGS environment variable replaces these flags, so callers that set it must +# also enable the NuTachyon cfg. +rustflags = [ + '--cfg', + 'zcash_unstable="nutachyon"', +] rustdocflags = [ # Keep shared rustdoc lint levels synchronized with the book and lint # workflows. diff --git a/.github/workflows/semver-checks.yml b/.github/workflows/semver-checks.yml index 939bcc60ce..e49160316a 100644 --- a/.github/workflows/semver-checks.yml +++ b/.github/workflows/semver-checks.yml @@ -261,7 +261,7 @@ jobs: run: | # cargo-public-api builds both the current and published crate with # nightly. Nightly-only warnings are evidence, not the lint gate. - RUSTFLAGS='' cargo public-api diff latest -p "$PACKAGE" -sss + RUSTFLAGS='--cfg zcash_unstable="nutachyon"' cargo public-api diff latest -p "$PACKAGE" -sss warm-baselines: name: Warm semver baselines diff --git a/.github/workflows/test-crates.yml b/.github/workflows/test-crates.yml index d7556a3b32..8cc9dff3c0 100644 --- a/.github/workflows/test-crates.yml +++ b/.github/workflows/test-crates.yml @@ -101,8 +101,8 @@ jobs: cargo hack check --workspace cargo hack check --workspace --no-default-features env: - # Treat warnings as errors, preserving the old `features` job behavior. - RUSTFLAGS: -D warnings + # Treat warnings as errors while preserving the workspace NuTachyon cfg. + RUSTFLAGS: '--cfg zcash_unstable="nutachyon" -D warnings' - name: Check publishable crate packaging run: ./scripts/check-crate-packaging.sh diff --git a/Cargo.lock b/Cargo.lock index d902b6aaa0..09b6b6ba56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -119,9 +119,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arc-swap" @@ -197,12 +197,6 @@ dependencies = [ "rustversion", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - [[package]] name = "arrayvec" version = "0.7.8" @@ -224,13 +218,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -279,9 +273,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", "zeroize", @@ -289,9 +283,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", @@ -363,7 +357,7 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.8.9", "object", "rustc-demangle", "windows-link 0.2.1", @@ -383,15 +377,15 @@ checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64" -version = "0.22.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" [[package]] name = "base64ct" @@ -420,7 +414,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags", + "bitflags 2.13.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -429,7 +423,7 @@ dependencies = [ "regex", "rustc-hash", "shlex 1.3.0", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -465,9 +459,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.13.0" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" dependencies = [ "serde_core", ] @@ -478,7 +478,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b64e60c28b6d25ad92e8b367801ff9aa12b41d05fc8798055d296bace4a60cc" dependencies = [ - "bitflags", + "bitflags 2.13.1", "serde", ] @@ -496,38 +496,35 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" +checksum = "3560a7b1951efe814fcd721938313adc56753ca39f4b23847d7e9a2402f5dbff" dependencies = [ - "arrayref", "arrayvec", "constant_time_eq", ] [[package]] name = "blake2s_simd" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee29928bad1e3f94c9d1528da29e07a1d3d04817ae8332de1e8b846c8439f4b3" +checksum = "2380c0236432f7b22a70229df30f218f5293870c056beb76f5ca068e3273a366" dependencies = [ - "arrayref", "arrayvec", "constant_time_eq", ] [[package]] name = "blake3" -version = "1.8.5" +version = "1.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" dependencies = [ - "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", ] [[package]] @@ -561,7 +558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09dc0086e469182132244e9b8d313a0742e1132da43a08c24b9dd3c18e0faf3a" dependencies = [ "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -594,9 +591,9 @@ checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] name = "byteorder" @@ -643,9 +640,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.66" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -659,7 +656,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -670,9 +667,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -692,7 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "rand_core 0.10.1", ] @@ -761,9 +758,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ "glob", "libc", @@ -772,9 +769,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -782,9 +779,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -794,14 +791,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -825,7 +822,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -875,9 +872,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" dependencies = [ "bytes", "memchr", @@ -891,7 +888,7 @@ checksum = "b85f248a4de22d204ceabc6299d89d2c70fbd7f09fea53c06c852369652d8139" dependencies = [ "pathdiff", "serde_core", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "winnow", ] @@ -1018,9 +1015,9 @@ dependencies = [ [[package]] name = "cordyceps" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +checksum = "5b9ab7e0ca1d179628fa0172b2b97203c7fa0cd81be2448bd446fb9559ca9261" dependencies = [ "loom", "tracing", @@ -1069,9 +1066,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" dependencies = [ "libc", ] @@ -1093,9 +1090,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] @@ -1260,7 +1257,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1283,7 +1280,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1294,14 +1291,14 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "debugid" @@ -1313,6 +1310,37 @@ dependencies = [ "uuid", ] +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.20", +] + [[package]] name = "der" version = "0.7.10" @@ -1333,7 +1361,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1353,7 +1381,7 @@ checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1393,7 +1421,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "unicode-xid", ] @@ -1407,7 +1435,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.118", + "syn 2.0.119", "unicode-xid", ] @@ -1462,13 +1490,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -1499,7 +1527,7 @@ checksum = "ed6b3e31251e87acd1b74911aed84071c8364fc9087972748ade2f1094ccce34" dependencies = [ "documented-macros", "phf", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1514,7 +1542,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1574,9 +1602,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "embedded-io" @@ -1605,7 +1633,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1625,7 +1653,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1658,10 +1686,11 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.12" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +checksum = "c08309dbcc659c5549a24ddb9b27027640641b282ef5768267c7e675558986a3" dependencies = [ + "autocfg", "indenter", "once_cell", ] @@ -1669,7 +1698,16 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.1" -source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d42773cb15447644d170be20231a3268600e0c4cea8987d013b93ac973d3cf7" +dependencies = [ + "blake2b_simd", +] + +[[package]] +name = "f4jumble" +version = "0.1.1" +source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" dependencies = [ "blake2b_simd", ] @@ -1682,9 +1720,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "ff" @@ -1710,7 +1748,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1731,9 +1769,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "fixed-hash" @@ -1742,7 +1780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.6", + "rand 0.8.8", "rustc-hex", "static_assertions", ] @@ -1755,12 +1793,13 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.9.1", + "zlib-rs", ] [[package]] @@ -1833,7 +1872,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "serdect", - "thiserror 2.0.18", + "thiserror 2.0.20", "visibility", "zeroize", "zeroize_derive", @@ -1875,9 +1914,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -1903,9 +1942,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -1913,15 +1952,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -1930,9 +1969,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-lite" @@ -1949,32 +1988,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -2062,7 +2101,7 @@ checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2073,9 +2112,9 @@ checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "gloo-timers" @@ -2103,9 +2142,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.16" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -2113,7 +2152,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.14.0", + "indexmap 2.14.1", "slab", "tokio", "tokio-util", @@ -2176,14 +2215,14 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hdrhistogram" -version = "7.5.4" +version = "7.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +checksum = "f49d1053f4708f0af3cf9fc5bffc7e68a914a3c45becb231c80068c9c3f78bea" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "byteorder", "flate2", - "nom", + "nom 8.0.0", "num-traits", ] @@ -2209,9 +2248,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" [[package]] name = "hex" @@ -2244,9 +2283,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.4", + "rand 0.9.5", "ring", - "thiserror 2.0.18", + "thiserror 2.0.20", "tinyvec", "tokio", "tracing", @@ -2266,10 +2305,10 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.4", + "rand 0.9.5", "resolv-conf", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tracing", ] @@ -2327,9 +2366,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -2337,9 +2376,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -2347,9 +2386,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -2397,9 +2436,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" dependencies = [ "atomic-waker", "bytes", @@ -2430,7 +2469,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] @@ -2463,7 +2502,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.6.5", "tokio", "tower-service", "tracing", @@ -2495,9 +2534,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -2509,9 +2548,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -2522,9 +2561,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -2536,16 +2575,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -2556,15 +2596,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -2617,7 +2657,7 @@ dependencies = [ "hyper", "hyper-util", "log", - "rand 0.9.4", + "rand 0.9.5", "tokio", "url", "xmltree", @@ -2640,7 +2680,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2671,9 +2711,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -2696,9 +2736,9 @@ dependencies = [ [[package]] name = "inferno" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c05b9ae050366b3363927f59d2c07f922a746e97e2e96f70d479a3c7dbbbe5" +checksum = "0c460d4fa06223667240720ab69a8045133755ae6dfbe100cf481b95e3a014f1" dependencies = [ "ahash", "itoa", @@ -2753,7 +2793,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.6.4", + "socket2 0.6.5", "widestring", "windows-registry", "windows-result 0.4.1", @@ -2762,9 +2802,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "iroh" @@ -2803,7 +2843,7 @@ dependencies = [ "pin-project", "pkarr", "portmapper", - "rand 0.8.6", + "rand 0.8.8", "reqwest 0.12.28", "ring", "rustls", @@ -2868,7 +2908,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2885,7 +2925,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.5.10", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tracing", "web-time", @@ -2899,13 +2939,13 @@ checksum = "929d5d8fa77d5c304d3ee7cae9aede31f13908bd049f9de8c7c0094ad6f7c535" dependencies = [ "bytes", "getrandom 0.2.17", - "rand 0.8.6", + "rand 0.8.8", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.18", + "thiserror 2.0.20", "tinyvec", "tracing", "web-time", @@ -2954,7 +2994,7 @@ dependencies = [ "pin-project", "pkarr", "postcard", - "rand 0.8.6", + "rand 0.8.8", "reqwest 0.12.28", "rustls", "rustls-pki-types", @@ -3025,6 +3065,59 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link 0.2.1", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.22.4" @@ -3037,7 +3130,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror 2.0.18", + "thiserror 2.0.20", "walkdir", "windows-link 0.2.1", ] @@ -3052,7 +3145,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3071,7 +3164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3086,9 +3179,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -3121,7 +3214,7 @@ dependencies = [ "http-body-util", "jsonrpsee-types", "parking_lot", - "rand 0.8.6", + "rand 0.8.8", "rustc-hash", "serde", "serde_json", @@ -3140,7 +3233,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3205,9 +3298,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libloading" @@ -3227,9 +3320,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "d7955dfc218a8afb29dfeffd540e3a6e96baeb94fe7138228dd7cc6937fbbf96" dependencies = [ "libc", ] @@ -3267,7 +3360,7 @@ checksum = "3f8ce05b56f3cbc65ec7d0908adb308ed91281e022f61c8c3a0c9388b5380b17" dependencies = [ "bindgen", "cc", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", "zcash_script", ] @@ -3280,9 +3373,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "litrs" @@ -3301,9 +3394,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "loom" @@ -3406,7 +3499,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "indexmap 2.14.0", + "indexmap 2.14.1", "ipnet", "metrics", "metrics-util", @@ -3427,7 +3520,7 @@ dependencies = [ "hashbrown 0.15.5", "metrics", "quanta", - "rand 0.9.4", + "rand 0.9.5", "rand_xoshiro", "sketches-ddsketch", ] @@ -3449,6 +3542,15 @@ name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" dependencies = [ "adler2", "simd-adler32", @@ -3456,9 +3558,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -3467,9 +3569,9 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.15" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" +checksum = "4293f18e7567a1caf3c584855554377025c65e0aa445344d04171f5ad63d19b9" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -3551,7 +3653,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3606,7 +3708,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0e7987b28514adf555dc1f9a5c30dfc3e50750bbaffb1aec41ca7b23dcd8e4" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.13.1", "byteorder", "libc", "log", @@ -3621,7 +3723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56d83370a96813d7c977f8b63054f1162df6e5784f1c598d689236564fb5a6f2" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.13.1", "byteorder", "libc", "log", @@ -3652,7 +3754,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -3692,7 +3794,7 @@ dependencies = [ "pin-project-lite", "serde", "snafu", - "socket2 0.6.4", + "socket2 0.6.5", "time", "tokio", "tokio-util", @@ -3709,7 +3811,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags", + "bitflags 2.13.1", "cfg-if", "cfg_aliases", "libc", @@ -3731,6 +3833,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "nonempty" version = "0.11.0" @@ -3789,9 +3900,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -3824,7 +3935,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3879,10 +3990,10 @@ dependencies = [ "documented", "jsonrpsee", "quote", - "schemars 1.2.1", + "schemars 1.2.2", "serde", "serde_json", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3901,7 +4012,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -3930,7 +4041,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "reqwest 0.13.4", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -3956,8 +4067,8 @@ dependencies = [ "opentelemetry", "percent-encoding", "portable-atomic", - "rand 0.9.4", - "thiserror 2.0.18", + "rand 0.9.5", + "thiserror 2.0.20", "tokio", "tokio-stream", ] @@ -3970,7 +4081,7 @@ checksum = "969ccca8ffc4fb105bd131a228107d5c9dd89d9d627edf3295cbe979156f9712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3981,9 +4092,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "owo-colors" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" +checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8" [[package]] name = "parity-scale-codec" @@ -4010,7 +4121,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4071,9 +4182,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.7" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" +checksum = "5a07a60cc7a4d00c91f95c685609d1d2f79050e6804b70ebedd7650f0b839bcf" dependencies = [ "memchr", "ucd-trie", @@ -4081,9 +4192,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.7" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4254325ecad416ab689e27ba51da03ba01a9632bc6e108f5fe7c3c4ad29d58" +checksum = "b3a83744a5c8455b8b3e0dc5031362780a347c878bdd11584d1a8984228cc88d" dependencies = [ "pest", "pest_generator", @@ -4091,22 +4202,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.7" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c4c0e91ead7a8f7acecbca6f003fc2e8282b1dbe2dd9c9d2f16aba42995e0a7" +checksum = "e0cd3451aa3de60d4b9a1e736885e4dea6b31617598026f12256ad566d63304a" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "pest_meta" -version = "2.8.7" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9744bc48116fee06334924bb5f2bad41eed5e89bd26e29b0b799f9a3f82c210" +checksum = "e04d3a0849e241d7dfce834c83b1c5edc8622009e8dd51a12ba1927c32f05496" dependencies = [ "pest", ] @@ -4119,7 +4230,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.14.0", + "indexmap 2.14.1", ] [[package]] @@ -4163,7 +4274,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4192,7 +4303,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4225,7 +4336,7 @@ dependencies = [ "serde", "sha1_smol", "simple-dns", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tracing", "url", @@ -4244,9 +4355,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "plotters" @@ -4294,7 +4405,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4331,9 +4442,18 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] [[package]] name = "portmapper" @@ -4353,11 +4473,11 @@ dependencies = [ "nested_enum_utils", "netwatch", "num_enum", - "rand 0.9.4", + "rand 0.9.5", "serde", "smallvec", "snafu", - "socket2 0.6.4", + "socket2 0.6.5", "time", "tokio", "tokio-util", @@ -4388,14 +4508,14 @@ checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -4456,7 +4576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4481,9 +4601,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -4496,9 +4616,9 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags", + "bitflags 2.13.1", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -4515,7 +4635,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4545,7 +4665,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn 2.0.118", + "syn 2.0.119", "tempfile", ] @@ -4559,7 +4679,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4577,16 +4697,16 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ - "bitflags", + "bitflags 2.13.1", "memchr", "unicase", ] [[package]] name = "pulldown-cmark-to-cmark" -version = "22.0.0" +version = "22.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50793def1b900256624a709439404384204a5dc3a6ec580281bfaac35e882e90" +checksum = "ab1ad36992cead65f02aa399a373a42730922f1525d988172634fdefdecb8a60" dependencies = [ "pulldown-cmark", ] @@ -4634,8 +4754,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.4", - "thiserror 2.0.18", + "socket2 0.6.5", + "thiserror 2.0.20", "tokio", "tracing", "web-time", @@ -4643,9 +4763,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83" dependencies = [ "aws-lc-rs", "bytes", @@ -4658,7 +4778,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.18", + "thiserror 2.0.20", "tinyvec", "tracing", "web-time", @@ -4673,16 +4793,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.6.5", "tracing", "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -4717,9 +4837,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4728,9 +4848,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4843,7 +4963,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags", + "bitflags 2.13.1", ] [[package]] @@ -4872,7 +4992,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.13.1", ] [[package]] @@ -4883,34 +5003,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] name = "regex" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -4920,9 +5040,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.15" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -4980,7 +5100,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] @@ -5095,7 +5215,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3" dependencies = [ - "bitflags", + "bitflags 2.13.1", "once_cell", "serde", "serde_derive", @@ -5142,7 +5262,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys", @@ -5151,9 +5271,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "log", @@ -5179,9 +5299,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "web-time", "zeroize", @@ -5216,9 +5336,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -5291,9 +5411,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", "ref-cast", @@ -5304,14 +5424,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -5361,7 +5481,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5380,9 +5500,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -5434,7 +5554,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ac170a5bba8bec6e3339c90432569d89641fa7a3d3e4f44987d24f0762e6adf" dependencies = [ - "rand 0.9.4", + "rand 0.9.5", "sentry-types", "serde", "serde_json", @@ -5447,7 +5567,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "235865e639f1d72414fa5d35374e105c292e2ee3a2f90919d961bbb486626ee6" dependencies = [ - "bitflags", + "bitflags 2.13.1", "log", "sentry-core", ] @@ -5458,7 +5578,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27701acc51e68db5281802b709010395bfcbcb128b1d0a4e5873680d3b47ff0c" dependencies = [ - "bitflags", + "bitflags 2.13.1", "sentry-backtrace", "sentry-core", "tracing-core", @@ -5473,10 +5593,10 @@ checksum = "56780cb5597d676bf22e6c11d1f062eb4def46390ea3bfb047bcbcf7dfd19bdb" dependencies = [ "debugid", "hex", - "rand 0.9.4", + "rand 0.9.5", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", "time", "url", "uuid", @@ -5484,9 +5604,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -5513,42 +5633,42 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] name = "serde_derive_internals" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ - "indexmap 2.14.0", + "indexmap 2.14.1", "itoa", "memchr", "serde", @@ -5579,18 +5699,19 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" dependencies = [ "base64 0.22.1", "bs58", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.14.0", + "indexmap 2.14.1", + "jiff", "schemars 0.9.0", - "schemars 1.2.1", + "schemars 1.2.2", "serde_core", "serde_json", "serde_with_macros", @@ -5599,14 +5720,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5700,15 +5821,15 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_cesu8" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ "rustc_version", "simdutf8", @@ -5732,7 +5853,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags", + "bitflags 2.13.1", ] [[package]] @@ -5778,7 +5899,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5793,9 +5914,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -5813,7 +5934,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.6", + "rand 0.8.8", "sha1", ] @@ -5878,9 +5999,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "str_stack" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" +checksum = "7f446288b699d66d0fd2e30d1cfe7869194312524b3b9252594868ed26ef056a" [[package]] name = "strsim" @@ -5906,7 +6027,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5930,7 +6051,7 @@ dependencies = [ "precis-core", "precis-profiles", "quoted-string-parser", - "rand 0.9.4", + "rand 0.9.5", ] [[package]] @@ -5948,8 +6069,8 @@ dependencies = [ "hex", "parking_lot", "pnet_packet", - "rand 0.9.4", - "socket2 0.6.4", + "rand 0.9.5", + "socket2 0.6.5", "thiserror 1.0.69", "tokio", "tracing", @@ -5974,9 +6095,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -6012,7 +6144,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -6021,7 +6153,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags", + "bitflags 2.13.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6092,11 +6224,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -6107,18 +6239,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -6127,7 +6259,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe075d7053dae61ac5413a34ea7d4913b6e6207844fd726bdd858b37ff72bf5" dependencies = [ - "bitflags", + "bitflags 2.13.1", "cfg-if", "libc", "log", @@ -6146,9 +6278,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "js-sys", @@ -6167,9 +6299,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -6177,9 +6309,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -6212,9 +6344,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -6222,7 +6354,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.4", + "socket2 0.6.5", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -6230,13 +6362,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -6251,9 +6383,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -6274,15 +6406,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", + "libc", "pin-project-lite", "tokio", ] @@ -6300,7 +6433,7 @@ dependencies = [ "getrandom 0.3.4", "http", "httparse", - "rand 0.9.4", + "rand 0.9.5", "ring", "rustls-pki-types", "simdutf8", @@ -6320,11 +6453,11 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ - "indexmap 2.14.0", + "indexmap 2.14.1", "serde_core", "serde_spanned", "toml_datetime", @@ -6344,11 +6477,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ - "indexmap 2.14.0", + "indexmap 2.14.1", "toml_datetime", "toml_parser", "winnow", @@ -6356,18 +6489,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tonic" @@ -6388,7 +6521,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.4", + "socket2 0.6.5", "sync_wrapper", "tokio", "tokio-rustls", @@ -6408,7 +6541,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -6433,7 +6566,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.118", + "syn 2.0.119", "tempfile", "tonic-build", ] @@ -6478,7 +6611,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.14.0", + "indexmap 2.14.1", "pin-project-lite", "slab", "sync_wrapper", @@ -6495,7 +6628,7 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags", + "bitflags 2.13.1", "bytes", "futures-util", "http", @@ -6553,7 +6686,7 @@ checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", "symlink", - "thiserror 2.0.18", + "thiserror 2.0.20", "time", "tracing-subscriber", ] @@ -6566,7 +6699,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -6695,7 +6828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -6745,9 +6878,9 @@ dependencies = [ [[package]] name = "uint" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +checksum = "6f9227a75a5a540a464c832ad4a4195dbdbecd8787610a56262721fde6f04f90" dependencies = [ "byteorder", "crunchy", @@ -6818,27 +6951,27 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "log", "percent-encoding", "rustls", "rustls-pki-types", "ureq-proto", "utf8-zero", - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] name = "ureq-proto" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "http", "httparse", "log", @@ -6877,9 +7010,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.4" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -6913,7 +7046,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -7011,9 +7144,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -7024,9 +7157,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -7034,9 +7167,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7044,22 +7177,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] @@ -7079,9 +7212,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -7099,9 +7232,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" dependencies = [ "rustls-pki-types", ] @@ -7112,14 +7245,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -7260,7 +7393,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -7271,7 +7404,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -7464,9 +7597,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -7487,16 +7620,16 @@ dependencies = [ "futures", "log", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", "windows 0.62.2", "windows-core 0.62.2", ] [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "ws_stream_wasm" @@ -7511,7 +7644,7 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper", - "thiserror 2.0.18", + "thiserror 2.0.20", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -7550,9 +7683,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" [[package]] name = "xmltree" @@ -7571,7 +7704,7 @@ dependencies = [ "serde", "sha2 0.10.9", "tempfile", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "tonic-prost-build", ] @@ -7594,7 +7727,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure 0.13.2", ] @@ -7626,7 +7759,7 @@ dependencies = [ "humantime-serde", "hyper", "hyper-util", - "indexmap 2.14.0", + "indexmap 2.14.1", "indicatif", "inferno", "insta", @@ -7644,7 +7777,7 @@ dependencies = [ "proptest", "proptest-derive", "prost", - "rand 0.8.6", + "rand 0.8.8", "rayon", "regex", "reqwest 0.12.28", @@ -7655,12 +7788,12 @@ dependencies = [ "sha2 0.10.9", "tar", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "thread-priority", "tokio", "tokio-stream", "tokio-util", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "tonic", "tonic-prost", "tonic-prost-build", @@ -7728,7 +7861,7 @@ name = "zakura-chain" version = "6.0.0" dependencies = [ "bech32", - "bitflags", + "bitflags 2.13.1", "bitflags-serde-legacy", "bitvec", "blake2b_simd", @@ -7754,13 +7887,13 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.10.2", - "rand 0.8.6", + "rand 0.8.8", "rand_chacha 0.10.0", "rand_core 0.10.1", "rand_core 0.6.4", "rayon", "ripemd 0.1.3", - "schemars 1.2.1", + "schemars 1.2.2", "secp256k1", "serde", "serde-big-array", @@ -7770,10 +7903,10 @@ dependencies = [ "spandoc", "strum", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tracing", - "uint 0.10.0", + "uint 0.10.1", "x25519-dalek", "zakura-halo2-proofs", "zakura-jubjub", @@ -7784,12 +7917,12 @@ dependencies = [ "zakura-sapling-crypto", "zakura-sinsemilla", "zakura-test", - "zcash_address", + "zcash_address 0.13.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", "zcash_history", "zcash_note_encryption", - "zcash_protocol", + "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", "zcash_script", - "zcash_transparent", + "zcash_transparent 0.10.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", ] [[package]] @@ -7812,13 +7945,13 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.10.2", - "rand 0.8.6", + "rand 0.8.8", "rayon", "serde", "spandoc", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "tower 0.4.13", "tracing", "zakura-bellman", @@ -7836,9 +7969,9 @@ dependencies = [ "zakura-test", "zakura-tower-batch-control", "zakura-tower-fallback", - "zcash_protocol", + "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", "zcash_script", - "zcash_transparent", + "zcash_transparent 0.10.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", ] [[package]] @@ -7901,8 +8034,8 @@ dependencies = [ "proptest", "rayon", "sha2 0.10.9", - "thiserror 2.0.18", - "toml 1.1.2+spec-1.1.0", + "thiserror 2.0.20", + "toml 1.1.4+spec-1.1.0", "zakura-chain", "zakura-test", ] @@ -7951,10 +8084,10 @@ dependencies = [ "subtle", "zakura-orchard", "zakura-sapling-crypto", - "zcash_address", - "zcash_encoding", - "zcash_protocol", - "zcash_transparent", + "zcash_address 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_encoding 0.4.0", + "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_transparent 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "zip32", ] @@ -7962,7 +8095,7 @@ dependencies = [ name = "zakura-network" version = "7.0.0" dependencies = [ - "bitflags", + "bitflags 2.13.1", "blake2b_simd", "byteorder", "bytes", @@ -7973,7 +8106,7 @@ dependencies = [ "hex", "howudoin", "humantime-serde", - "indexmap 2.14.0", + "indexmap 2.14.1", "iroh", "itertools 0.14.0", "lazy_static", @@ -7981,18 +8114,18 @@ dependencies = [ "pin-project", "proptest", "proptest-derive", - "rand 0.8.6", + "rand 0.8.8", "rayon", "regex", - "schemars 1.2.1", + "schemars 1.2.2", "serde", "serde_json", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tokio-stream", "tokio-util", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "tower 0.4.13", "tracing", "tracing-error", @@ -8104,11 +8237,11 @@ dependencies = [ "zakura-orchard", "zakura-redjubjub", "zakura-sapling-crypto", - "zcash_encoding", + "zcash_encoding 0.4.0", "zcash_note_encryption", - "zcash_protocol", + "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "zcash_script", - "zcash_transparent", + "zcash_transparent 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -8143,7 +8276,7 @@ dependencies = [ "hex", "rand_core 0.10.1", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", "zakura-jubjub", "zakura-pasta-curves", "zeroize", @@ -8157,7 +8290,7 @@ checksum = "37aa0ca5f5747caad4527ee92ad538271e7124949bc799cdc30b09c631b447fe" dependencies = [ "rand_core 0.10.1", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", "zakura-reddsa", "zeroize", ] @@ -8179,7 +8312,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", - "indexmap 2.14.0", + "indexmap 2.14.1", "insta", "jsonrpsee", "jsonrpsee-proc-macros", @@ -8192,9 +8325,9 @@ dependencies = [ "proptest", "prost", "rand 0.10.2", - "rand 0.8.6", + "rand 0.8.8", "rustls", - "schemars 1.2.1", + "schemars 1.2.2", "semver", "serde", "serde_json", @@ -8203,11 +8336,11 @@ dependencies = [ "strum_macros", "subtle", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tokio-rustls", "tokio-stream", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "tonic", "tonic-prost", "tonic-reflection", @@ -8224,10 +8357,10 @@ dependencies = [ "zakura-script", "zakura-state", "zakura-test", - "zcash_address", - "zcash_protocol", + "zcash_address 0.13.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", "zcash_script", - "zcash_transparent", + "zcash_transparent 0.10.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", ] [[package]] @@ -8271,11 +8404,11 @@ dependencies = [ "hex", "lazy_static", "libzcash_script", - "rand 0.8.6", + "rand 0.8.8", "ripemd 0.1.3", "secp256k1", "sha2 0.10.9", - "thiserror 2.0.18", + "thiserror 2.0.20", "zakura-chain", "zakura-primitives", "zakura-test", @@ -8309,7 +8442,7 @@ dependencies = [ "hex-literal", "howudoin", "humantime-serde", - "indexmap 2.14.0", + "indexmap 2.14.1", "insta", "itertools 0.14.0", "lazy_static", @@ -8317,7 +8450,7 @@ dependencies = [ "once_cell", "proptest", "proptest-derive", - "rand 0.8.6", + "rand 0.8.8", "rayon", "regex", "rlimit", @@ -8329,9 +8462,9 @@ dependencies = [ "sha2 0.10.9", "spandoc", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "tower 0.4.13", "tracing", "zakura-assets", @@ -8352,18 +8485,18 @@ dependencies = [ "futures", "hex", "humantime", - "indexmap 2.14.0", + "indexmap 2.14.1", "insta", "itertools 0.14.0", "lazy_static", "once_cell", "owo-colors", "proptest", - "rand 0.8.6", + "rand 0.8.8", "regex", "spandoc", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tower 0.4.13", "tracing", @@ -8380,7 +8513,7 @@ dependencies = [ "futures", "futures-core", "pin-project", - "rand 0.8.6", + "rand 0.8.8", "rayon", "tokio", "tokio-test", @@ -8415,7 +8548,7 @@ dependencies = [ "itertools 0.14.0", "serde_json", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tracing-error", "tracing-subscriber", @@ -8433,7 +8566,7 @@ dependencies = [ "clap", "sentry", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", "tracing-subscriber", "ureq", @@ -8442,14 +8575,28 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.13.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a854b28c07dba372f4410ea8ad62b4bf7d5c2bf8be32fc4b31bc0db6521a975" +dependencies = [ + "bech32", + "bs58", + "corez", + "f4jumble 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_encoding 0.4.0", + "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zcash_address" +version = "0.13.0" +source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" dependencies = [ "bech32", "bs58", "corez", - "f4jumble", - "zcash_encoding", - "zcash_protocol", + "f4jumble 0.1.1 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_encoding 0.4.0", + "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", ] [[package]] @@ -8464,14 +8611,24 @@ dependencies = [ ] [[package]] -name = "zcash_history" +name = "zcash_encoding" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d2ed9a9fa7b466a7adedab1ad5a21f8330e4943756912fc776cf7f3beae32b" +source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" +dependencies = [ + "corez", + "hex", + "nonempty", +] + +[[package]] +name = "zcash_history" +version = "0.6.0" +source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" dependencies = [ "blake2b_simd", - "byteorder", + "corez", "primitive-types", + "zcash_encoding 0.5.0", ] [[package]] @@ -8490,13 +8647,26 @@ dependencies = [ [[package]] name = "zcash_protocol" version = "0.10.5" -source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "314329b91ec4bbb517441840e47d0b2029bf0b946f086980c96c889c2d92dc5d" +dependencies = [ + "corez", + "document-features", + "hex", + "memuse", + "zcash_encoding 0.4.0", +] + +[[package]] +name = "zcash_protocol" +version = "0.10.5" +source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" dependencies = [ "corez", "document-features", "hex", "memuse", - "zcash_encoding", + "zcash_encoding 0.4.0", ] [[package]] @@ -8506,14 +8676,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f872800287d118be71bdf6fe8c869c6a6ff6fb0a5762f68fb2af54c97edf0f2" dependencies = [ "bip32", - "bitflags", + "bitflags 2.13.1", "bounded-vec", "hex", "ripemd 0.1.3", "secp256k1", "sha1", "sha2 0.10.9", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -8528,7 +8698,32 @@ dependencies = [ [[package]] name = "zcash_transparent" version = "0.10.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=91f448b5b1eacba09607faefd39e2204e4bb342d#91f448b5b1eacba09607faefd39e2204e4bb342d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "547c012778bae17f58007731af074d638aa146ab0ecfc120adebf23d049aff6c" +dependencies = [ + "bip32", + "bs58", + "corez", + "document-features", + "getset", + "hex", + "nonempty", + "ripemd 0.1.3", + "secp256k1", + "sha2 0.10.9", + "subtle", + "zcash_address 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_encoding 0.4.0", + "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_script", + "zcash_spec", + "zip32", +] + +[[package]] +name = "zcash_transparent" +version = "0.10.0" +source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" dependencies = [ "bip32", "bs58", @@ -8541,9 +8736,9 @@ dependencies = [ "secp256k1", "sha2 0.10.9", "subtle", - "zcash_address", - "zcash_encoding", - "zcash_protocol", + "zcash_address 0.13.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_encoding 0.4.0", + "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", "zcash_script", "zcash_spec", "zip32", @@ -8551,22 +8746,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8586,7 +8781,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure 0.13.2", ] @@ -8607,14 +8802,14 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -8623,9 +8818,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -8634,13 +8829,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -8656,8 +8851,14 @@ dependencies = [ "zcash_spec", ] +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml index 172b08767f..177bbca271 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,13 +51,13 @@ zakura-assets = { version = "=0.3462171.0" } incrementalmerkletree = { version = "0.8.2", features = ["legacy-api"] } orchard = { version = "1.0.1", package = "zakura-orchard" } sapling-crypto = { version = "1.0.1", package = "zakura-sapling-crypto" } -zcash_address = "0.13.0" -zcash_history = "0.5.0" +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } zcash_keys = { version = "1.0.1", package = "zakura-keys" } zcash_primitives = { version = "1.0.1", package = "zakura-primitives" } zcash_proofs = { version = "1.0.1", package = "zakura-proofs" } -zcash_transparent = "0.10.0" -zcash_protocol = "0.10.1" +zcash_transparent = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } +zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } abscissa_core = { version = "0.7", default-features = false } base64 = "0.22.1" bech32 = "0.11.0" diff --git a/crates/zakura-chain/src/primitives/zcash_history.rs b/crates/zakura-chain/src/primitives/zcash_history.rs index b075354861..a70394cba1 100644 --- a/crates/zakura-chain/src/primitives/zcash_history.rs +++ b/crates/zakura-chain/src/primitives/zcash_history.rs @@ -9,7 +9,7 @@ mod tests; use std::{collections::BTreeMap, io, sync::Arc}; use serde_big_array::BigArray; -pub use zcash_history::{V1, V2, V3}; +pub use zcash_history::{MAX_ENTRY_SIZE, V1, V2, V3}; use crate::{ block::{Block, ChainHistoryMmrRootHash, Header, Height}, diff --git a/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs b/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs index 09220f5b43..d44fc0d5dd 100644 --- a/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs +++ b/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs @@ -186,7 +186,7 @@ fn v3_history_node_hash_input_has_exact_serialized_size() -> Result<()> { // value in a V3 node. assert_eq!(encoded.len(), serialized_v3_node_data_size(&node_data)); assert_eq!(encoded.last(), Some(&1)); - assert_eq!(::zcash_history::MAX_NODE_DATA_SIZE, 317); + assert_eq!(::zcash_history::MAX_NODE_DATA_SIZE, 390); Ok(()) } diff --git a/crates/zakura-state/src/constants.rs b/crates/zakura-state/src/constants.rs index ab5b73eae2..cdef373d66 100644 --- a/crates/zakura-state/src/constants.rs +++ b/crates/zakura-state/src/constants.rs @@ -104,7 +104,7 @@ const DATABASE_FORMAT_VERSION: u64 = 28; /// - adding new column families, /// - changing the format of a column family in a compatible way, or /// - breaking changes with compatibility code in all supported Zebra versions. -const DATABASE_FORMAT_MINOR_VERSION: u64 = 1; +const DATABASE_FORMAT_MINOR_VERSION: u64 = 2; /// The database format patch version, incremented each time the on-disk database format has a /// significant format compatibility fix. diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs b/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs index 9c527c2314..560bd75e30 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs @@ -26,14 +26,17 @@ use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk}; /// An error decoding a persisted history-tree snapshot. #[derive(Debug, Error)] pub enum HistoryTreeDecodeError { - /// The snapshot does not use either supported serialization format. + /// The snapshot does not use any supported serialization format. #[error( - "history tree snapshot is neither the current nor legacy format: \ - current format error: {current}; legacy format error: {legacy}" + "history tree snapshot is neither the current, Ironwood, nor legacy format: \ + current format error: {current}; Ironwood format error: {ironwood}; \ + legacy format error: {legacy}" )] InvalidEncoding { /// The current-width decoding error. current: bincode::Error, + /// The Ironwood-width decoding error. + ironwood: bincode::Error, /// The legacy-width decoding error. legacy: bincode::Error, }, @@ -89,19 +92,26 @@ impl HistoryTreeParts { let bytes = bytes.as_ref(); let options = bincode::DefaultOptions::new(); - // Try the current entry width first. Databases written before NU6.3 widened - // `zcash_history::Entry` store narrower entries that fail to parse at the current width, - // so fall back to the legacy width and zero-pad each entry up to the current width. + // Try the current entry width first. Earlier databases store narrower entries, so fall + // back through the Ironwood and pre-Ironwood widths and zero-pad each entry to the current + // width. // // Legacy-width rows can fail the current-width decoder with errors other than // `UnexpectedEof`, because the wider entry can read into the next legacy entry and // interpret arbitrary entry bytes as bincode control bytes. match options.deserialize::(bytes) { Ok(parts) => Ok(parts), - Err(current) => options - .deserialize::(bytes) - .map(HistoryTreeParts::from) - .map_err(|legacy| HistoryTreeDecodeError::InvalidEncoding { current, legacy }), + Err(current) => match options.deserialize::(bytes) { + Ok(parts) => Ok(parts.into()), + Err(ironwood) => options + .deserialize::(bytes) + .map(HistoryTreeParts::from) + .map_err(|legacy| HistoryTreeDecodeError::InvalidEncoding { + current, + ironwood, + legacy, + }), + }, } } @@ -152,6 +162,46 @@ impl IntoDisk for HistoryTreeParts { /// before NU6.3 widened `zcash_history::NodeData`. const LEGACY_MAX_ENTRY_SIZE: usize = 253; +/// The width of a history-tree [`zcash_history::Entry`] from NU6.3 until NuTachyon support +/// reserved space for V4 node data. +const IRONWOOD_MAX_ENTRY_SIZE: usize = 326; + +/// A mirror of [`HistoryTreeParts`] using the NU6.3 [`zcash_history::Entry`] width. +#[derive(serde::Serialize, serde::Deserialize)] +struct IronwoodHistoryTreeParts { + network_kind: NetworkKind, + size: u32, + peaks: BTreeMap, + current_height: Height, +} + +/// A history-tree entry serialized at the NU6.3 [`IRONWOOD_MAX_ENTRY_SIZE`] width. +#[derive(serde::Serialize, serde::Deserialize)] +struct IronwoodEntry { + #[serde(with = "BigArray")] + inner: [u8; IRONWOOD_MAX_ENTRY_SIZE], +} + +impl From for HistoryTreeParts { + fn from(ironwood: IronwoodHistoryTreeParts) -> Self { + HistoryTreeParts { + network_kind: ironwood.network_kind, + size: ironwood.size, + peaks: ironwood + .peaks + .into_iter() + .map(|(index, entry)| { + ( + index, + zcash_history::Entry::from_raw_bytes_padded(&entry.inner), + ) + }) + .collect(), + current_height: ironwood.current_height, + } + } +} + /// A mirror of [`HistoryTreeParts`] using the pre-NU6.3 [`zcash_history::Entry`] width. #[derive(serde::Serialize, serde::Deserialize)] struct LegacyHistoryTreeParts { diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs b/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs index b7403a40f8..4daabf8bdf 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs @@ -73,6 +73,26 @@ fn legacy_parts_from(tree: &NonEmptyHistoryTree) -> LegacyHistoryTreeParts { } } +fn ironwood_parts_from(tree: &NonEmptyHistoryTree) -> IronwoodHistoryTreeParts { + IronwoodHistoryTreeParts { + network_kind: tree.network().kind(), + size: tree.size(), + peaks: tree + .peaks() + .iter() + .map(|(index, entry)| { + let serialized = bincode::DefaultOptions::new() + .serialize(entry) + .expect("history tree entry serialization succeeds"); + let mut inner = [0; IRONWOOD_MAX_ENTRY_SIZE]; + inner.copy_from_slice(&serialized[..IRONWOOD_MAX_ENTRY_SIZE]); + (*index, IronwoodEntry { inner }) + }) + .collect(), + current_height: tree.current_height(), + } +} + fn ephemeral_db(network: &Network) -> ZakuraDb { ZakuraDb::new( &Config::ephemeral(), @@ -135,6 +155,30 @@ fn history_tree_parts_reads_legacy_entry_width() { assert!(parts.as_bytes().len() > legacy_bytes.len()); } +/// A history tree written with the NU6.3 entry width must be read in place and zero-padded to the +/// current width. +#[test] +fn history_tree_parts_reads_ironwood_entry_width() { + let tree = valid_history_tree(); + let ironwood = ironwood_parts_from(&tree); + let ironwood_bytes = bincode::DefaultOptions::new() + .serialize(&ironwood) + .expect("Ironwood serialization succeeds"); + + let parts = + HistoryTreeParts::try_from_bytes(&ironwood_bytes).expect("Ironwood snapshot must decode"); + + assert_eq!(parts.network_kind, tree.network().kind()); + assert_eq!(parts.size, tree.size()); + assert_eq!(parts.current_height, tree.current_height()); + assert_eq!(parts.peaks.len(), tree.peaks().len()); + assert_eq!( + parts.as_bytes(), + HistoryTreeParts::from(ironwood).as_bytes() + ); + assert!(parts.as_bytes().len() > ironwood_bytes.len()); +} + /// A legacy-width row can fail current-width decoding with a non-EOF error if the wider entry /// consumes into the next legacy map entry and interprets entry bytes as bincode control bytes. #[test] @@ -154,7 +198,7 @@ fn history_tree_parts_reads_legacy_entry_width_after_non_eof_current_error() { LegacyEntry { inner: { let mut inner = [0xCD; LEGACY_MAX_ENTRY_SIZE]; - inner[72] = 0xFE; + inner[zcash_history::MAX_ENTRY_SIZE - LEGACY_MAX_ENTRY_SIZE - 1] = 0xFE; inner }, }, @@ -216,7 +260,7 @@ fn history_tree_parts_round_trips_current_width() { } #[test] -fn malformed_history_tree_parts_return_both_decode_errors() { +fn malformed_history_tree_parts_return_all_decode_errors() { let error = match HistoryTreeParts::try_from_bytes([0xFF]) { Ok(_) => panic!("malformed bytes must fail"), Err(error) => error, @@ -226,6 +270,7 @@ fn malformed_history_tree_parts_return_both_decode_errors() { error, HistoryTreeDecodeError::InvalidEncoding { current: _, + ironwood: _, legacy: _ } )); @@ -337,6 +382,7 @@ fn try_history_tree_propagates_malformed_snapshot() { error, HistoryTreeDecodeError::InvalidEncoding { current: _, + ironwood: _, legacy: _ } )); diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs b/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs index ebfd66084a..163b31816d 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs @@ -528,7 +528,7 @@ mod tests { "missing opened column family {expected}" ); } - assert_eq!(db.format_version_in_code().minor, 1); + assert_eq!(db.format_version_in_code().minor, 2); drop(db); let reopened = ZakuraDb::new( diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs b/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs index e0b1f58f27..42f3d4e5dc 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs @@ -144,7 +144,11 @@ fn format_upgrades( )), Box::new(drop_header_root_auth_frontier::Upgrade), Box::new(unauthenticated_commitment_roots::Upgrade), - ] as [Box; 10]) + Box::new(no_migration::NoMigration::new( + "widen history tree entries for NuTachyon", + Version::new(28, 2, 5), + )), + ] as [Box; 11]) .into_iter() .filter(move |upgrade| upgrade.version() > min_version()) } @@ -1109,13 +1113,14 @@ fn vct_format_changes_include_root_auth_metadata_updates() { let upgrades: Vec<_> = format_upgrades(Some(Version::new(27, 3, 0))).collect(); - assert_eq!(upgrades.len(), 6); + assert_eq!(upgrades.len(), 7); assert_eq!(upgrades[0].version(), Version::new(28, 0, 0)); assert_eq!(upgrades[1].version(), Version::new(28, 0, 1)); assert_eq!(upgrades[2].version(), Version::new(28, 0, 2)); assert_eq!(upgrades[3].version(), Version::new(28, 1, 3)); assert_eq!(upgrades[4].version(), Version::new(28, 1, 4)); assert_eq!(upgrades[5].version(), Version::new(28, 1, 5)); + assert_eq!(upgrades[6].version(), Version::new(28, 2, 5)); assert!( !upgrades[3].needs_migration(), "the header-chain column families are created on open without rebasing authenticated roots" diff --git a/crates/zakura-utils/coverage b/crates/zakura-utils/coverage index 942681d4b2..d3509ca8b5 100755 --- a/crates/zakura-utils/coverage +++ b/crates/zakura-utils/coverage @@ -5,7 +5,7 @@ set -o xtrace export CARGO_TARGET_DIR="coverage-target" export SKIP_NETWORK_TESTS=1 export LLVM_PROFILE_FILE="${PWD}/$CARGO_TARGET_DIR/coverage/test.%m.profraw" -export RUSTFLAGS="-Zinstrument-coverage -C link-dead-code -C debuginfo=2" +export RUSTFLAGS='--cfg zcash_unstable="nutachyon" -Zinstrument-coverage -C link-dead-code -C debuginfo=2' rm -rf ./"$CARGO_TARGET_DIR"/coverage mkdir -p ./$CARGO_TARGET_DIR/coverage From e76543d3ae48ee0e32a683fb87e06dc85eb3f8b6 Mon Sep 17 00:00:00 2001 From: c-node Date: Mon, 31 Aug 2026 15:27:53 -0400 Subject: [PATCH 03/15] build: update NuTachyon dependencies --- Cargo.lock | 1415 ++++++++--------- Cargo.toml | 24 +- .../zakura-chain/src/transaction/serialize.rs | 13 + 3 files changed, 718 insertions(+), 734 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09b6b6ba56..9bc664064a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -119,9 +119,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.104" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arc-swap" @@ -197,6 +197,12 @@ dependencies = [ "rustversion", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.8" @@ -218,13 +224,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.92" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] @@ -273,9 +279,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.18.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" dependencies = [ "aws-lc-sys", "zeroize", @@ -283,9 +289,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.44.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" dependencies = [ "cc", "cmake", @@ -357,7 +363,7 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.9", + "miniz_oxide", "object", "rustc-demangle", "windows-link 0.2.1", @@ -377,15 +383,15 @@ checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" [[package]] name = "base64" -version = "0.22.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.23.1" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -414,7 +420,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.13.1", + "bitflags", "cexpr", "clang-sys", "itertools 0.13.0", @@ -423,7 +429,7 @@ dependencies = [ "regex", "rustc-hash", "shlex 1.3.0", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -459,15 +465,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" dependencies = [ "serde_core", ] @@ -478,7 +478,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b64e60c28b6d25ad92e8b367801ff9aa12b41d05fc8798055d296bace4a60cc" dependencies = [ - "bitflags 2.13.1", + "bitflags", "serde", ] @@ -496,35 +496,38 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.5" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3560a7b1951efe814fcd721938313adc56753ca39f4b23847d7e9a2402f5dbff" +checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" dependencies = [ + "arrayref", "arrayvec", "constant_time_eq", ] [[package]] name = "blake2s_simd" -version = "1.0.5" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380c0236432f7b22a70229df30f218f5293870c056beb76f5ca068e3273a366" +checksum = "ee29928bad1e3f94c9d1528da29e07a1d3d04817ae8332de1e8b846c8439f4b3" dependencies = [ + "arrayref", "arrayvec", "constant_time_eq", ] [[package]] name = "blake3" -version = "1.8.7" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ + "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures 0.3.1", + "cpufeatures 0.3.0", ] [[package]] @@ -558,7 +561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09dc0086e469182132244e9b8d313a0742e1132da43a08c24b9dd3c18e0faf3a" dependencies = [ "serde", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -591,9 +594,9 @@ checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.25.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -640,9 +643,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.4" +version = "1.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ "find-msvc-tools", "jobserver", @@ -656,7 +659,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -667,9 +670,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" @@ -689,7 +692,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", - "cpufeatures 0.3.1", + "cpufeatures 0.3.0", "rand_core 0.10.1", ] @@ -758,9 +761,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.9.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -769,9 +772,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.6" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -779,9 +782,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.6" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -791,14 +794,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.4" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] @@ -822,7 +825,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -872,9 +875,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" -version = "4.6.8" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -888,7 +891,7 @@ checksum = "b85f248a4de22d204ceabc6299d89d2c70fbd7f09fea53c06c852369652d8139" dependencies = [ "pathdiff", "serde_core", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "winnow", ] @@ -1015,9 +1018,9 @@ dependencies = [ [[package]] name = "cordyceps" -version = "0.3.5" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9ab7e0ca1d179628fa0172b2b97203c7fa0cd81be2448bd446fb9559ca9261" +checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" dependencies = [ "loom", "tracing", @@ -1066,9 +1069,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" dependencies = [ "libc", ] @@ -1090,9 +1093,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1257,7 +1260,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1280,7 +1283,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1291,14 +1294,14 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] name = "data-encoding" -version = "2.11.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "debugid" @@ -1310,37 +1313,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror 2.0.20", -] - [[package]] name = "der" version = "0.7.10" @@ -1361,7 +1333,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1381,7 +1353,7 @@ checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1421,7 +1393,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", "unicode-xid", ] @@ -1435,7 +1407,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.119", + "syn 2.0.118", "unicode-xid", ] @@ -1490,13 +1462,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] @@ -1527,7 +1499,7 @@ checksum = "ed6b3e31251e87acd1b74911aed84071c8364fc9087972748ade2f1094ccce34" dependencies = [ "documented-macros", "phf", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -1542,7 +1514,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1602,9 +1574,9 @@ dependencies = [ [[package]] name = "either" -version = "1.18.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "embedded-io" @@ -1633,7 +1605,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1653,7 +1625,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1686,24 +1658,14 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.14" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08309dbcc659c5549a24ddb9b27027640641b282ef5768267c7e675558986a3" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ - "autocfg", "indenter", "once_cell", ] -[[package]] -name = "f4jumble" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d42773cb15447644d170be20231a3268600e0c4cea8987d013b93ac973d3cf7" -dependencies = [ - "blake2b_simd", -] - [[package]] name = "f4jumble" version = "0.1.1" @@ -1720,9 +1682,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "ff" @@ -1748,7 +1710,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -1769,9 +1731,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.11" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed-hash" @@ -1780,7 +1742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.8", + "rand 0.8.6", "rustc-hex", "static_assertions", ] @@ -1793,13 +1755,12 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.10" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", - "miniz_oxide 0.9.1", - "zlib-rs", + "miniz_oxide", ] [[package]] @@ -1872,7 +1833,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "serdect", - "thiserror 2.0.20", + "thiserror 2.0.18", "visibility", "zeroize", "zeroize_derive", @@ -1914,9 +1875,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1942,9 +1903,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1952,15 +1913,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1969,9 +1930,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1988,32 +1949,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] name = "futures-sink" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -2101,7 +2062,7 @@ checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -2112,9 +2073,9 @@ checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gloo-timers" @@ -2142,9 +2103,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.19" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" dependencies = [ "atomic-waker", "bytes", @@ -2152,7 +2113,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.14.1", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -2215,14 +2176,14 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hdrhistogram" -version = "7.6.0" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d1053f4708f0af3cf9fc5bffc7e68a914a3c45becb231c80068c9c3f78bea" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "byteorder", "flate2", - "nom 8.0.0", + "nom", "num-traits", ] @@ -2248,9 +2209,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2283,9 +2244,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.5", + "rand 0.9.4", "ring", - "thiserror 2.0.20", + "thiserror 2.0.18", "tinyvec", "tokio", "tracing", @@ -2305,10 +2266,10 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.5", + "rand 0.9.4", "resolv-conf", "smallvec", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -2366,9 +2327,9 @@ dependencies = [ [[package]] name = "http" -version = "1.5.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -2376,9 +2337,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -2386,9 +2347,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.5" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", @@ -2436,9 +2397,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.11.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -2469,7 +2430,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.9", + "webpki-roots 1.0.8", ] [[package]] @@ -2502,7 +2463,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.5", + "socket2 0.6.4", "tokio", "tower-service", "tracing", @@ -2534,9 +2495,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", @@ -2548,9 +2509,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -2561,9 +2522,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -2575,17 +2536,16 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -2596,15 +2556,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.3.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -2657,7 +2617,7 @@ dependencies = [ "hyper", "hyper-util", "log", - "rand 0.9.5", + "rand 0.9.4", "tokio", "url", "xmltree", @@ -2680,7 +2640,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -2711,9 +2671,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -2736,9 +2696,9 @@ dependencies = [ [[package]] name = "inferno" -version = "0.12.8" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c460d4fa06223667240720ab69a8045133755ae6dfbe100cf481b95e3a014f1" +checksum = "d2c05b9ae050366b3363927f59d2c07f922a746e97e2e96f70d479a3c7dbbbe5" dependencies = [ "ahash", "itoa", @@ -2793,7 +2753,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ - "socket2 0.6.5", + "socket2 0.6.4", "widestring", "windows-registry", "windows-result 0.4.1", @@ -2802,9 +2762,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iroh" @@ -2843,7 +2803,7 @@ dependencies = [ "pin-project", "pkarr", "portmapper", - "rand 0.8.8", + "rand 0.8.6", "reqwest 0.12.28", "ring", "rustls", @@ -2908,7 +2868,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -2925,7 +2885,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.5.10", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -2939,13 +2899,13 @@ checksum = "929d5d8fa77d5c304d3ee7cae9aede31f13908bd049f9de8c7c0094ad6f7c535" dependencies = [ "bytes", "getrandom 0.2.17", - "rand 0.8.8", + "rand 0.8.6", "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.20", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -2994,7 +2954,7 @@ dependencies = [ "pin-project", "pkarr", "postcard", - "rand 0.8.8", + "rand 0.8.6", "reqwest 0.12.28", "rustls", "rustls-pki-types", @@ -3065,59 +3025,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jiff" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" -dependencies = [ - "defmt", - "jiff-core", - "jiff-static", - "jiff-tzdb-platform", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", - "windows-link 0.2.1", -] - -[[package]] -name = "jiff-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" -dependencies = [ - "defmt", -] - -[[package]] -name = "jiff-static" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" -dependencies = [ - "jiff-core", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "jiff-tzdb" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" - -[[package]] -name = "jiff-tzdb-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" -dependencies = [ - "jiff-tzdb", -] - [[package]] name = "jni" version = "0.22.4" @@ -3130,7 +3037,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror 2.0.20", + "thiserror 2.0.18", "walkdir", "windows-link 0.2.1", ] @@ -3145,7 +3052,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -3164,7 +3071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -3179,9 +3086,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.104" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -3214,7 +3121,7 @@ dependencies = [ "http-body-util", "jsonrpsee-types", "parking_lot", - "rand 0.8.8", + "rand 0.8.6", "rustc-hash", "serde", "serde_json", @@ -3233,7 +3140,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -3298,9 +3205,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.189" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -3320,9 +3227,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.21" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7955dfc218a8afb29dfeffd540e3a6e96baeb94fe7138228dd7cc6937fbbf96" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "libc", ] @@ -3360,7 +3267,7 @@ checksum = "3f8ce05b56f3cbc65ec7d0908adb308ed91281e022f61c8c3a0c9388b5380b17" dependencies = [ "bindgen", "cc", - "thiserror 2.0.20", + "thiserror 2.0.18", "tracing", "zcash_script", ] @@ -3373,9 +3280,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "litrs" @@ -3394,9 +3301,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.34" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "loom" @@ -3461,6 +3368,15 @@ dependencies = [ "rayon", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "git+https://github.com/tachyon-zcash/maybe-rayon.git?rev=5c37ee0dd448e8c1edc4529d0b8897ecaff5aad1#5c37ee0dd448e8c1edc4529d0b8897ecaff5aad1" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "md5" version = "0.7.0" @@ -3499,7 +3415,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "indexmap 2.14.1", + "indexmap 2.14.0", "ipnet", "metrics", "metrics-util", @@ -3520,7 +3436,7 @@ dependencies = [ "hashbrown 0.15.5", "metrics", "quanta", - "rand 0.9.5", + "rand 0.9.4", "rand_xoshiro", "sketches-ddsketch", ] @@ -3542,15 +3458,6 @@ name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "miniz_oxide" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" dependencies = [ "adler2", "simd-adler32", @@ -3558,9 +3465,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -3569,9 +3476,9 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.16" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4293f18e7567a1caf3c584855554377025c65e0aa445344d04171f5ad63d19b9" +checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -3653,7 +3560,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -3708,7 +3615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0e7987b28514adf555dc1f9a5c30dfc3e50750bbaffb1aec41ca7b23dcd8e4" dependencies = [ "anyhow", - "bitflags 2.13.1", + "bitflags", "byteorder", "libc", "log", @@ -3723,7 +3630,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56d83370a96813d7c977f8b63054f1162df6e5784f1c598d689236564fb5a6f2" dependencies = [ "anyhow", - "bitflags 2.13.1", + "bitflags", "byteorder", "libc", "log", @@ -3754,7 +3661,7 @@ dependencies = [ "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -3794,7 +3701,7 @@ dependencies = [ "pin-project-lite", "serde", "snafu", - "socket2 0.6.5", + "socket2 0.6.4", "time", "tokio", "tokio-util", @@ -3811,7 +3718,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.13.1", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -3833,15 +3740,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - [[package]] name = "nonempty" version = "0.11.0" @@ -3900,9 +3798,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.47" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] @@ -3935,7 +3833,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -3990,10 +3888,10 @@ dependencies = [ "documented", "jsonrpsee", "quote", - "schemars 1.2.2", + "schemars 1.2.1", "serde", "serde_json", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4012,7 +3910,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -4041,7 +3939,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "reqwest 0.13.4", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -4067,8 +3965,8 @@ dependencies = [ "opentelemetry", "percent-encoding", "portable-atomic", - "rand 0.9.5", - "thiserror 2.0.20", + "rand 0.9.4", + "thiserror 2.0.18", "tokio", "tokio-stream", ] @@ -4081,7 +3979,7 @@ checksum = "969ccca8ffc4fb105bd131a228107d5c9dd89d9d627edf3295cbe979156f9712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4092,9 +3990,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "owo-colors" -version = "4.4.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "parity-scale-codec" @@ -4121,7 +4019,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4182,9 +4080,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.9.0" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07a60cc7a4d00c91f95c685609d1d2f79050e6804b70ebedd7650f0b839bcf" +checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" dependencies = [ "memchr", "ucd-trie", @@ -4192,9 +4090,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.9.0" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a83744a5c8455b8b3e0dc5031362780a347c878bdd11584d1a8984228cc88d" +checksum = "4b4254325ecad416ab689e27ba51da03ba01a9632bc6e108f5fe7c3c4ad29d58" dependencies = [ "pest", "pest_generator", @@ -4202,22 +4100,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.9.0" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd3451aa3de60d4b9a1e736885e4dea6b31617598026f12256ad566d63304a" +checksum = "6c4c0e91ead7a8f7acecbca6f003fc2e8282b1dbe2dd9c9d2f16aba42995e0a7" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] name = "pest_meta" -version = "2.9.0" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d3a0849e241d7dfce834c83b1c5edc8622009e8dd51a12ba1927c32f05496" +checksum = "f9744bc48116fee06334924bb5f2bad41eed5e89bd26e29b0b799f9a3f82c210" dependencies = [ "pest", ] @@ -4230,7 +4128,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.14.1", + "indexmap 2.14.0", ] [[package]] @@ -4274,7 +4172,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4303,7 +4201,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4336,7 +4234,7 @@ dependencies = [ "serde", "sha1_smol", "simple-dns", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -4355,9 +4253,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.34" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plotters" @@ -4405,7 +4303,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4442,18 +4340,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.15.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" - -[[package]] -name = "portable-atomic-util" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" -dependencies = [ - "portable-atomic", -] +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portmapper" @@ -4473,11 +4362,11 @@ dependencies = [ "nested_enum_utils", "netwatch", "num_enum", - "rand 0.9.5", + "rand 0.9.4", "serde", "smallvec", "snafu", - "socket2 0.6.5", + "socket2 0.6.4", "time", "tokio", "tokio-util", @@ -4508,14 +4397,14 @@ checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] name = "potential_utf" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -4576,7 +4465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4601,9 +4490,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.107" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -4616,9 +4505,9 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.13.1", + "bitflags", "num-traits", - "rand 0.9.5", + "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -4635,7 +4524,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4665,7 +4554,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn 2.0.119", + "syn 2.0.118", "tempfile", ] @@ -4679,7 +4568,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -4697,16 +4586,16 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ - "bitflags 2.13.1", + "bitflags", "memchr", "unicase", ] [[package]] name = "pulldown-cmark-to-cmark" -version = "22.0.1" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab1ad36992cead65f02aa399a373a42730922f1525d988172634fdefdecb8a60" +checksum = "50793def1b900256624a709439404384204a5dc3a6ec580281bfaac35e882e90" dependencies = [ "pulldown-cmark", ] @@ -4754,8 +4643,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.5", - "thiserror 2.0.20", + "socket2 0.6.4", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -4763,9 +4652,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.17" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", @@ -4778,7 +4667,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.20", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -4793,16 +4682,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.5", + "socket2 0.6.4", "tracing", "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.47" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -4835,11 +4724,105 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "ragu" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "blake2b_simd", + "ragu_arithmetic", + "ragu_circuits", + "ragu_core", + "ragu_pasta", + "ragu_pcd", + "ragu_primitives", +] + +[[package]] +name = "ragu_arithmetic" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "ff", + "group", + "maybe-rayon 0.1.1 (git+https://github.com/tachyon-zcash/maybe-rayon.git?rev=5c37ee0dd448e8c1edc4529d0b8897ecaff5aad1)", + "ragu_macros", + "rand 0.10.2", + "zakura-pasta-curves 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ragu_circuits" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "blake2b_simd", + "maybe-rayon 0.1.1 (git+https://github.com/tachyon-zcash/maybe-rayon.git?rev=5c37ee0dd448e8c1edc4529d0b8897ecaff5aad1)", + "ragu_arithmetic", + "ragu_core", + "ragu_primitives", +] + +[[package]] +name = "ragu_core" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "ragu_arithmetic", + "ragu_macros", + "thiserror 2.0.18", +] + +[[package]] +name = "ragu_macros" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "ragu_pasta" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "lazy_static", + "ragu_arithmetic", +] + +[[package]] +name = "ragu_pcd" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "maybe-rayon 0.1.1 (git+https://github.com/tachyon-zcash/maybe-rayon.git?rev=5c37ee0dd448e8c1edc4529d0b8897ecaff5aad1)", + "ragu_arithmetic", + "ragu_circuits", + "ragu_core", + "ragu_primitives", +] + +[[package]] +name = "ragu_primitives" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/ragu?rev=f4f6315b10ed96944c40b2bbdbca0fe06af66ce0#f4f6315b10ed96944c40b2bbdbca0fe06af66ce0" +dependencies = [ + "ragu_arithmetic", + "ragu_core", + "ragu_macros", + "thiserror 2.0.18", +] + [[package]] name = "rand" -version = "0.8.8" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4848,9 +4831,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4963,7 +4946,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.13.1", + "bitflags", ] [[package]] @@ -4992,7 +4975,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.1", + "bitflags", ] [[package]] @@ -5003,34 +4986,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] name = "ref-cast" -version = "1.0.27" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.27" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] name = "regex" -version = "1.13.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" dependencies = [ "aho-corasick", "memchr", @@ -5040,9 +5023,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.18" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" dependencies = [ "aho-corasick", "memchr", @@ -5100,7 +5083,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.9", + "webpki-roots 1.0.8", ] [[package]] @@ -5215,7 +5198,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3" dependencies = [ - "bitflags 2.13.1", + "bitflags", "once_cell", "serde", "serde_derive", @@ -5262,7 +5245,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.1", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -5271,9 +5254,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.43" +version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ "aws-lc-rs", "log", @@ -5299,9 +5282,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -5336,9 +5319,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.15" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -5411,9 +5394,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -5424,14 +5407,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] @@ -5481,7 +5464,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.1", + "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5500,9 +5483,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.3.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" [[package]] name = "semver" @@ -5554,7 +5537,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ac170a5bba8bec6e3339c90432569d89641fa7a3d3e4f44987d24f0762e6adf" dependencies = [ - "rand 0.9.5", + "rand 0.9.4", "sentry-types", "serde", "serde_json", @@ -5567,7 +5550,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "235865e639f1d72414fa5d35374e105c292e2ee3a2f90919d961bbb486626ee6" dependencies = [ - "bitflags 2.13.1", + "bitflags", "log", "sentry-core", ] @@ -5578,7 +5561,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27701acc51e68db5281802b709010395bfcbcb128b1d0a4e5873680d3b47ff0c" dependencies = [ - "bitflags 2.13.1", + "bitflags", "sentry-backtrace", "sentry-core", "tracing-core", @@ -5593,10 +5576,10 @@ checksum = "56780cb5597d676bf22e6c11d1f062eb4def46390ea3bfb047bcbcf7dfd19bdb" dependencies = [ "debugid", "hex", - "rand 0.9.5", + "rand 0.9.4", "serde", "serde_json", - "thiserror 2.0.20", + "thiserror 2.0.18", "time", "url", "uuid", @@ -5604,9 +5587,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -5633,42 +5616,42 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] name = "serde_derive_internals" -version = "0.30.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ - "indexmap 2.14.1", + "indexmap 2.14.0", "itoa", "memchr", "serde", @@ -5699,19 +5682,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.22.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64 0.22.1", "bs58", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.14.1", - "jiff", + "indexmap 2.14.0", "schemars 0.9.0", - "schemars 1.2.2", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -5720,14 +5702,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.22.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -5821,15 +5803,15 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.10" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simd_cesu8" -version = "1.2.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" dependencies = [ "rustc_version", "simdutf8", @@ -5853,7 +5835,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee851d0e5e7af3721faea1843e8015e820a234f81fda3dea9247e15bac9a86a" dependencies = [ - "bitflags 2.13.1", + "bitflags", ] [[package]] @@ -5899,7 +5881,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -5914,9 +5896,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -5934,7 +5916,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.8", + "rand 0.8.6", "sha1", ] @@ -5999,9 +5981,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "str_stack" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f446288b699d66d0fd2e30d1cfe7869194312524b3b9252594868ed26ef056a" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" [[package]] name = "strsim" @@ -6027,7 +6009,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -6051,7 +6033,7 @@ dependencies = [ "precis-core", "precis-profiles", "quoted-string-parser", - "rand 0.9.5", + "rand 0.9.4", ] [[package]] @@ -6069,8 +6051,8 @@ dependencies = [ "hex", "parking_lot", "pnet_packet", - "rand 0.9.5", - "socket2 0.6.5", + "rand 0.9.4", + "socket2 0.6.4", "thiserror 1.0.69", "tokio", "tracing", @@ -6095,20 +6077,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.4" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -6144,7 +6115,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -6153,7 +6124,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.13.1", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6224,11 +6195,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.20", + "thiserror-impl 2.0.18", ] [[package]] @@ -6239,18 +6210,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] @@ -6259,7 +6230,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe075d7053dae61ac5413a34ea7d4913b6e6207844fd726bdd858b37ff72bf5" dependencies = [ - "bitflags 2.13.1", + "bitflags", "cfg-if", "libc", "log", @@ -6278,9 +6249,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.55" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", "js-sys", @@ -6299,9 +6270,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.32" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -6309,9 +6280,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -6344,9 +6315,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.53.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -6354,7 +6325,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.5", + "socket2 0.6.4", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -6362,13 +6333,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] @@ -6383,9 +6354,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -6406,16 +6377,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.19" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "libc", "pin-project-lite", "tokio", ] @@ -6433,7 +6403,7 @@ dependencies = [ "getrandom 0.3.4", "http", "httparse", - "rand 0.9.5", + "rand 0.9.4", "ring", "rustls-pki-types", "simdutf8", @@ -6453,11 +6423,11 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.4+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "indexmap 2.14.1", + "indexmap 2.14.0", "serde_core", "serde_spanned", "toml_datetime", @@ -6477,11 +6447,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.13+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ - "indexmap 2.14.1", + "indexmap 2.14.0", "toml_datetime", "toml_parser", "winnow", @@ -6489,18 +6459,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.3+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.1.2+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" @@ -6521,7 +6491,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.5", + "socket2 0.6.4", "sync_wrapper", "tokio", "tokio-rustls", @@ -6541,7 +6511,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -6566,7 +6536,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.119", + "syn 2.0.118", "tempfile", "tonic-build", ] @@ -6611,7 +6581,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.14.1", + "indexmap 2.14.0", "pin-project-lite", "slab", "sync_wrapper", @@ -6628,7 +6598,7 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.13.1", + "bitflags", "bytes", "futures-util", "http", @@ -6686,7 +6656,7 @@ checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", "symlink", - "thiserror 2.0.20", + "thiserror 2.0.18", "time", "tracing-subscriber", ] @@ -6699,7 +6669,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -6828,7 +6798,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" dependencies = [ "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -6878,9 +6848,9 @@ dependencies = [ [[package]] name = "uint" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f9227a75a5a540a464c832ad4a4195dbdbecd8787610a56262721fde6f04f90" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" dependencies = [ "byteorder", "crunchy", @@ -6951,27 +6921,27 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ - "base64 0.23.1", + "base64 0.22.1", "log", "percent-encoding", "rustls", "rustls-pki-types", "ureq-proto", "utf8-zero", - "webpki-roots 1.0.9", + "webpki-roots 1.0.8", ] [[package]] name = "ureq-proto" -version = "0.6.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" dependencies = [ - "base64 0.23.1", + "base64 0.22.1", "http", "httparse", "log", @@ -7010,9 +6980,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.26.0" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -7046,7 +7016,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -7144,9 +7114,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -7157,9 +7127,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.77" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -7167,9 +7137,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7177,22 +7147,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.127" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] @@ -7212,9 +7182,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.104" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -7232,9 +7202,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.9" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] @@ -7245,14 +7215,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.9", + "webpki-roots 1.0.8", ] [[package]] name = "webpki-roots" -version = "1.0.9" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -7393,7 +7363,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -7404,7 +7374,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -7597,9 +7567,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -7620,16 +7590,16 @@ dependencies = [ "futures", "log", "serde", - "thiserror 2.0.20", + "thiserror 2.0.18", "windows 0.62.2", "windows-core 0.62.2", ] [[package]] name = "writeable" -version = "0.6.4" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "ws_stream_wasm" @@ -7644,7 +7614,7 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper", - "thiserror 2.0.20", + "thiserror 2.0.18", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -7683,9 +7653,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.29" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xmltree" @@ -7704,7 +7674,7 @@ dependencies = [ "serde", "sha2 0.10.9", "tempfile", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tonic-prost-build", ] @@ -7727,7 +7697,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", "synstructure 0.13.2", ] @@ -7759,7 +7729,7 @@ dependencies = [ "humantime-serde", "hyper", "hyper-util", - "indexmap 2.14.1", + "indexmap 2.14.0", "indicatif", "inferno", "insta", @@ -7777,7 +7747,7 @@ dependencies = [ "proptest", "proptest-derive", "prost", - "rand 0.8.8", + "rand 0.8.6", "rayon", "regex", "reqwest 0.12.28", @@ -7788,12 +7758,12 @@ dependencies = [ "sha2 0.10.9", "tar", "tempfile", - "thiserror 2.0.20", + "thiserror 2.0.18", "thread-priority", "tokio", "tokio-stream", "tokio-util", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tonic", "tonic-prost", "tonic-prost-build", @@ -7829,8 +7799,7 @@ checksum = "1518d74211e0ae2422cab4560acdc67f72ae8fcdb871294eb03476a5a4586a74" [[package]] name = "zakura-bellman" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c72c84165959b2578cbf8800ff491e74d819397b53658bbf1b2b170b3ee9fc" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "bitvec", "blake2s_simd", @@ -7848,6 +7817,16 @@ name = "zakura-bls12-381" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d9c833a85b5ba3303a5f14ed760d613bee8a9769923da5a1e7f51851849e7f5" +dependencies = [ + "ff", + "rand_core 0.10.1", + "subtle", +] + +[[package]] +name = "zakura-bls12-381" +version = "1.0.1" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "ff", "group", @@ -7861,7 +7840,7 @@ name = "zakura-chain" version = "6.0.0" dependencies = [ "bech32", - "bitflags 2.13.1", + "bitflags", "bitflags-serde-legacy", "bitvec", "blake2b_simd", @@ -7887,13 +7866,13 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.10.2", - "rand 0.8.8", + "rand 0.8.6", "rand_chacha 0.10.0", "rand_core 0.10.1", "rand_core 0.6.4", "rayon", "ripemd 0.1.3", - "schemars 1.2.2", + "schemars 1.2.1", "secp256k1", "serde", "serde-big-array", @@ -7903,26 +7882,26 @@ dependencies = [ "spandoc", "strum", "tempfile", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tracing", - "uint 0.10.1", + "uint 0.10.0", "x25519-dalek", "zakura-halo2-proofs", - "zakura-jubjub", + "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-orchard", "zakura-primitives", - "zakura-reddsa", + "zakura-reddsa 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-redjubjub", "zakura-sapling-crypto", "zakura-sinsemilla", "zakura-test", - "zcash_address 0.13.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_address", "zcash_history", "zcash_note_encryption", - "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_protocol", "zcash_script", - "zcash_transparent 0.10.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_transparent", ] [[package]] @@ -7945,17 +7924,17 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.10.2", - "rand 0.8.8", + "rand 0.8.6", "rayon", "serde", "spandoc", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tower 0.4.13", "tracing", "zakura-bellman", - "zakura-bls12-381", + "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-chain", "zakura-halo2-proofs", "zakura-header-chain", @@ -7969,16 +7948,15 @@ dependencies = [ "zakura-test", "zakura-tower-batch-control", "zakura-tower-fallback", - "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_protocol", "zcash_script", - "zcash_transparent 0.10.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_transparent", ] [[package]] name = "zakura-halo2-gadgets" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c601160505e513507664516f16a38b8e875a31b48155a93964b6e97c1e07d315" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "arrayvec", "ff", @@ -7986,43 +7964,40 @@ dependencies = [ "subtle", "zakura-halo2-poseidon", "zakura-halo2-proofs", - "zakura-pasta-curves", + "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-sinsemilla", ] [[package]] name = "zakura-halo2-legacy-pdqsort" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e2ecc4b9f643499164caceb5fe3339d1cdd9088dac084dd14314d6f6a75b0e" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" [[package]] name = "zakura-halo2-poseidon" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ec02070913e40067ae2df9aab21139a55b239f289140d116af5d5e25e5240" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "bitvec", "ff", "group", - "zakura-pasta-curves", + "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", ] [[package]] name = "zakura-halo2-proofs" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c39132f1dae675ef4105321da11dee1d70c2b07bbc7d78a31c4044ed381fd74" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "blake2b_simd", "ff", "group", - "maybe-rayon", + "maybe-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.10.2", "rand_core 0.10.1", "tracing", "zakura-halo2-legacy-pdqsort", - "zakura-pasta-curves", + "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", ] [[package]] @@ -8034,8 +8009,8 @@ dependencies = [ "proptest", "rayon", "sha2 0.10.9", - "thiserror 2.0.20", - "toml 1.1.4+spec-1.1.0", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", "zakura-chain", "zakura-test", ] @@ -8063,14 +8038,26 @@ dependencies = [ "group", "rand_core 0.10.1", "subtle", - "zakura-bls12-381", + "zakura-bls12-381 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zakura-jubjub" +version = "1.0.1" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +dependencies = [ + "bitvec", + "ff", + "group", + "rand_core 0.10.1", + "subtle", + "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", ] [[package]] name = "zakura-keys" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac02340ce2b8eecc13047a936efe6545cc6de3761e2eda11eb5ab30cde8a6bed" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "bech32", "blake2b_simd", @@ -8084,10 +8071,10 @@ dependencies = [ "subtle", "zakura-orchard", "zakura-sapling-crypto", - "zcash_address 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_address", "zcash_encoding 0.4.0", - "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_transparent 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_protocol", + "zcash_transparent", "zip32", ] @@ -8095,7 +8082,7 @@ dependencies = [ name = "zakura-network" version = "7.0.0" dependencies = [ - "bitflags 2.13.1", + "bitflags", "blake2b_simd", "byteorder", "bytes", @@ -8106,7 +8093,7 @@ dependencies = [ "hex", "howudoin", "humantime-serde", - "indexmap 2.14.1", + "indexmap 2.14.0", "iroh", "itertools 0.14.0", "lazy_static", @@ -8114,18 +8101,18 @@ dependencies = [ "pin-project", "proptest", "proptest-derive", - "rand 0.8.8", + "rand 0.8.6", "rayon", "regex", - "schemars 1.2.2", + "schemars 1.2.1", "serde", "serde_json", "tempfile", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tokio-stream", "tokio-util", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tower 0.4.13", "tracing", "tracing-error", @@ -8154,8 +8141,7 @@ dependencies = [ [[package]] name = "zakura-orchard" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f40234883ea702da695f1cbf470a0ba3b9fe0ac51d24af588a058afc383d68" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "aes", "bitvec", @@ -8179,8 +8165,8 @@ dependencies = [ "zakura-halo2-gadgets", "zakura-halo2-poseidon", "zakura-halo2-proofs", - "zakura-pasta-curves", - "zakura-reddsa", + "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-reddsa 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-sinsemilla", "zcash_note_encryption", "zcash_spec", @@ -8190,8 +8176,7 @@ dependencies = [ [[package]] name = "zakura-pairing" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d3f4e77bb286c0f1770c90e721edcd99bd493acce6199734fa1eaeb4037f12" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "group", ] @@ -8201,12 +8186,26 @@ name = "zakura-pasta-curves" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c906d3541e9e6b71aaa77af25c229a0c1ed38df58930fa75f2fd569632989dd" +dependencies = [ + "blake2b_simd", + "ff", + "group", + "once_cell", + "rand 0.10.2", + "static_assertions", + "subtle", +] + +[[package]] +name = "zakura-pasta-curves" +version = "1.0.1" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "blake2b_simd", "cc", "ff", "group", - "maybe-rayon", + "maybe-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell", "rand 0.10.2", "static_assertions", @@ -8216,8 +8215,7 @@ dependencies = [ [[package]] name = "zakura-primitives" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268f6d451f4870f763b5ba51e5f84fd2843d86b103cc01b8fbf9cae84115c36c" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "blake2b_simd", "block-buffer 0.11.0-rc.3", @@ -8233,22 +8231,22 @@ dependencies = [ "rand_core 0.10.1", "secp256k1", "sha2 0.10.9", - "zakura-jubjub", + "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-orchard", "zakura-redjubjub", "zakura-sapling-crypto", "zcash_encoding 0.4.0", "zcash_note_encryption", - "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_protocol", "zcash_script", - "zcash_transparent 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_tachyon", + "zcash_transparent", ] [[package]] name = "zakura-proofs" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce73755c38fa3d0de97d420d547855b0111083b0f1270ab7d4e53896cb16a7c" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "blake2b_simd", "document-features", @@ -8257,8 +8255,8 @@ dependencies = [ "rand_core 0.10.1", "wagyu-zcash-parameters", "zakura-bellman", - "zakura-bls12-381", - "zakura-jubjub", + "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-primitives", "zakura-redjubjub", "zakura-sapling-crypto", @@ -8276,22 +8274,38 @@ dependencies = [ "hex", "rand_core 0.10.1", "serde", - "thiserror 2.0.20", - "zakura-jubjub", - "zakura-pasta-curves", + "thiserror 2.0.18", + "zakura-jubjub 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zakura-pasta-curves 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "zakura-reddsa" +version = "1.0.1" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +dependencies = [ + "blake2b_simd", + "frost-rerandomized", + "group", + "hex", + "rand_core 0.10.1", + "serde", + "thiserror 2.0.18", + "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zeroize", ] [[package]] name = "zakura-redjubjub" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37aa0ca5f5747caad4527ee92ad538271e7124949bc799cdc30b09c631b447fe" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "rand_core 0.10.1", "serde", - "thiserror 2.0.20", - "zakura-reddsa", + "thiserror 2.0.18", + "zakura-reddsa 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zeroize", ] @@ -8312,7 +8326,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", - "indexmap 2.14.1", + "indexmap 2.14.0", "insta", "jsonrpsee", "jsonrpsee-proc-macros", @@ -8325,9 +8339,9 @@ dependencies = [ "proptest", "prost", "rand 0.10.2", - "rand 0.8.8", + "rand 0.8.6", "rustls", - "schemars 1.2.2", + "schemars 1.2.1", "semver", "serde", "serde_json", @@ -8336,11 +8350,11 @@ dependencies = [ "strum_macros", "subtle", "tempfile", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tokio-rustls", "tokio-stream", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tonic", "tonic-prost", "tonic-reflection", @@ -8357,17 +8371,16 @@ dependencies = [ "zakura-script", "zakura-state", "zakura-test", - "zcash_address 0.13.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", - "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_address", + "zcash_protocol", "zcash_script", - "zcash_transparent 0.10.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_transparent", ] [[package]] name = "zakura-sapling-crypto" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcd5a8073598e33ffaa2694ca59d7c522a60dd349a3d74d239e6eb929733107" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "aes", "bitvec", @@ -8389,8 +8402,8 @@ dependencies = [ "subtle", "tracing", "zakura-bellman", - "zakura-bls12-381", - "zakura-jubjub", + "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", "zakura-redjubjub", "zcash_note_encryption", "zcash_spec", @@ -8404,11 +8417,11 @@ dependencies = [ "hex", "lazy_static", "libzcash_script", - "rand 0.8.8", + "rand 0.8.6", "ripemd 0.1.3", "secp256k1", "sha2 0.10.9", - "thiserror 2.0.20", + "thiserror 2.0.18", "zakura-chain", "zakura-primitives", "zakura-test", @@ -8418,13 +8431,12 @@ dependencies = [ [[package]] name = "zakura-sinsemilla" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043da1b236d2b976b2046112811dc1be3f734c566a6edb1a0a52b5379231e212" +source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" dependencies = [ "group", "once_cell", "subtle", - "zakura-pasta-curves", + "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", ] [[package]] @@ -8442,7 +8454,7 @@ dependencies = [ "hex-literal", "howudoin", "humantime-serde", - "indexmap 2.14.1", + "indexmap 2.14.0", "insta", "itertools 0.14.0", "lazy_static", @@ -8450,7 +8462,7 @@ dependencies = [ "once_cell", "proptest", "proptest-derive", - "rand 0.8.8", + "rand 0.8.6", "rayon", "regex", "rlimit", @@ -8462,9 +8474,9 @@ dependencies = [ "sha2 0.10.9", "spandoc", "tempfile", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", - "toml 1.1.4+spec-1.1.0", + "toml 1.1.2+spec-1.1.0", "tower 0.4.13", "tracing", "zakura-assets", @@ -8485,18 +8497,18 @@ dependencies = [ "futures", "hex", "humantime", - "indexmap 2.14.1", + "indexmap 2.14.0", "insta", "itertools 0.14.0", "lazy_static", "once_cell", "owo-colors", "proptest", - "rand 0.8.8", + "rand 0.8.6", "regex", "spandoc", "tempfile", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tower 0.4.13", "tracing", @@ -8513,7 +8525,7 @@ dependencies = [ "futures", "futures-core", "pin-project", - "rand 0.8.8", + "rand 0.8.6", "rayon", "tokio", "tokio-test", @@ -8548,7 +8560,7 @@ dependencies = [ "itertools 0.14.0", "serde_json", "tempfile", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tracing-error", "tracing-subscriber", @@ -8566,26 +8578,12 @@ dependencies = [ "clap", "sentry", "serde_json", - "thiserror 2.0.20", + "thiserror 2.0.18", "tracing", "tracing-subscriber", "ureq", ] -[[package]] -name = "zcash_address" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a854b28c07dba372f4410ea8ad62b4bf7d5c2bf8be32fc4b31bc0db6521a975" -dependencies = [ - "bech32", - "bs58", - "corez", - "f4jumble 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_encoding 0.4.0", - "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "zcash_address" version = "0.13.0" @@ -8594,9 +8592,9 @@ dependencies = [ "bech32", "bs58", "corez", - "f4jumble 0.1.1 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "f4jumble", "zcash_encoding 0.4.0", - "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_protocol", ] [[package]] @@ -8644,19 +8642,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "zcash_protocol" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314329b91ec4bbb517441840e47d0b2029bf0b946f086980c96c889c2d92dc5d" -dependencies = [ - "corez", - "document-features", - "hex", - "memuse", - "zcash_encoding 0.4.0", -] - [[package]] name = "zcash_protocol" version = "0.10.5" @@ -8676,14 +8661,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f872800287d118be71bdf6fe8c869c6a6ff6fb0a5762f68fb2af54c97edf0f2" dependencies = [ "bip32", - "bitflags 2.13.1", + "bitflags", "bounded-vec", "hex", "ripemd 0.1.3", "secp256k1", "sha1", "sha2 0.10.9", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -8696,28 +8681,20 @@ dependencies = [ ] [[package]] -name = "zcash_transparent" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "547c012778bae17f58007731af074d638aa146ab0ecfc120adebf23d049aff6c" +name = "zcash_tachyon" +version = "0.0.0" +source = "git+https://github.com/tachyon-zcash/tachyon?rev=2dc7010c3e7771c539fd1c03a06bc6be1a7316c7#2dc7010c3e7771c539fd1c03a06bc6be1a7316c7" dependencies = [ - "bip32", - "bs58", + "blake2b_simd", "corez", - "document-features", - "getset", - "hex", - "nonempty", - "ripemd 0.1.3", - "secp256k1", - "sha2 0.10.9", - "subtle", - "zcash_address 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_encoding 0.4.0", - "zcash_protocol 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_script", - "zcash_spec", - "zip32", + "derive_more 2.1.1", + "ff", + "group", + "lazy_static", + "ragu", + "rand_core 0.10.1", + "zakura-pasta-curves 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zakura-reddsa 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -8736,9 +8713,9 @@ dependencies = [ "secp256k1", "sha2 0.10.9", "subtle", - "zcash_address 0.13.0 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_address", "zcash_encoding 0.4.0", - "zcash_protocol 0.10.5 (git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1)", + "zcash_protocol", "zcash_script", "zcash_spec", "zip32", @@ -8746,22 +8723,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.56" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.56" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] @@ -8781,7 +8758,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", "synstructure 0.13.2", ] @@ -8802,14 +8779,14 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 2.0.118", ] [[package]] name = "zerotrie" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -8818,9 +8795,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.8" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -8829,13 +8806,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.6" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 2.0.118", ] [[package]] @@ -8851,14 +8828,8 @@ dependencies = [ "zcash_spec", ] -[[package]] -name = "zlib-rs" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" - [[package]] name = "zmij" -version = "1.0.23" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 177bbca271..23d0b8322b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,26 +49,26 @@ edition = "2021" # gate refers to `zakura-assets` and nothing else. zakura-assets = { version = "=0.3462171.0" } incrementalmerkletree = { version = "0.8.2", features = ["legacy-api"] } -orchard = { version = "1.0.1", package = "zakura-orchard" } -sapling-crypto = { version = "1.0.1", package = "zakura-sapling-crypto" } +orchard = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-orchard" } +sapling-crypto = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-sapling-crypto" } zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } -zcash_keys = { version = "1.0.1", package = "zakura-keys" } -zcash_primitives = { version = "1.0.1", package = "zakura-primitives" } -zcash_proofs = { version = "1.0.1", package = "zakura-proofs" } +zcash_keys = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-keys" } +zcash_primitives = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-primitives" } +zcash_proofs = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-proofs" } zcash_transparent = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } abscissa_core = { version = "0.7", default-features = false } base64 = "0.22.1" bech32 = "0.11.0" -bellman = { version = "1.0.1", package = "zakura-bellman" } +bellman = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-bellman" } bincode = "1.3" bitflags = "2.9" bitflags-serde-legacy = "0.1.1" bitvec = "1.0" blake2b_simd = "1.0" blake2s_simd = "1.0" -bls12_381 = { version = "1.0.1", package = "zakura-bls12-381" } +bls12_381 = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-bls12-381" } bs58 = "0.5" byteorder = "1.5" bytes = "1.10" @@ -87,7 +87,7 @@ futures = "0.3.31" futures-core = "0.3.31" futures-util = "0.3.31" group = "0.14" -halo2 = { version = "1.0.1", package = "zakura-halo2-proofs" } +halo2 = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-halo2-proofs" } hex = "0.4.3" hex-literal = "0.4" howudoin = "0.1" @@ -106,7 +106,7 @@ itertools = "0.14" jsonrpsee = "0.24.8" jsonrpsee-proc-macros = "0.24.9" jsonrpsee-types = "0.24.9" -jubjub = { version = "1.0.1", package = "zakura-jubjub" } +jubjub = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-jubjub" } lazy_static = "1.4" libzcash_script = "0.1" log = "0.4.27" @@ -129,8 +129,8 @@ rand_10 = { package = "rand", version = "0.10" } rand_chacha_10 = { package = "rand_chacha", version = "0.10" } rand_core_10 = { package = "rand_core", version = "0.10" } rayon = "1.10" -reddsa = { version = "1.0.1", package = "zakura-reddsa" } -redjubjub = { version = "1.0.1", package = "zakura-redjubjub" } +reddsa = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-reddsa" } +redjubjub = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-redjubjub" } regex = "1.11" reqwest = { version = "0.12", default-features = false } ripemd = "0.1" @@ -145,7 +145,7 @@ serde_json = "1.0.140" serde_with = "3.12" sha2 = "0.10" schemars = "1" -sinsemilla = { version = "1.0.1", package = "zakura-sinsemilla" } +sinsemilla = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-sinsemilla" } spandoc = "0.2" anyhow = "1.0" strum = "0.27" diff --git a/crates/zakura-chain/src/transaction/serialize.rs b/crates/zakura-chain/src/transaction/serialize.rs index 4b61cc4c37..c05bbb33cc 100644 --- a/crates/zakura-chain/src/transaction/serialize.rs +++ b/crates/zakura-chain/src/transaction/serialize.rs @@ -913,6 +913,10 @@ impl ZcashSerialize for Transaction { &mut writer, ALLOW_CROSS_ADDRESS_BIT, )?; + zcash_primitives::transaction::components::tachyon::write_v7_bundle( + None, + &mut writer, + )?; } } Ok(()) @@ -1305,6 +1309,15 @@ impl ZcashDeserialize for Transaction { &mut limited_reader, ALLOW_CROSS_ADDRESS_BIT, )?; + if zcash_primitives::transaction::components::tachyon::read_v7_bundle( + &mut limited_reader, + )? + .is_some() + { + return Err(SerializationError::Parse( + "Tachyon bundles are not yet supported", + )); + } Ok(Transaction::V7 { network_upgrade, From 1b80031eb1bfd633343bc6deac1dc2cc2f3322e3 Mon Sep 17 00:00:00 2001 From: c-node Date: Mon, 31 Aug 2026 15:39:59 -0400 Subject: [PATCH 04/15] build: preserve cfg-off MSRV checks --- .github/workflows/lint.yml | 4 ++++ crates/zakura-chain/src/parameters/network_upgrade.rs | 1 + crates/zakura-chain/src/transaction/serialize.rs | 10 ++++++++++ crates/zakura-chain/src/transaction/tests/vectors.rs | 1 + 4 files changed, 16 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 791a0f24e1..bfebfdaf47 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -134,6 +134,10 @@ jobs: # coverage. `zakura-watchdog` is excluded: it depends on `sentry`, which # requires a newer rustc than the workspace library MSRV. - run: cargo check --workspace --bins --exclude zakura-watchdog + env: + # NuTachyon's Tachyon dependency requires Rust 1.97. Clear the repository-level + # opt-in cfg so this job continues to check the supported cfg-off build on 1.91. + RUSTFLAGS: "" docs: name: docs diff --git a/crates/zakura-chain/src/parameters/network_upgrade.rs b/crates/zakura-chain/src/parameters/network_upgrade.rs index 696c87c6ad..ee57782f93 100644 --- a/crates/zakura-chain/src/parameters/network_upgrade.rs +++ b/crates/zakura-chain/src/parameters/network_upgrade.rs @@ -534,6 +534,7 @@ impl From for NetworkUpgrade { zcash_protocol::consensus::NetworkUpgrade::Nu6_3 => Self::Nu6_3, #[cfg(zcash_unstable = "nu7")] zcash_protocol::consensus::NetworkUpgrade::Nu7 => Self::Nu7, + #[cfg(zcash_unstable = "nutachyon")] zcash_protocol::consensus::NetworkUpgrade::NuTachyon => Self::NuTachyon, #[cfg(zcash_unstable = "zfuture")] zcash_protocol::consensus::NetworkUpgrade::ZFuture => Self::ZFuture, diff --git a/crates/zakura-chain/src/transaction/serialize.rs b/crates/zakura-chain/src/transaction/serialize.rs index c05bbb33cc..2ef3bd33c9 100644 --- a/crates/zakura-chain/src/transaction/serialize.rs +++ b/crates/zakura-chain/src/transaction/serialize.rs @@ -913,10 +913,13 @@ impl ZcashSerialize for Transaction { &mut writer, ALLOW_CROSS_ADDRESS_BIT, )?; + #[cfg(zcash_unstable = "nutachyon")] zcash_primitives::transaction::components::tachyon::write_v7_bundle( None, &mut writer, )?; + #[cfg(not(zcash_unstable = "nutachyon"))] + writer.write_u8(0)?; } } Ok(()) @@ -1309,6 +1312,7 @@ impl ZcashDeserialize for Transaction { &mut limited_reader, ALLOW_CROSS_ADDRESS_BIT, )?; + #[cfg(zcash_unstable = "nutachyon")] if zcash_primitives::transaction::components::tachyon::read_v7_bundle( &mut limited_reader, )? @@ -1318,6 +1322,12 @@ impl ZcashDeserialize for Transaction { "Tachyon bundles are not yet supported", )); } + #[cfg(not(zcash_unstable = "nutachyon"))] + if limited_reader.read_u8()? != 0 { + return Err(SerializationError::Parse( + "Tachyon bundles are not available in this build", + )); + } Ok(Transaction::V7 { network_upgrade, diff --git a/crates/zakura-chain/src/transaction/tests/vectors.rs b/crates/zakura-chain/src/transaction/tests/vectors.rs index 8553ada55c..29d0883a0a 100644 --- a/crates/zakura-chain/src/transaction/tests/vectors.rs +++ b/crates/zakura-chain/src/transaction/tests/vectors.rs @@ -1830,6 +1830,7 @@ fn v6_version_group_id_matches_librustzcash_and_wire_format() { } #[test] +#[cfg(zcash_unstable = "nutachyon")] fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { use crate::parameters::TX_V7_VERSION_GROUP_ID; From ef481849f0b1d09a4611b9aedbb6606f562fc9c3 Mon Sep 17 00:00:00 2001 From: c-node Date: Mon, 31 Aug 2026 15:47:11 -0400 Subject: [PATCH 05/15] build: unify Tachyon curve dependencies --- Cargo.lock | 100 +++++++++++------------------------------------------ Cargo.toml | 4 +++ 2 files changed, 25 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bc664064a..6ddaa78810 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4748,7 +4748,7 @@ dependencies = [ "maybe-rayon 0.1.1 (git+https://github.com/tachyon-zcash/maybe-rayon.git?rev=5c37ee0dd448e8c1edc4529d0b8897ecaff5aad1)", "ragu_macros", "rand 0.10.2", - "zakura-pasta-curves 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zakura-pasta-curves", ] [[package]] @@ -7812,17 +7812,6 @@ dependencies = [ "zakura-pairing", ] -[[package]] -name = "zakura-bls12-381" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9c833a85b5ba3303a5f14ed760d613bee8a9769923da5a1e7f51851849e7f5" -dependencies = [ - "ff", - "rand_core 0.10.1", - "subtle", -] - [[package]] name = "zakura-bls12-381" version = "1.0.1" @@ -7888,10 +7877,10 @@ dependencies = [ "uint 0.10.0", "x25519-dalek", "zakura-halo2-proofs", - "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-jubjub", "zakura-orchard", "zakura-primitives", - "zakura-reddsa 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-reddsa", "zakura-redjubjub", "zakura-sapling-crypto", "zakura-sinsemilla", @@ -7934,7 +7923,7 @@ dependencies = [ "tower 0.4.13", "tracing", "zakura-bellman", - "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-bls12-381", "zakura-chain", "zakura-halo2-proofs", "zakura-header-chain", @@ -7964,7 +7953,7 @@ dependencies = [ "subtle", "zakura-halo2-poseidon", "zakura-halo2-proofs", - "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-pasta-curves", "zakura-sinsemilla", ] @@ -7981,7 +7970,7 @@ dependencies = [ "bitvec", "ff", "group", - "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-pasta-curves", ] [[package]] @@ -7997,7 +7986,7 @@ dependencies = [ "rand_core 0.10.1", "tracing", "zakura-halo2-legacy-pdqsort", - "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-pasta-curves", ] [[package]] @@ -8027,20 +8016,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "zakura-jubjub" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b10077e35b311a780366567771c4730245a4df7bd23b1f3414444b0e5c3a9cd" -dependencies = [ - "bitvec", - "ff", - "group", - "rand_core 0.10.1", - "subtle", - "zakura-bls12-381 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "zakura-jubjub" version = "1.0.1" @@ -8051,7 +8026,7 @@ dependencies = [ "group", "rand_core 0.10.1", "subtle", - "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-bls12-381", ] [[package]] @@ -8165,8 +8140,8 @@ dependencies = [ "zakura-halo2-gadgets", "zakura-halo2-poseidon", "zakura-halo2-proofs", - "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", - "zakura-reddsa 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-pasta-curves", + "zakura-reddsa", "zakura-sinsemilla", "zcash_note_encryption", "zcash_spec", @@ -8181,21 +8156,6 @@ dependencies = [ "group", ] -[[package]] -name = "zakura-pasta-curves" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c906d3541e9e6b71aaa77af25c229a0c1ed38df58930fa75f2fd569632989dd" -dependencies = [ - "blake2b_simd", - "ff", - "group", - "once_cell", - "rand 0.10.2", - "static_assertions", - "subtle", -] - [[package]] name = "zakura-pasta-curves" version = "1.0.1" @@ -8231,7 +8191,7 @@ dependencies = [ "rand_core 0.10.1", "secp256k1", "sha2 0.10.9", - "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-jubjub", "zakura-orchard", "zakura-redjubjub", "zakura-sapling-crypto", @@ -8255,31 +8215,13 @@ dependencies = [ "rand_core 0.10.1", "wagyu-zcash-parameters", "zakura-bellman", - "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", - "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-bls12-381", + "zakura-jubjub", "zakura-primitives", "zakura-redjubjub", "zakura-sapling-crypto", ] -[[package]] -name = "zakura-reddsa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecd8e46ef79bd3675733cee6741c432ccd259b9e060e4838d2cc311a654744f" -dependencies = [ - "blake2b_simd", - "frost-rerandomized", - "group", - "hex", - "rand_core 0.10.1", - "serde", - "thiserror 2.0.18", - "zakura-jubjub 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zakura-pasta-curves 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize", -] - [[package]] name = "zakura-reddsa" version = "1.0.1" @@ -8292,8 +8234,8 @@ dependencies = [ "rand_core 0.10.1", "serde", "thiserror 2.0.18", - "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", - "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-jubjub", + "zakura-pasta-curves", "zeroize", ] @@ -8305,7 +8247,7 @@ dependencies = [ "rand_core 0.10.1", "serde", "thiserror 2.0.18", - "zakura-reddsa 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-reddsa", "zeroize", ] @@ -8402,8 +8344,8 @@ dependencies = [ "subtle", "tracing", "zakura-bellman", - "zakura-bls12-381 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", - "zakura-jubjub 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-bls12-381", + "zakura-jubjub", "zakura-redjubjub", "zcash_note_encryption", "zcash_spec", @@ -8436,7 +8378,7 @@ dependencies = [ "group", "once_cell", "subtle", - "zakura-pasta-curves 1.0.1 (git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8)", + "zakura-pasta-curves", ] [[package]] @@ -8693,8 +8635,8 @@ dependencies = [ "lazy_static", "ragu", "rand_core 0.10.1", - "zakura-pasta-curves 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zakura-reddsa 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zakura-pasta-curves", + "zakura-reddsa", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 23d0b8322b..4add503da0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -183,6 +183,10 @@ zcash_note_encryption = "0.4.1" zcash_script = "0.4.5" config = { version = "0.15.14", default-features = false, features = ["toml"] } +[patch.crates-io] +zakura-pasta-curves = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8" } +zakura-reddsa = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8" } + # All cargo-release configuration lives in this table. Do not add a root # release.toml: it would silently shadow every setting here (cargo-release # reads a workspace release.toml in preference to this metadata table). From 66079ca276c526e91e15bb2e40781178467d03c3 Mon Sep 17 00:00:00 2001 From: c-node Date: Tue, 1 Sep 2026 01:54:56 -0400 Subject: [PATCH 06/15] build: update NuTachyon dependency pins --- Cargo.lock | 69 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 36 ++++++++++++++-------------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ddaa78810..cac0f3a2c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1669,7 +1669,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.1" -source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" +source = "git+https://github.com/zcash/librustzcash.git?rev=c7235b1fc4b4296b8af2e26bf2f6adb24f873f56#c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" dependencies = [ "blake2b_simd", ] @@ -7799,7 +7799,7 @@ checksum = "1518d74211e0ae2422cab4560acdc67f72ae8fcdb871294eb03476a5a4586a74" [[package]] name = "zakura-bellman" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "bitvec", "blake2s_simd", @@ -7815,7 +7815,7 @@ dependencies = [ [[package]] name = "zakura-bls12-381" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "ff", "group", @@ -7877,6 +7877,7 @@ dependencies = [ "uint 0.10.0", "x25519-dalek", "zakura-halo2-proofs", + "zakura-history", "zakura-jubjub", "zakura-orchard", "zakura-primitives", @@ -7886,7 +7887,6 @@ dependencies = [ "zakura-sinsemilla", "zakura-test", "zcash_address", - "zcash_history", "zcash_note_encryption", "zcash_protocol", "zcash_script", @@ -7945,7 +7945,7 @@ dependencies = [ [[package]] name = "zakura-halo2-gadgets" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "arrayvec", "ff", @@ -7960,12 +7960,12 @@ dependencies = [ [[package]] name = "zakura-halo2-legacy-pdqsort" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" [[package]] name = "zakura-halo2-poseidon" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "bitvec", "ff", @@ -7976,7 +7976,7 @@ dependencies = [ [[package]] name = "zakura-halo2-proofs" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "blake2b_simd", "ff", @@ -8004,6 +8004,17 @@ dependencies = [ "zakura-test", ] +[[package]] +name = "zakura-history" +version = "1.0.0" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +dependencies = [ + "blake2b_simd", + "corez", + "primitive-types", + "zcash_encoding 0.5.0", +] + [[package]] name = "zakura-jsonl-trace" version = "1.2.0" @@ -8019,7 +8030,7 @@ dependencies = [ [[package]] name = "zakura-jubjub" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "bitvec", "ff", @@ -8032,7 +8043,7 @@ dependencies = [ [[package]] name = "zakura-keys" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "bech32", "blake2b_simd", @@ -8116,7 +8127,7 @@ dependencies = [ [[package]] name = "zakura-orchard" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "aes", "bitvec", @@ -8151,7 +8162,7 @@ dependencies = [ [[package]] name = "zakura-pairing" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "group", ] @@ -8159,7 +8170,7 @@ dependencies = [ [[package]] name = "zakura-pasta-curves" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "blake2b_simd", "cc", @@ -8175,7 +8186,7 @@ dependencies = [ [[package]] name = "zakura-primitives" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "blake2b_simd", "block-buffer 0.11.0-rc.3", @@ -8206,7 +8217,7 @@ dependencies = [ [[package]] name = "zakura-proofs" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "blake2b_simd", "document-features", @@ -8225,7 +8236,7 @@ dependencies = [ [[package]] name = "zakura-reddsa" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "blake2b_simd", "frost-rerandomized", @@ -8242,7 +8253,7 @@ dependencies = [ [[package]] name = "zakura-redjubjub" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "rand_core 0.10.1", "serde", @@ -8322,7 +8333,7 @@ dependencies = [ [[package]] name = "zakura-sapling-crypto" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "aes", "bitvec", @@ -8373,7 +8384,7 @@ dependencies = [ [[package]] name = "zakura-sinsemilla" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=a22713f15769fd3dfcf0c1816fcde6114ac344e8#a22713f15769fd3dfcf0c1816fcde6114ac344e8" +source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" dependencies = [ "group", "once_cell", @@ -8529,7 +8540,7 @@ dependencies = [ [[package]] name = "zcash_address" version = "0.13.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" +source = "git+https://github.com/zcash/librustzcash.git?rev=c7235b1fc4b4296b8af2e26bf2f6adb24f873f56#c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" dependencies = [ "bech32", "bs58", @@ -8553,24 +8564,14 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2871fd3c37eaeb635c815a9c97495553bf8237a481b9f8b37eba661764bf937" dependencies = [ "corez", "hex", "nonempty", ] -[[package]] -name = "zcash_history" -version = "0.6.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" -dependencies = [ - "blake2b_simd", - "corez", - "primitive-types", - "zcash_encoding 0.5.0", -] - [[package]] name = "zcash_note_encryption" version = "0.4.2" @@ -8587,7 +8588,7 @@ dependencies = [ [[package]] name = "zcash_protocol" version = "0.10.5" -source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" +source = "git+https://github.com/zcash/librustzcash.git?rev=c7235b1fc4b4296b8af2e26bf2f6adb24f873f56#c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" dependencies = [ "corez", "document-features", @@ -8642,7 +8643,7 @@ dependencies = [ [[package]] name = "zcash_transparent" version = "0.10.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=1643fcfe846d50426452dda3fe4ef2182470fac1#1643fcfe846d50426452dda3fe4ef2182470fac1" +source = "git+https://github.com/zcash/librustzcash.git?rev=c7235b1fc4b4296b8af2e26bf2f6adb24f873f56#c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" dependencies = [ "bip32", "bs58", diff --git a/Cargo.toml b/Cargo.toml index 4add503da0..bf716673c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,26 +49,26 @@ edition = "2021" # gate refers to `zakura-assets` and nothing else. zakura-assets = { version = "=0.3462171.0" } incrementalmerkletree = { version = "0.8.2", features = ["legacy-api"] } -orchard = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-orchard" } -sapling-crypto = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-sapling-crypto" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } -zcash_keys = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-keys" } -zcash_primitives = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-primitives" } -zcash_proofs = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-proofs" } -zcash_transparent = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } -zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "1643fcfe846d50426452dda3fe4ef2182470fac1" } +orchard = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-orchard" } +sapling-crypto = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-sapling-crypto" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } +zcash_history = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-history" } +zcash_keys = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-keys" } +zcash_primitives = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-primitives" } +zcash_proofs = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-proofs" } +zcash_transparent = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } +zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } abscissa_core = { version = "0.7", default-features = false } base64 = "0.22.1" bech32 = "0.11.0" -bellman = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-bellman" } +bellman = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-bellman" } bincode = "1.3" bitflags = "2.9" bitflags-serde-legacy = "0.1.1" bitvec = "1.0" blake2b_simd = "1.0" blake2s_simd = "1.0" -bls12_381 = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-bls12-381" } +bls12_381 = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-bls12-381" } bs58 = "0.5" byteorder = "1.5" bytes = "1.10" @@ -87,7 +87,7 @@ futures = "0.3.31" futures-core = "0.3.31" futures-util = "0.3.31" group = "0.14" -halo2 = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-halo2-proofs" } +halo2 = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-halo2-proofs" } hex = "0.4.3" hex-literal = "0.4" howudoin = "0.1" @@ -106,7 +106,7 @@ itertools = "0.14" jsonrpsee = "0.24.8" jsonrpsee-proc-macros = "0.24.9" jsonrpsee-types = "0.24.9" -jubjub = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-jubjub" } +jubjub = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-jubjub" } lazy_static = "1.4" libzcash_script = "0.1" log = "0.4.27" @@ -129,8 +129,8 @@ rand_10 = { package = "rand", version = "0.10" } rand_chacha_10 = { package = "rand_chacha", version = "0.10" } rand_core_10 = { package = "rand_core", version = "0.10" } rayon = "1.10" -reddsa = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-reddsa" } -redjubjub = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-redjubjub" } +reddsa = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-reddsa" } +redjubjub = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-redjubjub" } regex = "1.11" reqwest = { version = "0.12", default-features = false } ripemd = "0.1" @@ -145,7 +145,7 @@ serde_json = "1.0.140" serde_with = "3.12" sha2 = "0.10" schemars = "1" -sinsemilla = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8", package = "zakura-sinsemilla" } +sinsemilla = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-sinsemilla" } spandoc = "0.2" anyhow = "1.0" strum = "0.27" @@ -184,8 +184,8 @@ zcash_script = "0.4.5" config = { version = "0.15.14", default-features = false, features = ["toml"] } [patch.crates-io] -zakura-pasta-curves = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8" } -zakura-reddsa = { git = "https://github.com/zakura-core/common.git", rev = "a22713f15769fd3dfcf0c1816fcde6114ac344e8" } +zakura-pasta-curves = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" } +zakura-reddsa = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" } # All cargo-release configuration lives in this table. Do not add a root # release.toml: it would silently shadow every setting here (cargo-release From 249c8627e3a60c11cfb23ae39c4a3f729b72c8ce Mon Sep 17 00:00:00 2001 From: c-node Date: Tue, 1 Sep 2026 15:20:51 -0400 Subject: [PATCH 07/15] feat: add Tachyon bundles to V7 transactions --- AGENTS.md | 2 +- Cargo.lock | 7 + Cargo.toml | 4 +- crates/xtask/src/main.rs | 2 +- crates/zakura-assets/Cargo.toml | 2 +- crates/zakura-chain/Cargo.toml | 1 + crates/zakura-chain/src/block.rs | 11 + crates/zakura-chain/src/block/arbitrary.rs | 12 + crates/zakura-chain/src/history_tree.rs | 86 +- .../src/history_tree/tests/vectors.rs | 65 +- crates/zakura-chain/src/lib.rs | 1 + .../src/parallel/commitment_aux_verify.rs | 13 + crates/zakura-chain/src/parallel/tree.rs | 7 + .../src/primitives/zcash_history.rs | 60 +- .../primitives/zcash_history/tests/vectors.rs | 47 ++ crates/zakura-chain/src/tachyon.rs | 215 +++++ crates/zakura-chain/src/transaction.rs | 92 ++- .../zakura-chain/src/transaction/serialize.rs | 31 +- .../src/transaction/tachyon_shielded.rs | 90 +++ .../src/transaction/tests/vectors.rs | 66 ++ crates/zakura-chain/src/value_balance.rs | 66 +- .../src/value_balance/arbitrary.rs | 8 +- .../src/value_balance/tests/prop.rs | 54 +- crates/zakura-consensus/Cargo.toml | 2 + crates/zakura-consensus/src/block.rs | 20 + crates/zakura-consensus/src/block/tachyon.rs | 84 ++ .../src/block/tachyon/tests.rs | 368 +++++++++ crates/zakura-consensus/src/error.rs | 40 + crates/zakura-consensus/src/primitives.rs | 1 + .../src/primitives/tachyon.rs | 34 + crates/zakura-consensus/src/transaction.rs | 30 +- .../zakura-consensus/src/transaction/check.rs | 30 + .../zakura-consensus/src/transaction/tests.rs | 2 + .../src/transaction/tests/prop.rs | 1 + .../src/transaction/tests/tachyon.rs | 406 ++++++++++ .../zakura-network/src/peer_set/initialize.rs | 7 +- .../src/zakura/block_sync/peer_registry.rs | 7 +- crates/zakura-rpc/Cargo.toml | 3 + crates/zakura-rpc/src/methods.rs | 19 + .../tests/snapshots/get_info@mainnet_10.snap | 2 +- .../tests/snapshots/get_info@testnet_10.snap | 2 +- .../get_network_info@mainnet_10.snap | 2 +- .../get_network_info@testnet_10.snap | 2 +- crates/zakura-rpc/src/methods/tests/utils.rs | 1 + .../src/methods/types/get_block_template.rs | 1 + .../types/get_block_template/tachyon.rs | 736 ++++++++++++++++++ crates/zakura-state/Cargo.toml | 1 + crates/zakura-state/src/constants.rs | 12 +- crates/zakura-state/src/error.rs | 33 + crates/zakura-state/src/lib.rs | 2 +- crates/zakura-state/src/request.rs | 23 + crates/zakura-state/src/response.rs | 21 +- crates/zakura-state/src/service.rs | 90 +++ crates/zakura-state/src/service/arbitrary.rs | 1 + .../zakura-state/src/service/check/anchors.rs | 32 + .../src/service/check/nullifier.rs | 21 + .../src/service/check/tests/vectors.rs | 1 + .../src/service/finalized_state.rs | 26 + .../finalized_state/commitment_aux_verify.rs | 64 +- .../finalized_state/disk_format/chain.rs | 17 +- .../disk_format/chain/tests.rs | 2 + .../disk_format/header_chain.rs | 5 +- .../finalized_state/disk_format/shielded.rs | 59 ++ .../finalized_state/disk_format/tests/prop.rs | 31 +- .../block_info_raw_data@mainnet_0.snap | 2 +- .../block_info_raw_data@mainnet_1.snap | 4 +- .../block_info_raw_data@mainnet_2.snap | 6 +- .../block_info_raw_data@testnet_0.snap | 2 +- .../block_info_raw_data@testnet_1.snap | 4 +- .../block_info_raw_data@testnet_2.snap | 6 +- .../tests/snapshots/column_family_names.snap | 4 + .../empty_column_families@mainnet_0.snap | 4 + .../empty_column_families@mainnet_1.snap | 4 + .../empty_column_families@mainnet_2.snap | 4 + .../empty_column_families@no_blocks.snap | 4 + .../empty_column_families@testnet_0.snap | 4 + .../empty_column_families@testnet_1.snap | 4 + .../empty_column_families@testnet_2.snap | 4 + ...p_chain_value_pool_raw_data@mainnet_0.snap | 2 +- ...p_chain_value_pool_raw_data@mainnet_1.snap | 2 +- ...p_chain_value_pool_raw_data@mainnet_2.snap | 2 +- ...p_chain_value_pool_raw_data@testnet_0.snap | 2 +- ...p_chain_value_pool_raw_data@testnet_1.snap | 2 +- ...p_chain_value_pool_raw_data@testnet_2.snap | 2 +- .../finalized_state/disk_format/upgrade.rs | 9 +- .../finalized_state/zakura_db/rollback.rs | 51 +- .../finalized_state/zakura_db/shielded.rs | 129 ++- .../src/service/non_finalized_state.rs | 2 + .../src/service/non_finalized_state/chain.rs | 387 ++++++++- .../service/non_finalized_state/tests/prop.rs | 22 +- .../non_finalized_state/tests/vectors.rs | 9 + .../write/tests/full_state_coherence.rs | 1 + .../write/vct_authentication_sweep/tests.rs | 4 + crates/zakurad/Cargo.toml | 2 +- docker/Dockerfile | 2 +- docker/ubuntu-package.Dockerfile | 2 +- docs/changelog/unreleased/795.md | 8 +- scripts/pack-assets-crate.py | 4 +- 98 files changed, 3732 insertions(+), 151 deletions(-) create mode 100644 crates/zakura-chain/src/tachyon.rs create mode 100644 crates/zakura-chain/src/transaction/tachyon_shielded.rs create mode 100644 crates/zakura-consensus/src/block/tachyon.rs create mode 100644 crates/zakura-consensus/src/block/tachyon/tests.rs create mode 100644 crates/zakura-consensus/src/primitives/tachyon.rs create mode 100644 crates/zakura-consensus/src/transaction/tests/tachyon.rs create mode 100644 crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs diff --git a/AGENTS.md b/AGENTS.md index 5e0cd3d357..ceb6285a5d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,7 +62,7 @@ cargo nextest run --profile zakura-integration Zakura is a Zcash full node implementation in Rust. It is a validator node — it excludes features not strictly needed for block validation and chain sync. - **Rust edition**: 2021 -- **MSRV**: 1.91 (unified across the library crates and the zakurad binary) +- **MSRV**: 1.97 (unified across the library crates and the zakurad binary) - **Database format version**: defined in `crates/zakura-state/src/constants.rs` ## Crate Architecture diff --git a/Cargo.lock b/Cargo.lock index cac0f3a2c3..e53f23edcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7890,6 +7890,7 @@ dependencies = [ "zcash_note_encryption", "zcash_protocol", "zcash_script", + "zcash_tachyon", "zcash_transparent", ] @@ -7912,6 +7913,7 @@ dependencies = [ "once_cell", "proptest", "proptest-derive", + "ragu", "rand 0.10.2", "rand 0.8.6", "rayon", @@ -7939,6 +7941,7 @@ dependencies = [ "zakura-tower-fallback", "zcash_protocol", "zcash_script", + "zcash_tachyon", "zcash_transparent", ] @@ -8268,6 +8271,7 @@ version = "8.0.0" dependencies = [ "anyhow", "base64 0.22.1", + "blake2b_simd", "bytes", "chrono", "color-eyre", @@ -8291,6 +8295,7 @@ dependencies = [ "phf", "proptest", "prost", + "ragu", "rand 0.10.2", "rand 0.8.6", "rustls", @@ -8327,6 +8332,7 @@ dependencies = [ "zcash_address", "zcash_protocol", "zcash_script", + "zcash_tachyon", "zcash_transparent", ] @@ -8440,6 +8446,7 @@ dependencies = [ "zakura-orchard", "zakura-sapling-crypto", "zakura-test", + "zcash_tachyon", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bf716673c6..b4b616a7cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/zakura-core/zakura" homepage = "https://github.com/zakura-core/zakura" keywords = ["zakura", "zcash"] -rust-version = "1.91" +rust-version = "1.97" edition = "2021" [workspace.dependencies] @@ -58,6 +58,8 @@ zcash_primitives = { git = "https://github.com/zakura-core/common.git", rev = "6 zcash_proofs = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-proofs" } zcash_transparent = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } +zcash_tachyon = { git = "https://github.com/tachyon-zcash/tachyon", rev = "2dc7010c3e7771c539fd1c03a06bc6be1a7316c7", version = "0.0.0", default-features = false } +ragu = { git = "https://github.com/tachyon-zcash/ragu", rev = "f4f6315b10ed96944c40b2bbdbca0fe06af66ce0", default-features = false, features = ["mock"] } abscissa_core = { version = "0.7", default-features = false } base64 = "0.22.1" bech32 = "0.11.0" diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 63864b7c68..5f61ab719a 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -13,7 +13,7 @@ mod rpc_artifacts; const DEFAULT_FEATURES: &str = "default-release-binaries"; const DEFAULT_UBUNTU_IMAGE: &str = "ubuntu:22.04"; -const DEFAULT_RUST_VERSION: &str = "1.91"; +const DEFAULT_RUST_VERSION: &str = "1.97"; const DEFAULT_IMAGE_TAG: &str = "zebra-ubuntu-package:local"; const OUTPUT_BINARY_NAME: &str = "zakurad"; diff --git a/crates/zakura-assets/Cargo.toml b/crates/zakura-assets/Cargo.toml index 6fb592dc0e..9aaf06f4f3 100644 --- a/crates/zakura-assets/Cargo.toml +++ b/crates/zakura-assets/Cargo.toml @@ -16,7 +16,7 @@ name = "zakura-assets" version = "0.3457371.0" authors = ["Zakura Contributors"] edition = "2021" -rust-version = "1.91" +rust-version = "1.97" license = "MIT OR Apache-2.0" repository = "https://github.com/zakura-core/zakura" documentation = "https://docs.rs/zakura" diff --git a/crates/zakura-chain/Cargo.toml b/crates/zakura-chain/Cargo.toml index 4cc633b695..7de38c59a8 100644 --- a/crates/zakura-chain/Cargo.toml +++ b/crates/zakura-chain/Cargo.toml @@ -89,6 +89,7 @@ sapling-crypto.workspace = true zcash_protocol.workspace = true zcash_address.workspace = true zcash_transparent = { workspace = true } +zcash_tachyon = { workspace = true } sinsemilla = { workspace = true } # Time diff --git a/crates/zakura-chain/src/block.rs b/crates/zakura-chain/src/block.rs index 56b0026d66..a1d211aa36 100644 --- a/crates/zakura-chain/src/block.rs +++ b/crates/zakura-chain/src/block.rs @@ -269,6 +269,17 @@ impl Block { .expect("number of transactions must fit u64") } + /// Count how many Tachyon transactions exist in a block, + /// i.e. transactions carrying a tachyon bundle (NuTachyon). + pub fn tachyon_transactions_count(&self) -> u64 { + self.transactions + .iter() + .filter(|tx| tx.has_tachyon_shielded_data()) + .count() + .try_into() + .expect("number of transactions must fit u64") + } + /// Returns the overall chain value pool change in this block---the negative sum of the /// transaction value balances in this block. /// diff --git a/crates/zakura-chain/src/block/arbitrary.rs b/crates/zakura-chain/src/block/arbitrary.rs index c26a1d844d..b0f910bcdd 100644 --- a/crates/zakura-chain/src/block/arbitrary.rs +++ b/crates/zakura-chain/src/block/arbitrary.rs @@ -430,6 +430,7 @@ impl Block { let mut sapling_tree = sapling::tree::NoteCommitmentTree::default(); let mut orchard_tree = orchard::tree::NoteCommitmentTree::default(); let mut ironwood_tree = ironwood::tree::NoteCommitmentTree::default(); + let mut tachyon_anchor = crate::tachyon::Anchor::default(); // The history tree usually takes care of "creating itself". But this // only works when blocks are pushed into it starting from genesis // (or at least pre-Heartwood, where the tree is not required). @@ -481,6 +482,15 @@ impl Block { // delete invalid transactions block.transactions = new_transactions; + if let Some(pool_height) = + crate::tachyon::pool_height(¤t.network, block.coinbase_height().unwrap()) + { + tachyon_anchor = tachyon_anchor + .advance_with_block(pool_height, block) + .expect("generated Tachyon stamps have valid anchor transitions") + .post_block; + } + // fix commitment (must be done after finishing changing the block) if generate_valid_commitments { let current_height = block.coinbase_height().unwrap(); @@ -537,6 +547,7 @@ impl Block { &sapling_tree.root(), &orchard_tree.root(), &ironwood_tree.root(), + &tachyon_anchor, ) .unwrap(); } else { @@ -547,6 +558,7 @@ impl Block { &sapling_tree.root(), &orchard_tree.root(), &ironwood_tree.root(), + &tachyon_anchor, ) .unwrap(), ); diff --git a/crates/zakura-chain/src/history_tree.rs b/crates/zakura-chain/src/history_tree.rs index cf161aa981..9e884f3c49 100644 --- a/crates/zakura-chain/src/history_tree.rs +++ b/crates/zakura-chain/src/history_tree.rs @@ -21,8 +21,9 @@ use crate::{ parameters::{Network, NetworkUpgrade}, primitives::zcash_history::{ Entry, Tree, V1 as PreOrchard, V2 as OrchardOnward, V3 as IronwoodOnward, + V4 as TachyonOnward, }, - sapling, + sapling, tachyon, }; /// An error describing why a history tree operation failed. @@ -60,6 +61,8 @@ enum InnerHistoryTree { OrchardOnward(Tree), /// An Ironwood-onward tree. IronwoodOnward(Tree), + /// A Tachyon-onward tree. + TachyonOnward(Tree), } /// History tree (Merkle mountain range) structure that contains information about @@ -132,7 +135,7 @@ impl NonEmptyHistoryTree { )?; InnerHistoryTree::OrchardOnward(tree) } - NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 | NetworkUpgrade::NuTachyon => { + NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 => { let tree = Tree::::new_from_cache( network, network_upgrade, @@ -142,6 +145,16 @@ impl NonEmptyHistoryTree { )?; InnerHistoryTree::IronwoodOnward(tree) } + NetworkUpgrade::NuTachyon => { + let tree = Tree::::new_from_cache( + network, + network_upgrade, + size, + &peaks, + &Default::default(), + )?; + InnerHistoryTree::TachyonOnward(tree) + } #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => { @@ -179,10 +192,17 @@ impl NonEmptyHistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Result { Self::from_parts( network, - HistoryTreeBlockParts::from_block(&block, sapling_root, orchard_root, ironwood_root), + HistoryTreeBlockParts::from_block( + &block, + sapling_root, + orchard_root, + ironwood_root, + tachyon_anchor, + ), ) } @@ -226,10 +246,14 @@ impl NonEmptyHistoryTree { )?; (InnerHistoryTree::OrchardOnward(tree), entry) } - NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 | NetworkUpgrade::NuTachyon => { + NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 => { let (tree, entry) = Tree::::new_from_parts(network, parts)?; (InnerHistoryTree::IronwoodOnward(tree), entry) } + NetworkUpgrade::NuTachyon => { + let (tree, entry) = Tree::::new_from_parts(network, parts)?; + (InnerHistoryTree::TachyonOnward(tree), entry) + } #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => { @@ -267,12 +291,14 @@ impl NonEmptyHistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Result<(), HistoryTreeError> { self.push_from_parts(HistoryTreeBlockParts::from_block( &block, sapling_root, orchard_root, ironwood_root, + tachyon_anchor, )) } @@ -326,6 +352,9 @@ impl NonEmptyHistoryTree { InnerHistoryTree::IronwoodOnward(tree) => tree .append_leaf_parts(parts) .map_err(|e| HistoryTreeError::InnerError { inner: e })?, + InnerHistoryTree::TachyonOnward(tree) => tree + .append_leaf_parts(parts) + .map_err(|e| HistoryTreeError::InnerError { inner: e })?, }; for entry in new_entries { // Not every entry is a peak; those will be trimmed later @@ -346,14 +375,21 @@ impl NonEmptyHistoryTree { &'a sapling::tree::Root, &'a orchard::tree::Root, &'a ironwood::tree::Root, + &'a tachyon::Anchor, ), >, >( &mut self, iter: T, ) -> Result<(), HistoryTreeError> { - for (block, sapling_root, orchard_root, ironwood_root) in iter { - self.push(block, sapling_root, orchard_root, ironwood_root)?; + for (block, sapling_root, orchard_root, ironwood_root, tachyon_anchor) in iter { + self.push( + block, + sapling_root, + orchard_root, + ironwood_root, + tachyon_anchor, + )?; } Ok(()) } @@ -460,6 +496,15 @@ impl NonEmptyHistoryTree { &Default::default(), )?) } + InnerHistoryTree::TachyonOnward(_) => { + InnerHistoryTree::TachyonOnward(Tree::::new_from_cache( + &self.network, + self.network_upgrade, + self.size, + &self.peaks, + &Default::default(), + )?) + } }; Ok(()) } @@ -470,6 +515,7 @@ impl NonEmptyHistoryTree { InnerHistoryTree::PreOrchard(tree) => tree.hash(), InnerHistoryTree::OrchardOnward(tree) => tree.hash(), InnerHistoryTree::IronwoodOnward(tree) => tree.hash(), + InnerHistoryTree::TachyonOnward(tree) => tree.hash(), } } @@ -527,6 +573,16 @@ impl Clone for NonEmptyHistoryTree { ) .expect("rebuilding an existing tree should always work"), ), + InnerHistoryTree::TachyonOnward(_) => InnerHistoryTree::TachyonOnward( + Tree::::new_from_cache( + &self.network, + self.network_upgrade, + self.size, + &self.peaks, + &Default::default(), + ) + .expect("rebuilding an existing tree should always work"), + ), }; NonEmptyHistoryTree { network: self.network.clone(), @@ -555,10 +611,17 @@ impl HistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Result { Self::from_parts( network, - HistoryTreeBlockParts::from_block(&block, sapling_root, orchard_root, ironwood_root), + HistoryTreeBlockParts::from_block( + &block, + sapling_root, + orchard_root, + ironwood_root, + tachyon_anchor, + ), ) } @@ -593,10 +656,17 @@ impl HistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Result<(), HistoryTreeError> { self.push_from_parts( network, - HistoryTreeBlockParts::from_block(&block, sapling_root, orchard_root, ironwood_root), + HistoryTreeBlockParts::from_block( + &block, + sapling_root, + orchard_root, + ironwood_root, + tachyon_anchor, + ), ) } diff --git a/crates/zakura-chain/src/history_tree/tests/vectors.rs b/crates/zakura-chain/src/history_tree/tests/vectors.rs index 09ca1c8e5a..34314e9265 100644 --- a/crates/zakura-chain/src/history_tree/tests/vectors.rs +++ b/crates/zakura-chain/src/history_tree/tests/vectors.rs @@ -16,6 +16,8 @@ use crate::{ serialization::ZcashDeserializeInto, }; +const DEFAULT_TACHYON_ANCHOR: crate::tachyon::Anchor = crate::tachyon::Anchor([0; 32]); + use color_eyre::eyre; use eyre::Result; @@ -69,6 +71,7 @@ fn push_and_prune_for_network_upgrade( &first_sapling_root, &Default::default(), &Default::default(), + &Default::default(), )?; assert_eq!(tree.size(), 1); @@ -102,6 +105,7 @@ fn push_and_prune_for_network_upgrade( &second_sapling_root, &Default::default(), &Default::default(), + &Default::default(), ) .unwrap(); @@ -176,6 +180,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + &Default::default(), )?; parts_tree.push_from_parts( &network, @@ -184,6 +189,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + &Default::default(), ), )?; @@ -196,6 +202,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &first_sapling_root, &Default::default(), &Default::default(), + &Default::default(), )?; parts_tree.push_from_parts( &network, @@ -204,6 +211,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &first_sapling_root, &Default::default(), &Default::default(), + &Default::default(), ), )?; @@ -226,6 +234,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &second_sapling_root, &Default::default(), &Default::default(), + &Default::default(), )?; parts_tree.push_from_parts( &network, @@ -234,6 +243,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &second_sapling_root, &Default::default(), &Default::default(), + &Default::default(), ), )?; @@ -278,9 +288,11 @@ fn from_cache_preserves_ironwood_history_version() -> Result<()> { sapling_root: &Default::default(), orchard_root: &Default::default(), ironwood_root: &ironwood_root, + tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: 0, orchard_tx: 0, ironwood_tx: 1, + tachyon_tx: 0, }, )?; // Decoding the same peaks as V2 succeeds, because the serialized V3 @@ -361,6 +373,7 @@ fn from_block_delegation_preserves_all_history_versions() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; let parts_tree = NonEmptyHistoryTree::from_parts( &network, @@ -370,9 +383,11 @@ fn from_block_delegation_preserves_all_history_versions() -> Result<()> { sapling_root: &sapling_root, orchard_root: &orchard_root, ironwood_root: &ironwood_root, + tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: block.sapling_transactions_count(), orchard_tx: block.orchard_transactions_count(), ironwood_tx: block.ironwood_transactions_count(), + tachyon_tx: block.tachyon_transactions_count(), }, )?; @@ -431,9 +446,11 @@ fn from_parts_selects_epoch_version_and_ignores_future_fields() -> Result<()> { sapling_root: &sapling_root, orchard_root, ironwood_root, + tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: 1, orchard_tx, ironwood_tx, + tachyon_tx: 0, }; assert_eq!( @@ -551,6 +568,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; let mut parts_tree = NonEmptyHistoryTree::from_parts( &network, @@ -559,6 +577,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ), )?; @@ -567,12 +586,14 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; parts_tree.push_from_parts(HistoryTreeBlockParts::from_block( &activation_block, &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ))?; let activation_tree = NonEmptyHistoryTree::from_block( &network, @@ -580,6 +601,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; let activation_without_ironwood = NonEmptyHistoryTree::from_block( &network, @@ -587,6 +609,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &Default::default(), + &Default::default(), )?; assert_eq!(block_tree.hash(), parts_tree.hash()); @@ -664,9 +687,11 @@ fn push_from_parts_routes_each_history_version() -> Result<()> { sapling_root, orchard_root, ironwood_root, + tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: 1, orchard_tx, ironwood_tx, + tachyon_tx: 0, } } let two_leaf_tree = |network: &Network, @@ -765,18 +790,21 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + &Default::default(), )?; sequential.push( second_block.clone(), &sapling_root, &orchard_root, &second_ironwood_root, + &Default::default(), )?; sequential.push( third_block.clone(), &sapling_root, &orchard_root, &third_ironwood_root, + &Default::default(), )?; let mut extended = NonEmptyHistoryTree::from_block( @@ -785,6 +813,7 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + &Default::default(), )?; extended.try_extend([ ( @@ -792,12 +821,14 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &second_ironwood_root, + &DEFAULT_TACHYON_ANCHOR, ), ( third_block.clone(), &sapling_root, &orchard_root, &third_ironwood_root, + &DEFAULT_TACHYON_ANCHOR, ), ])?; @@ -807,6 +838,7 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + &Default::default(), )?; wrong_last_root.try_extend([ ( @@ -814,12 +846,14 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &second_ironwood_root, + &DEFAULT_TACHYON_ANCHOR, ), ( third_block, &sapling_root, &orchard_root, &Default::default(), + &DEFAULT_TACHYON_ANCHOR, ), ])?; @@ -873,9 +907,11 @@ fn v3_pruning_and_hash_dispatch_match_unpruned_tree() -> Result<()> { sapling_root, orchard_root, ironwood_root, + tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: u64::from(height), orchard_tx: u64::from(height + 1), ironwood_tx: u64::from(height + 2), + tachyon_tx: 0, } } @@ -987,14 +1023,22 @@ fn v3_clone_preserves_root_and_future_append() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + &Default::default(), )?; original.push( block(2), &sapling_root, &orchard_root, &second_ironwood_root, + &Default::default(), + )?; + original.push( + block(3), + &sapling_root, + &orchard_root, + &third_ironwood_root, + &Default::default(), )?; - original.push(block(3), &sapling_root, &orchard_root, &third_ironwood_root)?; assert_eq!(original.peaks().len(), 2, "three leaves have two MMR peaks"); let mut cloned = original.clone(); @@ -1013,12 +1057,14 @@ fn v3_clone_preserves_root_and_future_append() -> Result<()> { &sapling_root, &orchard_root, &fourth_ironwood_root, + &Default::default(), )?; cloned.push( fourth_block, &sapling_root, &orchard_root, &fourth_ironwood_root, + &Default::default(), )?; assert_eq!(cloned.hash(), original.hash()); @@ -1081,10 +1127,17 @@ fn history_tree_from_block_matches_parts_across_history_versions() -> Result<()> &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; let parts_tree = HistoryTree::from_parts( &network, - HistoryTreeBlockParts::from_block(&block, &sapling_root, &orchard_root, &ironwood_root), + HistoryTreeBlockParts::from_block( + &block, + &sapling_root, + &orchard_root, + &ironwood_root, + &Default::default(), + ), )?; assert_eq!(block_tree.is_some(), should_exist); @@ -1138,6 +1191,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; parts_tree.push_from_parts( &network, @@ -1146,6 +1200,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ), )?; assert_eq!(block_tree.hash(), parts_tree.hash()); @@ -1156,6 +1211,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; parts_tree.push_from_parts( &network, @@ -1164,6 +1220,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ), )?; let activation_tree = HistoryTree::from_block( @@ -1172,6 +1229,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), )?; let activation_without_ironwood = HistoryTree::from_block( &network, @@ -1179,6 +1237,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &Default::default(), + &Default::default(), )?; assert_eq!(block_tree.hash(), parts_tree.hash()); @@ -1233,6 +1292,7 @@ fn upgrade_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade &sapling_root_prev, &Default::default(), &Default::default(), + &Default::default(), )?; assert_eq!(tree.size(), 1); @@ -1260,6 +1320,7 @@ fn upgrade_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade &activation_sapling_root, &Default::default(), &Default::default(), + &Default::default(), ) .unwrap(); diff --git a/crates/zakura-chain/src/lib.rs b/crates/zakura-chain/src/lib.rs index aa2d5104ee..b86fb4ede5 100644 --- a/crates/zakura-chain/src/lib.rs +++ b/crates/zakura-chain/src/lib.rs @@ -44,6 +44,7 @@ pub mod shutdown; pub mod sprout; pub mod subtree; pub mod subtree_verify; +pub mod tachyon; pub mod transaction; pub mod transparent; pub mod value_balance; diff --git a/crates/zakura-chain/src/parallel/commitment_aux_verify.rs b/crates/zakura-chain/src/parallel/commitment_aux_verify.rs index 015661ecac..a5740442c9 100644 --- a/crates/zakura-chain/src/parallel/commitment_aux_verify.rs +++ b/crates/zakura-chain/src/parallel/commitment_aux_verify.rs @@ -102,6 +102,10 @@ impl VerifiedHeaderCommitmentRoots { /// A supplied-root verification failure. #[derive(Debug, Error)] pub enum SuppliedRootsError { + /// The roots-only auxiliary format cannot reconstruct Tachyon history state. + #[error("Tachyon history commitments require block bodies")] + TachyonDataUnavailable, + /// A header commitment did not match its supplied auxiliary data. #[error("invalid header commitment: {0}")] InvalidHeaderCommitment(#[from] CommitmentError), @@ -143,6 +147,10 @@ where for (index, (header, roots)) in items.iter().enumerate() { let height = roots.height; + if NetworkUpgrade::current(network, height) >= NetworkUpgrade::NuTachyon { + return Err((height, SuppliedRootsError::TachyonDataUnavailable)); + } + header_commitment_is_valid_for_chain_history( header, height, @@ -183,9 +191,11 @@ where sapling_root: &roots.sapling_root, orchard_root: &roots.orchard_root, ironwood_root: &roots.ironwood_root, + tachyon_anchor: &Default::default(), sapling_tx: roots.sapling_tx, orchard_tx: roots.orchard_tx, ironwood_tx: roots.ironwood_tx, + tachyon_tx: 0, }, ) .map_err(Arc::new) @@ -480,6 +490,7 @@ mod tests { &Default::default(), &Default::default(), &Default::default(), + &Default::default(), ) .expect("empty history tree for a pre-Heartwood block") } @@ -914,6 +925,7 @@ mod tests { &act_root, &empty_orchard_root, &empty_ironwood_root(), + &Default::default(), ) .expect("activation block builds a history tree") .hash(), @@ -940,6 +952,7 @@ mod tests { &empty_sapling_root, &empty_orchard_root, &empty_ironwood_root(), + &Default::default(), ) .expect("the parent history tree builds"); let witness_roots = diff --git a/crates/zakura-chain/src/parallel/tree.rs b/crates/zakura-chain/src/parallel/tree.rs index 8d5c5e8138..24f5a59d7a 100644 --- a/crates/zakura-chain/src/parallel/tree.rs +++ b/crates/zakura-chain/src/parallel/tree.rs @@ -8,6 +8,7 @@ use crate::{ block::Block, ironwood, orchard, sapling, sprout, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeIndex}, + tachyon, }; /// An argument wrapper struct for note commitment trees. @@ -35,6 +36,12 @@ pub struct NoteCommitmentTrees { /// The Ironwood note commitment subtree. pub ironwood_subtree: Option>, + + /// The Tachyon pool anchor after this block. + pub tachyon_anchor: tachyon::Anchor, + + /// The epoch-boundary anchor if this block starts a Tachyon epoch. + pub tachyon_epoch_anchor: Option, } /// Note commitment tree errors. diff --git a/crates/zakura-chain/src/primitives/zcash_history.rs b/crates/zakura-chain/src/primitives/zcash_history.rs index a70394cba1..ba862b1c07 100644 --- a/crates/zakura-chain/src/primitives/zcash_history.rs +++ b/crates/zakura-chain/src/primitives/zcash_history.rs @@ -9,13 +9,13 @@ mod tests; use std::{collections::BTreeMap, io, sync::Arc}; use serde_big_array::BigArray; -pub use zcash_history::{MAX_ENTRY_SIZE, V1, V2, V3}; +pub use zcash_history::{MAX_ENTRY_SIZE, V1, V2, V3, V4}; use crate::{ block::{Block, ChainHistoryMmrRootHash, Header, Height}, ironwood, orchard, parameters::{Network, NetworkUpgrade}, - sapling, + sapling, tachyon, }; /// The block data needed to build a ZIP-221 history tree leaf. @@ -31,12 +31,16 @@ pub struct HistoryTreeBlockParts<'a> { pub orchard_root: &'a orchard::tree::Root, /// The block's Ironwood note commitment tree root. pub ironwood_root: &'a ironwood::tree::Root, + /// The Tachyon pool anchor after the block. + pub tachyon_anchor: &'a tachyon::Anchor, /// The number of Sapling transactions in the block. pub sapling_tx: u64, /// The number of Orchard transactions in the block. pub orchard_tx: u64, /// The number of Ironwood transactions in the block. pub ironwood_tx: u64, + /// The number of Tachyon transactions in the block. + pub tachyon_tx: u64, } impl<'a> HistoryTreeBlockParts<'a> { @@ -46,6 +50,7 @@ impl<'a> HistoryTreeBlockParts<'a> { sapling_root: &'a sapling::tree::Root, orchard_root: &'a orchard::tree::Root, ironwood_root: &'a ironwood::tree::Root, + tachyon_anchor: &'a tachyon::Anchor, ) -> Self { let height = block .coinbase_height() @@ -57,9 +62,11 @@ impl<'a> HistoryTreeBlockParts<'a> { sapling_root, orchard_root, ironwood_root, + tachyon_anchor, sapling_tx: block.sapling_transactions_count(), orchard_tx: block.orchard_transactions_count(), ironwood_tx: block.ironwood_transactions_count(), + tachyon_tx: block.tachyon_transactions_count(), } } } @@ -73,9 +80,16 @@ pub trait Version: zcash_history::Version { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Self::NodeData { Self::parts_to_history_node( - HistoryTreeBlockParts::from_block(&block, sapling_root, orchard_root, ironwood_root), + HistoryTreeBlockParts::from_block( + &block, + sapling_root, + orchard_root, + ironwood_root, + tachyon_anchor, + ), network, ) } @@ -146,9 +160,16 @@ impl Entry { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Self { - let node_data = - V::block_to_history_node(block, network, sapling_root, orchard_root, ironwood_root); + let node_data = V::block_to_history_node( + block, + network, + sapling_root, + orchard_root, + ironwood_root, + tachyon_anchor, + ); Self::from_node_data::(node_data) } @@ -227,13 +248,20 @@ impl Tree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Result<(Self, Entry), io::Error> { let height = block .coinbase_height() .expect("block must have coinbase height during contextual verification"); let network_upgrade = NetworkUpgrade::current(network, height); - let entry0 = - Entry::new_leaf::(block, network, sapling_root, orchard_root, ironwood_root); + let entry0 = Entry::new_leaf::( + block, + network, + sapling_root, + orchard_root, + ironwood_root, + tachyon_anchor, + ); let mut peaks = BTreeMap::new(); peaks.insert(0u32, entry0); Ok(( @@ -283,6 +311,7 @@ impl Tree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, + tachyon_anchor: &tachyon::Anchor, ) -> Result, zcash_history::Error> { let height = block .coinbase_height() @@ -302,6 +331,7 @@ impl Tree { sapling_root, orchard_root, ironwood_root, + tachyon_anchor, ); let appended = self.inner.append_leaf(node_data)?; @@ -486,3 +516,19 @@ impl Version for V3 { } } } + +impl Version for V4 { + fn parts_to_history_node( + parts: HistoryTreeBlockParts<'_>, + network: &Network, + ) -> Self::NodeData { + let node_data_v3 = V3::parts_to_history_node(parts, network); + let tachyon_anchor: [u8; 32] = parts.tachyon_anchor.into(); + Self::NodeData { + v3: node_data_v3, + start_tachyon_anchor: tachyon_anchor, + end_tachyon_anchor: tachyon_anchor, + tachyon_tx: parts.tachyon_tx, + } + } +} diff --git a/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs b/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs index d44fc0d5dd..2ace95c311 100644 --- a/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs +++ b/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs @@ -14,6 +14,7 @@ const U32_SIZE: usize = 4; const V1_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE + U32_SIZE * 4 + HISTORY_HASH_SIZE * 3; const V2_EXTRA_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE * 2; const V3_EXTRA_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE * 2; +const V4_EXTRA_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE * 2; /// Test the MMR tree using the activation block of a network upgrade /// and its next block. @@ -58,6 +59,7 @@ fn tree_for_network_upgrade(network: &Network, network_upgrade: NetworkUpgrade) &sapling_root0, &Default::default(), &Default::default(), + &Default::default(), )?; // Compute root hash of the MMR tree, which will be included in the next block @@ -88,6 +90,7 @@ fn tree_for_network_upgrade(network: &Network, network_upgrade: NetworkUpgrade) &sapling_root1, &Default::default(), &Default::default(), + &Default::default(), ) .unwrap(); @@ -114,6 +117,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &default_ironwood_root, + &Default::default(), ); let v1_non_default = ::block_to_history_node( block.clone(), @@ -121,6 +125,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &non_default_ironwood_root, + &Default::default(), ); assert_eq!( @@ -138,6 +143,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &default_ironwood_root, + &Default::default(), ); let v2_non_default = ::block_to_history_node( block, @@ -145,6 +151,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &non_default_ironwood_root, + &Default::default(), ); assert_eq!( @@ -173,6 +180,7 @@ fn v3_history_node_hash_input_has_exact_serialized_size() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ); assert_eq!(node_data.start_ironwood_root, ironwood_root_bytes); @@ -191,6 +199,39 @@ fn v3_history_node_hash_input_has_exact_serialized_size() -> Result<()> { Ok(()) } +#[test] +fn v4_history_node_commits_to_tachyon_state() -> Result<()> { + let network = Network::Mainnet; + let (block, sapling_root) = block_and_sapling_root(&network, NetworkUpgrade::Canopy)?; + let orchard_root = orchard::tree::Root::default(); + let ironwood_root = non_default_ironwood_root(); + let tachyon_anchor = crate::tachyon::Anchor([1; 32]); + + let mut node_data = ::block_to_history_node( + block, + &network, + &sapling_root, + &orchard_root, + &ironwood_root, + &tachyon_anchor, + ); + + assert_eq!(node_data.start_tachyon_anchor, tachyon_anchor.0); + assert_eq!(node_data.end_tachyon_anchor, tachyon_anchor.0); + + let without_tachyon_transaction = ::hash(&node_data); + node_data.tachyon_tx = 1; + let with_tachyon_transaction = ::hash(&node_data); + assert_ne!(without_tachyon_transaction, with_tachyon_transaction); + + let encoded = ::to_bytes(&node_data); + assert_eq!(encoded.len(), serialized_v4_node_data_size(&node_data)); + assert_eq!(encoded.last(), Some(&1)); + assert_eq!(::zcash_history::MAX_NODE_DATA_SIZE, 390); + + Ok(()) +} + fn block_and_sapling_root( network: &Network, network_upgrade: NetworkUpgrade, @@ -231,6 +272,12 @@ fn serialized_v3_node_data_size(data: &::zcash_history::NodeDataV3) -> usize { + compact_size(data.ironwood_tx) } +fn serialized_v4_node_data_size(data: &::zcash_history::NodeDataV4) -> usize { + serialized_v3_node_data_size(&data.v3) + + V4_EXTRA_FIXED_NODE_DATA_SIZE + + compact_size(data.tachyon_tx) +} + fn serialized_v2_node_data_size(data: &::zcash_history::NodeDataV2) -> usize { serialized_v1_node_data_size(&data.v1) + V2_EXTRA_FIXED_NODE_DATA_SIZE diff --git a/crates/zakura-chain/src/tachyon.rs b/crates/zakura-chain/src/tachyon.rs new file mode 100644 index 0000000000..de88e95539 --- /dev/null +++ b/crates/zakura-chain/src/tachyon.rs @@ -0,0 +1,215 @@ +//! Tachyon shielded-pool chain-state types. + +use std::{fmt, io}; + +use serde::{Deserialize, Serialize}; + +use crate::{ + block::Block, + parameters::{Network, NetworkUpgrade}, + serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, +}; + +/// The number of blocks in a Tachyon epoch. +pub const EPOCH_LENGTH: u32 = zcash_tachyon::constants::EPOCH_SIZE; + +/// Returns the zero-based Tachyon pool height at `height`. +pub fn pool_height(network: &Network, height: crate::block::Height) -> Option { + let activation_height = NetworkUpgrade::NuTachyon.activation_height(network)?; + height.0.checked_sub(activation_height.0) +} + +/// Returns the Tachyon epoch containing `pool_height`. +pub fn epoch_of_pool_height(pool_height: u32) -> u32 { + pool_height / EPOCH_LENGTH +} + +/// Returns `true` if `pool_height` is the first block in its epoch. +pub fn is_epoch_first(pool_height: u32) -> bool { + pool_height.is_multiple_of(EPOCH_LENGTH) +} + +/// Returns the Tachyon epoch containing `height`, if the pool is active. +pub fn epoch(network: &Network, height: crate::block::Height) -> Option { + pool_height(network, height).map(epoch_of_pool_height) +} + +/// Returns `true` if `earlier` is in `later`'s two-epoch consensus scan window. +pub fn within_scan_window( + network: &Network, + earlier: crate::block::Height, + later: crate::block::Height, +) -> bool { + match (epoch(network, earlier), epoch(network, later)) { + (Some(earlier_epoch), Some(later_epoch)) => earlier_epoch + 1 >= later_epoch, + _ => false, + } +} + +/// The running Tachyon pool anchor after a block. +#[derive(Clone, Copy, Default, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Anchor(pub [u8; 32]); + +impl fmt::Debug for Anchor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("tachyon::Anchor") + .field(&hex::encode(self.0)) + .finish() + } +} + +impl From<[u8; 32]> for Anchor { + fn from(bytes: [u8; 32]) -> Self { + Self(bytes) + } +} + +impl From for [u8; 32] { + fn from(anchor: Anchor) -> Self { + anchor.0 + } +} + +impl From<&Anchor> for [u8; 32] { + fn from(anchor: &Anchor) -> Self { + anchor.0 + } +} + +impl ZcashSerialize for Anchor { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + writer.write_all(&self.0) + } +} + +impl ZcashDeserialize for Anchor { + fn zcash_deserialize(mut reader: R) -> Result { + Ok(Self(reader.read_32_bytes()?)) + } +} + +impl From for Anchor { + fn from(anchor: zcash_tachyon::Anchor) -> Self { + let mut bytes = Vec::with_capacity(32); + anchor + .write(&mut bytes) + .expect("serializing a Tachyon anchor into a Vec is infallible"); + Self( + bytes + .try_into() + .expect("Tachyon anchors always encode as 32 bytes"), + ) + } +} + +/// A nullifier or note commitment revealed by a Tachyon proof stamp. +#[derive(Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Tachygram(pub [u8; 32]); + +impl fmt::Debug for Tachygram { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("tachyon::Tachygram") + .field(&hex::encode(self.0)) + .finish() + } +} + +impl From<[u8; 32]> for Tachygram { + fn from(bytes: [u8; 32]) -> Self { + Self(bytes) + } +} + +impl From for [u8; 32] { + fn from(tachygram: Tachygram) -> Self { + tachygram.0 + } +} + +impl From<&Tachygram> for [u8; 32] { + fn from(tachygram: &Tachygram) -> Self { + tachygram.0 + } +} + +impl From for Tachygram { + fn from(tachygram: zcash_tachyon::Tachygram) -> Self { + let field: halo2::pasta::pallas::Base = tachygram.into(); + Self(field.into()) + } +} + +impl Anchor { + fn to_tachyon(self) -> zcash_tachyon::Anchor { + zcash_tachyon::Anchor::read(&self.0[..]) + .expect("stored Tachyon anchors are canonical field-element encodings") + } + + /// Computes the Tachyon pool anchor after `block`. + pub fn advance_with_block( + &self, + pool_height: u32, + block: &Block, + ) -> Result { + use zcash_tachyon::TachyonBundle; + + let epoch = zcash_tachyon::EpochIndex(epoch_of_pool_height(pool_height)); + let mut anchor = if pool_height == 0 { + zcash_tachyon::Anchor::default() + } else { + self.to_tachyon() + }; + let epoch_boundary = if pool_height == 0 { + Some(Anchor::from(anchor)) + } else if is_epoch_first(pool_height) { + anchor = anchor.next_epoch(epoch)?; + Some(Anchor::from(anchor)) + } else { + None + }; + + for transaction in &block.transactions { + let Some(shielded_data) = transaction.tachyon_shielded_data() else { + continue; + }; + if let TachyonBundle::Proven(bundle) = &shielded_data.0 { + anchor = anchor.next_stamp(epoch, &bundle.stamp.tachygram_set)?; + } + } + + Ok(AnchorAdvance { + post_block: Anchor::from(anchor), + epoch_boundary, + }) + } +} + +/// The result of advancing the Tachyon anchor through a block. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct AnchorAdvance { + /// The anchor after the block. + pub post_block: Anchor, + /// The anchor immediately after an epoch lift, if this block starts an epoch. + pub epoch_boundary: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn anchor_serialization_round_trip() { + let anchor = Anchor([42; 32]); + let mut bytes = Vec::new(); + anchor.zcash_serialize(&mut bytes).unwrap(); + assert_eq!(Anchor::zcash_deserialize(&bytes[..]).unwrap(), anchor); + } + + #[test] + fn epoch_helpers_follow_epoch_length() { + assert_eq!(epoch_of_pool_height(0), 0); + assert_eq!(epoch_of_pool_height(EPOCH_LENGTH), 1); + assert!(is_epoch_first(0)); + assert!(is_epoch_first(EPOCH_LENGTH)); + } +} diff --git a/crates/zakura-chain/src/transaction.rs b/crates/zakura-chain/src/transaction.rs index e20d53025f..f683c8b133 100644 --- a/crates/zakura-chain/src/transaction.rs +++ b/crates/zakura-chain/src/transaction.rs @@ -11,6 +11,7 @@ mod lock_time; mod memo; mod serialize; mod sighash; +mod tachyon_shielded; mod txid; mod unmined; pub(crate) mod zip244; @@ -33,6 +34,7 @@ pub use serialize::{ MIN_TRANSPARENT_TX_V5_SIZE, }; pub use sighash::{HashType, SigHash, SigHasher}; +pub use tachyon_shielded::TachyonShieldedData; pub use unmined::{ zip317, UnminedTx, UnminedTxId, VerifiedUnminedTx, MEMPOOL_TRANSACTION_COST_THRESHOLD, }; @@ -170,8 +172,6 @@ pub enum Transaction { ironwood_shielded_data: Option, }, /// A `version = 7` transaction enabled by NuTachyon. - /// - /// V7 initially has the same fields and behavior as V6. V7 { /// The Network Upgrade for this transaction. /// @@ -191,6 +191,8 @@ pub enum Transaction { orchard_shielded_data: Option, /// The Ironwood data for this transaction, if any. ironwood_shielded_data: Option, + /// The Tachyon data for this transaction, if any. + tachyon_shielded_data: Option, }, } @@ -263,13 +265,30 @@ impl AttributedMemorySize for Transaction { orchard_shielded_data, ironwood_shielded_data, .. - } - | V7 { + } => ( + inputs, + outputs, + sapling_shielded_data + .as_ref() + .map_or(0, AttributedMemorySize::attributed_memory_size_bytes) + .saturating_add( + orchard_shielded_data + .as_ref() + .map_or(0, AttributedMemorySize::attributed_memory_size_bytes), + ) + .saturating_add( + ironwood_shielded_data + .as_ref() + .map_or(0, AttributedMemorySize::attributed_memory_size_bytes), + ), + ), + V7 { inputs, outputs, sapling_shielded_data, orchard_shielded_data, ironwood_shielded_data, + tachyon_shielded_data, .. } => ( inputs, @@ -286,6 +305,11 @@ impl AttributedMemorySize for Transaction { ironwood_shielded_data .as_ref() .map_or(0, AttributedMemorySize::attributed_memory_size_bytes), + ) + .saturating_add( + tachyon_shielded_data + .as_ref() + .map_or(0, AttributedMemorySize::attributed_memory_size_bytes), ), ), }; @@ -485,6 +509,7 @@ impl Transaction { .ironwood_flags() .unwrap_or_else(ironwood::Flags::empty) .contains(ironwood::Flags::ENABLE_SPENDS)) + || self.has_tachyon_actions() } /// Does this transaction have shielded outputs? @@ -503,6 +528,7 @@ impl Transaction { .ironwood_flags() .unwrap_or_else(ironwood::Flags::empty) .contains(ironwood::Flags::ENABLE_OUTPUTS)) + || self.has_tachyon_actions() } /// Does this transaction have transparent or shielded outputs? @@ -1437,6 +1463,48 @@ impl Transaction { self.ironwood_shielded_data().is_some() } + // Tachyon + + /// Access the Tachyon shielded data in this transaction, if present. + pub fn tachyon_shielded_data(&self) -> Option<&TachyonShieldedData> { + match self { + Transaction::V7 { + tachyon_shielded_data, + .. + } => tachyon_shielded_data.as_ref(), + _ => None, + } + } + + /// Returns `true` if this transaction carries a Tachyon bundle. + pub fn has_tachyon_shielded_data(&self) -> bool { + self.tachyon_shielded_data().is_some() + } + + /// Returns the tachygrams revealed by this transaction's proof stamp. + pub fn tachyon_tachygrams(&self) -> Vec { + match self.tachyon_shielded_data() { + Some(shielded_data) => match &shielded_data.0 { + zcash_tachyon::TachyonBundle::Proven(bundle) => bundle + .stamp + .tachygrams + .iter() + .copied() + .map(crate::tachyon::Tachygram::from) + .collect(), + zcash_tachyon::TachyonBundle::NoBundle + | zcash_tachyon::TachyonBundle::Adjunct(_) => Vec::new(), + }, + None => Vec::new(), + } + } + + /// Returns `true` if this transaction has any Tachyon actions. + pub fn has_tachyon_actions(&self) -> bool { + self.tachyon_shielded_data() + .is_some_and(|data| !data.actions().is_empty()) + } + // value balances /// Return the transparent value balance, @@ -1822,6 +1890,20 @@ impl Transaction { ValueBalance::from_ironwood_amount(ironwood_value_balance) } + /// Return the Tachyon value balance, the change in the transaction value pool due to + /// Tachyon actions. + pub fn tachyon_value_balance(&self) -> ValueBalance { + let tachyon_value_balance = self + .tachyon_shielded_data() + .map(|shielded_data| { + Amount::try_from(i64::from(shielded_data.value_balance())) + .expect("Tachyon value balances are range-checked when the bundle is parsed") + }) + .unwrap_or_else(Amount::zero); + + ValueBalance::from_tachyon_amount(tachyon_value_balance) + } + /// Returns the value balances for this transaction using the provided transparent outputs. #[cfg(any(test, feature = "proptest-impl"))] pub(crate) fn value_balance_from_outputs( @@ -1833,6 +1915,7 @@ impl Transaction { + self.sapling_value_balance() + self.orchard_value_balance() + self.ironwood_value_balance() + + self.tachyon_value_balance() } /// Returns the value balances for this transaction. @@ -1891,6 +1974,7 @@ impl Transaction { + self.sapling_value_balance() + self.orchard_value_balance() + self.ironwood_value_balance() + + self.tachyon_value_balance() } /// Converts [`Transaction`] to [`zcash_primitives::transaction::Transaction`]. diff --git a/crates/zakura-chain/src/transaction/serialize.rs b/crates/zakura-chain/src/transaction/serialize.rs index 2ef3bd33c9..f598d691d3 100644 --- a/crates/zakura-chain/src/transaction/serialize.rs +++ b/crates/zakura-chain/src/transaction/serialize.rs @@ -884,6 +884,7 @@ impl ZcashSerialize for Transaction { sapling_shielded_data, orchard_shielded_data, ironwood_shielded_data, + tachyon_shielded_data, } => { if *network_upgrade < NetworkUpgrade::NuTachyon { return Err(io::Error::new( @@ -915,7 +916,7 @@ impl ZcashSerialize for Transaction { )?; #[cfg(zcash_unstable = "nutachyon")] zcash_primitives::transaction::components::tachyon::write_v7_bundle( - None, + tachyon_shielded_data.as_ref().map(|bundle| &bundle.0), &mut writer, )?; #[cfg(not(zcash_unstable = "nutachyon"))] @@ -1313,21 +1314,20 @@ impl ZcashDeserialize for Transaction { ALLOW_CROSS_ADDRESS_BIT, )?; #[cfg(zcash_unstable = "nutachyon")] - if zcash_primitives::transaction::components::tachyon::read_v7_bundle( - &mut limited_reader, - )? - .is_some() - { - return Err(SerializationError::Parse( - "Tachyon bundles are not yet supported", - )); - } + let tachyon_shielded_data = + zcash_primitives::transaction::components::tachyon::read_v7_bundle( + &mut limited_reader, + )? + .map(TachyonShieldedData::from); #[cfg(not(zcash_unstable = "nutachyon"))] - if limited_reader.read_u8()? != 0 { - return Err(SerializationError::Parse( - "Tachyon bundles are not available in this build", - )); - } + let tachyon_shielded_data = match limited_reader.read_u8()? { + 0 => None, + _ => { + return Err(SerializationError::Parse( + "Tachyon bundles are not available in this build", + )); + } + }; Ok(Transaction::V7 { network_upgrade, @@ -1338,6 +1338,7 @@ impl ZcashDeserialize for Transaction { sapling_shielded_data, orchard_shielded_data, ironwood_shielded_data, + tachyon_shielded_data, }) } (_, _) => Err(SerializationError::Parse("bad tx header")), diff --git a/crates/zakura-chain/src/transaction/tachyon_shielded.rs b/crates/zakura-chain/src/transaction/tachyon_shielded.rs new file mode 100644 index 0000000000..fc034cc58e --- /dev/null +++ b/crates/zakura-chain/src/transaction/tachyon_shielded.rs @@ -0,0 +1,90 @@ +//! The Tachyon shielded data carried by V7 transactions. + +use zcash_tachyon::TachyonBundle; + +use crate::memory::{inline_size_bytes, vec_capacity_bytes, AttributedMemorySize}; + +/// A non-empty Tachyon bundle. +/// +/// `TachyonBundle::NoBundle` is represented by `None` on [`super::Transaction::V7`]. +#[derive(Clone, Debug)] +pub struct TachyonShieldedData(pub TachyonBundle); + +impl TachyonShieldedData { + /// Returns the bundle's actions. + pub fn actions(&self) -> &[zcash_tachyon::Action] { + match &self.0 { + TachyonBundle::NoBundle => &[], + TachyonBundle::Proven(bundle) => &bundle.actions, + TachyonBundle::Adjunct(bundle) => &bundle.actions, + } + } + + /// Returns the bundle's value balance. + pub fn value_balance(&self) -> zcash_tachyon::value::Balance { + match &self.0 { + TachyonBundle::NoBundle => zcash_tachyon::value::Balance::ZERO, + TachyonBundle::Proven(bundle) => bundle.value_balance, + TachyonBundle::Adjunct(bundle) => bundle.value_balance, + } + } +} + +impl From for TachyonShieldedData { + fn from(bundle: TachyonBundle) -> Self { + Self(bundle) + } +} + +impl PartialEq for TachyonShieldedData { + fn eq(&self, other: &Self) -> bool { + let mut self_bytes = Vec::new(); + let mut other_bytes = Vec::new(); + self.0 + .write(&mut self_bytes) + .expect("serializing a Tachyon bundle into a Vec is infallible"); + other + .0 + .write(&mut other_bytes) + .expect("serializing a Tachyon bundle into a Vec is infallible"); + self_bytes == other_bytes + } +} + +impl Eq for TachyonShieldedData {} + +impl AttributedMemorySize for TachyonShieldedData { + fn attributed_memory_size_bytes(&self) -> u64 { + let bundle = match &self.0 { + TachyonBundle::NoBundle => return 0, + TachyonBundle::Proven(bundle) => bundle, + TachyonBundle::Adjunct(bundle) => { + return vec_capacity_bytes(&bundle.actions) + .saturating_add(vec_capacity_bytes(&bundle.memo)); + } + }; + + vec_capacity_bytes(&bundle.actions) + .saturating_add(vec_capacity_bytes(&bundle.memo)) + .saturating_add( + u64::try_from(bundle.stamp.tachygrams.len()) + .unwrap_or(u64::MAX) + .saturating_mul(inline_size_bytes::()), + ) + .saturating_add( + u64::try_from(std::mem::size_of_val(bundle.stamp.proof.as_ref())) + .unwrap_or(u64::MAX), + ) + } +} + +#[cfg(any(test, feature = "proptest-impl"))] +impl serde::Serialize for TachyonShieldedData { + fn serialize(&self, serializer: S) -> Result { + use serde::ser::Error as _; + + let mut bytes = Vec::new(); + self.0.write(&mut bytes).map_err(S::Error::custom)?; + serde::Serialize::serialize(&bytes, serializer) + } +} diff --git a/crates/zakura-chain/src/transaction/tests/vectors.rs b/crates/zakura-chain/src/transaction/tests/vectors.rs index 29d0883a0a..4ad44affc1 100644 --- a/crates/zakura-chain/src/transaction/tests/vectors.rs +++ b/crates/zakura-chain/src/transaction/tests/vectors.rs @@ -1854,6 +1854,7 @@ fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { sapling_shielded_data: None, orchard_shielded_data: None, ironwood_shielded_data: None, + tachyon_shielded_data: None, }; empty_v7(NetworkUpgrade::Nu6_3) @@ -1903,6 +1904,71 @@ fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { )); } +#[test] +#[cfg(zcash_unstable = "nutachyon")] +fn v7_tachyon_bundle_round_trips() { + use group::{ + ff::{FromUniformBytes, PrimeField}, + CurveAffine, GroupEncoding, + }; + use halo2::pasta::pallas; + use reddsa::{orchard::SpendAuth, SigningKey, VerificationKey}; + + let _init_guard = zakura_test::init(); + + let signing_key = SigningKey::::try_from( + pallas::Scalar::from_uniform_bytes(&[0x42; 64]).to_repr(), + ) + .expect("the reduced scalar is a valid signing key"); + let verification_key = VerificationKey::::from(&signing_key); + let verification_key = Option::::from(pallas::Affine::from_bytes( + &<[u8; 32]>::from(verification_key), + )) + .expect("the verification key is a valid curve point"); + + let bundle = zcash_tachyon::TachyonBundle::Adjunct(zcash_tachyon::Bundle { + actions: vec![zcash_tachyon::Action { + cv: zcash_tachyon::value::Commitment::from(pallas::Affine::generator()), + rk: zcash_tachyon::keys::public::ActionVerificationKey::try_from(verification_key) + .expect("a non-identity verification key is valid"), + sig: zcash_tachyon::action::Signature::read(&[0x01; 64][..]) + .expect("test signature bytes are canonical"), + }], + value_balance: zcash_tachyon::value::Balance::try_from(100i64) + .expect("100 is a valid Tachyon value balance"), + binding_sig: zcash_tachyon::bundle::Signature::read(&[0x02; 64][..]) + .expect("test signature bytes are canonical"), + memo: vec![0xAB; 5], + stamp: zcash_tachyon::PointerStamp::try_from([0xEE; 64]) + .expect("a 64-byte witnessed transaction ID is valid"), + }); + let tx = Transaction::V7 { + network_upgrade: NetworkUpgrade::NuTachyon, + lock_time: LockTime::unlocked(), + expiry_height: Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data: Some(TachyonShieldedData(bundle)), + }; + + let bytes = tx + .zcash_serialize_to_vec() + .expect("a V7 transaction with a Tachyon bundle serializes"); + let decoded = Transaction::zcash_deserialize(&bytes[..]) + .expect("a serialized V7 Tachyon transaction deserializes"); + + assert_eq!(decoded, tx); + assert_eq!(decoded.hash(), tx.hash()); + assert_eq!(decoded.auth_digest(), tx.auth_digest()); + assert_eq!( + decoded.tachyon_value_balance().tachyon_amount(), + Amount::::try_from(100).expect("100 zatoshi is valid"), + ); +} + #[test] fn v6_transactions_accept_nu6_3_and_later_branch_ids() { use crate::parameters::TX_V6_VERSION_GROUP_ID; diff --git a/crates/zakura-chain/src/value_balance.rs b/crates/zakura-chain/src/value_balance.rs index b01e8f6449..7c47f5f003 100644 --- a/crates/zakura-chain/src/value_balance.rs +++ b/crates/zakura-chain/src/value_balance.rs @@ -27,6 +27,7 @@ pub struct ValueBalance { orchard: Amount, deferred: Amount, ironwood: Amount, + tachyon: Amount, } impl ValueBalance @@ -73,6 +74,14 @@ where } } + /// Creates a [`ValueBalance`] from the given Tachyon amount. + pub fn from_tachyon_amount(tachyon_amount: Amount) -> Self { + ValueBalance { + tachyon: tachyon_amount, + ..ValueBalance::zero() + } + } + /// Get the transparent amount from the [`ValueBalance`]. pub fn transparent_amount(&self) -> Amount { self.transparent @@ -147,6 +156,17 @@ where self } + /// Get the Tachyon amount from the [`ValueBalance`]. + pub fn tachyon_amount(&self) -> Amount { + self.tachyon + } + + /// Insert a Tachyon value balance without changing the other pools. + pub fn set_tachyon_value_balance(&mut self, tachyon_value_balance: ValueBalance) -> &Self { + self.tachyon = tachyon_value_balance.tachyon; + self + } + /// Creates a [`ValueBalance`] where all the pools are zero. pub fn zero() -> Self { let zero = Amount::zero(); @@ -157,6 +177,7 @@ where orchard: zero, deferred: zero, ironwood: zero, + tachyon: zero, } } @@ -173,6 +194,7 @@ where orchard: self.orchard.constrain().map_err(Orchard)?, deferred: self.deferred.constrain().map_err(Deferred)?, ironwood: self.ironwood.constrain().map_err(Ironwood)?, + tachyon: self.tachyon.constrain().map_err(Tachyon)?, }) } } @@ -199,7 +221,12 @@ impl ValueBalance { // like the orchard bundle; it is zero for transactions without an ironwood bundle. // // This will error if the remaining value in the transaction value pool is negative. - (self.transparent + self.sprout + self.sapling + self.orchard + self.ironwood)? + (self.transparent + + self.sprout + + self.sapling + + self.orchard + + self.ironwood + + self.tachyon)? .constrain::() } } @@ -350,10 +377,9 @@ impl ValueBalance { /// To byte array /// - /// The `ironwood` pool (NU6.3 onward) is appended after `deferred`, so that records written by - /// earlier Zebra versions (32 bytes without `deferred`, or 40 bytes with it) remain parsable by + /// New pools are appended in activation order, so older records remain parsable by /// [`Self::from_bytes`]. - pub fn to_bytes(self) -> [u8; 48] { + pub fn to_bytes(self) -> [u8; 56] { match [ self.transparent.to_bytes(), self.sprout.to_bytes(), @@ -361,28 +387,29 @@ impl ValueBalance { self.orchard.to_bytes(), self.deferred.to_bytes(), self.ironwood.to_bytes(), + self.tachyon.to_bytes(), ] .concat() .try_into() { Ok(bytes) => bytes, _ => unreachable!( - "six [u8; 8] should always concat with no error into a single [u8; 48]" + "seven [u8; 8] should always concat with no error into a single [u8; 56]" ), } } /// From byte array /// - /// Accepts 32-byte (pre-`deferred`), 40-byte (pre-`ironwood`), and 48-byte records; missing - /// trailing pools default to zero. + /// Accepts 32-byte (pre-`deferred`), 40-byte (pre-`ironwood`), 48-byte (pre-`tachyon`), + /// and 56-byte records; missing trailing pools default to zero. #[allow(clippy::unwrap_in_result)] pub fn from_bytes(bytes: &[u8]) -> Result, ValueBalanceError> { let bytes_length = bytes.len(); // Return an error early if bytes don't have the right length instead of panicking later. match bytes_length { - 32 | 40 | 48 => {} + 32 | 40 | 48 | 56 => {} _ => return Err(Unparsable), }; @@ -416,7 +443,7 @@ impl ValueBalance { let deferred = match bytes_length { 32 => Amount::zero(), - 40 | 48 => Amount::from_bytes( + 40 | 48 | 56 => Amount::from_bytes( bytes[32..40] .try_into() .expect("deferred amount should be parsable"), @@ -427,7 +454,7 @@ impl ValueBalance { let ironwood = match bytes_length { 32 | 40 => Amount::zero(), - 48 => Amount::from_bytes( + 48 | 56 => Amount::from_bytes( bytes[40..48] .try_into() .expect("ironwood amount should be parsable"), @@ -436,6 +463,17 @@ impl ValueBalance { _ => return Err(Unparsable), }; + let tachyon = match bytes_length { + 32 | 40 | 48 => Amount::zero(), + 56 => Amount::from_bytes( + bytes[48..56] + .try_into() + .expect("tachyon amount should be parsable"), + ) + .map_err(Tachyon)?, + _ => return Err(Unparsable), + }; + Ok(ValueBalance { transparent, sprout, @@ -443,6 +481,7 @@ impl ValueBalance { orchard, deferred, ironwood, + tachyon, }) } } @@ -468,6 +507,9 @@ pub enum ValueBalanceError { /// ironwood amount error {0} Ironwood(amount::Error), + /// Tachyon amount error {0} + Tachyon(amount::Error), + /// ValueBalance is unparsable Unparsable, } @@ -481,6 +523,7 @@ impl fmt::Display for ValueBalanceError { Orchard(e) => format!("orchard amount err: {e}"), Deferred(e) => format!("deferred amount err: {e}"), Ironwood(e) => format!("ironwood amount err: {e}"), + Tachyon(e) => format!("tachyon amount err: {e}"), Unparsable => "value balance is unparsable".to_string(), }) } @@ -499,6 +542,7 @@ where orchard: (self.orchard + rhs.orchard).map_err(Orchard)?, deferred: (self.deferred + rhs.deferred).map_err(Deferred)?, ironwood: (self.ironwood + rhs.ironwood).map_err(Ironwood)?, + tachyon: (self.tachyon + rhs.tachyon).map_err(Tachyon)?, }) } } @@ -549,6 +593,7 @@ where orchard: (self.orchard - rhs.orchard).map_err(Orchard)?, deferred: (self.deferred - rhs.deferred).map_err(Deferred)?, ironwood: (self.ironwood - rhs.ironwood).map_err(Ironwood)?, + tachyon: (self.tachyon - rhs.tachyon).map_err(Tachyon)?, }) } } @@ -619,6 +664,7 @@ where orchard: self.orchard.neg(), deferred: self.deferred.neg(), ironwood: self.ironwood.neg(), + tachyon: self.tachyon.neg(), } } } diff --git a/crates/zakura-chain/src/value_balance/arbitrary.rs b/crates/zakura-chain/src/value_balance/arbitrary.rs index 9cbdbeb73a..741e8609ad 100644 --- a/crates/zakura-chain/src/value_balance/arbitrary.rs +++ b/crates/zakura-chain/src/value_balance/arbitrary.rs @@ -12,15 +12,17 @@ impl Arbitrary for ValueBalance { any::>(), any::>(), any::>(), + any::>(), ) .prop_map( - |(transparent, sprout, sapling, orchard, deferred, ironwood)| Self { + |(transparent, sprout, sapling, orchard, deferred, ironwood, tachyon)| Self { transparent, sprout, sapling, orchard, deferred, ironwood, + tachyon, }, ) .boxed() @@ -40,15 +42,17 @@ impl Arbitrary for ValueBalance { any::>(), any::>(), any::>(), + any::>(), ) .prop_map( - |(transparent, sprout, sapling, orchard, deferred, ironwood)| Self { + |(transparent, sprout, sapling, orchard, deferred, ironwood, tachyon)| Self { transparent, sprout, sapling, orchard, deferred, ironwood, + tachyon, }, ) .boxed() diff --git a/crates/zakura-chain/src/value_balance/tests/prop.rs b/crates/zakura-chain/src/value_balance/tests/prop.rs index 162721e023..3c6fd27e01 100644 --- a/crates/zakura-chain/src/value_balance/tests/prop.rs +++ b/crates/zakura-chain/src/value_balance/tests/prop.rs @@ -18,9 +18,10 @@ proptest! { let orchard = value_balance1.orchard + value_balance2.orchard; let deferred = value_balance1.deferred + value_balance2.deferred; let ironwood = value_balance1.ironwood + value_balance2.ironwood; + let tachyon = value_balance1.tachyon + value_balance2.tachyon; - match (transparent, sprout, sapling, orchard, deferred, ironwood) { - (Ok(transparent), Ok(sprout), Ok(sapling), Ok(orchard), Ok(deferred), Ok(ironwood)) => prop_assert_eq!( + match (transparent, sprout, sapling, orchard, deferred, ironwood, tachyon) { + (Ok(transparent), Ok(sprout), Ok(sapling), Ok(orchard), Ok(deferred), Ok(ironwood), Ok(tachyon)) => prop_assert_eq!( value_balance1 + value_balance2, Ok(ValueBalance { transparent, @@ -28,7 +29,8 @@ proptest! { sapling, orchard, deferred, - ironwood + ironwood, + tachyon }) ), _ => prop_assert!( @@ -39,7 +41,8 @@ proptest! { | ValueBalanceError::Sapling(_) | ValueBalanceError::Orchard(_) | ValueBalanceError::Deferred(_) - | ValueBalanceError::Ironwood(_)) + | ValueBalanceError::Ironwood(_) + | ValueBalanceError::Tachyon(_)) ) ), } @@ -57,9 +60,10 @@ proptest! { let orchard = value_balance1.orchard - value_balance2.orchard; let deferred = value_balance1.deferred - value_balance2.deferred; let ironwood = value_balance1.ironwood - value_balance2.ironwood; + let tachyon = value_balance1.tachyon - value_balance2.tachyon; - match (transparent, sprout, sapling, orchard, deferred, ironwood) { - (Ok(transparent), Ok(sprout), Ok(sapling), Ok(orchard), Ok(deferred), Ok(ironwood)) => prop_assert_eq!( + match (transparent, sprout, sapling, orchard, deferred, ironwood, tachyon) { + (Ok(transparent), Ok(sprout), Ok(sapling), Ok(orchard), Ok(deferred), Ok(ironwood), Ok(tachyon)) => prop_assert_eq!( value_balance1 - value_balance2, Ok(ValueBalance { transparent, @@ -67,7 +71,8 @@ proptest! { sapling, orchard, deferred, - ironwood + ironwood, + tachyon }) ), _ => prop_assert!(matches!( @@ -77,7 +82,8 @@ proptest! { | ValueBalanceError::Sapling(_) | ValueBalanceError::Orchard(_) | ValueBalanceError::Deferred(_) - | ValueBalanceError::Ironwood(_)) + | ValueBalanceError::Ironwood(_) + | ValueBalanceError::Tachyon(_)) )), } } @@ -97,9 +103,10 @@ proptest! { let orchard = value_balance1.orchard + value_balance2.orchard; let deferred = value_balance1.deferred + value_balance2.deferred; let ironwood = value_balance1.ironwood + value_balance2.ironwood; + let tachyon = value_balance1.tachyon + value_balance2.tachyon; - match (transparent, sprout, sapling, orchard, deferred, ironwood) { - (Ok(transparent), Ok(sprout), Ok(sapling), Ok(orchard), Ok(deferred), Ok(ironwood)) => prop_assert_eq!( + match (transparent, sprout, sapling, orchard, deferred, ironwood, tachyon) { + (Ok(transparent), Ok(sprout), Ok(sapling), Ok(orchard), Ok(deferred), Ok(ironwood), Ok(tachyon)) => prop_assert_eq!( collection.iter().sum::, ValueBalanceError>>(), Ok(ValueBalance { transparent, @@ -107,7 +114,8 @@ proptest! { sapling, orchard, deferred, - ironwood + ironwood, + tachyon }) ), _ => prop_assert!(matches!( @@ -117,7 +125,8 @@ proptest! { | ValueBalanceError::Sapling(_) | ValueBalanceError::Orchard(_) | ValueBalanceError::Deferred(_) - | ValueBalanceError::Ironwood(_)) + | ValueBalanceError::Ironwood(_) + | ValueBalanceError::Tachyon(_)) )) } } @@ -132,7 +141,7 @@ proptest! { } #[test] - fn value_balance_deserialization(bytes in any::<[u8; 48]>()) { + fn value_balance_deserialization(bytes in any::<[u8; 56]>()) { let _init_guard = zakura_test::init(); if let Ok(deserialized) = ValueBalance::::from_bytes(&bytes) { @@ -140,7 +149,7 @@ proptest! { } } - /// The legacy version of [`ValueBalance`] had 32 bytes compared to the current 48 bytes, + /// The legacy version of [`ValueBalance`] had 32 bytes compared to the current 56 bytes, /// but it's possible to correctly instantiate the current version of [`ValueBalance`] from /// the legacy format, so we test if Zebra can still deserialize the legacy format. #[test] @@ -149,7 +158,7 @@ proptest! { if let Ok(deserialized) = ValueBalance::::from_bytes(&bytes) { let deserialized = deserialized.to_bytes(); - let mut extended_bytes = [0u8; 48]; + let mut extended_bytes = [0u8; 56]; extended_bytes[..32].copy_from_slice(&bytes); prop_assert_eq!(extended_bytes, deserialized); } @@ -165,10 +174,23 @@ proptest! { if let Ok(deserialized) = ValueBalance::::from_bytes(&bytes) { let deserialized = deserialized.to_bytes(); - let mut extended_bytes = [0u8; 48]; + let mut extended_bytes = [0u8; 56]; extended_bytes[..40].copy_from_slice(&bytes); prop_assert_eq!(extended_bytes, deserialized); } } + /// The pre-Tachyon value balance was a 48-byte prefix of the current format. + #[test] + fn pre_tachyon_value_balance_deserialization(bytes in any::<[u8; 48]>()) { + let _init_guard = zakura_test::init(); + + if let Ok(deserialized) = ValueBalance::::from_bytes(&bytes) { + let deserialized = deserialized.to_bytes(); + let mut extended_bytes = [0u8; 56]; + extended_bytes[..48].copy_from_slice(&bytes); + prop_assert_eq!(extended_bytes, deserialized); + } + } + } diff --git a/crates/zakura-consensus/Cargo.toml b/crates/zakura-consensus/Cargo.toml index 460cf956a5..fdc8d3b850 100644 --- a/crates/zakura-consensus/Cargo.toml +++ b/crates/zakura-consensus/Cargo.toml @@ -74,6 +74,7 @@ zakura-chain = { path = "../zakura-chain", version = "6.0.0" } zakura-header-chain = { path = "../zakura-header-chain", version = "1.0.0" } zcash_protocol.workspace = true +zcash_tachyon.workspace = true # prod feature progress-bar howudoin = { workspace = true, optional = true } @@ -83,6 +84,7 @@ proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } [dev-dependencies] +ragu = { workspace = true } color-eyre = { workspace = true } hex = { workspace = true } diff --git a/crates/zakura-consensus/src/block.rs b/crates/zakura-consensus/src/block.rs index 565db91780..2bfe4fdd8c 100644 --- a/crates/zakura-consensus/src/block.rs +++ b/crates/zakura-consensus/src/block.rs @@ -36,6 +36,7 @@ use crate::{error::*, primitives, transaction as tx, BoxError}; pub mod check; pub mod request; pub mod subsidy; +pub(crate) mod tachyon; pub use request::Request; @@ -125,6 +126,16 @@ impl VerifyBlockError { consensus("block.too_many_transparent_signature_operations") } BlockError::SummingMinerFees { .. } => consensus("block.summing_miner_fees"), + BlockError::DuplicateTachygram => consensus("block.duplicate_tachygram"), + BlockError::TachyonAggregateNotFound => { + consensus("block.tachyon_aggregate_not_found") + } + BlockError::TachyonCoverageMismatch => consensus("block.tachyon_coverage_mismatch"), + BlockError::TachyonDuplicateAction => consensus("block.tachyon_duplicate_action"), + BlockError::TachyonTachygramArityMismatch => { + consensus("block.tachyon_tachygram_arity_mismatch") + } + BlockError::TachyonProofInvalid(_) => consensus("block.tachyon_proof_invalid"), BlockError::InvalidHeaderEncoding(_) | BlockError::MissingHeight(_) | BlockError::MaxHeight(_, _, _) @@ -317,6 +328,11 @@ where check::merkle_root_validity(&network, &block, &transaction_hashes)?; + let mut tachyon_checks = tachyon::coherence(&block)? + .into_iter() + .map(primitives::tachyon::verify_proof_stamp) + .collect::>(); + // Since errors cause an early exit, try to do the // quick checks first. @@ -403,6 +419,10 @@ where // Check the summed block totals + while let Some(result) = tachyon_checks.next().await { + result?; + } + if sigops > MAX_BLOCK_SIGOPS { Err(BlockError::TooManyTransparentSignatureOperations { height, diff --git a/crates/zakura-consensus/src/block/tachyon.rs b/crates/zakura-consensus/src/block/tachyon.rs new file mode 100644 index 0000000000..b6c84021c5 --- /dev/null +++ b/crates/zakura-consensus/src/block/tachyon.rs @@ -0,0 +1,84 @@ +//! Block-level Tachyon consensus rules. + +use std::collections::{BTreeSet, HashMap}; + +use zakura_chain::{block::Block, transaction::WtxId}; +use zcash_tachyon::{ + stamp::StampState as _, Bundle, PointerStamp, ProofStamp, Tachygram, TachyonBundle, + VerifyCoverageError, +}; + +use crate::error::BlockError; + +#[cfg(test)] +mod tests; + +/// A proof-stamped bundle and the pointer-stamped bundles it covers. +#[derive(Debug)] +pub(crate) struct AggregateCoverage { + pub bundle: Bundle, + pub adjuncts: Vec>, +} + +/// Checks block-wide Tachygram distinctness and aggregate coverage coherence. +pub(crate) fn coherence(block: &Block) -> Result, BlockError> { + let mut seen_tachygrams = BTreeSet::::new(); + let mut aggregates = Vec::::new(); + let mut aggregates_by_wtxid = HashMap::<[u8; 64], usize>::new(); + let mut adjuncts = Vec::<([u8; 64], Bundle)>::new(); + + for transaction in &block.transactions { + let Some(tachyon_shielded_data) = transaction.tachyon_shielded_data() else { + continue; + }; + + match &tachyon_shielded_data.0 { + TachyonBundle::NoBundle => {} + TachyonBundle::Proven(bundle) => { + for &tachygram in &bundle.stamp.tachygrams { + if !seen_tachygrams.insert(tachygram) { + return Err(BlockError::DuplicateTachygram); + } + } + + let wtxid = WtxId::from(transaction.as_ref()).as_bytes(); + aggregates_by_wtxid.insert(wtxid, aggregates.len()); + aggregates.push(AggregateCoverage { + bundle: bundle.clone(), + adjuncts: Vec::new(), + }); + } + TachyonBundle::Adjunct(bundle) => { + adjuncts.push((bundle.stamp.stamp_digest(), bundle.clone())); + } + } + } + + for (target_wtxid, bundle) in adjuncts { + let Some(&aggregate_index) = aggregates_by_wtxid.get(&target_wtxid) else { + return Err(BlockError::TachyonAggregateNotFound); + }; + aggregates[aggregate_index].adjuncts.push(bundle); + } + + for aggregate in &aggregates { + let adjunct_refs: Vec<_> = aggregate + .adjuncts + .iter() + .map(|adjunct| adjunct.as_dyn()) + .collect(); + + aggregate + .bundle + .verify_coverage(&adjunct_refs) + .map_err(|error| match error { + VerifyCoverageError::DuplicateActions => BlockError::TachyonDuplicateAction, + VerifyCoverageError::StampActionsMismatch => BlockError::TachyonCoverageMismatch, + VerifyCoverageError::TachygramArityMismatch => { + BlockError::TachyonTachygramArityMismatch + } + })?; + } + + Ok(aggregates) +} diff --git a/crates/zakura-consensus/src/block/tachyon/tests.rs b/crates/zakura-consensus/src/block/tachyon/tests.rs new file mode 100644 index 0000000000..e0d538094d --- /dev/null +++ b/crates/zakura-consensus/src/block/tachyon/tests.rs @@ -0,0 +1,368 @@ +//! Tests for the block-level tachyon consensus rules. + +use std::sync::Arc; + +use zakura_chain::{ + block::{Block, Height}, + parameters::NetworkUpgrade, + serialization::ZcashDeserialize, + transaction::{LockTime, Transaction, WtxId}, +}; + +use zcash_tachyon::{ + action, bundle, effect, + entropy::ActionEntropy, + keys::private, + note::{CommitmentTrapdoor, Note}, + nullifier, value, Anchor, Bundle, PointerStamp, ProofStamp, Tachygram, TachygramSetPoly, + TachyonBundle, +}; + +use crate::error::BlockError; + +use super::{coherence, AggregateCoverage}; + +/// Computes `hStampActionsTachyon` over descriptor bytes. +/// +/// This duplicates tachyon's private `blake2b::action_descriptor_digest` (BLAKE2b-256, +/// personalization `Tachyon-Actions`, over the sorted concatenated 64-byte descriptors). Tests +/// using it self-validate via [`Bundle::verify_coverage`], so drift from the tachyon crate fails +/// loudly instead of silently testing against the wrong digest. +fn action_descriptor_digest(descriptors: &[action::Descriptor]) -> [u8; 32] { + // `Vec<[u8; 64]>: FromIterator` is tachyon's public descriptor-bytes conversion. + let mut descriptor_bytes: Vec<[u8; 64]> = descriptors.iter().copied().collect(); + descriptor_bytes.sort_unstable(); + + let mut state = blake2b_simd::Params::new() + .hash_length(32) + .personal(b"Tachyon-Actions") + .to_state(); + for descriptor in &descriptor_bytes { + state.update(descriptor); + } + state + .finalize() + .as_bytes() + .try_into() + .expect("hash length is 32") +} + +/// A structurally valid action with a random `rk` and dummy signature: block-level coherence +/// never checks signatures. +fn dummy_action() -> zcash_tachyon::Action { + use halo2::pasta::group::CurveAffine; + + let mut rng = rand_10::rng(); + + let sk = private::SpendingKey::random(&mut rng); + let note = Note { + pk: sk.derive_payment_key(), + value: value::Positive::try_from(100u64).expect("valid value"), + psi: nullifier::Trapdoor::random(&mut rng), + rcm: CommitmentTrapdoor::random(&mut rng), + }; + let alpha = ActionEntropy::random(&mut rng).randomizer::(note.commitment()); + let rk = private::ActionSigningKey::new(&alpha).derive_action_public(); + + zcash_tachyon::Action { + cv: value::Commitment::from(halo2::pasta::pallas::Affine::generator()), + rk, + sig: action::Signature::read(&[0x01u8; 64][..]).expect("64 bytes"), + } +} + +/// The all-zero anchor. +fn zero_anchor() -> Anchor { + Anchor::read(&[0u8; 32][..]).expect("zero anchor reads") +} + +/// A proof-stamped bundle with the given actions, tachygrams, and covered-actions digest. +fn proven_bundle( + actions: Vec, + tachygrams: Vec, + coverage: [u8; 32], +) -> Bundle { + let tachygram_set = tachygrams.iter().copied().collect::(); + Bundle { + actions, + value_balance: value::Balance::ZERO, + binding_sig: bundle::Signature::read(&[0x02u8; 64][..]).expect("64 bytes"), + memo: Vec::new(), + stamp: ProofStamp { + coverage, + tachygram_set: tachygram_set.commit(), + tachygrams: tachygrams.into_iter().collect(), + anchor: zero_anchor(), + proof: Box::new(ragu::Proof::trivial()), + }, + } +} + +/// A pointer-stamped bundle with the given actions, naming `target` as its covering aggregate. +fn adjunct_bundle(actions: Vec, target: [u8; 64]) -> Bundle { + Bundle { + actions, + value_balance: value::Balance::ZERO, + binding_sig: bundle::Signature::read(&[0x02u8; 64][..]).expect("64 bytes"), + memo: Vec::new(), + stamp: PointerStamp::try_from(target).expect("nonzero wtxid"), + } +} + +/// A V7 transaction carrying only the given tachyon bundle. +fn v7_transaction(tachyon_bundle: TachyonBundle) -> Arc { + Arc::new(Transaction::V7 { + network_upgrade: NetworkUpgrade::NuTachyon, + lock_time: LockTime::min_lock_time_timestamp(), + expiry_height: Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data: Some(tachyon_bundle.into()), + }) +} + +/// A block containing the given transactions (the header contents are irrelevant to coherence). +fn block_with(transactions: Vec>) -> Block { + let template = Block::zcash_deserialize(&zakura_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) + .expect("hardcoded test block deserializes"); + + Block { + header: template.header, + transactions, + } +} + +fn tachygram(value: u64) -> Tachygram { + Tachygram::from(halo2::pasta::pallas::Base::from(value)) +} + +/// Rule 1: two proof stamps sharing a tachygram anywhere in the block are rejected. +#[test] +fn duplicate_tachygram_across_block_is_rejected() { + let _init_guard = zakura_test::init(); + + let action_a = dummy_action(); + let action_b = dummy_action(); + let digest_a = action_descriptor_digest(&[action_a.descriptor()]); + let digest_b = action_descriptor_digest(&[action_b.descriptor()]); + + let block = block_with(vec![ + v7_transaction(TachyonBundle::Proven(proven_bundle( + vec![action_a], + vec![tachygram(1), tachygram(2)], + digest_a, + ))), + v7_transaction(TachyonBundle::Proven(proven_bundle( + vec![action_b], + vec![tachygram(2), tachygram(3)], + digest_b, + ))), + ]); + + assert_eq!( + coherence(&block).err(), + Some(BlockError::DuplicateTachygram) + ); +} + +/// Rule 2: a pointer stamp naming a wtxid with no proof-stamped transaction in the block is +/// rejected. +#[test] +fn pointer_to_missing_aggregate_is_rejected() { + let _init_guard = zakura_test::init(); + + let block = block_with(vec![v7_transaction(TachyonBundle::Adjunct( + adjunct_bundle(vec![dummy_action()], [0xEEu8; 64]), + ))]); + + assert_eq!( + coherence(&block).err(), + Some(BlockError::TachyonAggregateNotFound) + ); +} + +/// Rule 3: a proof stamp whose carried covered-actions digest doesn't match its actions is +/// rejected. +#[test] +fn coverage_mismatch_is_rejected() { + let _init_guard = zakura_test::init(); + + let block = block_with(vec![v7_transaction(TachyonBundle::Proven(proven_bundle( + vec![dummy_action()], + vec![], + // Deliberately not the digest of the bundle's actions. + [0u8; 32], + )))]); + + assert_eq!( + coherence(&block).err(), + Some(BlockError::TachyonCoverageMismatch) + ); +} + +/// Rule 3: the same action descriptor covered twice (in the aggregate and in an adjunct naming +/// it) is rejected, even with a digest computed over the duplicated descriptor list. +#[test] +fn duplicate_covered_action_is_rejected() { + let _init_guard = zakura_test::init(); + + let action = dummy_action(); + let covered_digest = action_descriptor_digest(&[action.descriptor(), action.descriptor()]); + let aggregate_tx = v7_transaction(TachyonBundle::Proven(proven_bundle( + vec![action], + vec![tachygram(1)], + covered_digest, + ))); + + let aggregate_wtxid = WtxId::from(aggregate_tx.as_ref()).as_bytes(); + let adjunct_tx = v7_transaction(TachyonBundle::Adjunct(adjunct_bundle( + vec![action], + aggregate_wtxid, + ))); + + let block = block_with(vec![aggregate_tx, adjunct_tx]); + + assert_eq!( + coherence(&block).err(), + Some(BlockError::TachyonDuplicateAction) + ); +} + +/// Rules 2-3 positive path: an aggregate covering its own actions plus a pointing adjunct's +/// actions passes coherence, and the returned work item groups the adjunct with its aggregate. +#[test] +fn aggregate_with_adjunct_passes_coherence() { + let _init_guard = zakura_test::init(); + + let own_action = dummy_action(); + let adjunct_action = dummy_action(); + + let covered_digest = + action_descriptor_digest(&[own_action.descriptor(), adjunct_action.descriptor()]); + // Two tachygrams per covered action (the aggregate's own and the adjunct's). + let aggregate_tx = v7_transaction(TachyonBundle::Proven(proven_bundle( + vec![own_action], + vec![tachygram(1), tachygram(2), tachygram(3), tachygram(4)], + covered_digest, + ))); + + let aggregate_wtxid = WtxId::from(aggregate_tx.as_ref()).as_bytes(); + let adjunct_tx = v7_transaction(TachyonBundle::Adjunct(adjunct_bundle( + vec![adjunct_action], + aggregate_wtxid, + ))); + + // Self-validation: the hand-rolled digest must agree with tachyon's coverage check. + { + let Some(data) = aggregate_tx.tachyon_shielded_data() else { + panic!("aggregate transaction has a tachyon bundle"); + }; + let TachyonBundle::Proven(aggregate) = &data.0 else { + panic!("aggregate transaction bundle is proof-stamped"); + }; + let Some(data) = adjunct_tx.tachyon_shielded_data() else { + panic!("adjunct transaction has a tachyon bundle"); + }; + let TachyonBundle::Adjunct(adjunct) = &data.0 else { + panic!("adjunct transaction bundle is pointer-stamped"); + }; + aggregate + .verify_coverage(&[adjunct.as_dyn()]) + .expect("hand-rolled action_descriptor_digest must not drift from the tachyon crate"); + } + + let block = block_with(vec![aggregate_tx, adjunct_tx]); + + let aggregates = coherence(&block).expect("coherent block passes"); + assert_eq!(aggregates.len(), 1, "one proof stamp to verify"); + assert_eq!( + aggregates[0].adjuncts.len(), + 1, + "the work item groups the pointing adjunct with its aggregate", + ); +} + +/// An autonome (proof-stamped bundle covering only its own actions) passes coherence by itself. +#[test] +fn autonome_passes_coherence() { + let _init_guard = zakura_test::init(); + + let action = dummy_action(); + let digest = action_descriptor_digest(&[action.descriptor()]); + + let block = block_with(vec![v7_transaction(TachyonBundle::Proven(proven_bundle( + vec![action], + vec![tachygram(7), tachygram(8)], + digest, + )))]); + + let aggregates = coherence(&block).expect("coherent block passes"); + assert_eq!(aggregates.len(), 1); + assert!(aggregates[0].adjuncts.is_empty()); +} + +/// Rule 4 negative path: a proof stamp with a correct covered-actions digest but an invalid +/// (trivial mock) proof fails proof verification. +#[tokio::test(flavor = "multi_thread")] +async fn trivial_proof_fails_verification() { + let _init_guard = zakura_test::init(); + + let action = dummy_action(); + let digest = action_descriptor_digest(&[action.descriptor()]); + let aggregate = AggregateCoverage { + bundle: proven_bundle(vec![action], vec![tachygram(1)], digest), + adjuncts: Vec::new(), + }; + + let result = crate::primitives::tachyon::verify_proof_stamp(aggregate).await; + assert!( + matches!(result, Err(BlockError::TachyonProofInvalid(_))), + "expected TachyonProofInvalid, got: {result:?}", + ); +} + +/// Rule 4 positive path: a genuinely proven (mock proof system) output stamp verifies. +#[tokio::test(flavor = "multi_thread")] +async fn real_mock_proof_passes_verification() { + let _init_guard = zakura_test::init(); + + let mut rng = rand_10::rng(); + + let sk = private::SpendingKey::random(&mut rng); + let note = Note { + pk: sk.derive_payment_key(), + value: value::Positive::try_from(100u64).expect("valid value"), + psi: nullifier::Trapdoor::random(&mut rng), + rcm: CommitmentTrapdoor::random(&mut rng), + }; + let theta = ActionEntropy::random(&mut rng); + let rcv = value::Trapdoor::random(&mut rng); + let output_plan = action::Plan::output(note, theta, rcv); + + let plan = bundle::Plan::new(vec![], vec![output_plan]); + let pak = sk.derive_proof_private(); + let ask = sk.derive_auth_private(); + + let stamp = plan + .stamp_plan(zero_anchor()) + .prove(&mut rng, &pak, vec![]) + .expect("output-only stamp plan proves under the mock proof system"); + + // Proof verification never checks signatures, so the sighash is arbitrary here. + let bundle = plan + .sign(&mut rng, &[0u8; 32], &ask) + .expect("bundle plan signs") + .stamp(stamp); + + let aggregate = AggregateCoverage { + bundle, + adjuncts: Vec::new(), + }; + + crate::primitives::tachyon::verify_proof_stamp(aggregate) + .await + .expect("a genuinely proven stamp verifies"); +} diff --git a/crates/zakura-consensus/src/error.rs b/crates/zakura-consensus/src/error.rs index 76e323712f..d021c2bf8e 100644 --- a/crates/zakura-consensus/src/error.rs +++ b/crates/zakura-consensus/src/error.rs @@ -290,6 +290,15 @@ pub enum TransactionError { #[error("Ironwood proof has a non-canonical size")] IronwoodProofSize, + #[error("Tachyon action value commitment and verification key must not be identity: {0}")] + TachyonIdentityAction(String), + + #[error("Tachyon action and binding signatures must verify: {0}")] + TachyonSignatureInvalid(String), + + #[error("the mempool only accepts autonome Tachyon transactions")] + NonAutonomeTachyon, + #[error("unexpected error")] Other(String), } @@ -492,6 +501,11 @@ impl TransactionError { Self::Balance(_) => consensus("transaction.balance"), Self::OrchardProofSize => consensus("transaction.orchard_proof_size"), Self::IronwoodProofSize => consensus("transaction.ironwood_proof_size"), + Self::TachyonIdentityAction(_) => consensus("transaction.tachyon_identity_action"), + Self::TachyonSignatureInvalid(_) => consensus("transaction.tachyon_signature_invalid"), + Self::NonAutonomeTachyon => { + BodyVerificationClass::Retryable(TransientBodyFailureKind::VerifierUnavailable) + } Self::ImmatureTransparentCoinbaseSpend { .. } => { consensus("transaction.immature_transparent_coinbase_spend") } @@ -545,6 +559,8 @@ impl TransactionError { | OrchardHasEnableCrossAddress | OrchardProofSize | IronwoodProofSize + | TachyonIdentityAction(_) + | TachyonSignatureInvalid(_) | WrongConsensusBranchId | MissingConsensusBranchId | LockedUntilAfterBlockHeight(_) @@ -717,6 +733,24 @@ pub enum BlockError { source: amount::Error, }, + #[error("all Tachygrams in a block must be distinct")] + DuplicateTachygram, + + #[error("a Tachyon pointer stamp must refer to a proof-stamped transaction in the same block")] + TachyonAggregateNotFound, + + #[error("a Tachyon proof stamp's covered-actions digest does not match its aggregate")] + TachyonCoverageMismatch, + + #[error("action descriptors covered by a Tachyon proof stamp must be distinct")] + TachyonDuplicateAction, + + #[error("a Tachyon proof stamp must publish exactly two Tachygrams per covered action")] + TachyonTachygramArityMismatch, + + #[error("a Tachyon proof stamp must verify: {0}")] + TachyonProofInvalid(String), + #[error("unexpected error occurred: {0}")] Other(String), } @@ -755,6 +789,12 @@ impl BlockError { | BadMerkleRoot { .. } | WrongTransactionConsensusBranchId | TooManyTransparentSignatureOperations { .. } => 100, + DuplicateTachygram + | TachyonAggregateNotFound + | TachyonCoverageMismatch + | TachyonDuplicateAction + | TachyonTachygramArityMismatch + | TachyonProofInvalid(_) => 100, Transaction(err) => err.mempool_misbehavior_score(), _other => 0, } diff --git a/crates/zakura-consensus/src/primitives.rs b/crates/zakura-consensus/src/primitives.rs index eeed3cd999..250c650093 100644 --- a/crates/zakura-consensus/src/primitives.rs +++ b/crates/zakura-consensus/src/primitives.rs @@ -17,6 +17,7 @@ pub mod halo2; pub mod redjubjub; pub mod redpallas; pub mod sapling; +pub mod tachyon; /// The maximum batch size for any of the batch verifiers. const MAX_BATCH_SIZE: usize = 64; diff --git a/crates/zakura-consensus/src/primitives/tachyon.rs b/crates/zakura-consensus/src/primitives/tachyon.rs new file mode 100644 index 0000000000..e82769b2cf --- /dev/null +++ b/crates/zakura-consensus/src/primitives/tachyon.rs @@ -0,0 +1,34 @@ +//! Asynchronous verification of Tachyon proof stamps. + +use crate::{block::tachyon::AggregateCoverage, error::BlockError}; + +use super::spawn_fifo; + +/// Verifies a Tachyon aggregate's proof stamp against all covered actions. +pub async fn verify_proof_stamp(aggregate: AggregateCoverage) -> Result<(), BlockError> { + spawn_fifo(move || { + let adjunct_refs: Vec<_> = aggregate + .adjuncts + .iter() + .map(|adjunct| adjunct.as_dyn()) + .collect(); + + match aggregate + .bundle + .verify_proof(&mut rand_10::rng(), &adjunct_refs) + { + Ok(true) => Ok(()), + Ok(false) => Err(BlockError::TachyonProofInvalid( + "proof stamp was disproved".to_string(), + )), + Err(error) => Err(BlockError::TachyonProofInvalid(error.to_string())), + } + }) + .await + .map_err(|_| { + BlockError::Other( + "threadpool unexpectedly dropped response channel sender; is Zakura shutting down?" + .to_string(), + ) + })? +} diff --git a/crates/zakura-consensus/src/transaction.rs b/crates/zakura-consensus/src/transaction.rs index ec58f149b8..b3cf3aa892 100644 --- a/crates/zakura-consensus/src/transaction.rs +++ b/crates/zakura-consensus/src/transaction.rs @@ -486,6 +486,10 @@ where // Transaction parsing enforces the same rule. Repeat it here for // defense-in-depth and transactions constructed in memory. check::shielded_proof_size_is_canonical(&tx)?; + check::tachyon_actions_have_valid_digests(&tx)?; + if req.is_mempool() { + check::tachyon_bundle_is_autonome(&tx)?; + } // Validate the coinbase input consensus rules if req.is_mempool() && tx.is_coinbase() { @@ -1151,7 +1155,8 @@ where &sighash, nu, wtx_id, - ))) + )) + .and(Self::verify_tachyon_signatures(&transaction, &sighash))) } /// Verifies if a V6 `transaction` is supported by `network_upgrade`. @@ -1184,6 +1189,29 @@ where Ok(()) } + /// Queues verification of a V7 transaction's Tachyon action and binding signatures. + fn verify_tachyon_signatures(tx: &Transaction, sighash: &SigHash) -> AsyncChecks { + use zcash_tachyon::TachyonBundle; + + let mut async_checks = AsyncChecks::new(); + let Some(tachyon_shielded_data) = tx.tachyon_shielded_data() else { + return async_checks; + }; + + let bundle = tachyon_shielded_data.0.clone(); + let sighash_bytes = sighash.0; + async_checks.push(primitives::spawn_fifo_and_convert(move || { + match &bundle { + TachyonBundle::NoBundle => Ok(()), + TachyonBundle::Proven(bundle) => bundle.verify_signatures(&sighash_bytes), + TachyonBundle::Adjunct(bundle) => bundle.verify_signatures(&sighash_bytes), + } + .map_err(|error| TransactionError::TachyonSignatureInvalid(error.to_string())) + })); + + async_checks + } + /// Verifies if a transaction's transparent inputs are valid using the provided /// `script_verifier` and `cached_ffi_transaction`. /// diff --git a/crates/zakura-consensus/src/transaction/check.rs b/crates/zakura-consensus/src/transaction/check.rs index 32550a6914..c89657cded 100644 --- a/crates/zakura-consensus/src/transaction/check.rs +++ b/crates/zakura-consensus/src/transaction/check.rs @@ -908,3 +908,33 @@ pub fn consensus_branch_id( Ok(()) } + +/// Checks that no Tachyon action's value commitment or verification key is the identity point. +pub fn tachyon_actions_have_valid_digests(tx: &Transaction) -> Result<(), TransactionError> { + let Some(tachyon_shielded_data) = tx.tachyon_shielded_data() else { + return Ok(()); + }; + + for action in tachyon_shielded_data.actions() { + action + .digest() + .map_err(|error| TransactionError::TachyonIdentityAction(error.to_string()))?; + } + + Ok(()) +} + +/// Checks that a Tachyon transaction is autonome before it enters the mempool. +pub fn tachyon_bundle_is_autonome(tx: &Transaction) -> Result<(), TransactionError> { + let Some(tachyon_shielded_data) = tx.tachyon_shielded_data() else { + return Ok(()); + }; + + match &tachyon_shielded_data.0 { + zcash_tachyon::TachyonBundle::Proven(bundle) if bundle.is_autonome() => Ok(()), + zcash_tachyon::TachyonBundle::NoBundle => Ok(()), + zcash_tachyon::TachyonBundle::Proven(_) | zcash_tachyon::TachyonBundle::Adjunct(_) => { + Err(TransactionError::NonAutonomeTachyon) + } + } +} diff --git a/crates/zakura-consensus/src/transaction/tests.rs b/crates/zakura-consensus/src/transaction/tests.rs index 415f71a058..6d1e6a9697 100644 --- a/crates/zakura-consensus/src/transaction/tests.rs +++ b/crates/zakura-consensus/src/transaction/tests.rs @@ -54,6 +54,8 @@ use super::{check, Request, Verifier}; #[cfg(test)] mod prop; +#[cfg(test)] +mod tachyon; /// Returns the timeout duration for tests, extended when running under coverage /// instrumentation to account for the performance overhead. diff --git a/crates/zakura-consensus/src/transaction/tests/prop.rs b/crates/zakura-consensus/src/transaction/tests/prop.rs index 3ec7dc80e5..5cf02a1cb2 100644 --- a/crates/zakura-consensus/src/transaction/tests/prop.rs +++ b/crates/zakura-consensus/src/transaction/tests/prop.rs @@ -339,6 +339,7 @@ fn mock_transparent_transaction( sapling_shielded_data: None, orchard_shielded_data: None, ironwood_shielded_data: None, + tachyon_shielded_data: None, network_upgrade, }, invalid_version => unreachable!("invalid transaction version: {}", invalid_version), diff --git a/crates/zakura-consensus/src/transaction/tests/tachyon.rs b/crates/zakura-consensus/src/transaction/tests/tachyon.rs new file mode 100644 index 0000000000..fd0d372f72 --- /dev/null +++ b/crates/zakura-consensus/src/transaction/tests/tachyon.rs @@ -0,0 +1,406 @@ +//! Tests for V7 (tachyon) transaction verification. + +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; + +use chrono::Utc; +use tower::{service_fn, ServiceExt}; + +use zakura_chain::{ + block::Height, + parameters::{testnet::ConfiguredActivationHeights, Network, NetworkUpgrade}, + transaction::{HashType, LockTime, Transaction}, +}; +use zcash_tachyon::{ + action, bundle, effect, + entropy::ActionEntropy, + keys::private, + note::{CommitmentTrapdoor, Note}, + nullifier, value, PointerStamp, ProofStamp, Tachygram, TachygramSetPoly, TachyonBundle, +}; + +use crate::{ + error::TransactionError, + transaction::{check, Request, Response, Verifier}, +}; + +/// A regtest network with NuTachyon (tachyon) scheduled, and NuTachyon's activation height. +fn nutachyon_network() -> (Network, Height) { + let network = Network::new_regtest( + ConfiguredActivationHeights { + canopy: Some(1), + nu5: Some(2), + nu6: Some(3), + nu6_1: Some(4), + nu6_2: Some(5), + nu6_3: Some(6), + nu7: Some(8), + nu_tachyon: Some(10), + ..Default::default() + } + .into(), + ); + let height = NetworkUpgrade::NuTachyon + .activation_height(&network) + .expect("NuTachyon activation height is configured"); + (network, height) +} + +/// A V7 transaction with the given tachyon bundle and no other transfers. +fn v7_transaction( + network_upgrade: NetworkUpgrade, + tachyon_bundle: Option, +) -> Transaction { + Transaction::V7 { + network_upgrade, + lock_time: LockTime::min_lock_time_timestamp(), + expiry_height: Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data: tachyon_bundle.map(Into::into), + } +} + +/// A proof stamp with an unasserted covered-actions digest and no proof: tx-level verification +/// never checks the stamp's proof or coverage (those are block-level rules). +fn mock_proof_stamp(tachygrams: Vec) -> ProofStamp { + let tachygram_set = tachygrams.iter().copied().collect::(); + ProofStamp { + coverage: [0u8; 32], + tachygram_set: tachygram_set.commit(), + tachygrams: tachygrams.into_iter().collect(), + anchor: zcash_tachyon::Anchor::read(&[0u8; 64][..]).expect("zero anchor reads"), + proof: Box::new(ragu::Proof::trivial()), + } +} + +/// Computes the covered-actions digest used by a proof stamp. +fn action_descriptor_digest(actions: &[zcash_tachyon::Action]) -> [u8; 32] { + let mut descriptors: Vec<[u8; 64]> = actions.iter().map(|action| action.descriptor()).collect(); + descriptors.sort_unstable(); + + let mut state = blake2b_simd::Params::new() + .hash_length(32) + .personal(b"Tachyon-Actions") + .to_state(); + for descriptor in descriptors { + state.update(&descriptor); + } + state + .finalize() + .as_bytes() + .try_into() + .expect("hash length is 32") +} + +/// A random note worth `value` zatoshis, with its spending key. +fn random_note(rng: &mut impl rand_10::CryptoRng, value: u64) -> (private::SpendingKey, Note) { + let sk = private::SpendingKey::random(rng); + let note = Note { + pk: sk.derive_payment_key(), + value: value::Positive::try_from(value).expect("value is positive and below MAX_MONEY"), + psi: nullifier::Trapdoor::random(rng), + rcm: CommitmentTrapdoor::random(rng), + }; + (sk, note) +} + +/// A spend-only bundle plan worth `value`, with its spend authorizing key. +/// +/// A spend contributes its value positively to the transaction value pool, so the resulting +/// transaction has a valid (non-negative) miner fee. +fn spend_bundle_plan(value: u64) -> (bundle::Plan, private::SpendAuthorizingKey) { + let mut rng = rand_10::rng(); + + let (sk, note) = random_note(&mut rng, value); + let ask = sk.derive_auth_private(); + + let theta = ActionEntropy::random(&mut rng); + let rcv = value::Trapdoor::random(&mut rng); + let spend_plan = action::Plan::spend(note, theta, rcv, |alpha| { + ask.derive_action_private(&alpha).derive_action_public() + }); + + (bundle::Plan::new(vec![spend_plan], vec![]), ask) +} + +/// A properly-signed spend-only bundle over its own transaction's sighash, in the unproven state. +/// +/// The V7 sighash commits to the bundle's effecting data (action descriptors and value balance) +/// but not its signatures or stamp, so the sighash is computed from a draft bundle carrying +/// placeholder signatures, and the returned bundle then signs over it. Attaching the returned +/// bundle (under any stamp) to [`v7_transaction`] yields the same sighash the signatures commit +/// to. +fn signed_spend_bundle(value: u64) -> zcash_tachyon::Bundle { + let mut rng = rand_10::rng(); + + let (plan, ask) = spend_bundle_plan(value); + + let draft = plan + .sign(&mut rng, &[0u8; 32], &ask) + .expect("bundle plan has matching signatures and an in-range value balance"); + let draft_tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven(draft.stamp(mock_proof_stamp(vec![])))), + ); + let sighash = v7_sighash(&draft_tx); + + plan.sign(&mut rng, &sighash, &ask) + .expect("bundle plan has matching signatures and an in-range value balance") +} + +/// The sighash the tachyon bundle's signatures must commit to. +/// +/// The V7 sighash commits to the tachyon bundle's effecting data (action descriptors and value +/// balance), but not its signatures or stamp — so a bundle with placeholder signatures produces +/// the same sighash as its properly-signed counterpart under any stamp. +fn v7_sighash(tx: &Transaction) -> [u8; 32] { + tx.sighash( + NetworkUpgrade::NuTachyon, + HashType::ALL, + Arc::new(Vec::new()), + None, + ) + .expect("sighash is computable for a V7 transaction with no transparent inputs") + .0 +} + +/// Verifies `tx` through the full transaction verifier with a block request at `height`. +async fn verify_block_transaction( + network: &Network, + height: Height, + tx: Transaction, +) -> Result { + // No transparent inputs anywhere in these tests, so the verifier never calls the state. + let state = service_fn(|_| async { unreachable!("state is not queried") }); + let verifier = Verifier::new_for_tests(network, state); + + verifier + .oneshot(Request::Block { + transaction_hash: tx.hash(), + transaction: Arc::new(tx), + known_utxos: Arc::new(HashMap::new()), + known_outpoint_hashes: Arc::new(HashSet::new()), + height, + time: Utc::now(), + }) + .await +} + +/// The mempool accepts only proof stamps that cover their own transaction's actions. +#[test] +fn mempool_accepts_only_autonome_tachyon_transactions() { + let bundle = signed_spend_bundle(100); + let mut stamp = mock_proof_stamp(vec![]); + stamp.coverage = action_descriptor_digest(&bundle.actions); + let autonome = bundle.stamp(stamp); + + let autonome_tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven(autonome.clone())), + ); + assert_eq!(check::tachyon_bundle_is_autonome(&autonome_tx), Ok(())); + + let mut aggregate = autonome.clone(); + aggregate.stamp.coverage = [0xAA; 32]; + let aggregate_tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven(aggregate)), + ); + assert_eq!( + check::tachyon_bundle_is_autonome(&aggregate_tx), + Err(TransactionError::NonAutonomeTachyon), + ); + + let adjunct_tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Adjunct(autonome.strip( + PointerStamp::try_from([0xEE; 64]).expect("nonzero wtxid"), + ))), + ); + assert_eq!( + check::tachyon_bundle_is_autonome(&adjunct_tx), + Err(TransactionError::NonAutonomeTachyon), + ); +} + +/// The V7 sighash commits to the tachyon bundle's effecting data, but not its stamp. +#[tokio::test(flavor = "multi_thread")] +async fn v7_sighash_commits_to_tachyon_bundle() { + let _init_guard = zakura_test::init(); + + let mut rng = rand_10::rng(); + let (plan, ask) = spend_bundle_plan(100); + let bundle = plan + .sign(&mut rng, &[0u8; 32], &ask) + .expect("bundle plan has matching signatures and an in-range value balance"); + + let no_bundle_sighash = v7_sighash(&v7_transaction(NetworkUpgrade::NuTachyon, None)); + let proven_sighash = v7_sighash(&v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven( + bundle.clone().stamp(mock_proof_stamp(vec![])), + )), + )); + let adjunct_sighash = v7_sighash(&v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Adjunct( + bundle + .stamp(mock_proof_stamp(vec![])) + .strip(PointerStamp::try_from([0xEEu8; 64]).expect("nonzero wtxid")), + )), + )); + let other_bundle_sighash = v7_sighash(&v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven( + signed_spend_bundle(100).stamp(mock_proof_stamp(vec![])), + )), + )); + + // The sighash changes when a bundle is present, and differs between distinct action sets. + assert_ne!(no_bundle_sighash, proven_sighash); + assert_ne!(proven_sighash, other_bundle_sighash); + + // The stamp is excluded, so the sighash is invariant across stamping and stripping. + assert_eq!(proven_sighash, adjunct_sighash); +} + +/// A V7 transaction with a properly-signed tachyon bundle passes verification, whether +/// proof-stamped or pointer-stamped: proof coverage is a block-level rule. +#[tokio::test(flavor = "multi_thread")] +async fn v7_with_signed_tachyon_bundle_is_accepted() { + let _init_guard = zakura_test::init(); + let (network, height) = nutachyon_network(); + + // Proof-stamped: the tx-level rules don't verify the proof itself. + let proven = signed_spend_bundle(100).stamp(mock_proof_stamp(vec![])); + let tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven(proven)), + ); + verify_block_transaction(&network, height, tx) + .await + .expect("a signed proof-stamped tachyon transaction should verify"); + + // Pointer-stamped: signature checks still run, proof coverage is deferred to the block. + // The sighash excludes the stamp, so stripping a signed bundle keeps its signatures valid. + let adjunct = signed_spend_bundle(100) + .stamp(mock_proof_stamp(vec![])) + .strip(PointerStamp::try_from([0xEEu8; 64]).expect("nonzero wtxid")); + let tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Adjunct(adjunct)), + ); + verify_block_transaction(&network, height, tx) + .await + .expect("a signed pointer-stamped tachyon transaction should verify"); +} + +/// A tachyon bundle whose signatures don't commit to the transaction sighash is rejected. +#[tokio::test(flavor = "multi_thread")] +async fn v7_with_wrong_sighash_signatures_is_rejected() { + let _init_guard = zakura_test::init(); + let (network, height) = nutachyon_network(); + + // Sign over a different message than the transaction's sighash. + let mut rng = rand_10::rng(); + let (plan, ask) = spend_bundle_plan(100); + let bundle = plan + .sign(&mut rng, &[0x42u8; 32], &ask) + .expect("bundle plan has matching signatures and an in-range value balance") + .stamp(mock_proof_stamp(vec![])); + let tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven(bundle)), + ); + + let result = verify_block_transaction(&network, height, tx).await; + assert!( + matches!(result, Err(TransactionError::TachyonSignatureInvalid(_))), + "expected TachyonSignatureInvalid, got: {result:?}", + ); +} + +/// A tachyon action whose value commitment is the identity point is rejected. +#[tokio::test(flavor = "multi_thread")] +async fn v7_with_identity_cv_is_rejected() { + use halo2::pasta::group::CurveAffine; + + let _init_guard = zakura_test::init(); + let (network, height) = nutachyon_network(); + + let mut rng = rand_10::rng(); + + // A structurally valid action, except its cv is the identity point. The identity check runs + // before signature verification, so the dummy signature bytes are never checked. + let (_, note) = random_note(&mut rng, 100); + let theta = ActionEntropy::random(&mut rng); + let alpha = theta.randomizer::(note.commitment()); + let rk = private::ActionSigningKey::new(&alpha).derive_action_public(); + + let action = zcash_tachyon::Action { + cv: value::Commitment::from(halo2::pasta::pallas::Affine::identity()), + rk, + sig: action::Signature::read(&[0x01u8; 64][..]).expect("64 bytes"), + }; + + let bundle = zcash_tachyon::Bundle { + actions: vec![action], + value_balance: value::Balance::ZERO, + binding_sig: bundle::Signature::read(&[0x02u8; 64][..]).expect("64 bytes"), + memo: Vec::new(), + stamp: mock_proof_stamp(vec![]), + }; + let tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven(bundle)), + ); + + let result = verify_block_transaction(&network, height, tx).await; + assert!( + matches!(result, Err(TransactionError::TachyonIdentityAction(_))), + "expected TachyonIdentityAction, got: {result:?}", + ); +} + +// Distinctness of the tachygrams within one proof stamp is enforced at parse time in +// `zcash_tachyon`: they deserialize into a set, and the wire parser rejects duplicate or +// unsorted entries. A duplicate-tachygram transaction is unrepresentable in Zebra, so there +// is no transaction-level semantic check (or test) for it. + +/// V7 transactions are rejected before NuTachyon activation, including under NU7 +/// itself (the upstream v6-transaction upgrade). +#[tokio::test(flavor = "multi_thread")] +async fn v7_is_rejected_before_nu_tachyon() { + let _init_guard = zakura_test::init(); + let (network, _) = nutachyon_network(); + + let nu7_height = NetworkUpgrade::Nu7 + .activation_height(&network) + .expect("NU7 activation height is configured"); + + // The bundle's actions let the transaction pass the has-inputs-and-outputs check, so the + // network-upgrade gate is what rejects it. The gate errors before any signature check runs, + // so the signatures can be arbitrary. + let mut rng = rand_10::rng(); + let (plan, ask) = spend_bundle_plan(100); + let bundle = plan + .sign(&mut rng, &[0u8; 32], &ask) + .expect("bundle plan has matching signatures and an in-range value balance") + .stamp(mock_proof_stamp(vec![])); + let tx = v7_transaction( + NetworkUpgrade::NuTachyon, + Some(TachyonBundle::Proven(bundle)), + ); + + let result = verify_block_transaction(&network, nu7_height, tx).await; + assert!( + matches!(result, Err(TransactionError::WrongConsensusBranchId)), + "expected WrongConsensusBranchId, got: {result:?}", + ); +} diff --git a/crates/zakura-network/src/peer_set/initialize.rs b/crates/zakura-network/src/peer_set/initialize.rs index 77272cb1d2..b829b83db3 100644 --- a/crates/zakura-network/src/peer_set/initialize.rs +++ b/crates/zakura-network/src/peer_set/initialize.rs @@ -1280,13 +1280,10 @@ where // not invent local budget on failure. let restore_replenishment_demand = remaining_replenishment_demand.load(Ordering::Relaxed) > 0; - // Rust 1.97 replaces this API with `try_update`. - // Zakura supports Rust 1.91. - #[allow(deprecated)] - let _ = remaining_replenishment_demand.fetch_update( + let _ = remaining_replenishment_demand.try_update( Ordering::Relaxed, Ordering::Relaxed, - |demand| Some(demand.saturating_sub(1)), + |demand: usize| Some(demand.saturating_sub(1)), ); if active_outbound_connections.update_count() diff --git a/crates/zakura-network/src/zakura/block_sync/peer_registry.rs b/crates/zakura-network/src/zakura/block_sync/peer_registry.rs index b9cefd4d88..47ec6878db 100644 --- a/crates/zakura-network/src/zakura/block_sync/peer_registry.rs +++ b/crates/zakura-network/src/zakura/block_sync/peer_registry.rs @@ -563,15 +563,12 @@ impl PeerRegistry { return SessionAdmission::Parked; } - // Rust 1.97 replaces this API with `try_update`. - // Zakura supports Rust 1.91. - #[allow(deprecated)] let generation = self .next_generation - .fetch_update( + .try_update( std::sync::atomic::Ordering::Relaxed, std::sync::atomic::Ordering::Relaxed, - |generation| generation.checked_add(1), + |generation: u64| generation.checked_add(1), ) .unwrap_or_else(|_| panic!("block-sync routine generation counter is exhausted")); peers diff --git a/crates/zakura-rpc/Cargo.toml b/crates/zakura-rpc/Cargo.toml index 08e6059209..e31dcff4f1 100644 --- a/crates/zakura-rpc/Cargo.toml +++ b/crates/zakura-rpc/Cargo.toml @@ -107,6 +107,7 @@ zcash_primitives = { workspace = true, features = ["transparent-inputs", "non-st zcash_protocol = { workspace = true } zcash_script = { workspace = true } zcash_transparent = { workspace = true } +zcash_tachyon = { workspace = true } zakura-chain = { path = "../zakura-chain", version = "6.0.0", features = [ "json-conversion", @@ -124,6 +125,7 @@ tokio-rustls = { version = "0.26.4", default-features = false, features = ["logg der = "0.7.10" [dev-dependencies] +blake2b_simd = { workspace = true } bytes = { workspace = true } http-body = { workspace = true } insta = { workspace = true, features = ["redactions", "json", "ron"] } @@ -132,6 +134,7 @@ tempfile = { workspace = true } toml = { workspace = true } proptest = { workspace = true } +ragu = { workspace = true } tokio = { workspace = true, features = ["full", "tracing", "test-util"] } diff --git a/crates/zakura-rpc/src/methods.rs b/crates/zakura-rpc/src/methods.rs index c1710f215a..4f7684b4ad 100644 --- a/crates/zakura-rpc/src/methods.rs +++ b/crates/zakura-rpc/src/methods.rs @@ -2725,6 +2725,25 @@ where mempool_tx_deps, ); + #[cfg(test)] + let (dependency_depths, selected_txs): (Vec<_>, Vec<_>) = mempool_txs.into_iter().unzip(); + #[cfg(not(test))] + let selected_txs = mempool_txs; + + let selected_txs = types::get_block_template::tachyon::aggregate_transactions( + self.network.clone(), + height, + chain_info.tip_hash, + read_state, + selected_txs, + ) + .await; + + #[cfg(test)] + let mempool_txs: Vec<_> = dependency_depths.into_iter().zip(selected_txs).collect(); + #[cfg(not(test))] + let mempool_txs = selected_txs; + tracing::debug!( selected_mempool_tx_hashes = ?mempool_txs .iter() diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_info@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_info@mainnet_10.snap index 6aa8f2e1aa..b06015ec0e 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_info@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_info@mainnet_10.snap @@ -6,7 +6,7 @@ expression: info "version": 100, "build": "v0.0.1", "subversion": "[SubVersion]", - "protocolversion": 170160, + "protocolversion": 170190, "blocks": 10, "connections": 0, "difficulty": 1.0, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_info@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_info@testnet_10.snap index 894224b8b3..fc9a0e52cd 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_info@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_info@testnet_10.snap @@ -6,7 +6,7 @@ expression: info "version": 100, "build": "v0.0.1", "subversion": "[SubVersion]", - "protocolversion": 170160, + "protocolversion": 170190, "blocks": 10, "connections": 0, "difficulty": 1.0, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@mainnet_10.snap index 7f7eb1ccdb..aa90ae69e6 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@mainnet_10.snap @@ -5,7 +5,7 @@ expression: get_network_info { "version": 100, "subversion": "RPC test", - "protocolversion": 170160, + "protocolversion": 170190, "localservices": "0000000000000001", "timeoffset": 0, "connections": 1, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@testnet_10.snap index 7f7eb1ccdb..aa90ae69e6 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_network_info@testnet_10.snap @@ -5,7 +5,7 @@ expression: get_network_info { "version": 100, "subversion": "RPC test", - "protocolversion": 170160, + "protocolversion": 170190, "localservices": "0000000000000001", "timeoffset": 0, "connections": 1, diff --git a/crates/zakura-rpc/src/methods/tests/utils.rs b/crates/zakura-rpc/src/methods/tests/utils.rs index 9d6dde1405..69b149350a 100644 --- a/crates/zakura-rpc/src/methods/tests/utils.rs +++ b/crates/zakura-rpc/src/methods/tests/utils.rs @@ -22,6 +22,7 @@ pub fn fake_history_tree(network: &Network) -> Arc { &first_sapling_root, &Default::default(), &Default::default(), + &Default::default(), ) .unwrap(); diff --git a/crates/zakura-rpc/src/methods/types/get_block_template.rs b/crates/zakura-rpc/src/methods/types/get_block_template.rs index df4702fb3b..97082d88ea 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template.rs @@ -3,6 +3,7 @@ pub mod constants; pub mod parameters; pub mod proposal; +pub mod tachyon; pub mod zip317; #[cfg(test)] diff --git a/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs new file mode 100644 index 0000000000..b7e41c3371 --- /dev/null +++ b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs @@ -0,0 +1,736 @@ +//! Tachyon transaction aggregation for mined block templates. + +use std::{ + collections::{BTreeMap, BTreeSet, HashSet}, + sync::Arc, + time::Duration, +}; + +use rand_10::rngs::ThreadRng; +use tokio::time::timeout; +use tower::{BoxError, Service, ServiceExt}; +use zakura_chain::{ + block::{self, Block}, + parameters::Network, + tachyon, + transaction::{Transaction, UnminedTx, VerifiedUnminedTx, WtxId}, +}; +use zakura_state::{ReadRequest, ReadResponse, TachyonMiningData}; +use zcash_tachyon::{Bundle, EpochIndex, PointerStamp, ProofStamp, TachyonBundle}; + +const STATE_QUERY_TIMEOUT: Duration = Duration::from_secs(5); +const AGGREGATION_TIMEOUT: Duration = Duration::from_secs(30); + +/// Aggregate the selected autonome Tachyon transactions without changing their effecting data. +/// +/// A Tachyon transaction is omitted unless its anchor and tachygrams are valid for the exact +/// candidate tip. Proving failures leave the remaining safe transactions autonome, so block +/// template generation remains available while aggregation is temporarily unavailable. +pub async fn aggregate_transactions( + network: Network, + candidate_height: block::Height, + tip_hash: block::Hash, + mut read_state: S, + transactions: Vec, +) -> Vec +where + S: Service + Send + Clone + 'static, + S::Future: Send + 'static, +{ + if !transactions + .iter() + .any(|tx| tx.transaction.transaction().has_tachyon_shielded_data()) + { + return transactions; + } + + let candidates = transactions + .iter() + .filter_map(|tx| autonome_bundle(tx.transaction.transaction())) + .collect::>(); + + if candidates.is_empty() { + return without_tachyon_transactions(transactions); + } + + let anchors = candidates + .iter() + .map(|bundle| tachyon::Anchor::from(bundle.stamp.anchor)) + .collect::>(); + let tachygrams = candidates + .into_iter() + .flat_map(|bundle| bundle.stamp.tachygrams.iter().copied()) + .map(tachyon::Tachygram::from) + .collect::>(); + + let request = ReadRequest::TachyonMiningData { + anchors, + tachygrams, + tip_hash, + candidate_height, + }; + let state_response = timeout(STATE_QUERY_TIMEOUT, async move { + read_state.ready().await?.call(request).await + }) + .await; + + let mining_data = match state_response { + Ok(Ok(ReadResponse::TachyonMiningData(Some(mining_data)))) => mining_data, + Ok(Ok(ReadResponse::TachyonMiningData(None))) => { + return without_tachyon_transactions(transactions); + } + Ok(Ok(_)) => unreachable!("TachyonMiningData requests have matching responses"), + Ok(Err(error)) => { + tracing::warn!(?error, "could not load Tachyon mining data"); + return without_tachyon_transactions(transactions); + } + Err(_) => { + tracing::warn!("timed out loading Tachyon mining data"); + return without_tachyon_transactions(transactions); + } + }; + + let transactions = valid_for_candidate(&network, candidate_height, transactions, &mining_data); + if transactions + .iter() + .filter_map(|tx| autonome_bundle(tx.transaction.transaction())) + .count() + < 2 + { + return transactions; + } + + let fallback_transactions = transactions.clone(); + let aggregation = tokio::task::spawn_blocking(move || { + aggregate_with_data(&network, transactions, mining_data) + }); + + match timeout(AGGREGATION_TIMEOUT, aggregation).await { + Ok(Ok(Ok(transactions))) => transactions, + Ok(Ok(Err(error))) => { + tracing::warn!(%error, "could not aggregate Tachyon transactions"); + fallback_transactions + } + Ok(Err(error)) => { + tracing::warn!(?error, "Tachyon aggregation task failed"); + fallback_transactions + } + Err(_) => { + tracing::warn!("timed out aggregating Tachyon transactions"); + fallback_transactions + } + } +} + +fn valid_for_candidate( + network: &Network, + candidate_height: block::Height, + transactions: Vec, + mining_data: &TachyonMiningData, +) -> Vec { + let mut seen_descriptors = BTreeSet::new(); + let mut seen_tachygrams = BTreeSet::new(); + let mut omitted_ids = HashSet::new(); + let mut valid = Vec::with_capacity(transactions.len()); + + // ZIP-317 selection orders parents before descendants, so recording every omitted mined ID + // removes its dependent transactions transitively in this single pass. + for transaction in transactions { + let mined_id = transaction.transaction.id().mined_id(); + if transaction + .transaction + .transaction() + .spent_outpoints() + .any(|outpoint| omitted_ids.contains(&outpoint.hash)) + { + omitted_ids.insert(mined_id); + continue; + } + + let Some(data) = transaction + .transaction + .transaction() + .tachyon_shielded_data() + else { + valid.push(transaction); + continue; + }; + let TachyonBundle::Proven(bundle) = &data.0 else { + omitted_ids.insert(mined_id); + continue; + }; + + if !bundle.is_autonome() || bundle.verify_coverage(&[]).is_err() { + omitted_ids.insert(mined_id); + continue; + } + + let anchor = tachyon::Anchor::from(bundle.stamp.anchor); + let Some(&anchor_height) = mining_data.anchor_heights.get(&anchor) else { + omitted_ids.insert(mined_id); + continue; + }; + if !tachyon::within_scan_window(network, anchor_height, candidate_height) { + omitted_ids.insert(mined_id); + continue; + } + + let descriptors = bundle + .actions + .iter() + .map(|action| action.descriptor()) + .collect::>(); + if descriptors + .iter() + .any(|item| seen_descriptors.contains(item)) + { + omitted_ids.insert(mined_id); + continue; + } + + let tachygrams = bundle + .stamp + .tachygrams + .iter() + .copied() + .collect::>(); + if tachygrams.iter().any(|item| { + seen_tachygrams.contains(item) + || mining_data + .revealed_tachygrams + .contains(&tachyon::Tachygram::from(*item)) + }) { + omitted_ids.insert(mined_id); + continue; + } + + seen_descriptors.extend(descriptors); + seen_tachygrams.extend(tachygrams); + valid.push(transaction); + } + + valid +} + +fn without_tachyon_transactions(transactions: Vec) -> Vec { + let mut omitted_ids = HashSet::new(); + let mut valid = Vec::with_capacity(transactions.len()); + + // Preserve dependency closure when a Tachyon parent is omitted. + for transaction in transactions { + let mined_id = transaction.transaction.id().mined_id(); + let depends_on_omitted = transaction + .transaction + .transaction() + .spent_outpoints() + .any(|outpoint| omitted_ids.contains(&outpoint.hash)); + + if transaction + .transaction + .transaction() + .has_tachyon_shielded_data() + || depends_on_omitted + { + omitted_ids.insert(mined_id); + } else { + valid.push(transaction); + } + } + + valid +} + +fn aggregate_with_data( + network: &Network, + mut transactions: Vec, + mining_data: TachyonMiningData, +) -> Result, String> { + let mut epoch_groups = BTreeMap::>::new(); + + for (index, transaction) in transactions.iter().enumerate() { + let Some(bundle) = autonome_bundle(transaction.transaction.transaction()) else { + continue; + }; + let anchor = tachyon::Anchor::from(bundle.stamp.anchor); + let Some(&height) = mining_data.anchor_heights.get(&anchor) else { + continue; + }; + let Some(epoch) = tachyon::epoch(network, height) else { + continue; + }; + + epoch_groups.entry(epoch).or_default().push((index, height)); + } + + let mut rng = rand_10::rng(); + for (epoch, group) in epoch_groups { + if group.len() < 2 { + continue; + } + + aggregate_epoch_group( + &mut rng, + &mut transactions, + &mining_data.blocks, + EpochIndex(epoch), + &group, + )?; + } + + Ok(transactions) +} + +fn aggregate_epoch_group( + rng: &mut ThreadRng, + transactions: &mut [VerifiedUnminedTx], + blocks: &BTreeMap>, + epoch: EpochIndex, + group: &[(usize, block::Height)], +) -> Result<(), String> { + let target_height = group + .iter() + .map(|(_index, height)| *height) + .max() + .expect("non-empty aggregation groups have a target height"); + let target_anchor = group + .iter() + .find_map(|(index, height)| { + (*height == target_height) + .then(|| autonome_bundle(transactions[*index].transaction.transaction())) + .flatten() + .map(|bundle| bundle.stamp.anchor) + }) + .expect("group transactions have autonome bundles"); + + let mut lifted_stamps = Vec::with_capacity(group.len()); + + for &(index, start_height) in group { + let bundle = autonome_bundle(transactions[index].transaction.transaction()) + .expect("group transactions have autonome bundles"); + let descriptors = bundle + .actions + .iter() + .map(|action| action.descriptor()) + .collect::>(); + let mut lifted_bundle = bundle.clone(); + + if lifted_bundle.stamp.anchor != target_anchor { + let mut next_bundles = Vec::new(); + for height_value in (start_height.0 + 1)..=target_height.0 { + let height = block::Height(height_value); + let block = blocks + .get(&height) + .ok_or_else(|| format!("missing block at {height:?} for Tachyon lift"))?; + next_bundles.extend( + block + .transactions + .iter() + .filter_map(|transaction| proof_bundle(transaction)), + ); + } + + lifted_bundle = lifted_bundle + .lift(rng, &[], (epoch, &next_bundles)) + .map_err(|error| format!("could not lift Tachyon proof stamp: {error}"))?; + } + + lifted_stamps.push((lifted_bundle.stamp, descriptors)); + } + + let mut lifted_stamps = lifted_stamps.into_iter(); + let (mut merged_stamp, mut merged_descriptors) = lifted_stamps + .next() + .expect("non-empty aggregation groups have a first stamp"); + for (stamp, descriptors) in lifted_stamps { + let combined_descriptors = merged_descriptors + .union(&descriptors) + .copied() + .collect::>(); + merged_stamp = ProofStamp::merge( + rng, + (merged_stamp, merged_descriptors), + (stamp, descriptors), + ) + .map_err(|error| format!("could not merge Tachyon proof stamps: {error}"))?; + merged_descriptors = combined_descriptors; + } + + let aggregate_index = group[0].0; + let mut aggregate_bundle = + autonome_bundle(transactions[aggregate_index].transaction.transaction()) + .expect("group transactions have autonome bundles") + .clone(); + aggregate_bundle.stamp = merged_stamp; + replace_bundle( + &mut transactions[aggregate_index], + TachyonBundle::Proven(aggregate_bundle), + ); + + let aggregate_wtxid = WtxId::from( + transactions[aggregate_index] + .transaction + .transaction() + .as_ref(), + ) + .as_bytes(); + let pointer = PointerStamp::try_from(aggregate_wtxid) + .map_err(|error| format!("invalid Tachyon aggregate wtxid: {error}"))?; + + for &(index, _height) in &group[1..] { + let adjunct = autonome_bundle(transactions[index].transaction.transaction()) + .expect("group transactions have autonome bundles") + .clone() + .strip(pointer); + replace_bundle(&mut transactions[index], TachyonBundle::Adjunct(adjunct)); + } + + Ok(()) +} + +fn autonome_bundle(transaction: &Transaction) -> Option<&Bundle> { + let bundle = proof_bundle(transaction)?; + bundle.is_autonome().then_some(bundle) +} + +fn proof_bundle(transaction: &Transaction) -> Option<&Bundle> { + let data = transaction.tachyon_shielded_data()?; + let TachyonBundle::Proven(bundle) = &data.0 else { + return None; + }; + Some(bundle) +} + +fn replace_bundle(transaction: &mut VerifiedUnminedTx, bundle: TachyonBundle) { + let mut mined_transaction = transaction.transaction.transaction().as_ref().clone(); + let Transaction::V7 { + tachyon_shielded_data, + .. + } = &mut mined_transaction + else { + unreachable!("Tachyon bundles only appear in V7 transactions"); + }; + *tachyon_shielded_data = Some(bundle.into()); + transaction.transaction = UnminedTx::from(Arc::new(mined_transaction)); +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use zakura_chain::{ + amount::Amount, + block::{Block, Height}, + parameters::{testnet::ConfiguredActivationHeights, NetworkUpgrade}, + serialization::ZcashDeserialize, + transaction::LockTime, + transparent, + }; + use zcash_tachyon::{ + entropy::ActionEntropy, + keys::private, + note::{CommitmentTrapdoor, Note}, + nullifier, + stamp::StampState as _, + value, Anchor, TachygramSetPoly, + }; + + use super::*; + + #[test] + fn same_anchor_autonome_transactions_are_aggregated() { + let network = nutachyon_network(); + let anchor = Anchor::read(&[0u8; 32][..]).expect("zero anchor reads"); + let original = vec![verified_transaction(anchor), verified_transaction(anchor)]; + let original_ids = original + .iter() + .map(|tx| tx.transaction.id().mined_id()) + .collect::>(); + let mining_data = TachyonMiningData { + anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), + blocks: BTreeMap::new(), + revealed_tachygrams: HashSet::new(), + }; + + let original = valid_for_candidate(&network, Height(11), original, &mining_data); + let aggregated = aggregate_with_data(&network, original, mining_data) + .expect("same-anchor stamps can be merged"); + + assert_aggregated(&aggregated, &original_ids); + } + + #[test] + fn older_autonome_stamp_is_lifted_before_aggregation() { + let network = nutachyon_network(); + let start_anchor = Anchor::read(&[0u8; 32][..]).expect("zero anchor reads"); + let intervening_transaction = verified_transaction(start_anchor) + .transaction + .transaction() + .clone(); + let intervening_bundle = proof_bundle(&intervening_transaction) + .expect("intervening transaction has a proof-stamped bundle"); + let target_anchor = start_anchor + .next_stamp(EpochIndex(0), &intervening_bundle.stamp.tachygram_set) + .expect("intervening stamp advances the anchor"); + let original = vec![ + verified_transaction(start_anchor), + verified_transaction(target_anchor), + ]; + let original_ids = original + .iter() + .map(|tx| tx.transaction.id().mined_id()) + .collect::>(); + + let mut intervening_block = + Block::zcash_deserialize(&zakura_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) + .expect("hardcoded genesis block deserializes"); + intervening_block.transactions = vec![intervening_transaction]; + let mining_data = TachyonMiningData { + anchor_heights: HashMap::from([ + (tachyon::Anchor::from(start_anchor), Height(10)), + (tachyon::Anchor::from(target_anchor), Height(11)), + ]), + blocks: BTreeMap::from([(Height(11), Arc::new(intervening_block))]), + revealed_tachygrams: HashSet::new(), + }; + + let original = valid_for_candidate(&network, Height(12), original, &mining_data); + let aggregated = aggregate_with_data(&network, original, mining_data) + .expect("the older stamp can be lifted and merged"); + + assert_aggregated(&aggregated, &original_ids); + let TachyonBundle::Proven(aggregate) = &aggregated[0] + .transaction + .transaction() + .tachyon_shielded_data() + .expect("aggregate has Tachyon data") + .0 + else { + panic!("the first transaction should carry the aggregate proof"); + }; + assert_eq!(aggregate.stamp.anchor, target_anchor); + } + + #[test] + fn stale_anchor_transactions_are_omitted() { + let network = nutachyon_network(); + let anchor = Anchor::read(&[0u8; 32][..]).expect("zero anchor reads"); + let parent = verified_transaction(anchor); + let child = verified_dependent_transaction(parent.transaction.id().mined_id()); + let transactions = vec![parent, child]; + let mining_data = TachyonMiningData { + anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), + blocks: BTreeMap::new(), + revealed_tachygrams: HashSet::new(), + }; + let candidate_height = Height(10 + 2 * tachyon::EPOCH_LENGTH); + + assert!( + valid_for_candidate(&network, candidate_height, transactions, &mining_data).is_empty(), + "stale-anchor transactions and their descendants must not enter a block template", + ); + } + + #[test] + fn already_revealed_tachygram_transactions_are_omitted() { + let network = nutachyon_network(); + let anchor = Anchor::read(&[0u8; 32][..]).expect("zero anchor reads"); + let transaction = verified_transaction(anchor); + let revealed = autonome_bundle(transaction.transaction.transaction()) + .expect("test transaction is autonome") + .stamp + .tachygrams + .iter() + .next() + .copied() + .expect("test stamp has tachygrams"); + let mining_data = TachyonMiningData { + anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), + blocks: BTreeMap::new(), + revealed_tachygrams: HashSet::from([tachyon::Tachygram::from(revealed)]), + }; + + assert!( + valid_for_candidate(&network, Height(11), vec![transaction], &mining_data).is_empty(), + "a tachygram already revealed in the scan window must not enter a block template", + ); + } + + fn nutachyon_network() -> Network { + Network::new_regtest( + ConfiguredActivationHeights { + canopy: Some(1), + nu5: Some(2), + nu6: Some(3), + nu6_1: Some(4), + nu6_2: Some(5), + nu6_3: Some(6), + nu7: Some(8), + nu_tachyon: Some(10), + ..Default::default() + } + .into(), + ) + } + + fn assert_aggregated( + aggregated: &[VerifiedUnminedTx], + original_ids: &[zakura_chain::transaction::Hash], + ) { + assert_eq!( + aggregated + .iter() + .map(|tx| tx.transaction.id().mined_id()) + .collect::>(), + original_ids, + "aggregation must not change mined transaction IDs", + ); + + let TachyonBundle::Proven(aggregate) = &aggregated[0] + .transaction + .transaction() + .tachyon_shielded_data() + .expect("aggregate has Tachyon data") + .0 + else { + panic!("the first transaction should carry the aggregate proof"); + }; + let TachyonBundle::Adjunct(adjunct) = &aggregated[1] + .transaction + .transaction() + .tachyon_shielded_data() + .expect("adjunct has Tachyon data") + .0 + else { + panic!("the second transaction should point to the aggregate"); + }; + + assert!(aggregate.is_aggregate()); + let adjunct_dyn: &Bundle = adjunct; + assert!(aggregate.is_covering(&[adjunct_dyn])); + assert_eq!( + adjunct.stamp.stamp_digest(), + WtxId::from(aggregated[0].transaction.transaction().as_ref()).as_bytes(), + ); + } + + fn verified_transaction(anchor: Anchor) -> VerifiedUnminedTx { + let bundle = autonome_bundle_at(anchor); + let transaction = Transaction::V7 { + network_upgrade: NetworkUpgrade::NuTachyon, + lock_time: LockTime::min_lock_time_timestamp(), + expiry_height: Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data: Some(TachyonBundle::Proven(bundle).into()), + }; + + VerifiedUnminedTx::new( + UnminedTx::from(transaction), + Amount::try_from(100_000u64).expect("fee is in range"), + 0, + 0, + Arc::new(Vec::new()), + ) + .expect("test transaction pays the conventional fee") + } + + fn verified_dependent_transaction( + parent: zakura_chain::transaction::Hash, + ) -> VerifiedUnminedTx { + let transaction = Transaction::V7 { + network_upgrade: NetworkUpgrade::NuTachyon, + lock_time: LockTime::min_lock_time_timestamp(), + expiry_height: Height(0), + inputs: vec![transparent::Input::PrevOut { + outpoint: transparent::OutPoint { + hash: parent, + index: 0, + }, + unlock_script: transparent::Script::new(&[]), + sequence: 0, + }], + outputs: vec![transparent::Output::new( + Amount::zero(), + transparent::Script::new(&[]), + )], + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data: None, + }; + + VerifiedUnminedTx::new( + UnminedTx::from(transaction), + Amount::try_from(100_000u64).expect("fee is in range"), + 0, + 0, + Arc::new(Vec::new()), + ) + .expect("test transaction pays the conventional fee") + } + + fn autonome_bundle_at(anchor: Anchor) -> Bundle { + let mut rng = rand_10::rng(); + let spending_key = private::SpendingKey::random(&mut rng); + let note = Note { + pk: spending_key.derive_payment_key(), + value: value::Positive::try_from(100u64).expect("positive value"), + psi: nullifier::Trapdoor::random(&mut rng), + rcm: CommitmentTrapdoor::random(&mut rng), + }; + let padding_note = Note { + pk: spending_key.derive_payment_key(), + value: value::Positive::try_from(1u64).expect("positive value"), + psi: nullifier::Trapdoor::random(&mut rng), + rcm: CommitmentTrapdoor::random(&mut rng), + }; + let tachygrams = + BTreeSet::from([note.commitment().into(), padding_note.commitment().into()]); + let ask = spending_key.derive_auth_private(); + let theta = ActionEntropy::random(&mut rng); + let rcv = value::Trapdoor::random(&mut rng); + let spend = zcash_tachyon::action::Plan::spend(note, theta, rcv, |alpha| { + ask.derive_action_private(&alpha).derive_action_public() + }); + let plan = zcash_tachyon::bundle::Plan::new(vec![spend], vec![]); + let bundle = plan + .sign(&mut rng, &[0u8; 32], &ask) + .expect("test bundle signs"); + let coverage = action_descriptor_digest(&bundle.actions); + let tachygram_set = tachygrams + .iter() + .copied() + .collect::() + .commit(); + + bundle.stamp(ProofStamp { + coverage, + anchor, + tachygram_set, + tachygrams, + proof: Box::new(ragu::Proof::trivial()), + }) + } + + fn action_descriptor_digest(actions: &[zcash_tachyon::Action]) -> [u8; 32] { + let mut descriptors: Vec<[u8; 64]> = + actions.iter().map(|action| action.descriptor()).collect(); + descriptors.sort_unstable(); + + let mut state = blake2b_simd::Params::new() + .hash_length(32) + .personal(b"Tachyon-Actions") + .to_state(); + for descriptor in descriptors { + state.update(&descriptor); + } + state + .finalize() + .as_bytes() + .try_into() + .expect("hash length is 32") + } +} diff --git a/crates/zakura-state/Cargo.toml b/crates/zakura-state/Cargo.toml index 17f750f3d3..f6a20a828a 100644 --- a/crates/zakura-state/Cargo.toml +++ b/crates/zakura-state/Cargo.toml @@ -72,6 +72,7 @@ tower = { workspace = true, features = ["buffer", "util"] } tracing = { workspace = true } sapling-crypto = { workspace = true } +zcash_tachyon = { workspace = true } zakura-assets = { workspace = true } zakura-node-services = { path = "../zakura-node-services", version = "3.2.1" } diff --git a/crates/zakura-state/src/constants.rs b/crates/zakura-state/src/constants.rs index cdef373d66..377073d7cd 100644 --- a/crates/zakura-state/src/constants.rs +++ b/crates/zakura-state/src/constants.rs @@ -95,7 +95,7 @@ pub const MAX_PRUNE_HEIGHTS_PER_COMMIT: u32 = 100; /// /// Instead of using this constant directly, use [`constants::state_database_format_version_in_code()`] /// or [`config::database_format_version_on_disk()`] to get the full semantic format version. -const DATABASE_FORMAT_VERSION: u64 = 28; +const DATABASE_FORMAT_VERSION: u64 = 29; /// The database format minor version, incremented each time the on-disk database format has a /// significant data format change. @@ -104,11 +104,17 @@ const DATABASE_FORMAT_VERSION: u64 = 28; /// - adding new column families, /// - changing the format of a column family in a compatible way, or /// - breaking changes with compatibility code in all supported Zebra versions. -const DATABASE_FORMAT_MINOR_VERSION: u64 = 2; +/// +/// Version history: +/// - 29.0.0: adds the Tachyon anchor and Tachygram column families, widens the chain value +/// balance from 48 to 56 bytes, and widens history entries for the V4 history-node fields. +/// Existing value-balance and history rows remain readable, so version 29 can reuse a version +/// 28 database without a row migration. +const DATABASE_FORMAT_MINOR_VERSION: u64 = 0; /// The database format patch version, incremented each time the on-disk database format has a /// significant format compatibility fix. -const DATABASE_FORMAT_PATCH_VERSION: u64 = 5; +const DATABASE_FORMAT_PATCH_VERSION: u64 = 0; /// Returns the full semantic version of the currently running state database format code. /// diff --git a/crates/zakura-state/src/error.rs b/crates/zakura-state/src/error.rs index d598a82229..477695417c 100644 --- a/crates/zakura-state/src/error.rs +++ b/crates/zakura-state/src/error.rs @@ -767,6 +767,9 @@ pub enum ValidateContextError { #[error("error updating a note commitment tree: {0}")] NoteCommitmentTreeError(#[from] zakura_chain::parallel::tree::NoteCommitmentTreeError), + #[error("error advancing the Tachyon anchor: {0}")] + TachyonAnchorError(#[source] Arc), + #[error("error building the history tree: {0}")] HistoryTreeError(#[from] Arc), @@ -820,6 +823,28 @@ pub enum ValidateContextError { tx_index_in_block: Option, transaction_hash: transaction::Hash, }, + + #[error("unknown Tachyon anchor {anchor:?}")] + #[non_exhaustive] + UnknownTachyonAnchor { + anchor: zakura_chain::tachyon::Anchor, + height: Option, + tx_index_in_block: Option, + transaction_hash: transaction::Hash, + }, + + #[error("duplicate Tachyon Tachygram {tachygram:?}, in finalized state: {in_finalized_state}")] + #[non_exhaustive] + DuplicateTachyonTachygram { + tachygram: zakura_chain::tachyon::Tachygram, + in_finalized_state: bool, + }, +} + +impl From for ValidateContextError { + fn from(error: zcash_tachyon::AnchorError) -> Self { + Self::TachyonAnchorError(Arc::new(error)) + } } impl ValidateContextError { @@ -850,6 +875,7 @@ impl ValidateContextError { Self::VctSproutHandoffRootMismatch { .. } | Self::CumulativeWorkOverflow { .. } | Self::NoteCommitmentTreeError(_) + | Self::TachyonAnchorError(_) | Self::HistoryTreeError(_) => { BodyVerificationClass::Retryable(TransientBodyFailureKind::Storage) } @@ -942,6 +968,10 @@ impl ValidateContextError { Self::UnknownSaplingAnchor { .. } => consensus("context.unknown_sapling_anchor"), Self::UnknownOrchardAnchor { .. } => consensus("context.unknown_orchard_anchor"), Self::UnknownIronwoodAnchor { .. } => consensus("context.unknown_ironwood_anchor"), + Self::UnknownTachyonAnchor { .. } => consensus("context.unknown_tachyon_anchor"), + Self::DuplicateTachyonTachygram { .. } => { + consensus("context.duplicate_tachyon_tachygram") + } } } @@ -964,6 +994,7 @@ impl ValidateContextError { | ValidateContextError::DuplicateSaplingNullifier { .. } | ValidateContextError::DuplicateOrchardNullifier { .. } | ValidateContextError::DuplicateIronwoodNullifier { .. } + | ValidateContextError::DuplicateTachyonTachygram { .. } | ValidateContextError::NegativeRemainingTransactionValue { .. } | ValidateContextError::AddValuePool { .. } | ValidateContextError::InvalidBlockCommitment(_) @@ -971,6 +1002,7 @@ impl ValidateContextError { | ValidateContextError::UnknownSaplingAnchor { .. } | ValidateContextError::UnknownOrchardAnchor { .. } | ValidateContextError::UnknownIronwoodAnchor { .. } => 100, + ValidateContextError::UnknownTachyonAnchor { .. } => 100, // Residual arithmetic failures in our own value summation, not // consensus violations. `remaining_transaction_value()` and @@ -1000,6 +1032,7 @@ impl ValidateContextError { | ValidateContextError::CumulativeWorkOverflow { .. } | ValidateContextError::OrphanedBlock { .. } | ValidateContextError::NoteCommitmentTreeError(_) + | ValidateContextError::TachyonAnchorError(_) | ValidateContextError::HistoryTreeError(_) => 0, } } diff --git a/crates/zakura-state/src/lib.rs b/crates/zakura-state/src/lib.rs index 3210385ce8..dba706ecb8 100644 --- a/crates/zakura-state/src/lib.rs +++ b/crates/zakura-state/src/lib.rs @@ -66,7 +66,7 @@ pub use request::Spend; pub use response::{ AnyTx, BlockSyncBodyMetadata, GetBlockTemplateChainInfo, KnownBlock, MinedTx, - NonFinalizedBlocksListener, ReadResponse, Response, + NonFinalizedBlocksListener, ReadResponse, Response, TachyonMiningData, }; #[cfg(any(test, feature = "header-fuzz"))] pub use service::finalized_state::{replay_recovery_rows_bytes, RecoveryRowsReplaySummary}; diff --git a/crates/zakura-state/src/request.rs b/crates/zakura-state/src/request.rs index 39dda3c979..6305d0ea27 100644 --- a/crates/zakura-state/src/request.rs +++ b/crates/zakura-state/src/request.rs @@ -26,6 +26,7 @@ use zakura_chain::{ serialization::SerializationError, sprout, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeIndex}, + tachyon, transaction::{self, UnminedTx}, transparent, value_balance::{ValueBalance, ValueBalanceError}, @@ -370,6 +371,8 @@ impl Treestate { sapling_subtree: Option>, orchard_subtree: Option>, ironwood_subtree: Option>, + tachyon_anchor: zakura_chain::tachyon::Anchor, + tachyon_epoch_anchor: Option, history_tree: Arc, ) -> Self { Self { @@ -381,6 +384,8 @@ impl Treestate { orchard_subtree, ironwood, ironwood_subtree, + tachyon_anchor, + tachyon_epoch_anchor, }, history_tree, } @@ -1460,6 +1465,23 @@ pub enum ReadRequest { /// with the pool values of the current best chain tip. TipPoolValues, + /// Returns the chain data needed to aggregate transactions rooted at Tachyon anchors. + /// + /// Returns [`ReadResponse::TachyonMiningData`] if `tip_hash` is still the current best-chain + /// tip. Unknown anchors are omitted. For every epoch represented by a known anchor, the + /// response also contains the blocks between the earliest and latest requested anchors and + /// any requested tachygrams already revealed within the candidate's two-epoch window. + TachyonMiningData { + /// Anchors referenced by the selected autonome transactions. + anchors: HashSet, + /// Tachygrams revealed by the selected autonome transactions. + tachygrams: HashSet, + /// The chain tip on which the block template is being built. + tip_hash: block::Hash, + /// The height of the candidate block being built on `tip_hash`. + candidate_height: block::Height, + }, + /// Looks up the block info after a block by hash or height in the current best chain. /// /// * [`ReadResponse::BlockInfo(Some(pool_values))`](ReadResponse::BlockInfo) if the block is in the best chain; @@ -1936,6 +1958,7 @@ impl ReadRequest { ReadRequest::Tip => "tip", ReadRequest::FinalizedTip => "finalized_tip", ReadRequest::TipPoolValues => "tip_pool_values", + ReadRequest::TachyonMiningData { .. } => "tachyon_mining_data", ReadRequest::BlockInfo(_) => "block_info", ReadRequest::Depth(_) => "depth", ReadRequest::Block(_) => "block", diff --git a/crates/zakura-state/src/response.rs b/crates/zakura-state/src/response.rs index 817773d093..52ab0844d2 100644 --- a/crates/zakura-state/src/response.rs +++ b/crates/zakura-state/src/response.rs @@ -1,7 +1,7 @@ //! State [`tower::Service`] response types. use std::{ - collections::{BTreeMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, sync::Arc, }; @@ -14,6 +14,7 @@ use zakura_chain::{ ironwood, orchard, sapling, serialization::DateTime32, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, + tachyon, transaction::{self, Transaction}, transparent, value_balance::ValueBalance, @@ -35,6 +36,19 @@ use crate::{ #[cfg(test)] mod tests; +/// Best-chain data used to aggregate selected autonome Tachyon transactions. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TachyonMiningData { + /// Heights that created the requested anchors. Unknown anchors are omitted. + pub anchor_heights: HashMap, + + /// Blocks needed to advance from each epoch's earliest requested anchor to its latest one. + pub blocks: BTreeMap>, + + /// Requested tachygrams already revealed in the candidate block's two-epoch window. + pub revealed_tachygrams: HashSet, +} + #[derive(Clone, Debug, PartialEq, Eq)] /// A response to a [`StateService`](crate::service::StateService) [`Request`]. pub enum Response { @@ -427,6 +441,10 @@ pub enum ReadResponse { value_balance: ValueBalance, }, + /// Response to [`ReadRequest::TachyonMiningData`], or `None` when the requested chain tip is + /// no longer current. + TachyonMiningData(Option), + /// Response to [`ReadRequest::BlockInfo`] with /// the block info after the specified block. BlockInfo(Option), @@ -701,6 +719,7 @@ impl TryFrom for Response { | ReadResponse::PruningInfo { .. } | ReadResponse::BlockRoots(_) | ReadResponse::TipPoolValues { .. } + | ReadResponse::TachyonMiningData(_) | ReadResponse::BlockInfo(_) | ReadResponse::TransactionIdsForBlock(_) | ReadResponse::AnyChainTransactionIdsForBlock(_) diff --git a/crates/zakura-state/src/service.rs b/crates/zakura-state/src/service.rs index 186b6db2ac..f0826c9f57 100644 --- a/crates/zakura-state/src/service.rs +++ b/crates/zakura-state/src/service.rs @@ -2499,6 +2499,96 @@ impl Service for ReadStateService { }) } + ReadRequest::TachyonMiningData { + anchors, + tachygrams, + tip_hash, + candidate_height, + } => { + let best_chain = state.latest_best_chain(); + if read::tip(best_chain.clone(), &state.db).is_none_or( + |(current_tip_height, current_tip_hash)| { + current_tip_hash != tip_hash + || current_tip_height.0.checked_add(1) != Some(candidate_height.0) + }, + ) { + return Ok(ReadResponse::TachyonMiningData(None)); + } + + let anchor_heights: HashMap<_, _> = anchors + .into_iter() + .filter_map(|anchor| { + best_chain + .as_ref() + .and_then(|chain| chain.tachyon_anchors.get(&anchor)) + .and_then(|heights| heights.last().copied()) + .or_else(|| state.db.tachyon_anchor_height(&anchor)) + .map(|height| (anchor, height)) + }) + .collect(); + + let mut epoch_ranges = HashMap::new(); + for &height in anchor_heights.values() { + let Some(epoch) = zakura_chain::tachyon::epoch(&state.network, height) else { + continue; + }; + + epoch_ranges + .entry(epoch) + .and_modify(|(start, end): &mut (block::Height, block::Height)| { + *start = (*start).min(height); + *end = (*end).max(height); + }) + .or_insert((height, height)); + } + + let mut blocks = BTreeMap::new(); + for (start, end) in epoch_ranges.into_values() { + if start == end { + continue; + } + + for height_value in (start.0 + 1)..=end.0 { + let height = block::Height(height_value); + if let Some(block) = + read::block(best_chain.clone(), &state.db, height.into()) + { + blocks.insert(height, block); + } + } + } + + let revealed_tachygrams = tachygrams + .into_iter() + .filter(|tachygram| { + let in_window = |revealed_height| { + zakura_chain::tachyon::within_scan_window( + &state.network, + revealed_height, + candidate_height, + ) + }; + + best_chain + .as_ref() + .and_then(|chain| chain.tachyon_tachygrams.get(tachygram)) + .is_some_and(|heights| heights.iter().copied().any(in_window)) + || state + .db + .tachyon_tachygram_revealed_height(tachygram) + .is_some_and(in_window) + }) + .collect(); + + Ok(ReadResponse::TachyonMiningData(Some( + crate::response::TachyonMiningData { + anchor_heights, + blocks, + revealed_tachygrams, + }, + ))) + } + // Used by getblock ReadRequest::BlockInfo(hash_or_height) => Ok(ReadResponse::BlockInfo( read::block_info(state.latest_best_chain(), &state.db, hash_or_height), diff --git a/crates/zakura-state/src/service/arbitrary.rs b/crates/zakura-state/src/service/arbitrary.rs index cae90342fe..57bd272151 100644 --- a/crates/zakura-state/src/service/arbitrary.rs +++ b/crates/zakura-state/src/service/arbitrary.rs @@ -175,6 +175,7 @@ impl Strategy for PreparedChain { &Default::default(), &Default::default(), &Default::default(), + &Default::default(), ) .expect("history tree should be created"); *chain = Some(( diff --git a/crates/zakura-state/src/service/check/anchors.rs b/crates/zakura-state/src/service/check/anchors.rs index 3e39fd8eb0..a48ffea6f2 100644 --- a/crates/zakura-state/src/service/check/anchors.rs +++ b/crates/zakura-state/src/service/check/anchors.rs @@ -154,6 +154,38 @@ fn sapling_orchard_ironwood_anchors_refer_to_final_treestates( ); } + if let Some(tachyon_shielded_data) = transaction.tachyon_shielded_data() { + if let zcash_tachyon::TachyonBundle::Proven(bundle) = &tachyon_shielded_data.0 { + let anchor = zakura_chain::tachyon::Anchor::from(bundle.stamp.anchor); + let network = finalized_state.network(); + let anchor_in_window = |anchor_height: Height| match height { + Some(height) => { + zakura_chain::tachyon::within_scan_window(&network, anchor_height, height) + } + None => true, + }; + let in_parent_chain = parent_chain.is_some_and(|chain| { + chain + .tachyon_anchors + .get(&anchor) + .is_some_and(|heights| heights.iter().copied().any(anchor_in_window)) + }); + + if !in_parent_chain + && !finalized_state + .tachyon_anchor_height(&anchor) + .is_some_and(anchor_in_window) + { + return Err(ValidateContextError::UnknownTachyonAnchor { + anchor, + height, + tx_index_in_block, + transaction_hash, + }); + } + } + } + Ok(()) } diff --git a/crates/zakura-state/src/service/check/nullifier.rs b/crates/zakura-state/src/service/check/nullifier.rs index c8914cb3ac..9bf3bcb572 100644 --- a/crates/zakura-state/src/service/check/nullifier.rs +++ b/crates/zakura-state/src/service/check/nullifier.rs @@ -62,6 +62,27 @@ pub(crate) fn no_duplicates_in_finalized_chain( } } + let network = finalized_state.network(); + for transaction in &semantically_verified.block.transactions { + for tachygram in transaction.tachyon_tachygrams() { + if finalized_state + .tachyon_tachygram_revealed_height(&tachygram) + .is_some_and(|revealed_height| { + zakura_chain::tachyon::within_scan_window( + &network, + revealed_height, + semantically_verified.height, + ) + }) + { + return Err(ValidateContextError::DuplicateTachyonTachygram { + tachygram, + in_finalized_state: true, + }); + } + } + } + Ok(()) } diff --git a/crates/zakura-state/src/service/check/tests/vectors.rs b/crates/zakura-state/src/service/check/tests/vectors.rs index 59b871ddd7..be207f3365 100644 --- a/crates/zakura-state/src/service/check/tests/vectors.rs +++ b/crates/zakura-state/src/service/check/tests/vectors.rs @@ -89,6 +89,7 @@ fn block_commitment_uses_the_precomputed_auth_data_root() { &sapling_root, &Default::default(), &Default::default(), + &Default::default(), ) .expect("NU5 parent builds a history tree"); diff --git a/crates/zakura-state/src/service/finalized_state.rs b/crates/zakura-state/src/service/finalized_state.rs index 6fafed123d..0605bf36b9 100644 --- a/crates/zakura-state/src/service/finalized_state.rs +++ b/crates/zakura-state/src/service/finalized_state.rs @@ -214,6 +214,10 @@ pub const STATE_COLUMN_FAMILIES_IN_CODE: &[&str] = &[ "ironwood_anchors", "ironwood_note_commitment_tree", "ironwood_note_commitment_subtree", + "tachyon_anchors", + "tachyon_anchor_by_height", + "tachyon_epoch_anchor_by_epoch", + "tachyon_tachygrams", // Chain "history_tree", "tip_chain_value_pool", @@ -897,6 +901,12 @@ impl FinalizedState { }), }; + // The roots-only VCT payload does not carry Tachyon anchors or transaction + // counts, so NuTachyon blocks must use the body-derived commitment path. + let vct_roots = vct_roots.filter(|_| { + NetworkUpgrade::current(&network, height) < NetworkUpgrade::NuTachyon + }); + let mut vct_write = VctWriteData::default(); if let Some((sapling_root, orchard_root, ironwood_root)) = vct_roots { @@ -1028,6 +1038,7 @@ impl FinalizedState { commitment_aux_verify::verify_commitment_roots( &network, (*history_tree).clone(), + prev_note_commitment_trees.tachyon_anchor, verification_items, ) }) @@ -1171,6 +1182,8 @@ impl FinalizedState { orchard_subtree: None, ironwood: ironwood_frontier, ironwood_subtree: None, + tachyon_anchor: note_commitment_trees.tachyon_anchor, + tachyon_epoch_anchor: None, }; // The handoff writes the real final frontier as the tip @@ -1256,10 +1269,22 @@ impl FinalizedState { commitment_result.expect("scope has already finished")?; // Update the history tree (depends on both operations above). + if let Some(pool_height) = + zakura_chain::tachyon::pool_height(&network, checkpoint_verified.height) + { + let advance = note_commitment_trees + .tachyon_anchor + .advance_with_block(pool_height, &block) + .map_err(ValidateContextError::from)?; + note_commitment_trees.tachyon_anchor = advance.post_block; + note_commitment_trees.tachyon_epoch_anchor = advance.epoch_boundary; + } + let history_tree_mut = Arc::make_mut(&mut history_tree); let sapling_root = note_commitment_trees.sapling.root(); let orchard_root = note_commitment_trees.orchard.root(); let ironwood_root = note_commitment_trees.ironwood.root(); + let tachyon_anchor = note_commitment_trees.tachyon_anchor; history_tree_mut .push( &network, @@ -1267,6 +1292,7 @@ impl FinalizedState { &sapling_root, &orchard_root, &ironwood_root, + &tachyon_anchor, ) .map_err(Arc::new) .map_err(ValidateContextError::from)?; diff --git a/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs b/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs index 0a75849788..83a97ac3bb 100644 --- a/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs +++ b/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs @@ -246,6 +246,7 @@ pub(crate) fn verify_supplied_ironwood_root_below_nu6_3( pub(crate) fn verify_commitment_roots( network: &Network, mut history_tree: HistoryTree, + mut tachyon_anchor: zakura_chain::tachyon::Anchor, blocks_to_verify: I, ) -> Result where @@ -295,6 +296,13 @@ where auth_data_root, ) .map_err(|error| match error { + SuppliedRootsError::TachyonDataUnavailable => { + ValidateContextError::HistoryTreeError(Arc::new( + zakura_chain::history_tree::HistoryTreeError::InvalidCachedTree { + reason: "Tachyon history commitments require block bodies", + }, + )) + } SuppliedRootsError::InvalidHeaderCommitment(error) => { ValidateContextError::InvalidBlockCommitment(error) } @@ -328,10 +336,25 @@ where verify_supplied_ironwood_root_below_nu6_3(network, height, &ironwood_root) .map_err(|error| CommitmentRootVerificationError::CurrentRoots { height, error })?; + if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, height) { + tachyon_anchor = tachyon_anchor + .advance_with_block(pool_height, &block) + .map_err(ValidateContextError::from) + .map_err(|error| CommitmentRootVerificationError::CurrentRoots { height, error })? + .post_block; + } + // Fold this block's supplied roots into the running MMR (builds the leaf // from the block body tx-counts + the roots). history_tree - .push(network, block, &sapling_root, &orchard_root, &ironwood_root) + .push( + network, + block, + &sapling_root, + &orchard_root, + &ironwood_root, + &tachyon_anchor, + ) .map_err(Arc::new) .map_err(ValidateContextError::from) .map_err(|error| CommitmentRootVerificationError::CurrentRoots { height, error })?; @@ -367,6 +390,7 @@ mod tests { &Default::default(), &Default::default(), &Default::default(), + &Default::default(), ) .expect("empty history tree for a pre-Heartwood block") } @@ -702,7 +726,7 @@ mod tests { Some(next_block.auth_data_root()), ), ]; - verify_commitment_roots(&Mainnet, empty_history_tree(), ok_items) + verify_commitment_roots(&Mainnet, empty_history_tree(), Default::default(), ok_items) .expect("real roots verify against the headers"); // Negative + lag: a wrong root at the activation height (here, the next @@ -717,8 +741,13 @@ mod tests { Some(next_block.auth_data_root()), ), ]; - let failure = verify_commitment_roots(&Mainnet, empty_history_tree(), bad_items) - .expect_err("a wrong root must be rejected"); + let failure = verify_commitment_roots( + &Mainnet, + empty_history_tree(), + Default::default(), + bad_items, + ) + .expect_err("a wrong root must be rejected"); assert_eq!( failure.height().0, activation + 1, @@ -824,7 +853,7 @@ mod tests { let items: Vec<_> = (start..=end + 1).map(item_at).collect(); // Positive: every supplied root in the range is confirmed by the V2 headers. - verify_commitment_roots(&Mainnet, seed.clone(), items.clone()) + verify_commitment_roots(&Mainnet, seed.clone(), Default::default(), items.clone()) .expect("real NU5 roots verify against the headers"); eprintln!("VCT NU5 positive: {} blocks verified", items.len()); @@ -848,7 +877,7 @@ mod tests { .as_mut() .expect("test verification item has roots") .0 = wrong_root; - let failure = verify_commitment_roots(&Mainnet, seed, bad_items) + let failure = verify_commitment_roots(&Mainnet, seed, Default::default(), bad_items) .expect_err("a wrong NU5 root must be rejected"); assert_eq!( failure.height().0, @@ -1060,15 +1089,14 @@ mod tests { .0; for &(start, end) in ranges.iter().filter(|(start, _)| *start < heartwood) { let items = (start..=end).map(|height| verification_item_at(Height(height))); - verify_commitment_roots(&Mainnet, empty_history_tree(), items).unwrap_or_else( - |failure| { + verify_commitment_roots(&Mainnet, empty_history_tree(), Default::default(), items) + .unwrap_or_else(|failure| { panic!( "pre-Heartwood roots failed at {:?}: {}", failure.height(), failure.error() ) - }, - ); + }); eprintln!("validated direct pre-Heartwood commitments for {start}..={end}"); } @@ -1104,6 +1132,7 @@ mod tests { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ) .expect("network-upgrade activation starts a history-tree epoch"); let confirm_end = end @@ -1112,14 +1141,15 @@ mod tests { let items = (activation + 1..=confirm_end).map(|height| verification_item_at(Height(height))); - verify_commitment_roots(&Mainnet, history_tree, items).unwrap_or_else(|failure| { - panic!( - "{upgrade:?} MMR linkage failed at {:?} while validating through \ + verify_commitment_roots(&Mainnet, history_tree, Default::default(), items) + .unwrap_or_else(|failure| { + panic!( + "{upgrade:?} MMR linkage failed at {:?} while validating through \ {confirm_end}: {}", - failure.height(), - failure.error() - ) - }); + failure.height(), + failure.error() + ) + }); eprintln!( "validated {upgrade:?} MMR linkage for {activation}..={end} \ (confirmed by {confirm_end})" diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs b/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs index 560bd75e30..96ca1a4290 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs @@ -58,7 +58,7 @@ pub enum HistoryTreeDecodeError { } impl IntoDisk for ValueBalance { - type Bytes = [u8; 48]; + type Bytes = [u8; 56]; fn as_bytes(&self) -> Self::Bytes { self.to_bytes() @@ -260,18 +260,31 @@ impl IntoDisk for BlockInfo { impl FromDisk for BlockInfo { fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { - // We have two different DB formats, one from NU6_1 (Lockbox) one for Ironwood and onwards. + // We have different DB formats for NU6.1, Ironwood, and NuTachyon onward. const NU6_1_VALUE_BALANCE_LEN: usize = 40; const IRONWOOD_VALUE_BALANCE_LEN: usize = 48; + const TACHYON_VALUE_BALANCE_LEN: usize = 56; const BLOCK_SIZE_LEN: usize = 4; const NU6_1_BLOCK_INFO_LEN: usize = NU6_1_VALUE_BALANCE_LEN + BLOCK_SIZE_LEN; const IRONWOOD_BLOCK_INFO_LEN: usize = IRONWOOD_VALUE_BALANCE_LEN + BLOCK_SIZE_LEN; + const TACHYON_BLOCK_INFO_LEN: usize = TACHYON_VALUE_BALANCE_LEN + BLOCK_SIZE_LEN; let bytes = bytes.as_ref(); // We want to be forward-compatible, so this must work even if the // size of the buffer is larger than expected. match bytes.len() { + TACHYON_BLOCK_INFO_LEN.. => { + let value_pools = + ValueBalance::::from_bytes(&bytes[..TACHYON_VALUE_BALANCE_LEN]) + .expect("must work for 56 bytes"); + let size = u32::from_le_bytes( + bytes[TACHYON_VALUE_BALANCE_LEN..TACHYON_VALUE_BALANCE_LEN + BLOCK_SIZE_LEN] + .try_into() + .expect("must be 4 bytes"), + ); + BlockInfo::new(value_pools, size) + } IRONWOOD_BLOCK_INFO_LEN.. => { let value_pools = ValueBalance::::from_bytes(&bytes[..IRONWOOD_VALUE_BALANCE_LEN]) diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs b/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs index 4daabf8bdf..e95525112e 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs @@ -40,9 +40,11 @@ fn valid_history_tree() -> NonEmptyHistoryTree { sapling_root: &sapling_root, orchard_root: &orchard_root, ironwood_root: &ironwood_root, + tachyon_anchor: &Default::default(), sapling_tx: 0, orchard_tx: 0, ironwood_tx: 0, + tachyon_tx: 0, }, ) .expect("test history tree must be valid") diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs b/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs index 163b31816d..edc2274e78 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/header_chain.rs @@ -528,7 +528,10 @@ mod tests { "missing opened column family {expected}" ); } - assert_eq!(db.format_version_in_code().minor, 2); + assert_eq!( + db.format_version_in_code(), + state_database_format_version_in_code() + ); drop(db); let reopened = ZakuraDb::new( diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs b/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs index c34ce15806..3870308147 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs @@ -11,6 +11,7 @@ use zakura_chain::{ block::{merkle::AuthDataRoot, Height}, ironwood, orchard, sapling, sprout, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, + tachyon, }; use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk}; @@ -87,6 +88,64 @@ impl FromDisk for orchard::tree::Root { } } +impl IntoDisk for tachyon::Anchor { + type Bytes = [u8; 32]; + + fn as_bytes(&self) -> Self::Bytes { + self.into() + } +} + +impl FromDisk for tachyon::Anchor { + fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { + let bytes: [u8; 32] = bytes + .as_ref() + .try_into() + .expect("Tachyon anchors have a fixed 32-byte disk encoding"); + bytes.into() + } +} + +impl IntoDisk for tachyon::Tachygram { + type Bytes = [u8; 32]; + + fn as_bytes(&self) -> Self::Bytes { + self.into() + } +} + +impl FromDisk for tachyon::Tachygram { + fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { + let bytes: [u8; 32] = bytes + .as_ref() + .try_into() + .expect("Tachygrams have a fixed 32-byte disk encoding"); + bytes.into() + } +} + +/// An on-disk Tachyon epoch index, encoded big-endian for sort order. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct TachyonEpoch(pub u32); + +impl IntoDisk for TachyonEpoch { + type Bytes = [u8; 4]; + + fn as_bytes(&self) -> Self::Bytes { + self.0.to_be_bytes() + } +} + +impl FromDisk for TachyonEpoch { + fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { + let bytes: [u8; 4] = bytes + .as_ref() + .try_into() + .expect("Tachyon epochs have a fixed 4-byte disk encoding"); + Self(u32::from_be_bytes(bytes)) + } +} + /// The per-height Sapling and Orchard note-commitment roots, as stored in the /// `commitment_roots_by_height` index (keyed by [`Height`]). /// diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs b/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs index c40206c7a5..7147444b1e 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs @@ -576,16 +576,41 @@ fn roundtrip_value_balance() { } #[test] -fn roundtrip_block_info_with_ironwood_value_pool() { +fn roundtrip_block_info_with_tachyon_value_pool() { let _init_guard = zakura_test::init(); let ironwood_amount = Amount::::try_from(7).expect("7 zatoshi is a valid amount"); - let block_info = BlockInfo::new(ValueBalance::from_ironwood_amount(ironwood_amount), 123); + let tachyon_amount = Amount::::try_from(13).expect("13 zatoshi is a valid amount"); + let block_info = BlockInfo::new( + (ValueBalance::from_ironwood_amount(ironwood_amount) + + ValueBalance::from_tachyon_amount(tachyon_amount)) + .expect("small non-negative pool balances have a valid sum"), + 123, + ); - assert_eq!(block_info.as_bytes().len(), 52); + assert_eq!(block_info.as_bytes().len(), 60); assert_value_properties(block_info); } +#[test] +fn block_info_decodes_ironwood_value_pools() { + let _init_guard = zakura_test::init(); + + let ironwood_amount = Amount::::try_from(7).expect("7 zatoshi is a valid amount"); + let ironwood_value_pools = ValueBalance::from_ironwood_amount(ironwood_amount); + let mut ironwood_bytes = ironwood_value_pools.as_bytes()[..48].to_vec(); + ironwood_bytes.extend_from_slice(&123_u32.to_le_bytes()); + + let block_info = BlockInfo::from_bytes(ironwood_bytes); + + assert_eq!(block_info.value_pools().ironwood_amount(), ironwood_amount); + assert_eq!( + block_info.value_pools().tachyon_amount(), + Amount::::zero() + ); + assert_eq!(block_info.size(), 123); +} + #[test] fn block_info_decodes_legacy_value_pools() { let _init_guard = zakura_test::init(); diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_0.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_0.snap index 8841b8565e..07978821dc 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_0.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_0.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "000000", - v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", + v: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_1.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_1.snap index d67bac0b1e..346db9eff3 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_1.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_1.snap @@ -5,10 +5,10 @@ expression: cf_data [ KV( k: "000000", - v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", + v: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", ), KV( k: "000001", - v: "24f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051060000", + v: "24f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051060000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_2.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_2.snap index 7adf04d44a..ebda32d2fd 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_2.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@mainnet_2.snap @@ -5,14 +5,14 @@ expression: cf_data [ KV( k: "000000", - v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", + v: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", ), KV( k: "000001", - v: "24f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051060000", + v: "24f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051060000", ), KV( k: "000002", - v: "6cdc0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051060000", + v: "6cdc02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051060000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_0.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_0.snap index 8841b8565e..07978821dc 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_0.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_0.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "000000", - v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", + v: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_1.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_1.snap index 853b7bec64..0eb5739915 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_1.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_1.snap @@ -5,10 +5,10 @@ expression: cf_data [ KV( k: "000000", - v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", + v: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", ), KV( k: "000001", - v: "24f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052060000", + v: "24f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052060000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_2.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_2.snap index 29cda9499f..7508da84e0 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_2.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/block_info_raw_data@testnet_2.snap @@ -5,14 +5,14 @@ expression: cf_data [ KV( k: "000000", - v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", + v: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009c060000", ), KV( k: "000001", - v: "24f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052060000", + v: "24f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052060000", ), KV( k: "000002", - v: "6cdc0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052060000", + v: "6cdc02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052060000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap index 5f9bbd55e3..6a72fd5fcb 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/column_family_names.snap @@ -41,6 +41,10 @@ expression: cf_names "sprout_anchors", "sprout_note_commitment_tree", "sprout_nullifiers", + "tachyon_anchor_by_height", + "tachyon_anchors", + "tachyon_epoch_anchor_by_epoch", + "tachyon_tachygrams", "tip_chain_value_pool", "tx_by_loc", "tx_loc_by_hash", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap index db60cc5ab7..b7c4315eab 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_0.snap @@ -26,6 +26,10 @@ expression: empty_column_families "sapling_note_commitment_subtree: no entries", "sapling_nullifiers: no entries", "sprout_nullifiers: no entries", + "tachyon_anchor_by_height: no entries", + "tachyon_anchors: no entries", + "tachyon_epoch_anchor_by_epoch: no entries", + "tachyon_tachygrams: no entries", "tx_loc_by_spent_out_loc: no entries", "tx_loc_by_transparent_addr_loc: no entries", "utxo_by_out_loc: no entries", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap index 6f6e13334c..79765ebce3 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_1.snap @@ -25,6 +25,10 @@ expression: empty_column_families "sapling_note_commitment_subtree: no entries", "sapling_nullifiers: no entries", "sprout_nullifiers: no entries", + "tachyon_anchor_by_height: no entries", + "tachyon_anchors: no entries", + "tachyon_epoch_anchor_by_epoch: no entries", + "tachyon_tachygrams: no entries", "tx_loc_by_spent_out_loc: no entries", "vct_sync_metadata: no entries", "zakura_header_body_size_by_height: no entries", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap index 6f6e13334c..79765ebce3 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@mainnet_2.snap @@ -25,6 +25,10 @@ expression: empty_column_families "sapling_note_commitment_subtree: no entries", "sapling_nullifiers: no entries", "sprout_nullifiers: no entries", + "tachyon_anchor_by_height: no entries", + "tachyon_anchors: no entries", + "tachyon_epoch_anchor_by_epoch: no entries", + "tachyon_tachygrams: no entries", "tx_loc_by_spent_out_loc: no entries", "vct_sync_metadata: no entries", "zakura_header_body_size_by_height: no entries", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap index 6d1314558c..3d5c8c9ef3 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@no_blocks.snap @@ -40,6 +40,10 @@ expression: empty_column_families "sprout_anchors: no entries", "sprout_note_commitment_tree: no entries", "sprout_nullifiers: no entries", + "tachyon_anchor_by_height: no entries", + "tachyon_anchors: no entries", + "tachyon_epoch_anchor_by_epoch: no entries", + "tachyon_tachygrams: no entries", "tip_chain_value_pool: no entries", "tx_by_loc: no entries", "tx_loc_by_hash: no entries", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap index db60cc5ab7..b7c4315eab 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_0.snap @@ -26,6 +26,10 @@ expression: empty_column_families "sapling_note_commitment_subtree: no entries", "sapling_nullifiers: no entries", "sprout_nullifiers: no entries", + "tachyon_anchor_by_height: no entries", + "tachyon_anchors: no entries", + "tachyon_epoch_anchor_by_epoch: no entries", + "tachyon_tachygrams: no entries", "tx_loc_by_spent_out_loc: no entries", "tx_loc_by_transparent_addr_loc: no entries", "utxo_by_out_loc: no entries", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap index 6f6e13334c..79765ebce3 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_1.snap @@ -25,6 +25,10 @@ expression: empty_column_families "sapling_note_commitment_subtree: no entries", "sapling_nullifiers: no entries", "sprout_nullifiers: no entries", + "tachyon_anchor_by_height: no entries", + "tachyon_anchors: no entries", + "tachyon_epoch_anchor_by_epoch: no entries", + "tachyon_tachygrams: no entries", "tx_loc_by_spent_out_loc: no entries", "vct_sync_metadata: no entries", "zakura_header_body_size_by_height: no entries", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap index 6f6e13334c..79765ebce3 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/empty_column_families@testnet_2.snap @@ -25,6 +25,10 @@ expression: empty_column_families "sapling_note_commitment_subtree: no entries", "sapling_nullifiers: no entries", "sprout_nullifiers: no entries", + "tachyon_anchor_by_height: no entries", + "tachyon_anchors: no entries", + "tachyon_epoch_anchor_by_epoch: no entries", + "tachyon_tachygrams: no entries", "tx_loc_by_spent_out_loc: no entries", "vct_sync_metadata: no entries", "zakura_header_body_size_by_height: no entries", diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_0.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_0.snap index 265231f491..1387f97a82 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_0.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_0.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "", - v: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_1.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_1.snap index d0457c51a5..48ed2dbd87 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_1.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_1.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "", - v: "24f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + v: "24f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_2.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_2.snap index aca4654f3f..c37a66fc80 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_2.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@mainnet_2.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "", - v: "6cdc02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + v: "6cdc020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_0.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_0.snap index 265231f491..1387f97a82 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_0.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_0.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "", - v: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + v: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_1.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_1.snap index d0457c51a5..48ed2dbd87 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_1.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_1.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "", - v: "24f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + v: "24f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_2.snap b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_2.snap index aca4654f3f..c37a66fc80 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_2.snap +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/snapshots/tip_chain_value_pool_raw_data@testnet_2.snap @@ -5,6 +5,6 @@ expression: cf_data [ KV( k: "", - v: "6cdc02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + v: "6cdc020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ), ] diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs b/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs index 42f3d4e5dc..b94b1e363c 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs @@ -148,7 +148,11 @@ fn format_upgrades( "widen history tree entries for NuTachyon", Version::new(28, 2, 5), )), - ] as [Box; 11]) + Box::new(no_migration::NoMigration::new( + "add Tachyon state and widen chain value balance and history entries", + Version::new(29, 0, 0), + )), + ] as [Box; 12]) .into_iter() .filter(move |upgrade| upgrade.version() > min_version()) } @@ -1113,7 +1117,7 @@ fn vct_format_changes_include_root_auth_metadata_updates() { let upgrades: Vec<_> = format_upgrades(Some(Version::new(27, 3, 0))).collect(); - assert_eq!(upgrades.len(), 7); + assert_eq!(upgrades.len(), 8); assert_eq!(upgrades[0].version(), Version::new(28, 0, 0)); assert_eq!(upgrades[1].version(), Version::new(28, 0, 1)); assert_eq!(upgrades[2].version(), Version::new(28, 0, 2)); @@ -1121,6 +1125,7 @@ fn vct_format_changes_include_root_auth_metadata_updates() { assert_eq!(upgrades[4].version(), Version::new(28, 1, 4)); assert_eq!(upgrades[5].version(), Version::new(28, 1, 5)); assert_eq!(upgrades[6].version(), Version::new(28, 2, 5)); + assert_eq!(upgrades[7].version(), Version::new(29, 0, 0)); assert!( !upgrades[3].needs_migration(), "the header-chain column families are created on open without rebasing authenticated roots" diff --git a/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs b/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs index c41b27b94b..2210d3507b 100644 --- a/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs +++ b/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs @@ -238,6 +238,10 @@ pub enum RollbackFinalizedStateError { #[error("failed to rebuild history tree")] HistoryTree(#[from] HistoryTreeError), + /// Rebuilding the Tachyon anchor failed. + #[error("failed to rebuild the Tachyon anchor")] + TachyonAnchor(#[from] zcash_tachyon::AnchorError), + /// Computing a block subsidy failed. #[error("failed to compute block subsidy")] Subsidy(#[from] SubsidyError), @@ -606,14 +610,39 @@ fn rebuild_history_tree_from_upgrade_activation( let (block, sapling_root, orchard_root, ironwood_root) = history_rebuild_inputs_at_height(db, start_height)?; - let mut history_tree = - HistoryTree::from_block(network, block, &sapling_root, &orchard_root, &ironwood_root)?; + let mut tachyon_anchor = zakura_chain::tachyon::Anchor::default(); + if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, start_height) { + tachyon_anchor = tachyon_anchor + .advance_with_block(pool_height, &block)? + .post_block; + } + let mut history_tree = HistoryTree::from_block( + network, + block, + &sapling_root, + &orchard_root, + &ironwood_root, + &tachyon_anchor, + )?; for height in ((start_height.0 + 1)..=target_height.0).map(Height) { let (block, sapling_root, orchard_root, ironwood_root) = history_rebuild_inputs_at_height(db, height)?; - history_tree.push(network, block, &sapling_root, &orchard_root, &ironwood_root)?; + if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, height) { + tachyon_anchor = tachyon_anchor + .advance_with_block(pool_height, &block)? + .post_block; + } + + history_tree.push( + network, + block, + &sapling_root, + &orchard_root, + &ironwood_root, + &tachyon_anchor, + )?; } Ok(history_tree) @@ -679,12 +708,26 @@ fn rebuild_treestate_to_height( .ok_or(RollbackFinalizedStateError::MissingBlock { height })?; note_commitment_trees.update_trees_parallel(&block)?; + if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, height) { + let advance = note_commitment_trees + .tachyon_anchor + .advance_with_block(pool_height, &block)?; + note_commitment_trees.tachyon_anchor = advance.post_block; + note_commitment_trees.tachyon_epoch_anchor = advance.epoch_boundary; + } retained_sprout_roots.insert(note_commitment_trees.sprout.root()); let sapling_root = note_commitment_trees.sapling.root(); let orchard_root = note_commitment_trees.orchard.root(); let ironwood_root = note_commitment_trees.ironwood.root(); - history_tree.push(network, block, &sapling_root, &orchard_root, &ironwood_root)?; + history_tree.push( + network, + block, + &sapling_root, + &orchard_root, + &ironwood_root, + ¬e_commitment_trees.tachyon_anchor, + )?; } Ok(RebuiltTreestate { diff --git a/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs b/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs index a10688fbd5..f3a747995c 100644 --- a/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs +++ b/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs @@ -23,6 +23,7 @@ use zakura_chain::{ parallel::{commitment_aux::BlockCommitmentRoots, tree::NoteCommitmentTrees}, sapling, sprout, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, + tachyon, transaction::Transaction, }; @@ -30,7 +31,7 @@ use crate::{ request::{FinalizedBlock, Treestate}, service::finalized_state::{ disk_db::{DiskWriteBatch, ReadDisk, WriteDisk}, - disk_format::RawBytes, + disk_format::{shielded::TachyonEpoch, RawBytes}, vct::VctWriteData, zakura_db::ZakuraDb, }, @@ -182,6 +183,48 @@ impl ZakuraDb { self.db.zs_contains(&ironwood_anchors, &ironwood_anchor) } + /// Returns the finalized height that created `tachyon_anchor` while it remains retained. + #[allow(clippy::unwrap_in_result)] + pub fn tachyon_anchor_height(&self, tachyon_anchor: &tachyon::Anchor) -> Option { + let tachyon_anchors = self.db.cf_handle("tachyon_anchors").unwrap(); + self.db.zs_get(&tachyon_anchors, tachyon_anchor) + } + + /// Returns the Tachyon pool anchor after the finalized tip. + pub fn tachyon_anchor_for_tip(&self) -> tachyon::Anchor { + let anchors_by_height = self.db.cf_handle("tachyon_anchor_by_height").unwrap(); + self.db + .zs_last_key_value(&anchors_by_height) + .map(|(_height, anchor): (Height, tachyon::Anchor)| anchor) + .unwrap_or_default() + } + + /// Returns the finalized height that revealed `tachygram` while it remains retained. + #[allow(clippy::unwrap_in_result)] + pub fn tachyon_tachygram_revealed_height( + &self, + tachygram: &tachyon::Tachygram, + ) -> Option { + let tachygrams = self.db.cf_handle("tachyon_tachygrams").unwrap(); + self.db.zs_get(&tachygrams, tachygram) + } + + /// Returns the boundary anchor for a finalized Tachyon epoch. + #[allow(clippy::unwrap_in_result)] + pub fn tachyon_epoch_anchor(&self, epoch: u32) -> Option { + let anchors = self.db.cf_handle("tachyon_epoch_anchor_by_epoch").unwrap(); + self.db.zs_get(&anchors, &TachyonEpoch(epoch)) + } + + /// Returns the latest finalized Tachyon epoch. + #[allow(clippy::unwrap_in_result)] + pub fn tachyon_current_epoch(&self) -> Option { + let anchors = self.db.cf_handle("tachyon_epoch_anchor_by_epoch").unwrap(); + self.db + .zs_last_key_value(&anchors) + .map(|(epoch, _anchor): (TachyonEpoch, tachyon::Anchor)| epoch.0) + } + // # Sprout trees /// Returns the Sprout note commitment tree of the finalized tip, @@ -716,6 +759,8 @@ impl ZakuraDb { orchard_subtree: self.orchard_subtree_for_tip(), ironwood: self.ironwood_tree_for_tip(), ironwood_subtree: self.ironwood_subtree_for_tip(), + tachyon_anchor: self.tachyon_anchor_for_tip(), + tachyon_epoch_anchor: None, }) } } @@ -745,6 +790,58 @@ impl DiskWriteBatch { for transaction in &finalized.block.transactions { self.prepare_nullifier_batch(zakura_db, transaction); } + + self.prepare_tachyon_tachygram_batch(zakura_db, finalized); + } + + /// Stores this block's Tachygrams and prunes data outside the two-epoch scan window. + pub fn prepare_tachyon_tachygram_batch( + &mut self, + zakura_db: &ZakuraDb, + finalized: &FinalizedBlock, + ) { + let height = finalized.height; + let block_tachygrams: Vec<_> = finalized + .block + .transactions + .iter() + .flat_map(|transaction| transaction.tachyon_tachygrams()) + .collect(); + let starts_epoch = + tachyon::pool_height(&zakura_db.network(), height).is_some_and(tachyon::is_epoch_first); + + if !starts_epoch && block_tachygrams.is_empty() { + return; + } + + let tachyon_tachygrams = zakura_db.db.cf_handle("tachyon_tachygrams").unwrap(); + if starts_epoch { + let cutoff = Height(height.0.saturating_sub(tachyon::EPOCH_LENGTH)); + let expired_tachygrams: Vec = zakura_db + .db + .zs_forward_range_iter(&tachyon_tachygrams, ..) + .filter(|&(_, revealed): &(tachyon::Tachygram, Height)| revealed < cutoff) + .map(|(tachygram, _)| tachygram) + .collect(); + for tachygram in expired_tachygrams { + self.zs_delete(&tachyon_tachygrams, tachygram); + } + + let tachyon_anchors = zakura_db.db.cf_handle("tachyon_anchors").unwrap(); + let expired_anchors: Vec = zakura_db + .db + .zs_forward_range_iter(&tachyon_anchors, ..) + .filter(|&(_, anchor_height): &(tachyon::Anchor, Height)| anchor_height < cutoff) + .map(|(anchor, _)| anchor) + .collect(); + for anchor in expired_anchors { + self.zs_delete(&tachyon_anchors, anchor); + } + } + + for tachygram in block_tachygrams { + self.zs_insert(&tachyon_tachygrams, tachygram, height); + } } /// Prepare a database batch containing `finalized.block`'s nullifiers, @@ -825,6 +922,23 @@ impl DiskWriteBatch { let orchard_tx = finalized.block.orchard_transactions_count(); let ironwood_tx = finalized.block.ironwood_transactions_count(); + let previous_tachyon_anchor = prev_note_commitment_trees.as_ref().map_or_else( + || zakura_db.tachyon_anchor_for_tip(), + |trees| trees.tachyon_anchor, + ); + if previous_tachyon_anchor != note_commitment_trees.tachyon_anchor { + self.create_tachyon_anchor(zakura_db, height, ¬e_commitment_trees.tachyon_anchor); + } + if let Some(epoch_anchor) = note_commitment_trees.tachyon_epoch_anchor { + let epoch = tachyon::epoch(&zakura_db.network(), *height) + .expect("a block carrying a Tachyon epoch anchor is NuTachyon-onward"); + let epoch_anchors = zakura_db + .db + .cf_handle("tachyon_epoch_anchor_by_epoch") + .unwrap(); + self.zs_insert(&epoch_anchors, TachyonEpoch(epoch), epoch_anchor); + } + // Record the upgrade height `U` once, on the first block this binary commits: the lowest // height in the serving index, and the boundary below which roots are served from the // pre-upgrade per-height trees instead. Written on both commit paths so it is set even for @@ -1097,6 +1211,19 @@ impl DiskWriteBatch { self.zs_insert(&sapling_subtree_cf, subtree.index, subtree.into_data()); } + /// Stores an end-of-block Tachyon anchor by value and height. + pub fn create_tachyon_anchor( + &mut self, + zakura_db: &ZakuraDb, + height: &Height, + anchor: &tachyon::Anchor, + ) { + let anchors = zakura_db.db.cf_handle("tachyon_anchors").unwrap(); + let anchors_by_height = zakura_db.db.cf_handle("tachyon_anchor_by_height").unwrap(); + self.zs_insert(&anchors, anchor, height); + self.zs_insert(&anchors_by_height, height, anchor); + } + /// Deletes the Sapling note commitment tree at the given [`Height`]. pub fn delete_sapling_tree(&mut self, zakura_db: &ZakuraDb, height: &Height) { let sapling_tree_cf = zakura_db diff --git a/crates/zakura-state/src/service/non_finalized_state.rs b/crates/zakura-state/src/service/non_finalized_state.rs index eb58c2e5a9..f90273b448 100644 --- a/crates/zakura-state/src/service/non_finalized_state.rs +++ b/crates/zakura-state/src/service/non_finalized_state.rs @@ -473,6 +473,7 @@ impl NonFinalizedState { finalized_state.sapling_tree_for_tip(), finalized_state.orchard_tree_for_tip(), finalized_state.ironwood_tree_for_tip(), + finalized_state.tachyon_anchor_for_tip(), finalized_state.history_tree(), finalized_state.finalized_value_pool(), ); @@ -538,6 +539,7 @@ impl NonFinalizedState { finalized_state.sapling_tree_for_tip(), finalized_state.orchard_tree_for_tip(), finalized_state.ironwood_tree_for_tip(), + finalized_state.tachyon_anchor_for_tip(), finalized_state.history_tree(), finalized_state.finalized_value_pool(), ); diff --git a/crates/zakura-state/src/service/non_finalized_state/chain.rs b/crates/zakura-state/src/service/non_finalized_state/chain.rs index 1021251f36..8c2692a413 100644 --- a/crates/zakura-state/src/service/non_finalized_state/chain.rs +++ b/crates/zakura-state/src/service/non_finalized_state/chain.rs @@ -24,6 +24,7 @@ use zakura_chain::{ serialization::ZcashSerialize as _, sprout, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, + tachyon, transaction::{ self, Transaction::{self, *}, @@ -217,6 +218,17 @@ pub struct ChainInner { /// This extra root is removed when the first non-finalized block is committed. pub(crate) ironwood_anchors_by_height: BTreeMap, + /// Tachyon end-of-block anchors and the heights that created them. + pub(crate) tachyon_anchors: HashMap>, + /// Tachyon end-of-block anchors indexed by height. + pub(crate) tachyon_anchors_by_height: BTreeMap, + /// Tachyon epoch-boundary anchors indexed by epoch. + pub(crate) tachyon_epoch_anchors_by_epoch: BTreeMap, + /// Tachygrams and every non-finalized height that revealed them. + pub(crate) tachyon_tachygrams: HashMap>, + /// Tachygrams revealed by each non-finalized block. + pub(crate) tachyon_tachygrams_by_height: BTreeMap>, + // Nullifiers // /// The Sprout nullifiers revealed by `blocks` and, if the `indexer` feature is selected, @@ -269,6 +281,7 @@ impl Chain { sapling_note_commitment_tree: Arc, orchard_note_commitment_tree: Arc, ironwood_note_commitment_tree: Arc, + finalized_tip_tachyon_anchor: tachyon::Anchor, history_tree: Arc, finalized_tip_chain_value_pools: ValueBalance, ) -> Self { @@ -294,6 +307,11 @@ impl Chain { ironwood_anchors_by_height: Default::default(), ironwood_trees_by_height: Default::default(), ironwood_subtrees: Default::default(), + tachyon_anchors: Default::default(), + tachyon_anchors_by_height: Default::default(), + tachyon_epoch_anchors_by_epoch: Default::default(), + tachyon_tachygrams: Default::default(), + tachyon_tachygrams_by_height: Default::default(), sprout_nullifiers: Default::default(), sapling_nullifiers: Default::default(), orchard_nullifiers: Default::default(), @@ -317,6 +335,12 @@ impl Chain { chain.add_ironwood_tree_and_anchor(finalized_tip_height, ironwood_note_commitment_tree); chain.add_history_tree(finalized_tip_height, history_tree); + // Only seed a real Tachyon anchor: the default anchor means the pool has not + // started (pre-NuTachyon finalized tip), and only NuTachyon-onward anchors are tracked. + if finalized_tip_tachyon_anchor != tachyon::Anchor::default() { + chain.add_tachyon_anchor(finalized_tip_height, finalized_tip_tachyon_anchor); + } + chain } @@ -1316,6 +1340,169 @@ impl Chain { } } + /// Returns the Tachyon anchor after `hash_or_height`. + pub fn tachyon_anchor(&self, hash_or_height: HashOrHeight) -> Option { + let height = + hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).copied())?; + Some( + self.tachyon_anchors_by_height + .range(..=height) + .next_back() + .map(|(_, anchor)| *anchor) + .unwrap_or_default(), + ) + } + + /// Returns the Tachyon anchor after this chain's tip. + pub fn tachyon_anchor_for_tip(&self) -> tachyon::Anchor { + self.tachyon_anchors_by_height + .last_key_value() + .map(|(_, anchor)| *anchor) + .unwrap_or_default() + } + + fn add_tachyon_anchor(&mut self, height: Height, anchor: tachyon::Anchor) { + assert_eq!( + self.tachyon_anchors_by_height.insert(height, anchor), + None, + "Tachyon anchors must be reverted before replacement", + ); + self.tachyon_anchors.entry(anchor).or_default().push(height); + } + + fn remove_tachyon_anchor(&mut self, position: RevertPosition, height: Height) { + let removed: Vec<_> = if position == RevertPosition::Root { + let removed = self + .tachyon_anchors_by_height + .range(..=height) + .map(|(height, anchor)| (*height, *anchor)) + .collect::>(); + for (height, _) in &removed { + self.tachyon_anchors_by_height.remove(height); + } + removed + } else { + self.tachyon_anchors_by_height + .remove(&height) + .map(|anchor| (height, anchor)) + .into_iter() + .collect() + }; + + for (anchor_height, anchor) in removed { + let heights = self + .tachyon_anchors + .get_mut(&anchor) + .expect("the Tachyon anchor value index mirrors its height index"); + let index = heights + .iter() + .position(|&height| height == anchor_height) + .expect("the Tachyon anchor height exists in both indexes"); + heights.swap_remove(index); + if heights.is_empty() { + self.tachyon_anchors.remove(&anchor); + } + } + } + + /// Returns the boundary anchor for `epoch`. + pub fn tachyon_epoch_anchor(&self, epoch: u32) -> Option { + self.tachyon_epoch_anchors_by_epoch.get(&epoch).copied() + } + + /// Returns the Tachyon epoch containing this chain's tip. + pub fn tachyon_current_epoch(&self) -> Option { + let tip_height = (!self.is_empty()).then(|| self.non_finalized_tip_height())?; + tachyon::epoch(&self.network, tip_height) + } + + fn add_tachyon_epoch_anchor(&mut self, epoch: u32, anchor: tachyon::Anchor) { + assert_eq!( + self.tachyon_epoch_anchors_by_epoch.insert(epoch, anchor), + None, + "a Tachyon epoch starts at most once per chain", + ); + } + + fn remove_tachyon_epoch_anchor(&mut self, position: RevertPosition, height: Height) { + let Some(pool_height) = tachyon::pool_height(&self.network, height) else { + return; + }; + if position == RevertPosition::Root { + let root_epoch = tachyon::epoch_of_pool_height(pool_height); + let epochs = self + .tachyon_epoch_anchors_by_epoch + .range(..=root_epoch) + .map(|(&epoch, _)| epoch) + .collect::>(); + for epoch in epochs { + self.tachyon_epoch_anchors_by_epoch.remove(&epoch); + } + } else if tachyon::is_epoch_first(pool_height) { + self.tachyon_epoch_anchors_by_epoch + .remove(&tachyon::epoch_of_pool_height(pool_height)); + } + } + + fn add_tachyon_tachygrams( + &mut self, + height: Height, + tachygrams: Vec, + ) -> Result<(), ValidateContextError> { + if tachygrams.is_empty() { + return Ok(()); + } + + let network = self.network.clone(); + for &tachygram in &tachygrams { + let revealed_heights = self.tachyon_tachygrams.entry(tachygram).or_default(); + if revealed_heights + .iter() + .any(|&revealed| tachyon::within_scan_window(&network, revealed, height)) + { + return Err(ValidateContextError::DuplicateTachyonTachygram { + tachygram, + in_finalized_state: false, + }); + } + revealed_heights.push(height); + } + self.tachyon_tachygrams_by_height.insert(height, tachygrams); + Ok(()) + } + + fn remove_tachyon_tachygrams(&mut self, position: RevertPosition, height: Height) { + let removed_heights: Vec<_> = if position == RevertPosition::Root { + self.tachyon_tachygrams_by_height + .range(..=height) + .map(|(height, _)| *height) + .collect() + } else { + self.tachyon_tachygrams_by_height + .contains_key(&height) + .then_some(height) + .into_iter() + .collect() + }; + + for height in removed_heights { + let tachygrams = self + .tachyon_tachygrams_by_height + .remove(&height) + .expect("the Tachygram height was selected from this index"); + for tachygram in tachygrams { + let revealed_heights = self + .tachyon_tachygrams + .get_mut(&tachygram) + .expect("the Tachygram value index mirrors its height index"); + revealed_heights.retain(|&revealed| revealed != height); + if revealed_heights.is_empty() { + self.tachyon_tachygrams.remove(&tachygram); + } + } + } + } + /// Returns the History tree of the tip of this [`Chain`], /// including all finalized blocks, and the non-finalized blocks below the chain tip. /// @@ -1391,6 +1578,14 @@ impl Chain { let sapling_subtree = self.sapling_subtree(hash_or_height); let orchard_subtree = self.orchard_subtree(hash_or_height); let ironwood_subtree = self.ironwood_subtree(hash_or_height); + let tachyon_anchor = self.tachyon_anchor(hash_or_height)?; + let height = + hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).copied())?; + let tachyon_epoch_anchor = tachyon::pool_height(&self.network, height) + .filter(|&pool_height| tachyon::is_epoch_first(pool_height)) + .and_then(|pool_height| { + self.tachyon_epoch_anchor(tachyon::epoch_of_pool_height(pool_height)) + }); Some(Treestate::new( sprout_tree, @@ -1400,6 +1595,8 @@ impl Chain { sapling_subtree, orchard_subtree, ironwood_subtree, + tachyon_anchor, + tachyon_epoch_anchor, history_tree, )) } @@ -1687,6 +1884,8 @@ impl Chain { orchard_subtree: self.orchard_subtree_for_tip(), ironwood: self.ironwood_note_commitment_tree_for_tip(), ironwood_subtree: self.ironwood_subtree_for_tip(), + tachyon_anchor: self.tachyon_anchor_for_tip(), + tachyon_epoch_anchor: None, }; let mut tree_result = None; @@ -1731,6 +1930,37 @@ impl Chain { let orchard_root = self.orchard_note_commitment_tree_for_tip().root(); let ironwood_root = self.ironwood_note_commitment_tree_for_tip().root(); + // Advance the Tachyon pool anchor with this block, if the pool has started. + // `nct.tachyon_anchor` still holds the parent block's anchor here. + // + // In builds without tachyon support V7 transactions cannot be parsed, so a NuTachyon + // chain cannot be synced and the anchor legitimately stays at its default. + if let Some(pool_height) = zakura_chain::tachyon::pool_height(&self.network, height) { + let advance = nct + .tachyon_anchor + .advance_with_block(pool_height, &contextually_valid.block)?; + nct.tachyon_anchor = advance.post_block; + nct.tachyon_epoch_anchor = advance.epoch_boundary; + self.add_tachyon_anchor(height, advance.post_block); + if let Some(boundary_anchor) = advance.epoch_boundary { + self.add_tachyon_epoch_anchor( + zakura_chain::tachyon::epoch_of_pool_height(pool_height), + boundary_anchor, + ); + } + } + let tachyon_anchor = nct.tachyon_anchor; + + // Add this block's tachygrams to the epoch working set, rejecting same-epoch + // duplicates within this chain. + let block_tachygrams: Vec<_> = contextually_valid + .block + .transactions + .iter() + .flat_map(|transaction| transaction.tachyon_tachygrams()) + .collect(); + self.add_tachyon_tachygrams(height, block_tachygrams)?; + // TODO: update the history trees in a rayon thread, if they show up in CPU profiles let mut history_tree = self.history_block_commitment_tree(); let history_tree_mut = Arc::make_mut(&mut history_tree); @@ -1741,6 +1971,7 @@ impl Chain { &sapling_root, &orchard_root, &ironwood_root, + &tachyon_anchor, ) .map_err(Arc::new)?; @@ -1862,7 +2093,8 @@ impl Chain { orchard_shielded_data, ironwood_shielded_data, .. - } => ( + } + => ( inputs, outputs, &None, @@ -2132,6 +2364,9 @@ impl UpdateWith for Chain { self.remove_sapling_tree_and_anchor(position, height); self.remove_orchard_tree_and_anchor(position, height); self.remove_ironwood_tree_and_anchor(position, height); + self.remove_tachyon_anchor(position, height); + self.remove_tachyon_epoch_anchor(position, height); + self.remove_tachyon_tachygrams(position, height); // TODO: move this to the history tree UpdateWith.revert...()? self.remove_history_tree(position, height); @@ -2742,3 +2977,153 @@ impl Chain { .insert(subtree.index, subtree.into_data()); } } + +#[cfg(test)] +mod tachyon_tests { + use zakura_chain::parameters::testnet::ConfiguredActivationHeights; + + use super::*; + + /// A chain on a regtest network with NuTachyon (tachyon) activated at height 10, forked + /// from a finalized tip at height 9 (so pool height 0 is chain height 10). + fn nutachyon_chain() -> Chain { + let network = Network::new_regtest( + ConfiguredActivationHeights { + canopy: Some(1), + nu5: Some(2), + nu6: Some(3), + nu6_1: Some(4), + nu6_2: Some(5), + nu6_3: Some(6), + nu7: Some(8), + nu_tachyon: Some(10), + ..Default::default() + } + .into(), + ); + + Chain::new( + &network, + Height(9), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ValueBalance::zero(), + ) + } + + fn tachygram(byte: u8) -> tachyon::Tachygram { + tachyon::Tachygram([byte; 32]) + } + + /// The height of the first block of `epoch` on the [`nutachyon_chain`] network + /// (NuTachyon activates at chain height 10). + fn epoch_first_height(epoch: u32) -> Height { + Height(10 + epoch * tachyon::EPOCH_LENGTH) + } + + #[test] + fn same_epoch_duplicate_tachygram_is_rejected() { + let mut chain = nutachyon_chain(); + + chain + .add_tachyon_tachygrams(Height(20), vec![tachygram(1), tachygram(2)]) + .expect("distinct tachygrams are accepted"); + + let result = chain.add_tachyon_tachygrams(Height(30), vec![tachygram(2)]); + assert!( + matches!( + result, + Err(ValidateContextError::DuplicateTachyonTachygram { + in_finalized_state: false, + .. + }) + ), + "expected DuplicateTachyonTachygram, got: {result:?}", + ); + } + + #[test] + fn duplicate_tachygram_scan_window_spans_two_epochs() { + let mut chain = nutachyon_chain(); + + // Epoch 0 (chain heights 10..10+EPOCH_LENGTH). + chain + .add_tachyon_tachygrams(Height(20), vec![tachygram(1)]) + .expect("first reveal is accepted"); + + // The same tachygram in the immediately following epoch is still within the + // two-epoch scan window: a repeat there is exactly what a double spend's + // next-epoch nullifier would look like. + let result = chain.add_tachyon_tachygrams(epoch_first_height(1), vec![tachygram(1)]); + assert!( + result.is_err(), + "adjacent-epoch duplicate must be rejected: {result:?}", + ); + + // Two epochs later the tachygram has left the scan window. (An honest repeat + // out here would imply a nullifier-derivation collision, but consensus no + // longer tracks it.) + chain + .add_tachyon_tachygrams(epoch_first_height(2), vec![tachygram(1)]) + .expect("a tachygram outside the two-epoch window is accepted again"); + + // And the fresh reveal re-arms the window. + let result = + chain.add_tachyon_tachygrams(Height(epoch_first_height(2).0 + 1), vec![tachygram(1)]); + assert!(result.is_err(), "same-epoch duplicate must be rejected"); + } + + #[test] + fn reverting_a_block_restores_its_tachygrams() { + let mut chain = nutachyon_chain(); + + chain + .add_tachyon_tachygrams(Height(20), vec![tachygram(1)]) + .expect("first reveal is accepted"); + chain + .add_tachyon_tachygrams(Height(30), vec![tachygram(2)]) + .expect("distinct tachygram is accepted"); + + // Fork off the tip block: its tachygram becomes revealable again. + chain.remove_tachyon_tachygrams(RevertPosition::Tip, Height(30)); + chain + .add_tachyon_tachygrams(Height(30), vec![tachygram(2)]) + .expect("a reverted tachygram is revealable again"); + + // A root revert (finalization) drops everything at or below the height. + chain.remove_tachyon_tachygrams(RevertPosition::Root, Height(30)); + assert!(chain.tachyon_tachygrams.is_empty()); + assert!(chain.tachyon_tachygrams_by_height.is_empty()); + } + + #[test] + fn epoch_anchors_are_tracked_and_reverted() { + let mut chain = nutachyon_chain(); + let anchor_0 = tachyon::Anchor([10; 32]); + let anchor_1 = tachyon::Anchor([11; 32]); + + chain.add_tachyon_epoch_anchor(0, anchor_0); + chain.add_tachyon_epoch_anchor(1, anchor_1); + assert_eq!(chain.tachyon_epoch_anchor(0), Some(anchor_0)); + assert_eq!(chain.tachyon_epoch_anchor(1), Some(anchor_1)); + + // Reverting epoch 1's first block from the tip removes its epoch anchor. + chain.remove_tachyon_epoch_anchor(RevertPosition::Tip, epoch_first_height(1)); + assert_eq!(chain.tachyon_epoch_anchor(1), None); + assert_eq!(chain.tachyon_epoch_anchor(0), Some(anchor_0)); + + // Reverting a mid-epoch block from the tip removes nothing. + chain.remove_tachyon_epoch_anchor(RevertPosition::Tip, Height(20)); + assert_eq!(chain.tachyon_epoch_anchor(0), Some(anchor_0)); + + // A root revert at an epoch's first block drops that epoch and everything before it. + chain.add_tachyon_epoch_anchor(1, anchor_1); + chain.remove_tachyon_epoch_anchor(RevertPosition::Root, epoch_first_height(0)); + assert_eq!(chain.tachyon_epoch_anchor(0), None); + assert_eq!(chain.tachyon_epoch_anchor(1), Some(anchor_1)); + } +} diff --git a/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs b/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs index 28a9e06807..4ca8ed0f21 100644 --- a/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs +++ b/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs @@ -71,7 +71,7 @@ fn push_genesis_chain() -> Result<()> { |((chain, count, network, empty_tree) in PreparedChain::default())| { prop_assert!(empty_tree.is_none()); - let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), empty_tree, ValueBalance::zero()); + let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), empty_tree, ValueBalance::zero()); // contains the block value pool changes and chain value pool balances for each height let mut chain_values = BTreeMap::new(); @@ -124,7 +124,7 @@ fn push_history_tree_chain() -> Result<()> { let count = std::cmp::min(count, chain.len() - 1); let chain = &chain[1..]; - let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, ValueBalance::zero()); + let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, ValueBalance::zero()); for block in chain .iter() @@ -173,6 +173,7 @@ fn forked_equals_pushed_genesis() -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), empty_tree.clone(), ValueBalance::zero(), ); @@ -195,6 +196,7 @@ fn forked_equals_pushed_genesis() -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), empty_tree, ValueBalance::zero(), ); @@ -293,8 +295,8 @@ fn forked_equals_pushed_history_tree() -> Result<()> { // use `fork_at_count` as the fork tip let fork_tip_hash = chain[fork_at_count - 1].hash; - let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree.clone(), ValueBalance::zero()); - let mut partial_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, ValueBalance::zero()); + let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree.clone(), ValueBalance::zero()); + let mut partial_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, ValueBalance::zero()); for block in chain .iter() @@ -361,7 +363,7 @@ fn finalized_equals_pushed_genesis() -> Result<()> { let fake_value_pool = ValueBalance::::fake_populated_pool(); - let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), empty_tree, fake_value_pool); + let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), empty_tree, fake_value_pool); for block in chain .clone() .take(finalized_count) { @@ -375,6 +377,7 @@ fn finalized_equals_pushed_genesis() -> Result<()> { full_chain.sapling_note_commitment_tree_for_tip(), full_chain.orchard_note_commitment_tree_for_tip(), full_chain.ironwood_note_commitment_tree_for_tip(), + Default::default(), full_chain.history_block_commitment_tree(), full_chain.chain_value_pools, ); @@ -439,7 +442,7 @@ fn finalized_equals_pushed_history_tree() -> Result<()> { let fake_value_pool = ValueBalance::::fake_populated_pool(); - let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, fake_value_pool); + let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, fake_value_pool); for block in chain .iter() .take(finalized_count) @@ -454,6 +457,7 @@ fn finalized_equals_pushed_history_tree() -> Result<()> { full_chain.sapling_note_commitment_tree_for_tip(), full_chain.orchard_note_commitment_tree_for_tip(), full_chain.ironwood_note_commitment_tree_for_tip(), + Default::default(), full_chain.history_block_commitment_tree(), full_chain.chain_value_pools, ); @@ -629,6 +633,7 @@ fn different_blocks_different_chains() -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + &Default::default(), ).unwrap().into() ) } else { @@ -642,14 +647,15 @@ fn different_blocks_different_chains() -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + &Default::default(), ).unwrap().into() ) } else { Default::default() }; - let chain1 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree1, ValueBalance::fake_populated_pool()); - let chain2 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree2, ValueBalance::fake_populated_pool()); + let chain1 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree1, ValueBalance::fake_populated_pool()); + let chain2 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree2, ValueBalance::fake_populated_pool()); let block1 = vec1[1].clone().prepare().test_with_zero_spent_utxos(); let block2 = vec2[1].clone().prepare().test_with_zero_spent_utxos(); diff --git a/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs b/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs index 6395ea9550..0ef6e54220 100644 --- a/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs +++ b/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs @@ -36,6 +36,7 @@ fn construct_empty() { Default::default(), Default::default(), Default::default(), + Default::default(), ValueBalance::zero(), ); } @@ -54,6 +55,7 @@ fn construct_single() -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), ValueBalance::fake_populated_pool(), ); @@ -89,6 +91,7 @@ fn construct_many() -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), ValueBalance::fake_populated_pool(), ); @@ -117,6 +120,7 @@ fn ord_matches_work() -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), ValueBalance::fake_populated_pool(), ); lesser_chain = lesser_chain.push(less_block.prepare().test_with_zero_spent_utxos())?; @@ -129,6 +133,7 @@ fn ord_matches_work() -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), ValueBalance::zero(), ); bigger_chain = bigger_chain.push(more_block.prepare().test_with_zero_spent_utxos())?; @@ -240,6 +245,7 @@ fn finalize_drops_empty_side_chain_for_network(network: Network) -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), ValueBalance::fake_populated_pool(), ) .push(root.clone().prepare().test_with_zero_spent_utxos())?; @@ -986,6 +992,7 @@ fn history_tree_is_updated_for_network_upgrade( &chain.sapling_note_commitment_tree_for_tip().root(), &chain.orchard_note_commitment_tree_for_tip().root(), &chain.ironwood_note_commitment_tree_for_tip().root(), + &Default::default(), ) .unwrap(); @@ -1066,6 +1073,7 @@ fn commitment_is_validated_for_network_upgrade(network: Network, network_upgrade &chain.sapling_note_commitment_tree_for_tip().root(), &chain.orchard_note_commitment_tree_for_tip().root(), &chain.ironwood_note_commitment_tree_for_tip().root(), + &Default::default(), ) .unwrap(); @@ -1173,6 +1181,7 @@ fn fork_drops_subtrees_above_fork_point() -> Result<()> { Default::default(), Default::default(), Default::default(), + Default::default(), ValueBalance::fake_populated_pool(), ); chain = chain.push(block1.clone().prepare().test_with_zero_spent_utxos())?; diff --git a/crates/zakura-state/src/service/write/tests/full_state_coherence.rs b/crates/zakura-state/src/service/write/tests/full_state_coherence.rs index 3403fff0b1..ec1bcc01ac 100644 --- a/crates/zakura-state/src/service/write/tests/full_state_coherence.rs +++ b/crates/zakura-state/src/service/write/tests/full_state_coherence.rs @@ -316,6 +316,7 @@ fn generated_nu5_graph_matches_full_state_before_finalization() { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ) .expect("the deterministic history tree advances"); blocks.push(block); diff --git a/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs b/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs index d5d4e2512a..a279a9de00 100644 --- a/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs +++ b/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs @@ -253,6 +253,7 @@ fn generate_chain(network: &Network) -> Vec> { &sapling_root, &orchard_root, &ironwood_root, + &Default::default(), ) .expect("the deterministic history tree advances"); blocks.push(block); @@ -635,6 +636,7 @@ impl Fixture { &empty_sapling_root(), &empty_orchard_root(), &empty_ironwood_root(), + &Default::default(), ) .expect("the generated history tree advances"); } @@ -660,6 +662,7 @@ impl Fixture { verify_commitment_roots( &self.network, self.history_tree_through(parent), + Default::default(), vec![ CommitmentRootVerification::with_roots( block.clone(), @@ -1079,6 +1082,7 @@ fn fold_placement_is_checked_before_every_history_tree_push() { &empty_sapling_root(), &empty_orchard_root(), &empty_ironwood_root(), + &Default::default(), ) .expect("the activation block creates the tree"); diff --git a/crates/zakurad/Cargo.toml b/crates/zakurad/Cargo.toml index 2f6e113ad8..1680b3cebc 100644 --- a/crates/zakurad/Cargo.toml +++ b/crates/zakurad/Cargo.toml @@ -24,7 +24,7 @@ categories = ["command-line-utilities", "cryptography::cryptocurrencies"] edition.workspace = true # Zakura can have a higher MSRV than the other crates since it is a binary. -rust-version = "1.91" +rust-version = "1.97" # Settings that impact runtime behaviour diff --git a/docker/Dockerfile b/docker/Dockerfile index 8de50716bc..79793a65dc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,7 +13,7 @@ # We first set default values for build arguments used across the stages. # Each stage must define the build arguments (ARGs) it uses. -ARG RUST_VERSION=1.91.0 +ARG RUST_VERSION=1.97.0 ARG FEATURES="default-release-binaries" ARG SHORT_SHA diff --git a/docker/ubuntu-package.Dockerfile b/docker/ubuntu-package.Dockerfile index f9c144b86c..7a83452a60 100644 --- a/docker/ubuntu-package.Dockerfile +++ b/docker/ubuntu-package.Dockerfile @@ -4,7 +4,7 @@ ARG UBUNTU_IMAGE=ubuntu:22.04 FROM ${UBUNTU_IMAGE} AS build ARG DEBIAN_FRONTEND=noninteractive -ARG RUST_VERSION=1.91 +ARG RUST_VERSION=1.97 ARG FEATURES="default-release-binaries" RUN apt-get update && \ diff --git a/docs/changelog/unreleased/795.md b/docs/changelog/unreleased/795.md index 0e62bc953b..33bfae47bd 100644 --- a/docs/changelog/unreleased/795.md +++ b/docs/changelog/unreleased/795.md @@ -1,4 +1,10 @@ ## Added -- Added preliminary NuTachyon network-upgrade and V7 transaction support +- Added NuTachyon network-upgrade and V7 transaction support, including Tachyon bundle + serialization, consensus validation, chain state, and block-template aggregation + ([#795](https://github.com/zakura-core/zakura/pull/795)). + +## Changed + +- Raised the minimum supported Rust version to 1.97 for the Tachyon and Ragu dependencies ([#795](https://github.com/zakura-core/zakura/pull/795)). diff --git a/scripts/pack-assets-crate.py b/scripts/pack-assets-crate.py index c23baf9caa..b036953123 100755 --- a/scripts/pack-assets-crate.py +++ b/scripts/pack-assets-crate.py @@ -234,7 +234,7 @@ def setUp(self) -> None: (self.crate / "src").mkdir(parents=True) (self.crate / "Cargo.toml").write_text( '[package]\nname = "example-assets"\nversion = "0.0.0"\n' - 'rust-version = "1.91"\n', + 'rust-version = "1.97"\n', encoding="utf-8", ) self.grid_path = Path(self.temp.name) / GRID_NAME @@ -259,7 +259,7 @@ def test_packs_a_valid_grid_and_derives_its_version(self) -> None: ) # The stamp must not disturb the neighbouring rust-version key. self.assertIn( - 'rust-version = "1.91"', + 'rust-version = "1.97"', (self.crate / "Cargo.toml").read_text(encoding="utf-8"), ) From 8b199728c7c4526129d019c35917b42c180234f2 Mon Sep 17 00:00:00 2001 From: c-node Date: Tue, 1 Sep 2026 16:31:08 -0400 Subject: [PATCH 08/15] fix: gate NuTachyon support behind unstable cfg --- AGENTS.md | 2 +- Cargo.toml | 2 +- crates/xtask/src/main.rs | 2 +- crates/zakura-assets/Cargo.toml | 2 +- crates/zakura-chain/Cargo.toml | 4 +- crates/zakura-chain/src/block.rs | 1 + crates/zakura-chain/src/block/arbitrary.rs | 9 +- crates/zakura-chain/src/block/commitment.rs | 10 +- crates/zakura-chain/src/history_tree.rs | 47 +++- .../src/history_tree/tests/vectors.rs | 78 ++++++ crates/zakura-chain/src/lib.rs | 1 + crates/zakura-chain/src/local_genesis.rs | 1 + .../src/parallel/commitment_aux_verify.rs | 7 + crates/zakura-chain/src/parallel/tree.rs | 5 +- .../src/parameters/network/testnet.rs | 12 +- .../src/parameters/network/tests/vectors.rs | 36 +-- .../src/parameters/network_upgrade.rs | 7 +- crates/zakura-chain/src/parameters/tests.rs | 7 +- .../src/parameters/transaction.rs | 1 + .../src/primitives/zcash_history.rs | 32 ++- .../primitives/zcash_history/tests/vectors.rs | 13 + crates/zakura-chain/src/transaction.rs | 249 ++++++++++++------ .../zakura-chain/src/transaction/arbitrary.rs | 6 +- .../zakura-chain/src/transaction/serialize.rs | 20 +- .../src/transaction/tests/vectors.rs | 30 ++- crates/zakura-chain/src/transaction/txid.rs | 4 +- .../zakura-chain/src/transaction/unmined.rs | 4 +- crates/zakura-chain/src/value_balance.rs | 45 +++- .../src/value_balance/arbitrary.rs | 60 +++++ .../src/value_balance/tests/prop.rs | 9 +- crates/zakura-consensus/Cargo.toml | 8 +- crates/zakura-consensus/src/block.rs | 9 + crates/zakura-consensus/src/error.rs | 18 +- crates/zakura-consensus/src/primitives.rs | 1 + .../zakura-consensus/src/primitives/halo2.rs | 5 +- crates/zakura-consensus/src/transaction.rs | 41 ++- .../zakura-consensus/src/transaction/check.rs | 2 + .../zakura-consensus/src/transaction/tests.rs | 2 +- .../src/transaction/tests/prop.rs | 3 + crates/zakura-network/src/constants.rs | 3 + .../zakura-network/src/peer_set/initialize.rs | 7 +- .../src/protocol/external/types.rs | 26 ++ .../src/zakura/block_sync/peer_registry.rs | 7 +- .../src/zakura/block_sync/tests.rs | 4 +- .../src/zakura/testkit/cluster.rs | 1 + .../src/zakura/testkit/mock_blocksync.rs | 4 +- crates/zakura-rpc/Cargo.toml | 8 +- crates/zakura-rpc/src/methods.rs | 1 + crates/zakura-rpc/src/methods/tests/utils.rs | 1 + .../src/methods/types/get_block_template.rs | 1 + crates/zakura-state/Cargo.toml | 4 +- crates/zakura-state/src/constants.rs | 9 + crates/zakura-state/src/error.rs | 18 +- crates/zakura-state/src/lib.rs | 4 +- crates/zakura-state/src/request.rs | 13 +- crates/zakura-state/src/response.rs | 15 +- crates/zakura-state/src/service.rs | 1 + crates/zakura-state/src/service/arbitrary.rs | 1 + .../zakura-state/src/service/check/anchors.rs | 1 + .../src/service/check/nullifier.rs | 2 + .../src/service/check/tests/vectors.rs | 1 + .../src/service/finalized_state.rs | 11 + .../finalized_state/commitment_aux_verify.rs | 82 ++++-- .../finalized_state/disk_format/chain.rs | 6 + .../disk_format/chain/tests.rs | 2 + .../finalized_state/disk_format/shielded.rs | 10 +- .../finalized_state/disk_format/tests/prop.rs | 2 + .../finalized_state/disk_format/upgrade.rs | 12 +- .../src/service/finalized_state/tests/prop.rs | 15 ++ .../service/finalized_state/tests/rollback.rs | 1 + .../finalized_state/vct_fast_sync_fixture.rs | 1 + .../finalized_state/zakura_db/rollback.rs | 8 + .../finalized_state/zakura_db/shielded.rs | 20 +- .../src/service/non_finalized_state.rs | 2 + .../src/service/non_finalized_state/chain.rs | 121 ++++++--- .../service/non_finalized_state/tests/prop.rs | 22 +- .../non_finalized_state/tests/vectors.rs | 9 + .../write/tests/full_state_coherence.rs | 2 + .../write/vct_authentication_sweep/tests.rs | 18 +- crates/zakura-state/src/tests.rs | 4 +- crates/zakurad/Cargo.toml | 2 +- .../components/mempool/storage/tests/prop.rs | 14 +- docker/Dockerfile | 2 +- docker/ubuntu-package.Dockerfile | 2 +- docs/changelog/unreleased/795.md | 5 - scripts/pack-assets-crate.py | 4 +- 86 files changed, 1026 insertions(+), 278 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ceb6285a5d..5e0cd3d357 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,7 +62,7 @@ cargo nextest run --profile zakura-integration Zakura is a Zcash full node implementation in Rust. It is a validator node — it excludes features not strictly needed for block validation and chain sync. - **Rust edition**: 2021 -- **MSRV**: 1.97 (unified across the library crates and the zakurad binary) +- **MSRV**: 1.91 (unified across the library crates and the zakurad binary) - **Database format version**: defined in `crates/zakura-state/src/constants.rs` ## Crate Architecture diff --git a/Cargo.toml b/Cargo.toml index b4b616a7cb..c9daf5e649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/zakura-core/zakura" homepage = "https://github.com/zakura-core/zakura" keywords = ["zakura", "zcash"] -rust-version = "1.97" +rust-version = "1.91" edition = "2021" [workspace.dependencies] diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 5f61ab719a..63864b7c68 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -13,7 +13,7 @@ mod rpc_artifacts; const DEFAULT_FEATURES: &str = "default-release-binaries"; const DEFAULT_UBUNTU_IMAGE: &str = "ubuntu:22.04"; -const DEFAULT_RUST_VERSION: &str = "1.97"; +const DEFAULT_RUST_VERSION: &str = "1.91"; const DEFAULT_IMAGE_TAG: &str = "zebra-ubuntu-package:local"; const OUTPUT_BINARY_NAME: &str = "zakurad"; diff --git a/crates/zakura-assets/Cargo.toml b/crates/zakura-assets/Cargo.toml index 9aaf06f4f3..6fb592dc0e 100644 --- a/crates/zakura-assets/Cargo.toml +++ b/crates/zakura-assets/Cargo.toml @@ -16,7 +16,7 @@ name = "zakura-assets" version = "0.3457371.0" authors = ["Zakura Contributors"] edition = "2021" -rust-version = "1.97" +rust-version = "1.91" license = "MIT OR Apache-2.0" repository = "https://github.com/zakura-core/zakura" documentation = "https://docs.rs/zakura" diff --git a/crates/zakura-chain/Cargo.toml b/crates/zakura-chain/Cargo.toml index 7de38c59a8..85c23f215f 100644 --- a/crates/zakura-chain/Cargo.toml +++ b/crates/zakura-chain/Cargo.toml @@ -89,7 +89,6 @@ sapling-crypto.workspace = true zcash_protocol.workspace = true zcash_address.workspace = true zcash_transparent = { workspace = true } -zcash_tachyon = { workspace = true } sinsemilla = { workspace = true } # Time @@ -130,6 +129,9 @@ proptest-derive = { workspace = true, optional = true } zakura-test = { path = "../zakura-test/", version = "2.1.0", optional = true } bounded-vec = { version = "0.9.0", features = ["serde"] } +[target.'cfg(zcash_unstable = "nutachyon")'.dependencies] +zcash_tachyon = { workspace = true } + [dev-dependencies] # Benchmarks criterion = { workspace = true, features = ["html_reports"] } diff --git a/crates/zakura-chain/src/block.rs b/crates/zakura-chain/src/block.rs index a1d211aa36..9a79d2d479 100644 --- a/crates/zakura-chain/src/block.rs +++ b/crates/zakura-chain/src/block.rs @@ -271,6 +271,7 @@ impl Block { /// Count how many Tachyon transactions exist in a block, /// i.e. transactions carrying a tachyon bundle (NuTachyon). + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_transactions_count(&self) -> u64 { self.transactions .iter() diff --git a/crates/zakura-chain/src/block/arbitrary.rs b/crates/zakura-chain/src/block/arbitrary.rs index b0f910bcdd..42ab0bc0ba 100644 --- a/crates/zakura-chain/src/block/arbitrary.rs +++ b/crates/zakura-chain/src/block/arbitrary.rs @@ -430,6 +430,7 @@ impl Block { let mut sapling_tree = sapling::tree::NoteCommitmentTree::default(); let mut orchard_tree = orchard::tree::NoteCommitmentTree::default(); let mut ironwood_tree = ironwood::tree::NoteCommitmentTree::default(); + #[cfg(zcash_unstable = "nutachyon")] let mut tachyon_anchor = crate::tachyon::Anchor::default(); // The history tree usually takes care of "creating itself". But this // only works when blocks are pushed into it starting from genesis @@ -482,6 +483,7 @@ impl Block { // delete invalid transactions block.transactions = new_transactions; + #[cfg(zcash_unstable = "nutachyon")] if let Some(pool_height) = crate::tachyon::pool_height(¤t.network, block.coinbase_height().unwrap()) { @@ -547,6 +549,7 @@ impl Block { &sapling_tree.root(), &orchard_tree.root(), &ironwood_tree.root(), + #[cfg(zcash_unstable = "nutachyon")] &tachyon_anchor, ) .unwrap(); @@ -558,6 +561,7 @@ impl Block { &sapling_tree.root(), &orchard_tree.root(), &ironwood_tree.root(), + #[cfg(zcash_unstable = "nutachyon")] &tachyon_anchor, ) .unwrap(), @@ -620,8 +624,9 @@ where Transaction::V6 { sapling_shielded_data, .. - } - | Transaction::V7 { + } => *sapling_shielded_data = None, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data, .. } => *sapling_shielded_data = None, diff --git a/crates/zakura-chain/src/block/commitment.rs b/crates/zakura-chain/src/block/commitment.rs index 90b721b256..77560a84a5 100644 --- a/crates/zakura-chain/src/block/commitment.rs +++ b/crates/zakura-chain/src/block/commitment.rs @@ -139,9 +139,13 @@ impl Commitment { } } (Heartwood | Canopy, _) => Ok(ChainHistoryRoot(ChainHistoryMmrRootHash(bytes))), - (Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7 | NuTachyon, _) => Ok( - ChainHistoryBlockTxAuthCommitment(ChainHistoryBlockTxAuthCommitmentHash(bytes)), - ), + (Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7, _) => Ok(ChainHistoryBlockTxAuthCommitment( + ChainHistoryBlockTxAuthCommitmentHash(bytes), + )), + #[cfg(zcash_unstable = "nutachyon")] + (NuTachyon, _) => Ok(ChainHistoryBlockTxAuthCommitment( + ChainHistoryBlockTxAuthCommitmentHash(bytes), + )), #[cfg(zcash_unstable = "zfuture")] (ZFuture, _) => Ok(ChainHistoryBlockTxAuthCommitment( diff --git a/crates/zakura-chain/src/history_tree.rs b/crates/zakura-chain/src/history_tree.rs index 9e884f3c49..79f1e18ca2 100644 --- a/crates/zakura-chain/src/history_tree.rs +++ b/crates/zakura-chain/src/history_tree.rs @@ -21,10 +21,11 @@ use crate::{ parameters::{Network, NetworkUpgrade}, primitives::zcash_history::{ Entry, Tree, V1 as PreOrchard, V2 as OrchardOnward, V3 as IronwoodOnward, - V4 as TachyonOnward, }, - sapling, tachyon, + sapling, }; +#[cfg(zcash_unstable = "nutachyon")] +use crate::{primitives::zcash_history::V4 as TachyonOnward, tachyon}; /// An error describing why a history tree operation failed. #[derive(Debug, Error)] @@ -62,6 +63,7 @@ enum InnerHistoryTree { /// An Ironwood-onward tree. IronwoodOnward(Tree), /// A Tachyon-onward tree. + #[cfg(zcash_unstable = "nutachyon")] TachyonOnward(Tree), } @@ -145,6 +147,7 @@ impl NonEmptyHistoryTree { )?; InnerHistoryTree::IronwoodOnward(tree) } + #[cfg(zcash_unstable = "nutachyon")] NetworkUpgrade::NuTachyon => { let tree = Tree::::new_from_cache( network, @@ -192,7 +195,7 @@ impl NonEmptyHistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Result { Self::from_parts( network, @@ -201,6 +204,7 @@ impl NonEmptyHistoryTree { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, ), ) @@ -250,6 +254,7 @@ impl NonEmptyHistoryTree { let (tree, entry) = Tree::::new_from_parts(network, parts)?; (InnerHistoryTree::IronwoodOnward(tree), entry) } + #[cfg(zcash_unstable = "nutachyon")] NetworkUpgrade::NuTachyon => { let (tree, entry) = Tree::::new_from_parts(network, parts)?; (InnerHistoryTree::TachyonOnward(tree), entry) @@ -291,13 +296,14 @@ impl NonEmptyHistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Result<(), HistoryTreeError> { self.push_from_parts(HistoryTreeBlockParts::from_block( &block, sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, )) } @@ -352,6 +358,7 @@ impl NonEmptyHistoryTree { InnerHistoryTree::IronwoodOnward(tree) => tree .append_leaf_parts(parts) .map_err(|e| HistoryTreeError::InnerError { inner: e })?, + #[cfg(zcash_unstable = "nutachyon")] InnerHistoryTree::TachyonOnward(tree) => tree .append_leaf_parts(parts) .map_err(|e| HistoryTreeError::InnerError { inner: e })?, @@ -367,6 +374,7 @@ impl NonEmptyHistoryTree { } /// Extend the history tree with the given blocks. + #[cfg(zcash_unstable = "nutachyon")] pub fn try_extend< 'a, T: IntoIterator< @@ -394,6 +402,28 @@ impl NonEmptyHistoryTree { Ok(()) } + /// Extend the history tree with the given blocks. + #[cfg(not(zcash_unstable = "nutachyon"))] + pub fn try_extend< + 'a, + T: IntoIterator< + Item = ( + Arc, + &'a sapling::tree::Root, + &'a orchard::tree::Root, + &'a ironwood::tree::Root, + ), + >, + >( + &mut self, + iter: T, + ) -> Result<(), HistoryTreeError> { + for (block, sapling_root, orchard_root, ironwood_root) in iter { + self.push(block, sapling_root, orchard_root, ironwood_root)?; + } + Ok(()) + } + /// Prune tree, removing all non-peak entries. fn prune(&mut self) -> Result<(), io::Error> { // Go through all the peaks of the tree. @@ -496,6 +526,7 @@ impl NonEmptyHistoryTree { &Default::default(), )?) } + #[cfg(zcash_unstable = "nutachyon")] InnerHistoryTree::TachyonOnward(_) => { InnerHistoryTree::TachyonOnward(Tree::::new_from_cache( &self.network, @@ -515,6 +546,7 @@ impl NonEmptyHistoryTree { InnerHistoryTree::PreOrchard(tree) => tree.hash(), InnerHistoryTree::OrchardOnward(tree) => tree.hash(), InnerHistoryTree::IronwoodOnward(tree) => tree.hash(), + #[cfg(zcash_unstable = "nutachyon")] InnerHistoryTree::TachyonOnward(tree) => tree.hash(), } } @@ -573,6 +605,7 @@ impl Clone for NonEmptyHistoryTree { ) .expect("rebuilding an existing tree should always work"), ), + #[cfg(zcash_unstable = "nutachyon")] InnerHistoryTree::TachyonOnward(_) => InnerHistoryTree::TachyonOnward( Tree::::new_from_cache( &self.network, @@ -611,7 +644,7 @@ impl HistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Result { Self::from_parts( network, @@ -620,6 +653,7 @@ impl HistoryTree { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, ), ) @@ -656,7 +690,7 @@ impl HistoryTree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Result<(), HistoryTreeError> { self.push_from_parts( network, @@ -665,6 +699,7 @@ impl HistoryTree { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, ), ) diff --git a/crates/zakura-chain/src/history_tree/tests/vectors.rs b/crates/zakura-chain/src/history_tree/tests/vectors.rs index 34314e9265..b1b9d09e8c 100644 --- a/crates/zakura-chain/src/history_tree/tests/vectors.rs +++ b/crates/zakura-chain/src/history_tree/tests/vectors.rs @@ -16,6 +16,7 @@ use crate::{ serialization::ZcashDeserializeInto, }; +#[cfg(zcash_unstable = "nutachyon")] const DEFAULT_TACHYON_ANCHOR: crate::tachyon::Anchor = crate::tachyon::Anchor([0; 32]); use color_eyre::eyre; @@ -71,6 +72,7 @@ fn push_and_prune_for_network_upgrade( &first_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; @@ -105,6 +107,7 @@ fn push_and_prune_for_network_upgrade( &second_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .unwrap(); @@ -180,6 +183,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; parts_tree.push_from_parts( @@ -189,6 +193,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ), )?; @@ -202,6 +207,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &first_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; parts_tree.push_from_parts( @@ -211,6 +217,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &first_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ), )?; @@ -234,6 +241,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &second_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; parts_tree.push_from_parts( @@ -243,6 +251,7 @@ fn parts_api_matches_block_api_for_network(network: Network) -> Result<()> { &second_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ), )?; @@ -288,10 +297,12 @@ fn from_cache_preserves_ironwood_history_version() -> Result<()> { sapling_root: &Default::default(), orchard_root: &Default::default(), ironwood_root: &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: 0, orchard_tx: 0, ironwood_tx: 1, + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: 0, }, )?; @@ -373,6 +384,7 @@ fn from_block_delegation_preserves_all_history_versions() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; let parts_tree = NonEmptyHistoryTree::from_parts( @@ -383,10 +395,12 @@ fn from_block_delegation_preserves_all_history_versions() -> Result<()> { sapling_root: &sapling_root, orchard_root: &orchard_root, ironwood_root: &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: block.sapling_transactions_count(), orchard_tx: block.orchard_transactions_count(), ironwood_tx: block.ironwood_transactions_count(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: block.tachyon_transactions_count(), }, )?; @@ -446,10 +460,12 @@ fn from_parts_selects_epoch_version_and_ignores_future_fields() -> Result<()> { sapling_root: &sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: 1, orchard_tx, ironwood_tx, + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: 0, }; @@ -568,6 +584,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; let mut parts_tree = NonEmptyHistoryTree::from_parts( @@ -577,6 +594,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ), )?; @@ -586,6 +604,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; parts_tree.push_from_parts(HistoryTreeBlockParts::from_block( @@ -593,6 +612,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ))?; let activation_tree = NonEmptyHistoryTree::from_block( @@ -601,6 +621,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; let activation_without_ironwood = NonEmptyHistoryTree::from_block( @@ -609,6 +630,7 @@ fn push_wrapper_matches_parts_across_nu6_3_activation() -> Result<()> { &sapling_root, &orchard_root, &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; @@ -687,10 +709,12 @@ fn push_from_parts_routes_each_history_version() -> Result<()> { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: 1, orchard_tx, ironwood_tx, + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: 0, } } @@ -790,6 +814,7 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; sequential.push( @@ -797,6 +822,7 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &second_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; sequential.push( @@ -804,6 +830,7 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &third_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; @@ -813,8 +840,10 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; + #[cfg(zcash_unstable = "nutachyon")] extended.try_extend([ ( second_block.clone(), @@ -831,6 +860,21 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &DEFAULT_TACHYON_ANCHOR, ), ])?; + #[cfg(not(zcash_unstable = "nutachyon"))] + extended.try_extend([ + ( + second_block.clone(), + &sapling_root, + &orchard_root, + &second_ironwood_root, + ), + ( + third_block.clone(), + &sapling_root, + &orchard_root, + &third_ironwood_root, + ), + ])?; let mut wrong_last_root = NonEmptyHistoryTree::from_block( &network, @@ -838,8 +882,10 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; + #[cfg(zcash_unstable = "nutachyon")] wrong_last_root.try_extend([ ( second_block, @@ -856,6 +902,21 @@ fn try_extend_forwards_ironwood_roots_in_order() -> Result<()> { &DEFAULT_TACHYON_ANCHOR, ), ])?; + #[cfg(not(zcash_unstable = "nutachyon"))] + wrong_last_root.try_extend([ + ( + second_block, + &sapling_root, + &orchard_root, + &second_ironwood_root, + ), + ( + third_block, + &sapling_root, + &orchard_root, + &Default::default(), + ), + ])?; assert_eq!(extended.hash(), sequential.hash()); assert_ne!(extended.hash(), wrong_last_root.hash()); @@ -907,10 +968,12 @@ fn v3_pruning_and_hash_dispatch_match_unpruned_tree() -> Result<()> { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &DEFAULT_TACHYON_ANCHOR, sapling_tx: u64::from(height), orchard_tx: u64::from(height + 1), ironwood_tx: u64::from(height + 2), + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: 0, } } @@ -1023,6 +1086,7 @@ fn v3_clone_preserves_root_and_future_append() -> Result<()> { &sapling_root, &orchard_root, &first_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; original.push( @@ -1030,6 +1094,7 @@ fn v3_clone_preserves_root_and_future_append() -> Result<()> { &sapling_root, &orchard_root, &second_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; original.push( @@ -1037,6 +1102,7 @@ fn v3_clone_preserves_root_and_future_append() -> Result<()> { &sapling_root, &orchard_root, &third_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; assert_eq!(original.peaks().len(), 2, "three leaves have two MMR peaks"); @@ -1057,6 +1123,7 @@ fn v3_clone_preserves_root_and_future_append() -> Result<()> { &sapling_root, &orchard_root, &fourth_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; cloned.push( @@ -1064,6 +1131,7 @@ fn v3_clone_preserves_root_and_future_append() -> Result<()> { &sapling_root, &orchard_root, &fourth_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; @@ -1127,6 +1195,7 @@ fn history_tree_from_block_matches_parts_across_history_versions() -> Result<()> &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; let parts_tree = HistoryTree::from_parts( @@ -1136,6 +1205,7 @@ fn history_tree_from_block_matches_parts_across_history_versions() -> Result<()> &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ), )?; @@ -1191,6 +1261,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; parts_tree.push_from_parts( @@ -1200,6 +1271,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ), )?; @@ -1211,6 +1283,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; parts_tree.push_from_parts( @@ -1220,6 +1293,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ), )?; @@ -1229,6 +1303,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; let activation_without_ironwood = HistoryTree::from_block( @@ -1237,6 +1312,7 @@ fn history_tree_push_wrapper_matches_parts_across_nu6_3_activation() -> Result<( &sapling_root, &orchard_root, &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; @@ -1292,6 +1368,7 @@ fn upgrade_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade &sapling_root_prev, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; @@ -1320,6 +1397,7 @@ fn upgrade_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade &activation_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .unwrap(); diff --git a/crates/zakura-chain/src/lib.rs b/crates/zakura-chain/src/lib.rs index b86fb4ede5..00645c19bd 100644 --- a/crates/zakura-chain/src/lib.rs +++ b/crates/zakura-chain/src/lib.rs @@ -44,6 +44,7 @@ pub mod shutdown; pub mod sprout; pub mod subtree; pub mod subtree_verify; +#[cfg(zcash_unstable = "nutachyon")] pub mod tachyon; pub mod transaction; pub mod transparent; diff --git a/crates/zakura-chain/src/local_genesis.rs b/crates/zakura-chain/src/local_genesis.rs index 2afbac5337..7b3d38446e 100644 --- a/crates/zakura-chain/src/local_genesis.rs +++ b/crates/zakura-chain/src/local_genesis.rs @@ -439,6 +439,7 @@ fn configured_activation_heights( nu6_2: (latest_network_upgrade >= Nu6_2).then_some(activation_height), nu6_3: (latest_network_upgrade >= Nu6_3).then_some(activation_height), nu7: (latest_network_upgrade >= Nu7).then_some(activation_height), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: (latest_network_upgrade >= NuTachyon).then_some(activation_height), }) } diff --git a/crates/zakura-chain/src/parallel/commitment_aux_verify.rs b/crates/zakura-chain/src/parallel/commitment_aux_verify.rs index a5740442c9..ad187b8b2b 100644 --- a/crates/zakura-chain/src/parallel/commitment_aux_verify.rs +++ b/crates/zakura-chain/src/parallel/commitment_aux_verify.rs @@ -104,6 +104,7 @@ impl VerifiedHeaderCommitmentRoots { pub enum SuppliedRootsError { /// The roots-only auxiliary format cannot reconstruct Tachyon history state. #[error("Tachyon history commitments require block bodies")] + #[cfg(zcash_unstable = "nutachyon")] TachyonDataUnavailable, /// A header commitment did not match its supplied auxiliary data. @@ -147,6 +148,7 @@ where for (index, (header, roots)) in items.iter().enumerate() { let height = roots.height; + #[cfg(zcash_unstable = "nutachyon")] if NetworkUpgrade::current(network, height) >= NetworkUpgrade::NuTachyon { return Err((height, SuppliedRootsError::TachyonDataUnavailable)); } @@ -191,10 +193,12 @@ where sapling_root: &roots.sapling_root, orchard_root: &roots.orchard_root, ironwood_root: &roots.ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &Default::default(), sapling_tx: roots.sapling_tx, orchard_tx: roots.orchard_tx, ironwood_tx: roots.ironwood_tx, + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: 0, }, ) @@ -490,6 +494,7 @@ mod tests { &Default::default(), &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("empty history tree for a pre-Heartwood block") @@ -925,6 +930,7 @@ mod tests { &act_root, &empty_orchard_root, &empty_ironwood_root(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("activation block builds a history tree") @@ -952,6 +958,7 @@ mod tests { &empty_sapling_root, &empty_orchard_root, &empty_ironwood_root(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("the parent history tree builds"); diff --git a/crates/zakura-chain/src/parallel/tree.rs b/crates/zakura-chain/src/parallel/tree.rs index 24f5a59d7a..09d7c25c0f 100644 --- a/crates/zakura-chain/src/parallel/tree.rs +++ b/crates/zakura-chain/src/parallel/tree.rs @@ -4,11 +4,12 @@ use std::sync::Arc; use thiserror::Error; +#[cfg(zcash_unstable = "nutachyon")] +use crate::tachyon; use crate::{ block::Block, ironwood, orchard, sapling, sprout, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeIndex}, - tachyon, }; /// An argument wrapper struct for note commitment trees. @@ -38,9 +39,11 @@ pub struct NoteCommitmentTrees { pub ironwood_subtree: Option>, /// The Tachyon pool anchor after this block. + #[cfg(zcash_unstable = "nutachyon")] pub tachyon_anchor: tachyon::Anchor, /// The epoch-boundary anchor if this block starts a Tachyon epoch. + #[cfg(zcash_unstable = "nutachyon")] pub tachyon_epoch_anchor: Option, } diff --git a/crates/zakura-chain/src/parameters/network/testnet.rs b/crates/zakura-chain/src/parameters/network/testnet.rs index 65f5d493dc..ed8c69f1a3 100644 --- a/crates/zakura-chain/src/parameters/network/testnet.rs +++ b/crates/zakura-chain/src/parameters/network/testnet.rs @@ -194,6 +194,7 @@ impl From<&BTreeMap> for ConfiguredActivationHeights { NetworkUpgrade::Nu6_2 => &mut configured_activation_heights.nu6_2, NetworkUpgrade::Nu6_3 => &mut configured_activation_heights.nu6_3, NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7, + #[cfg(zcash_unstable = "nutachyon")] NetworkUpgrade::NuTachyon => &mut configured_activation_heights.nu_tachyon, #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => &mut configured_activation_heights.zfuture, @@ -516,6 +517,7 @@ pub struct ConfiguredActivationHeights { #[serde(rename = "NU7")] pub nu7: Option, /// Activation height for `NuTachyon` network upgrade. + #[cfg(zcash_unstable = "nutachyon")] pub nu_tachyon: Option, /// Activation height for `ZFuture` network upgrade. #[serde(rename = "ZFuture")] @@ -540,6 +542,7 @@ impl ConfiguredActivationHeights { nu6_2, nu6_3, nu7, + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon, #[cfg(zcash_unstable = "zfuture")] zfuture, @@ -564,6 +567,7 @@ impl ConfiguredActivationHeights { nu6_2, nu6_3, nu7, + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon, #[cfg(zcash_unstable = "zfuture")] zfuture, @@ -762,6 +766,7 @@ impl ParametersBuilder { nu6_2, nu6_3, nu7, + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon, #[cfg(zcash_unstable = "zfuture")] zfuture, @@ -791,8 +796,11 @@ impl ParametersBuilder { .chain(nu6_1.into_iter().map(|h| (h, Nu6_1))) .chain(nu6_2.into_iter().map(|h| (h, Nu6_2))) .chain(nu6_3.into_iter().map(|h| (h, Nu6_3))) - .chain(nu7.into_iter().map(|h| (h, Nu7))) - .chain(nu_tachyon.into_iter().map(|h| (h, NuTachyon))); + .chain(nu7.into_iter().map(|h| (h, Nu7))); + + #[cfg(zcash_unstable = "nutachyon")] + let activation_heights = + activation_heights.chain(nu_tachyon.into_iter().map(|h| (h, NuTachyon))); #[cfg(zcash_unstable = "zfuture")] let activation_heights = diff --git a/crates/zakura-chain/src/parameters/network/tests/vectors.rs b/crates/zakura-chain/src/parameters/network/tests/vectors.rs index ff97809283..0bf3e4ac47 100644 --- a/crates/zakura-chain/src/parameters/network/tests/vectors.rs +++ b/crates/zakura-chain/src/parameters/network/tests/vectors.rs @@ -222,11 +222,13 @@ fn bip70_network_names_are_stable_for_every_network_kind() { fn activates_network_upgrades_correctly() { let expected_nu6_3_activation_height = 1; let expected_nu7_activation_height = 2; + #[cfg(zcash_unstable = "nutachyon")] let expected_nu_tachyon_activation_height = 3; let network = testnet::Parameters::build() .with_activation_heights(ConfiguredActivationHeights { nu6_3: Some(expected_nu6_3_activation_height), nu7: Some(expected_nu7_activation_height), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: Some(expected_nu_tachyon_activation_height), ..Default::default() }) @@ -248,6 +250,7 @@ fn activates_network_upgrades_correctly() { for nu in NetworkUpgrade::iter().skip(1) { let expected_activation_height = match nu { NetworkUpgrade::Nu7 => expected_nu7_activation_height, + #[cfg(zcash_unstable = "nutachyon")] NetworkUpgrade::NuTachyon => expected_nu_tachyon_activation_height, _ => expected_nu6_3_activation_height, }; @@ -343,22 +346,25 @@ fn configured_nu6_3_activation_preserves_upgrade_order() { "an overwritten NU6.3 activation must remain implicit at the NU7 height" ); - let nu_tachyon_network = testnet::Parameters::build() - .with_activation_heights(ConfiguredActivationHeights { - nu7: Some(activation_height.0), - nu_tachyon: Some(activation_height.0), - ..Default::default() - }) - .expect("same-height upgrades are valid") - .clear_funding_streams() - .to_network() - .expect("valid configured network"); + #[cfg(zcash_unstable = "nutachyon")] + { + let nu_tachyon_network = testnet::Parameters::build() + .with_activation_heights(ConfiguredActivationHeights { + nu7: Some(activation_height.0), + nu_tachyon: Some(activation_height.0), + ..Default::default() + }) + .expect("same-height upgrades are valid") + .clear_funding_streams() + .to_network() + .expect("valid configured network"); - assert_eq!( - NetworkUpgrade::current(&nu_tachyon_network, activation_height), - NetworkUpgrade::NuTachyon, - "NuTachyon must overwrite NU7 when both activate at the same height" - ); + assert_eq!( + NetworkUpgrade::current(&nu_tachyon_network, activation_height), + NetworkUpgrade::NuTachyon, + "NuTachyon must overwrite NU7 when both activate at the same height" + ); + } let out_of_order = testnet::Parameters::build() .with_activation_heights(ConfiguredActivationHeights { diff --git a/crates/zakura-chain/src/parameters/network_upgrade.rs b/crates/zakura-chain/src/parameters/network_upgrade.rs index ee57782f93..a9968ef639 100644 --- a/crates/zakura-chain/src/parameters/network_upgrade.rs +++ b/crates/zakura-chain/src/parameters/network_upgrade.rs @@ -68,6 +68,7 @@ pub enum NetworkUpgrade { #[serde(rename = "NU7")] Nu7, /// The Zcash protocol after the NuTachyon upgrade. + #[cfg(zcash_unstable = "nutachyon")] NuTachyon, #[cfg(zcash_unstable = "zfuture")] @@ -243,6 +244,7 @@ pub(crate) const CONSENSUS_BRANCH_IDS: &[(NetworkUpgrade, ConsensusBranchId)] = // TODO: set below to (Nu7, ConsensusBranchId(0x77190ad8)), once the same value is set in librustzcash #[cfg(any(test, feature = "zakura-test"))] (Nu7, ConsensusBranchId(0xfffffffe)), + #[cfg(zcash_unstable = "nutachyon")] (NuTachyon, ConsensusBranchId(0xfffffffc)), #[cfg(zcash_unstable = "zfuture")] (ZFuture, ConsensusBranchId(0xfffffffd)), @@ -405,10 +407,13 @@ impl NetworkUpgrade { pub fn target_spacing(&self) -> Duration { let spacing_seconds = match self { Genesis | BeforeOverwinter | Overwinter | Sapling => PRE_BLOSSOM_POW_TARGET_SPACING, - Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7 | NuTachyon => { + Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7 => { POST_BLOSSOM_POW_TARGET_SPACING.into() } + #[cfg(zcash_unstable = "nutachyon")] + NuTachyon => POST_BLOSSOM_POW_TARGET_SPACING.into(), + #[cfg(zcash_unstable = "zfuture")] ZFuture => POST_BLOSSOM_POW_TARGET_SPACING.into(), }; diff --git a/crates/zakura-chain/src/parameters/tests.rs b/crates/zakura-chain/src/parameters/tests.rs index d620c2d088..71f8c804e7 100644 --- a/crates/zakura-chain/src/parameters/tests.rs +++ b/crates/zakura-chain/src/parameters/tests.rs @@ -274,6 +274,7 @@ const NETWORK_UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ #[cfg(any(test, feature = "zakura-test"))] Nu6_3, Nu7, + #[cfg(zcash_unstable = "nutachyon")] NuTachyon, ]; @@ -290,7 +291,9 @@ fn full_activation_list_contains_all_upgrades() { let network = Network::Mainnet; let full_list = network.full_activation_list(); - // NU7 and NuTachyon are unscheduled on Mainnet, so they are absent from the full activation - // list even though they are always present in the iterator. + // Unscheduled upgrades are absent from the full activation list. + #[cfg(zcash_unstable = "nutachyon")] assert_eq!(full_list.len(), NetworkUpgrade::iter().count() - 2); + #[cfg(not(zcash_unstable = "nutachyon"))] + assert_eq!(full_list.len(), NetworkUpgrade::iter().count() - 1); } diff --git a/crates/zakura-chain/src/parameters/transaction.rs b/crates/zakura-chain/src/parameters/transaction.rs index 7f93db8a6f..193703dfe9 100644 --- a/crates/zakura-chain/src/parameters/transaction.rs +++ b/crates/zakura-chain/src/parameters/transaction.rs @@ -16,4 +16,5 @@ pub const TX_V5_VERSION_GROUP_ID: u32 = 0x26A7_270A; pub const TX_V6_VERSION_GROUP_ID: u32 = 0xD884_B698; /// The placeholder version group ID for version 7 transactions ("tach" in ASCII). +#[cfg(zcash_unstable = "nutachyon")] pub const TX_V7_VERSION_GROUP_ID: u32 = 0x7461_6368; diff --git a/crates/zakura-chain/src/primitives/zcash_history.rs b/crates/zakura-chain/src/primitives/zcash_history.rs index ba862b1c07..09f3789ed0 100644 --- a/crates/zakura-chain/src/primitives/zcash_history.rs +++ b/crates/zakura-chain/src/primitives/zcash_history.rs @@ -9,13 +9,17 @@ mod tests; use std::{collections::BTreeMap, io, sync::Arc}; use serde_big_array::BigArray; -pub use zcash_history::{MAX_ENTRY_SIZE, V1, V2, V3, V4}; +#[cfg(zcash_unstable = "nutachyon")] +pub use zcash_history::V4; +pub use zcash_history::{MAX_ENTRY_SIZE, V1, V2, V3}; +#[cfg(zcash_unstable = "nutachyon")] +use crate::tachyon; use crate::{ block::{Block, ChainHistoryMmrRootHash, Header, Height}, ironwood, orchard, parameters::{Network, NetworkUpgrade}, - sapling, tachyon, + sapling, }; /// The block data needed to build a ZIP-221 history tree leaf. @@ -32,6 +36,7 @@ pub struct HistoryTreeBlockParts<'a> { /// The block's Ironwood note commitment tree root. pub ironwood_root: &'a ironwood::tree::Root, /// The Tachyon pool anchor after the block. + #[cfg(zcash_unstable = "nutachyon")] pub tachyon_anchor: &'a tachyon::Anchor, /// The number of Sapling transactions in the block. pub sapling_tx: u64, @@ -40,6 +45,7 @@ pub struct HistoryTreeBlockParts<'a> { /// The number of Ironwood transactions in the block. pub ironwood_tx: u64, /// The number of Tachyon transactions in the block. + #[cfg(zcash_unstable = "nutachyon")] pub tachyon_tx: u64, } @@ -50,7 +56,7 @@ impl<'a> HistoryTreeBlockParts<'a> { sapling_root: &'a sapling::tree::Root, orchard_root: &'a orchard::tree::Root, ironwood_root: &'a ironwood::tree::Root, - tachyon_anchor: &'a tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &'a tachyon::Anchor, ) -> Self { let height = block .coinbase_height() @@ -62,10 +68,12 @@ impl<'a> HistoryTreeBlockParts<'a> { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, sapling_tx: block.sapling_transactions_count(), orchard_tx: block.orchard_transactions_count(), ironwood_tx: block.ironwood_transactions_count(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: block.tachyon_transactions_count(), } } @@ -80,7 +88,7 @@ pub trait Version: zcash_history::Version { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Self::NodeData { Self::parts_to_history_node( HistoryTreeBlockParts::from_block( @@ -88,6 +96,7 @@ pub trait Version: zcash_history::Version { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, ), network, @@ -160,7 +169,7 @@ impl Entry { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Self { let node_data = V::block_to_history_node( block, @@ -168,6 +177,7 @@ impl Entry { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, ); Self::from_node_data::(node_data) @@ -248,7 +258,7 @@ impl Tree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Result<(Self, Entry), io::Error> { let height = block .coinbase_height() @@ -260,6 +270,7 @@ impl Tree { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, ); let mut peaks = BTreeMap::new(); @@ -311,7 +322,7 @@ impl Tree { sapling_root: &sapling::tree::Root, orchard_root: &orchard::tree::Root, ironwood_root: &ironwood::tree::Root, - tachyon_anchor: &tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &tachyon::Anchor, ) -> Result, zcash_history::Error> { let height = block .coinbase_height() @@ -331,6 +342,7 @@ impl Tree { sapling_root, orchard_root, ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, ); let appended = self.inner.append_leaf(node_data)?; @@ -452,8 +464,9 @@ impl Version for zcash_history::V1 { | NetworkUpgrade::Nu6_1 | NetworkUpgrade::Nu6_2 | NetworkUpgrade::Nu6_3 - | NetworkUpgrade::Nu7 - | NetworkUpgrade::NuTachyon => {} + | NetworkUpgrade::Nu7 => {} + #[cfg(zcash_unstable = "nutachyon")] + NetworkUpgrade::NuTachyon => {} #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => {} }; @@ -517,6 +530,7 @@ impl Version for V3 { } } +#[cfg(zcash_unstable = "nutachyon")] impl Version for V4 { fn parts_to_history_node( parts: HistoryTreeBlockParts<'_>, diff --git a/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs b/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs index 2ace95c311..95c2f8af34 100644 --- a/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs +++ b/crates/zakura-chain/src/primitives/zcash_history/tests/vectors.rs @@ -14,6 +14,7 @@ const U32_SIZE: usize = 4; const V1_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE + U32_SIZE * 4 + HISTORY_HASH_SIZE * 3; const V2_EXTRA_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE * 2; const V3_EXTRA_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE * 2; +#[cfg(zcash_unstable = "nutachyon")] const V4_EXTRA_FIXED_NODE_DATA_SIZE: usize = HISTORY_HASH_SIZE * 2; /// Test the MMR tree using the activation block of a network upgrade @@ -59,6 +60,7 @@ fn tree_for_network_upgrade(network: &Network, network_upgrade: NetworkUpgrade) &sapling_root0, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), )?; @@ -90,6 +92,7 @@ fn tree_for_network_upgrade(network: &Network, network_upgrade: NetworkUpgrade) &sapling_root1, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .unwrap(); @@ -117,6 +120,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &default_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ); let v1_non_default = ::block_to_history_node( @@ -125,6 +129,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &non_default_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ); @@ -143,6 +148,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &default_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ); let v2_non_default = ::block_to_history_node( @@ -151,6 +157,7 @@ fn old_history_versions_ignore_ironwood_root() -> Result<()> { &sapling_root, &orchard_root, &non_default_ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ); @@ -180,6 +187,7 @@ fn v3_history_node_hash_input_has_exact_serialized_size() -> Result<()> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ); @@ -194,12 +202,16 @@ fn v3_history_node_hash_input_has_exact_serialized_size() -> Result<()> { // value in a V3 node. assert_eq!(encoded.len(), serialized_v3_node_data_size(&node_data)); assert_eq!(encoded.last(), Some(&1)); + #[cfg(zcash_unstable = "nutachyon")] assert_eq!(::zcash_history::MAX_NODE_DATA_SIZE, 390); + #[cfg(not(zcash_unstable = "nutachyon"))] + assert_eq!(::zcash_history::MAX_NODE_DATA_SIZE, 317); Ok(()) } #[test] +#[cfg(zcash_unstable = "nutachyon")] fn v4_history_node_commits_to_tachyon_state() -> Result<()> { let network = Network::Mainnet; let (block, sapling_root) = block_and_sapling_root(&network, NetworkUpgrade::Canopy)?; @@ -272,6 +284,7 @@ fn serialized_v3_node_data_size(data: &::zcash_history::NodeDataV3) -> usize { + compact_size(data.ironwood_tx) } +#[cfg(zcash_unstable = "nutachyon")] fn serialized_v4_node_data_size(data: &::zcash_history::NodeDataV4) -> usize { serialized_v3_node_data_size(&data.v3) + V4_EXTRA_FIXED_NODE_DATA_SIZE diff --git a/crates/zakura-chain/src/transaction.rs b/crates/zakura-chain/src/transaction.rs index f683c8b133..8f803a2102 100644 --- a/crates/zakura-chain/src/transaction.rs +++ b/crates/zakura-chain/src/transaction.rs @@ -11,6 +11,7 @@ mod lock_time; mod memo; mod serialize; mod sighash; +#[cfg(zcash_unstable = "nutachyon")] mod tachyon_shielded; mod txid; mod unmined; @@ -34,13 +35,16 @@ pub use serialize::{ MIN_TRANSPARENT_TX_V5_SIZE, }; pub use sighash::{HashType, SigHash, SigHasher}; +#[cfg(zcash_unstable = "nutachyon")] pub use tachyon_shielded::TachyonShieldedData; pub use unmined::{ zip317, UnminedTx, UnminedTxId, VerifiedUnminedTx, MEMPOOL_TRANSACTION_COST_THRESHOLD, }; use zcash_protocol::consensus; -use crate::parameters::{TX_V6_VERSION_GROUP_ID, TX_V7_VERSION_GROUP_ID}; +use crate::parameters::TX_V6_VERSION_GROUP_ID; +#[cfg(zcash_unstable = "nutachyon")] +use crate::parameters::TX_V7_VERSION_GROUP_ID; use crate::{ amount::{Amount, Error as AmountError, NegativeAllowed, NonNegative}, block, ironwood, @@ -172,6 +176,7 @@ pub enum Transaction { ironwood_shielded_data: Option, }, /// A `version = 7` transaction enabled by NuTachyon. + #[cfg(zcash_unstable = "nutachyon")] V7 { /// The Network Upgrade for this transaction. /// @@ -282,6 +287,7 @@ impl AttributedMemorySize for Transaction { .map_or(0, AttributedMemorySize::attributed_memory_size_bytes), ), ), + #[cfg(zcash_unstable = "nutachyon")] V7 { inputs, outputs, @@ -443,7 +449,9 @@ impl Transaction { | Transaction::V3 { .. } | Transaction::V4 { .. } => None, Transaction::V5 { .. } => Some(AuthDigest::from(self)), - Transaction::V6 { .. } | Transaction::V7 { .. } => Some(AuthDigest::from(self)), + Transaction::V6 { .. } => Some(AuthDigest::from(self)), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Some(AuthDigest::from(self)), } } @@ -463,7 +471,13 @@ impl Transaction { crate::primitives::zcash_primitives::txid_and_auth_digest(self); (txid, Some(auth_digest)) } - Transaction::V6 { .. } | Transaction::V7 { .. } => { + Transaction::V6 { .. } => { + let (txid, auth_digest) = + crate::primitives::zcash_primitives::txid_and_auth_digest(self); + (txid, Some(auth_digest)) + } + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => { let (txid, auth_digest) = crate::primitives::zcash_primitives::txid_and_auth_digest(self); (txid, Some(auth_digest)) @@ -580,7 +594,9 @@ impl Transaction { match self { Transaction::V1 { .. } | Transaction::V2 { .. } => false, Transaction::V3 { .. } | Transaction::V4 { .. } | Transaction::V5 { .. } => true, - Transaction::V6 { .. } | Transaction::V7 { .. } => true, + Transaction::V6 { .. } => true, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => true, } } @@ -603,6 +619,7 @@ impl Transaction { Transaction::V4 { .. } => 4, Transaction::V5 { .. } => 5, Transaction::V6 { .. } => 6, + #[cfg(zcash_unstable = "nutachyon")] Transaction::V7 { .. } => 7, } } @@ -615,7 +632,9 @@ impl Transaction { | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } | Transaction::V5 { lock_time, .. } => *lock_time, - Transaction::V6 { lock_time, .. } | Transaction::V7 { lock_time, .. } => *lock_time, + Transaction::V6 { lock_time, .. } => *lock_time, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { lock_time, .. } => *lock_time, }; // `zcashd` checks that the block height is greater than the lock height. @@ -663,7 +682,9 @@ impl Transaction { | Transaction::V3 { lock_time, .. } | Transaction::V4 { lock_time, .. } | Transaction::V5 { lock_time, .. } => *lock_time, - Transaction::V6 { lock_time, .. } | Transaction::V7 { lock_time, .. } => *lock_time, + Transaction::V6 { lock_time, .. } => *lock_time, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { lock_time, .. } => *lock_time, }; let mut lock_time_bytes = Vec::new(); lock_time @@ -700,7 +721,7 @@ impl Transaction { block::Height(0) => None, block::Height(expiry_height) => Some(block::Height(*expiry_height)), }, - Transaction::V6 { expiry_height, .. } | Transaction::V7 { expiry_height, .. } => { + Transaction::V6 { expiry_height, .. } => { match expiry_height { // # Consensus // @@ -710,6 +731,11 @@ impl Transaction { block::Height(expiry_height) => Some(block::Height(*expiry_height)), } } + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { expiry_height, .. } => match expiry_height { + block::Height(0) => None, + block::Height(expiry_height) => Some(block::Height(*expiry_height)), + }, } } @@ -728,8 +754,9 @@ impl Transaction { } => Some(*network_upgrade), Transaction::V6 { network_upgrade, .. - } - | Transaction::V7 { + } => Some(*network_upgrade), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { network_upgrade, .. } => Some(*network_upgrade), } @@ -745,7 +772,9 @@ impl Transaction { Transaction::V3 { ref inputs, .. } => inputs, Transaction::V4 { ref inputs, .. } => inputs, Transaction::V5 { ref inputs, .. } => inputs, - Transaction::V6 { ref inputs, .. } | Transaction::V7 { ref inputs, .. } => inputs, + Transaction::V6 { ref inputs, .. } => inputs, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { ref inputs, .. } => inputs, } } @@ -764,7 +793,9 @@ impl Transaction { Transaction::V3 { ref outputs, .. } => outputs, Transaction::V4 { ref outputs, .. } => outputs, Transaction::V5 { ref outputs, .. } => outputs, - Transaction::V6 { ref outputs, .. } | Transaction::V7 { ref outputs, .. } => outputs, + Transaction::V6 { ref outputs, .. } => outputs, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { ref outputs, .. } => outputs, } } @@ -813,7 +844,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -849,7 +882,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -885,7 +920,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => 0, - Transaction::V6 { .. } | Transaction::V7 { .. } => 0, + Transaction::V6 { .. } => 0, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => 0, } } @@ -925,7 +962,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -962,7 +1001,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => None, - Transaction::V6 { .. } | Transaction::V7 { .. } => None, + Transaction::V6 { .. } => None, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => None, } } @@ -971,7 +1012,9 @@ impl Transaction { match self { // No JoinSplits Transaction::V1 { .. } | Transaction::V5 { .. } => false, - Transaction::V6 { .. } | Transaction::V7 { .. } => false, + Transaction::V6 { .. } => false, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => false, // JoinSplits-on-BCTV14 Transaction::V2 { joinsplit_data, .. } | Transaction::V3 { joinsplit_data, .. } => { @@ -1019,7 +1062,9 @@ impl Transaction { } | Transaction::V1 { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -1036,8 +1081,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data, .. - } - | Transaction::V7 { + } => sapling_shielded_data.as_ref(), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data, .. } => sapling_shielded_data.as_ref(), @@ -1064,8 +1110,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => Box::new(sapling_shielded_data.anchors()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.anchors()), @@ -1085,8 +1132,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. - } - | Transaction::V7 { + } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -1118,8 +1166,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => Box::new(sapling_shielded_data.spends_per_anchor()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.spends_per_anchor()), @@ -1139,8 +1188,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. - } - | Transaction::V7 { + } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -1162,8 +1212,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => Box::new(sapling_shielded_data.outputs()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.outputs()), @@ -1183,8 +1234,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. - } - | Transaction::V7 { + } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -1208,8 +1260,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => Box::new(sapling_shielded_data.nullifiers()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.nullifiers()), @@ -1229,8 +1282,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. - } - | Transaction::V7 { + } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -1256,8 +1310,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => Box::new(sapling_shielded_data.note_commitments()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => Box::new(sapling_shielded_data.note_commitments()), @@ -1277,8 +1332,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. - } - | Transaction::V7 { + } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: None, .. } => Box::new(std::iter::empty()), @@ -1300,8 +1356,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data, .. - } - | Transaction::V7 { + } => sapling_shielded_data.is_some(), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data, .. } => sapling_shielded_data.is_some(), @@ -1329,8 +1386,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => sapling_shielded_data.point_encodings_are_valid(), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => sapling_shielded_data.point_encodings_are_valid(), @@ -1352,8 +1410,9 @@ impl Transaction { Transaction::V6 { orchard_shielded_data, .. - } - | Transaction::V7 { + } => orchard_shielded_data.as_ref(), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { orchard_shielded_data, .. } => orchard_shielded_data.as_ref(), @@ -1412,8 +1471,9 @@ impl Transaction { Transaction::V6 { ironwood_shielded_data, .. - } - | Transaction::V7 { + } => ironwood_shielded_data.as_ref(), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { ironwood_shielded_data, .. } => ironwood_shielded_data.as_ref(), @@ -1466,6 +1526,7 @@ impl Transaction { // Tachyon /// Access the Tachyon shielded data in this transaction, if present. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_shielded_data(&self) -> Option<&TachyonShieldedData> { match self { Transaction::V7 { @@ -1477,11 +1538,13 @@ impl Transaction { } /// Returns `true` if this transaction carries a Tachyon bundle. + #[cfg(zcash_unstable = "nutachyon")] pub fn has_tachyon_shielded_data(&self) -> bool { self.tachyon_shielded_data().is_some() } /// Returns the tachygrams revealed by this transaction's proof stamp. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_tachygrams(&self) -> Vec { match self.tachyon_shielded_data() { Some(shielded_data) => match &shielded_data.0 { @@ -1500,11 +1563,17 @@ impl Transaction { } /// Returns `true` if this transaction has any Tachyon actions. + #[cfg(zcash_unstable = "nutachyon")] pub fn has_tachyon_actions(&self) -> bool { self.tachyon_shielded_data() .is_some_and(|data| !data.actions().is_empty()) } + #[cfg(not(zcash_unstable = "nutachyon"))] + fn has_tachyon_actions(&self) -> bool { + false + } + // value balances /// Return the transparent value balance, @@ -1616,7 +1685,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -1664,7 +1735,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -1706,7 +1779,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(iter::empty()), + Transaction::V6 { .. } => Box::new(iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(iter::empty()), }; joinsplit_value_balances.map(ValueBalance::from_sprout_amount) @@ -1751,8 +1826,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => sapling_shielded_data.value_balance, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => sapling_shielded_data.value_balance, @@ -1771,8 +1847,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. - } - | Transaction::V7 { + } => Amount::zero(), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: None, .. } => Amount::zero(), @@ -1798,8 +1875,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => Some(sapling_shielded_data.binding_sig), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => Some(sapling_shielded_data.binding_sig), @@ -1892,6 +1970,7 @@ impl Transaction { /// Return the Tachyon value balance, the change in the transaction value pool due to /// Tachyon actions. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_value_balance(&self) -> ValueBalance { let tachyon_value_balance = self .tachyon_shielded_data() @@ -1904,6 +1983,11 @@ impl Transaction { ValueBalance::from_tachyon_amount(tachyon_value_balance) } + #[cfg(not(zcash_unstable = "nutachyon"))] + fn tachyon_value_balance(&self) -> ValueBalance { + ValueBalance::zero() + } + /// Returns the value balances for this transaction using the provided transparent outputs. #[cfg(any(test, feature = "proptest-impl"))] pub(crate) fn value_balance_from_outputs( @@ -2022,6 +2106,7 @@ impl Transaction { Transaction::V4 { .. } => Some(SAPLING_VERSION_GROUP_ID), Transaction::V5 { .. } => Some(TX_V5_VERSION_GROUP_ID), Transaction::V6 { .. } => Some(TX_V6_VERSION_GROUP_ID), + #[cfg(zcash_unstable = "nutachyon")] Transaction::V7 { .. } => Some(TX_V7_VERSION_GROUP_ID), } } @@ -2052,8 +2137,12 @@ impl Transaction { Transaction::V6 { ref mut network_upgrade, .. + } => { + *network_upgrade = nu; + Ok(()) } - | Transaction::V7 { + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { ref mut network_upgrade, .. } => { @@ -2088,8 +2177,9 @@ impl Transaction { Transaction::V6 { ref mut expiry_height, .. - } - | Transaction::V7 { + } => expiry_height, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { ref mut expiry_height, .. } => expiry_height, @@ -2104,9 +2194,9 @@ impl Transaction { Transaction::V3 { ref mut inputs, .. } => inputs, Transaction::V4 { ref mut inputs, .. } => inputs, Transaction::V5 { ref mut inputs, .. } => inputs, - Transaction::V6 { ref mut inputs, .. } | Transaction::V7 { ref mut inputs, .. } => { - inputs - } + Transaction::V6 { ref mut inputs, .. } => inputs, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { ref mut inputs, .. } => inputs, } } @@ -2136,8 +2226,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: Some(sapling_shielded_data), .. - } - | Transaction::V7 { + } => Some(&mut sapling_shielded_data.value_balance), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: Some(sapling_shielded_data), .. } => Some(&mut sapling_shielded_data.value_balance), @@ -2155,8 +2246,9 @@ impl Transaction { Transaction::V6 { sapling_shielded_data: None, .. - } - | Transaction::V7 { + } => None, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data: None, .. } => None, @@ -2208,7 +2300,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -2257,7 +2351,9 @@ impl Transaction { .. } | Transaction::V5 { .. } => Box::new(std::iter::empty()), - Transaction::V6 { .. } | Transaction::V7 { .. } => Box::new(std::iter::empty()), + Transaction::V6 { .. } => Box::new(std::iter::empty()), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Box::new(std::iter::empty()), } } @@ -2279,8 +2375,9 @@ impl Transaction { Transaction::V6 { orchard_shielded_data: Some(orchard_shielded_data), .. - } - | Transaction::V7 { + } => Some(orchard_shielded_data), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { orchard_shielded_data: Some(orchard_shielded_data), .. } => Some(orchard_shielded_data), @@ -2296,8 +2393,9 @@ impl Transaction { Transaction::V6 { orchard_shielded_data: None, .. - } - | Transaction::V7 { + } => None, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { orchard_shielded_data: None, .. } => None, @@ -2324,8 +2422,9 @@ impl Transaction { } => outputs, Transaction::V6 { ref mut outputs, .. - } - | Transaction::V7 { + } => outputs, + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { ref mut outputs, .. } => outputs, } diff --git a/crates/zakura-chain/src/transaction/arbitrary.rs b/crates/zakura-chain/src/transaction/arbitrary.rs index 5b931e140e..4b20cca4ed 100644 --- a/crates/zakura-chain/src/transaction/arbitrary.rs +++ b/crates/zakura-chain/src/transaction/arbitrary.rs @@ -805,7 +805,9 @@ impl Arbitrary for Transaction { Self::v5_strategy(ledger_state) ] .boxed(), - NetworkUpgrade::Nu7 | NetworkUpgrade::NuTachyon => Self::v5_strategy(ledger_state), + NetworkUpgrade::Nu7 => Self::v5_strategy(ledger_state), + #[cfg(zcash_unstable = "nutachyon")] + NetworkUpgrade::NuTachyon => Self::v5_strategy(ledger_state), #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => Self::v5_strategy(ledger_state), @@ -955,6 +957,7 @@ pub fn transaction_to_fake_v5( }, v5 @ V5 { .. } => v5.clone(), v6 @ V6 { .. } => v6.clone(), + #[cfg(zcash_unstable = "nutachyon")] v7 @ V7 { .. } => v7.clone(), } } @@ -1041,6 +1044,7 @@ pub fn v5_transactions<'b>( | Transaction::V4 { .. } => None, ref tx @ Transaction::V5 { .. } => Some(tx.clone()), ref tx @ Transaction::V6 { .. } => Some(tx.clone()), + #[cfg(zcash_unstable = "nutachyon")] ref tx @ Transaction::V7 { .. } => Some(tx.clone()), }) } diff --git a/crates/zakura-chain/src/transaction/serialize.rs b/crates/zakura-chain/src/transaction/serialize.rs index f598d691d3..4539e6ee49 100644 --- a/crates/zakura-chain/src/transaction/serialize.rs +++ b/crates/zakura-chain/src/transaction/serialize.rs @@ -21,7 +21,9 @@ use crate::{ }, }; -use crate::parameters::{TX_V6_VERSION_GROUP_ID, TX_V7_VERSION_GROUP_ID}; +use crate::parameters::TX_V6_VERSION_GROUP_ID; +#[cfg(zcash_unstable = "nutachyon")] +use crate::parameters::TX_V7_VERSION_GROUP_ID; use super::*; use crate::sapling; @@ -875,6 +877,7 @@ impl ZcashSerialize for Transaction { ALLOW_CROSS_ADDRESS_BIT, )?; } + #[cfg(zcash_unstable = "nutachyon")] Transaction::V7 { network_upgrade, lock_time, @@ -914,13 +917,10 @@ impl ZcashSerialize for Transaction { &mut writer, ALLOW_CROSS_ADDRESS_BIT, )?; - #[cfg(zcash_unstable = "nutachyon")] zcash_primitives::transaction::components::tachyon::write_v7_bundle( tachyon_shielded_data.as_ref().map(|bundle| &bundle.0), &mut writer, )?; - #[cfg(not(zcash_unstable = "nutachyon"))] - writer.write_u8(0)?; } } Ok(()) @@ -1282,6 +1282,7 @@ impl ZcashDeserialize for Transaction { ironwood_shielded_data, }) } + #[cfg(zcash_unstable = "nutachyon")] (7, true) => { let id = limited_reader.read_u32::()?; if id != TX_V7_VERSION_GROUP_ID { @@ -1313,22 +1314,11 @@ impl ZcashDeserialize for Transaction { &mut limited_reader, ALLOW_CROSS_ADDRESS_BIT, )?; - #[cfg(zcash_unstable = "nutachyon")] let tachyon_shielded_data = zcash_primitives::transaction::components::tachyon::read_v7_bundle( &mut limited_reader, )? .map(TachyonShieldedData::from); - #[cfg(not(zcash_unstable = "nutachyon"))] - let tachyon_shielded_data = match limited_reader.read_u8()? { - 0 => None, - _ => { - return Err(SerializationError::Parse( - "Tachyon bundles are not available in this build", - )); - } - }; - Ok(Transaction::V7 { network_upgrade, lock_time, diff --git a/crates/zakura-chain/src/transaction/tests/vectors.rs b/crates/zakura-chain/src/transaction/tests/vectors.rs index 4ad44affc1..bb8c2e4253 100644 --- a/crates/zakura-chain/src/transaction/tests/vectors.rs +++ b/crates/zakura-chain/src/transaction/tests/vectors.rs @@ -1721,8 +1721,36 @@ fn binding_signatures() { Transaction::V6 { sapling_shielded_data, .. + } => { + if let Some(sapling_shielded_data) = sapling_shielded_data { + // V6 txs have the outputs spent by their transparent inputs hashed into + // their SIGHASH, so we need to exclude txs with transparent inputs. + // + // References: + // + // + // + if tx.has_transparent_inputs() { + continue; + } + + let sighash = tx + .sighash(nu, HashType::ALL, Arc::new(Vec::new()), None) + .expect("network upgrade is valid for tx"); + + let bvk = redjubjub::VerificationKey::try_from( + sapling_shielded_data + .binding_verification_key() + .expect("test transaction has valid value commitments"), + ) + .expect("a valid redjubjub::VerificationKey"); + + bvk.verify(sighash.as_ref(), &sapling_shielded_data.binding_sig) + .expect("verification passes"); + } } - | Transaction::V7 { + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data, .. } => { diff --git a/crates/zakura-chain/src/transaction/txid.rs b/crates/zakura-chain/src/transaction/txid.rs index 6876f4c4e3..ad1b3ffbe5 100644 --- a/crates/zakura-chain/src/transaction/txid.rs +++ b/crates/zakura-chain/src/transaction/txid.rs @@ -28,7 +28,9 @@ impl<'a> TxIdBuilder<'a> { | Transaction::V3 { .. } | Transaction::V4 { .. } => self.txid_v1_to_v4(), Transaction::V5 { .. } => self.txid_v5(), - Transaction::V6 { .. } | Transaction::V7 { .. } => self.txid_v6(), + Transaction::V6 { .. } => self.txid_v6(), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => self.txid_v6(), } } diff --git a/crates/zakura-chain/src/transaction/unmined.rs b/crates/zakura-chain/src/transaction/unmined.rs index 28fa07b923..e3acc4cc0f 100644 --- a/crates/zakura-chain/src/transaction/unmined.rs +++ b/crates/zakura-chain/src/transaction/unmined.rs @@ -141,7 +141,9 @@ impl From<&Transaction> for UnminedTxId { match transaction { V1 { .. } | V2 { .. } | V3 { .. } | V4 { .. } => Legacy(transaction.into()), V5 { .. } => Witnessed(transaction.into()), - V6 { .. } | V7 { .. } => Witnessed(transaction.into()), + V6 { .. } => Witnessed(transaction.into()), + #[cfg(zcash_unstable = "nutachyon")] + V7 { .. } => Witnessed(transaction.into()), } } } diff --git a/crates/zakura-chain/src/value_balance.rs b/crates/zakura-chain/src/value_balance.rs index 7c47f5f003..c82b32af3b 100644 --- a/crates/zakura-chain/src/value_balance.rs +++ b/crates/zakura-chain/src/value_balance.rs @@ -18,6 +18,11 @@ mod tests; use ValueBalanceError::*; +#[cfg(not(zcash_unstable = "nutachyon"))] +const VALUE_BALANCE_BYTES: usize = 48; +#[cfg(zcash_unstable = "nutachyon")] +const VALUE_BALANCE_BYTES: usize = 56; + /// A balance in each chain value pool or transaction value pool. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct ValueBalance { @@ -27,6 +32,7 @@ pub struct ValueBalance { orchard: Amount, deferred: Amount, ironwood: Amount, + #[cfg(zcash_unstable = "nutachyon")] tachyon: Amount, } @@ -75,6 +81,7 @@ where } /// Creates a [`ValueBalance`] from the given Tachyon amount. + #[cfg(zcash_unstable = "nutachyon")] pub fn from_tachyon_amount(tachyon_amount: Amount) -> Self { ValueBalance { tachyon: tachyon_amount, @@ -157,11 +164,13 @@ where } /// Get the Tachyon amount from the [`ValueBalance`]. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_amount(&self) -> Amount { self.tachyon } /// Insert a Tachyon value balance without changing the other pools. + #[cfg(zcash_unstable = "nutachyon")] pub fn set_tachyon_value_balance(&mut self, tachyon_value_balance: ValueBalance) -> &Self { self.tachyon = tachyon_value_balance.tachyon; self @@ -177,6 +186,7 @@ where orchard: zero, deferred: zero, ironwood: zero, + #[cfg(zcash_unstable = "nutachyon")] tachyon: zero, } } @@ -194,6 +204,7 @@ where orchard: self.orchard.constrain().map_err(Orchard)?, deferred: self.deferred.constrain().map_err(Deferred)?, ironwood: self.ironwood.constrain().map_err(Ironwood)?, + #[cfg(zcash_unstable = "nutachyon")] tachyon: self.tachyon.constrain().map_err(Tachyon)?, }) } @@ -221,13 +232,12 @@ impl ValueBalance { // like the orchard bundle; it is zero for transactions without an ironwood bundle. // // This will error if the remaining value in the transaction value pool is negative. - (self.transparent - + self.sprout - + self.sapling - + self.orchard - + self.ironwood - + self.tachyon)? - .constrain::() + let remaining = + self.transparent + self.sprout + self.sapling + self.orchard + self.ironwood; + #[cfg(zcash_unstable = "nutachyon")] + let remaining = remaining? + self.tachyon; + + remaining?.constrain::() } } @@ -379,7 +389,7 @@ impl ValueBalance { /// /// New pools are appended in activation order, so older records remain parsable by /// [`Self::from_bytes`]. - pub fn to_bytes(self) -> [u8; 56] { + pub fn to_bytes(self) -> [u8; VALUE_BALANCE_BYTES] { match [ self.transparent.to_bytes(), self.sprout.to_bytes(), @@ -387,6 +397,7 @@ impl ValueBalance { self.orchard.to_bytes(), self.deferred.to_bytes(), self.ironwood.to_bytes(), + #[cfg(zcash_unstable = "nutachyon")] self.tachyon.to_bytes(), ] .concat() @@ -394,7 +405,7 @@ impl ValueBalance { { Ok(bytes) => bytes, _ => unreachable!( - "seven [u8; 8] should always concat with no error into a single [u8; 56]" + "the value balance fields should concat into the configured fixed-width array" ), } } @@ -408,10 +419,11 @@ impl ValueBalance { let bytes_length = bytes.len(); // Return an error early if bytes don't have the right length instead of panicking later. - match bytes_length { - 32 | 40 | 48 | 56 => {} - _ => return Err(Unparsable), - }; + let valid_length = matches!(bytes_length, 32 | 40 | 48) + || cfg!(zcash_unstable = "nutachyon") && bytes_length == 56; + if !valid_length { + return Err(Unparsable); + } let transparent = Amount::from_bytes( bytes[0..8] @@ -463,6 +475,7 @@ impl ValueBalance { _ => return Err(Unparsable), }; + #[cfg(zcash_unstable = "nutachyon")] let tachyon = match bytes_length { 32 | 40 | 48 => Amount::zero(), 56 => Amount::from_bytes( @@ -481,6 +494,7 @@ impl ValueBalance { orchard, deferred, ironwood, + #[cfg(zcash_unstable = "nutachyon")] tachyon, }) } @@ -508,6 +522,7 @@ pub enum ValueBalanceError { Ironwood(amount::Error), /// Tachyon amount error {0} + #[cfg(zcash_unstable = "nutachyon")] Tachyon(amount::Error), /// ValueBalance is unparsable @@ -523,6 +538,7 @@ impl fmt::Display for ValueBalanceError { Orchard(e) => format!("orchard amount err: {e}"), Deferred(e) => format!("deferred amount err: {e}"), Ironwood(e) => format!("ironwood amount err: {e}"), + #[cfg(zcash_unstable = "nutachyon")] Tachyon(e) => format!("tachyon amount err: {e}"), Unparsable => "value balance is unparsable".to_string(), }) @@ -542,6 +558,7 @@ where orchard: (self.orchard + rhs.orchard).map_err(Orchard)?, deferred: (self.deferred + rhs.deferred).map_err(Deferred)?, ironwood: (self.ironwood + rhs.ironwood).map_err(Ironwood)?, + #[cfg(zcash_unstable = "nutachyon")] tachyon: (self.tachyon + rhs.tachyon).map_err(Tachyon)?, }) } @@ -593,6 +610,7 @@ where orchard: (self.orchard - rhs.orchard).map_err(Orchard)?, deferred: (self.deferred - rhs.deferred).map_err(Deferred)?, ironwood: (self.ironwood - rhs.ironwood).map_err(Ironwood)?, + #[cfg(zcash_unstable = "nutachyon")] tachyon: (self.tachyon - rhs.tachyon).map_err(Tachyon)?, }) } @@ -664,6 +682,7 @@ where orchard: self.orchard.neg(), deferred: self.deferred.neg(), ironwood: self.ironwood.neg(), + #[cfg(zcash_unstable = "nutachyon")] tachyon: self.tachyon.neg(), } } diff --git a/crates/zakura-chain/src/value_balance/arbitrary.rs b/crates/zakura-chain/src/value_balance/arbitrary.rs index 741e8609ad..14cf1c1550 100644 --- a/crates/zakura-chain/src/value_balance/arbitrary.rs +++ b/crates/zakura-chain/src/value_balance/arbitrary.rs @@ -1,6 +1,7 @@ use crate::{amount::*, value_balance::*}; use proptest::prelude::*; +#[cfg(zcash_unstable = "nutachyon")] impl Arbitrary for ValueBalance { type Parameters = (); @@ -31,6 +32,36 @@ impl Arbitrary for ValueBalance { type Strategy = BoxedStrategy; } +#[cfg(not(zcash_unstable = "nutachyon"))] +impl Arbitrary for ValueBalance { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + ( + any::>(), + any::>(), + any::>(), + any::>(), + any::>(), + any::>(), + ) + .prop_map( + |(transparent, sprout, sapling, orchard, deferred, ironwood)| Self { + transparent, + sprout, + sapling, + orchard, + deferred, + ironwood, + }, + ) + .boxed() + } + + type Strategy = BoxedStrategy; +} + +#[cfg(zcash_unstable = "nutachyon")] impl Arbitrary for ValueBalance { type Parameters = (); @@ -60,3 +91,32 @@ impl Arbitrary for ValueBalance { type Strategy = BoxedStrategy; } + +#[cfg(not(zcash_unstable = "nutachyon"))] +impl Arbitrary for ValueBalance { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + ( + any::>(), + any::>(), + any::>(), + any::>(), + any::>(), + any::>(), + ) + .prop_map( + |(transparent, sprout, sapling, orchard, deferred, ironwood)| Self { + transparent, + sprout, + sapling, + orchard, + deferred, + ironwood, + }, + ) + .boxed() + } + + type Strategy = BoxedStrategy; +} diff --git a/crates/zakura-chain/src/value_balance/tests/prop.rs b/crates/zakura-chain/src/value_balance/tests/prop.rs index 3c6fd27e01..b703daa1d9 100644 --- a/crates/zakura-chain/src/value_balance/tests/prop.rs +++ b/crates/zakura-chain/src/value_balance/tests/prop.rs @@ -6,6 +6,7 @@ use crate::{amount::*, value_balance::*}; proptest! { #[test] + #[cfg(zcash_unstable = "nutachyon")] fn value_blance_add( value_balance1 in any::>(), value_balance2 in any::>()) @@ -48,6 +49,7 @@ proptest! { } } #[test] + #[cfg(zcash_unstable = "nutachyon")] fn value_balance_sub( value_balance1 in any::>(), value_balance2 in any::>()) @@ -89,6 +91,7 @@ proptest! { } #[test] + #[cfg(zcash_unstable = "nutachyon")] fn value_balance_sum( value_balance1 in any::>(), value_balance2 in any::>(), @@ -141,6 +144,7 @@ proptest! { } #[test] + #[cfg(zcash_unstable = "nutachyon")] fn value_balance_deserialization(bytes in any::<[u8; 56]>()) { let _init_guard = zakura_test::init(); @@ -158,7 +162,7 @@ proptest! { if let Ok(deserialized) = ValueBalance::::from_bytes(&bytes) { let deserialized = deserialized.to_bytes(); - let mut extended_bytes = [0u8; 56]; + let mut extended_bytes = [0u8; VALUE_BALANCE_BYTES]; extended_bytes[..32].copy_from_slice(&bytes); prop_assert_eq!(extended_bytes, deserialized); } @@ -174,7 +178,7 @@ proptest! { if let Ok(deserialized) = ValueBalance::::from_bytes(&bytes) { let deserialized = deserialized.to_bytes(); - let mut extended_bytes = [0u8; 56]; + let mut extended_bytes = [0u8; VALUE_BALANCE_BYTES]; extended_bytes[..40].copy_from_slice(&bytes); prop_assert_eq!(extended_bytes, deserialized); } @@ -182,6 +186,7 @@ proptest! { /// The pre-Tachyon value balance was a 48-byte prefix of the current format. #[test] + #[cfg(zcash_unstable = "nutachyon")] fn pre_tachyon_value_balance_deserialization(bytes in any::<[u8; 48]>()) { let _init_guard = zakura_test::init(); diff --git a/crates/zakura-consensus/Cargo.toml b/crates/zakura-consensus/Cargo.toml index fdc8d3b850..5aee9fa252 100644 --- a/crates/zakura-consensus/Cargo.toml +++ b/crates/zakura-consensus/Cargo.toml @@ -74,7 +74,6 @@ zakura-chain = { path = "../zakura-chain", version = "6.0.0" } zakura-header-chain = { path = "../zakura-header-chain", version = "1.0.0" } zcash_protocol.workspace = true -zcash_tachyon.workspace = true # prod feature progress-bar howudoin = { workspace = true, optional = true } @@ -83,8 +82,13 @@ howudoin = { workspace = true, optional = true } proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } -[dev-dependencies] +[target.'cfg(zcash_unstable = "nutachyon")'.dependencies] +zcash_tachyon.workspace = true + +[target.'cfg(zcash_unstable = "nutachyon")'.dev-dependencies] ragu = { workspace = true } + +[dev-dependencies] color-eyre = { workspace = true } hex = { workspace = true } diff --git a/crates/zakura-consensus/src/block.rs b/crates/zakura-consensus/src/block.rs index 2bfe4fdd8c..1b1a576edc 100644 --- a/crates/zakura-consensus/src/block.rs +++ b/crates/zakura-consensus/src/block.rs @@ -36,6 +36,7 @@ use crate::{error::*, primitives, transaction as tx, BoxError}; pub mod check; pub mod request; pub mod subsidy; +#[cfg(zcash_unstable = "nutachyon")] pub(crate) mod tachyon; pub use request::Request; @@ -126,15 +127,21 @@ impl VerifyBlockError { consensus("block.too_many_transparent_signature_operations") } BlockError::SummingMinerFees { .. } => consensus("block.summing_miner_fees"), + #[cfg(zcash_unstable = "nutachyon")] BlockError::DuplicateTachygram => consensus("block.duplicate_tachygram"), + #[cfg(zcash_unstable = "nutachyon")] BlockError::TachyonAggregateNotFound => { consensus("block.tachyon_aggregate_not_found") } + #[cfg(zcash_unstable = "nutachyon")] BlockError::TachyonCoverageMismatch => consensus("block.tachyon_coverage_mismatch"), + #[cfg(zcash_unstable = "nutachyon")] BlockError::TachyonDuplicateAction => consensus("block.tachyon_duplicate_action"), + #[cfg(zcash_unstable = "nutachyon")] BlockError::TachyonTachygramArityMismatch => { consensus("block.tachyon_tachygram_arity_mismatch") } + #[cfg(zcash_unstable = "nutachyon")] BlockError::TachyonProofInvalid(_) => consensus("block.tachyon_proof_invalid"), BlockError::InvalidHeaderEncoding(_) | BlockError::MissingHeight(_) @@ -328,6 +335,7 @@ where check::merkle_root_validity(&network, &block, &transaction_hashes)?; + #[cfg(zcash_unstable = "nutachyon")] let mut tachyon_checks = tachyon::coherence(&block)? .into_iter() .map(primitives::tachyon::verify_proof_stamp) @@ -419,6 +427,7 @@ where // Check the summed block totals + #[cfg(zcash_unstable = "nutachyon")] while let Some(result) = tachyon_checks.next().await { result?; } diff --git a/crates/zakura-consensus/src/error.rs b/crates/zakura-consensus/src/error.rs index d021c2bf8e..ff09f7ca20 100644 --- a/crates/zakura-consensus/src/error.rs +++ b/crates/zakura-consensus/src/error.rs @@ -291,12 +291,15 @@ pub enum TransactionError { IronwoodProofSize, #[error("Tachyon action value commitment and verification key must not be identity: {0}")] + #[cfg(zcash_unstable = "nutachyon")] TachyonIdentityAction(String), #[error("Tachyon action and binding signatures must verify: {0}")] + #[cfg(zcash_unstable = "nutachyon")] TachyonSignatureInvalid(String), #[error("the mempool only accepts autonome Tachyon transactions")] + #[cfg(zcash_unstable = "nutachyon")] NonAutonomeTachyon, #[error("unexpected error")] @@ -501,8 +504,11 @@ impl TransactionError { Self::Balance(_) => consensus("transaction.balance"), Self::OrchardProofSize => consensus("transaction.orchard_proof_size"), Self::IronwoodProofSize => consensus("transaction.ironwood_proof_size"), + #[cfg(zcash_unstable = "nutachyon")] Self::TachyonIdentityAction(_) => consensus("transaction.tachyon_identity_action"), + #[cfg(zcash_unstable = "nutachyon")] Self::TachyonSignatureInvalid(_) => consensus("transaction.tachyon_signature_invalid"), + #[cfg(zcash_unstable = "nutachyon")] Self::NonAutonomeTachyon => { BodyVerificationClass::Retryable(TransientBodyFailureKind::VerifierUnavailable) } @@ -559,13 +565,14 @@ impl TransactionError { | OrchardHasEnableCrossAddress | OrchardProofSize | IronwoodProofSize - | TachyonIdentityAction(_) - | TachyonSignatureInvalid(_) | WrongConsensusBranchId | MissingConsensusBranchId | LockedUntilAfterBlockHeight(_) | LockedUntilAfterBlockTime(_) => 100, + #[cfg(zcash_unstable = "nutachyon")] + TachyonIdentityAction(_) | TachyonSignatureInvalid(_) => 100, + // NU6.2 mempool transactions are invalid under NU6.3 rules, but // honest peers can relay them briefly while their chain tips converge. WrongConsensusBranchIdNu6_3GracePeriod => 0, @@ -734,21 +741,27 @@ pub enum BlockError { }, #[error("all Tachygrams in a block must be distinct")] + #[cfg(zcash_unstable = "nutachyon")] DuplicateTachygram, #[error("a Tachyon pointer stamp must refer to a proof-stamped transaction in the same block")] + #[cfg(zcash_unstable = "nutachyon")] TachyonAggregateNotFound, #[error("a Tachyon proof stamp's covered-actions digest does not match its aggregate")] + #[cfg(zcash_unstable = "nutachyon")] TachyonCoverageMismatch, #[error("action descriptors covered by a Tachyon proof stamp must be distinct")] + #[cfg(zcash_unstable = "nutachyon")] TachyonDuplicateAction, #[error("a Tachyon proof stamp must publish exactly two Tachygrams per covered action")] + #[cfg(zcash_unstable = "nutachyon")] TachyonTachygramArityMismatch, #[error("a Tachyon proof stamp must verify: {0}")] + #[cfg(zcash_unstable = "nutachyon")] TachyonProofInvalid(String), #[error("unexpected error occurred: {0}")] @@ -789,6 +802,7 @@ impl BlockError { | BadMerkleRoot { .. } | WrongTransactionConsensusBranchId | TooManyTransparentSignatureOperations { .. } => 100, + #[cfg(zcash_unstable = "nutachyon")] DuplicateTachygram | TachyonAggregateNotFound | TachyonCoverageMismatch diff --git a/crates/zakura-consensus/src/primitives.rs b/crates/zakura-consensus/src/primitives.rs index 250c650093..5d62eeef2d 100644 --- a/crates/zakura-consensus/src/primitives.rs +++ b/crates/zakura-consensus/src/primitives.rs @@ -17,6 +17,7 @@ pub mod halo2; pub mod redjubjub; pub mod redpallas; pub mod sapling; +#[cfg(zcash_unstable = "nutachyon")] pub mod tachyon; /// The maximum batch size for any of the batch verifiers. diff --git a/crates/zakura-consensus/src/primitives/halo2.rs b/crates/zakura-consensus/src/primitives/halo2.rs index 65c5db0e20..5f83455ef5 100644 --- a/crates/zakura-consensus/src/primitives/halo2.rs +++ b/crates/zakura-consensus/src/primitives/halo2.rs @@ -402,7 +402,10 @@ fn lazy_verifier_for(network_upgrade: NetworkUpgrade) -> &'static Lazy &VERIFIER_NU6_3_ONWARD, + Nu6_3 | Nu7 => &VERIFIER_NU6_3_ONWARD, + + #[cfg(zcash_unstable = "nutachyon")] + NuTachyon => &VERIFIER_NU6_3_ONWARD, // `ZFuture` is post-NU6.3 and inherits the NU6.3 circuit. Keep the // cfg-gated arm explicit so a future upgrade cannot silently fall back diff --git a/crates/zakura-consensus/src/transaction.rs b/crates/zakura-consensus/src/transaction.rs index b3cf3aa892..3b96b4acc5 100644 --- a/crates/zakura-consensus/src/transaction.rs +++ b/crates/zakura-consensus/src/transaction.rs @@ -486,7 +486,9 @@ where // Transaction parsing enforces the same rule. Repeat it here for // defense-in-depth and transactions constructed in memory. check::shielded_proof_size_is_canonical(&tx)?; + #[cfg(zcash_unstable = "nutachyon")] check::tachyon_actions_have_valid_digests(&tx)?; + #[cfg(zcash_unstable = "nutachyon")] if req.is_mempool() { check::tachyon_bundle_is_autonome(&tx)?; } @@ -616,7 +618,7 @@ where cached_ffi_transaction.clone(), wtx_id.expect("a v5 transaction has a witnessed transaction ID"), )?, - Transaction::V6 { .. } | Transaction::V7 { .. } => { + Transaction::V6 { .. } => { Self::verify_v6_or_v7_transaction( &req, &network, @@ -625,6 +627,14 @@ where wtx_id.expect("a v6 or v7 transaction has a witnessed transaction ID"), )? } + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => Self::verify_v6_or_v7_transaction( + &req, + &network, + script_verifier, + cached_ffi_transaction.clone(), + wtx_id.expect("a v6 or v7 transaction has a witnessed transaction ID"), + )?, }; if let Some(unmined_tx) = mempool_transaction { @@ -998,8 +1008,13 @@ where NetworkUpgrade::Genesis | NetworkUpgrade::BeforeOverwinter | NetworkUpgrade::Overwinter - | NetworkUpgrade::Nu7 - | NetworkUpgrade::NuTachyon => Err(TransactionError::UnsupportedByNetworkUpgrade( + | NetworkUpgrade::Nu7 => Err(TransactionError::UnsupportedByNetworkUpgrade( + transaction.version(), + network_upgrade, + )), + + #[cfg(zcash_unstable = "nutachyon")] + NetworkUpgrade::NuTachyon => Err(TransactionError::UnsupportedByNetworkUpgrade( transaction.version(), network_upgrade, )), @@ -1085,8 +1100,10 @@ where | NetworkUpgrade::Nu6_1 | NetworkUpgrade::Nu6_2 | NetworkUpgrade::Nu6_3 - | NetworkUpgrade::Nu7 - | NetworkUpgrade::NuTachyon => Ok(()), + | NetworkUpgrade::Nu7 => Ok(()), + + #[cfg(zcash_unstable = "nutachyon")] + NetworkUpgrade::NuTachyon => Ok(()), #[cfg(zcash_unstable = "zfuture")] NetworkUpgrade::ZFuture => Ok(()), @@ -1120,6 +1137,7 @@ where Transaction::V6 { .. } => { Self::verify_v6_transaction_network_upgrade(&transaction, nu)? } + #[cfg(zcash_unstable = "nutachyon")] Transaction::V7 { .. } => { Self::verify_v7_transaction_network_upgrade(&transaction, nu)? } @@ -1134,7 +1152,7 @@ where .sighasher() .sighash(HashType::ALL, None); - Ok(Self::verify_transparent_inputs_and_outputs( + let async_checks = Self::verify_transparent_inputs_and_outputs( request, script_verifier, cached_ffi_transaction, @@ -1155,8 +1173,13 @@ where &sighash, nu, wtx_id, - )) - .and(Self::verify_tachyon_signatures(&transaction, &sighash))) + )); + + #[cfg(zcash_unstable = "nutachyon")] + let async_checks = + async_checks.and(Self::verify_tachyon_signatures(&transaction, &sighash)); + + Ok(async_checks) } /// Verifies if a V6 `transaction` is supported by `network_upgrade`. @@ -1175,6 +1198,7 @@ where } /// Verifies if a V7 `transaction` is supported by `network_upgrade`. + #[cfg(zcash_unstable = "nutachyon")] fn verify_v7_transaction_network_upgrade( transaction: &Transaction, network_upgrade: NetworkUpgrade, @@ -1190,6 +1214,7 @@ where } /// Queues verification of a V7 transaction's Tachyon action and binding signatures. + #[cfg(zcash_unstable = "nutachyon")] fn verify_tachyon_signatures(tx: &Transaction, sighash: &SigHash) -> AsyncChecks { use zcash_tachyon::TachyonBundle; diff --git a/crates/zakura-consensus/src/transaction/check.rs b/crates/zakura-consensus/src/transaction/check.rs index c89657cded..100da98a42 100644 --- a/crates/zakura-consensus/src/transaction/check.rs +++ b/crates/zakura-consensus/src/transaction/check.rs @@ -910,6 +910,7 @@ pub fn consensus_branch_id( } /// Checks that no Tachyon action's value commitment or verification key is the identity point. +#[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_actions_have_valid_digests(tx: &Transaction) -> Result<(), TransactionError> { let Some(tachyon_shielded_data) = tx.tachyon_shielded_data() else { return Ok(()); @@ -925,6 +926,7 @@ pub fn tachyon_actions_have_valid_digests(tx: &Transaction) -> Result<(), Transa } /// Checks that a Tachyon transaction is autonome before it enters the mempool. +#[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_bundle_is_autonome(tx: &Transaction) -> Result<(), TransactionError> { let Some(tachyon_shielded_data) = tx.tachyon_shielded_data() else { return Ok(()); diff --git a/crates/zakura-consensus/src/transaction/tests.rs b/crates/zakura-consensus/src/transaction/tests.rs index 6d1e6a9697..7c5b60a02d 100644 --- a/crates/zakura-consensus/src/transaction/tests.rs +++ b/crates/zakura-consensus/src/transaction/tests.rs @@ -54,7 +54,7 @@ use super::{check, Request, Verifier}; #[cfg(test)] mod prop; -#[cfg(test)] +#[cfg(all(test, zcash_unstable = "nutachyon"))] mod tachyon; /// Returns the timeout duration for tests, extended when running under coverage diff --git a/crates/zakura-consensus/src/transaction/tests/prop.rs b/crates/zakura-consensus/src/transaction/tests/prop.rs index 5cf02a1cb2..10ae42fb7f 100644 --- a/crates/zakura-consensus/src/transaction/tests/prop.rs +++ b/crates/zakura-consensus/src/transaction/tests/prop.rs @@ -331,6 +331,7 @@ fn mock_transparent_transaction( ironwood_shielded_data: None, network_upgrade, }, + #[cfg(zcash_unstable = "nutachyon")] 7 => Transaction::V7 { inputs, outputs, @@ -371,6 +372,7 @@ fn sanitize_transaction_version( Nu5 | Nu6 | Nu6_1 | Nu6_2 => (4, 5), Nu6_3 => (4, 6), Nu7 => (5, 5), + #[cfg(zcash_unstable = "nutachyon")] NuTachyon => (5, 7), #[cfg(zcash_unstable = "zfuture")] @@ -384,6 +386,7 @@ fn sanitize_transaction_version( } #[test] +#[cfg(zcash_unstable = "nutachyon")] fn sanitize_transaction_version_handles_v6_and_v7_upgrades() { let network = zakura_chain::parameters::testnet::Parameters::build() .with_activation_heights( diff --git a/crates/zakura-network/src/constants.rs b/crates/zakura-network/src/constants.rs index 3d81dfdb38..0e766731e1 100644 --- a/crates/zakura-network/src/constants.rs +++ b/crates/zakura-network/src/constants.rs @@ -344,7 +344,10 @@ pub const TIMESTAMP_TRUNCATION_SECONDS: u32 = 30 * 60; // TODO: The NU7 and NuTachyon protocol versions are provisional. Update this constant and the // mapping in `Version::min_specified_for_upgrade` when their deployment ZIPs are published. +#[cfg(zcash_unstable = "nutachyon")] const CURRENT_NETWORK_PROTOCOL_VERSION_VALUE: u32 = 170_190; // NuTachyon (Mainnet + Testnet). +#[cfg(not(zcash_unstable = "nutachyon"))] +const CURRENT_NETWORK_PROTOCOL_VERSION_VALUE: u32 = 170_160; // NU6.3 (Mainnet + Testnet). /// The Zcash network protocol version implemented by this crate, and advertised /// during connection setup. diff --git a/crates/zakura-network/src/peer_set/initialize.rs b/crates/zakura-network/src/peer_set/initialize.rs index b829b83db3..77272cb1d2 100644 --- a/crates/zakura-network/src/peer_set/initialize.rs +++ b/crates/zakura-network/src/peer_set/initialize.rs @@ -1280,10 +1280,13 @@ where // not invent local budget on failure. let restore_replenishment_demand = remaining_replenishment_demand.load(Ordering::Relaxed) > 0; - let _ = remaining_replenishment_demand.try_update( + // Rust 1.97 replaces this API with `try_update`. + // Zakura supports Rust 1.91. + #[allow(deprecated)] + let _ = remaining_replenishment_demand.fetch_update( Ordering::Relaxed, Ordering::Relaxed, - |demand: usize| Some(demand.saturating_sub(1)), + |demand| Some(demand.saturating_sub(1)), ); if active_outbound_connections.update_count() diff --git a/crates/zakura-network/src/protocol/external/types.rs b/crates/zakura-network/src/protocol/external/types.rs index 5077ce03b7..b9ee01d6b6 100644 --- a/crates/zakura-network/src/protocol/external/types.rs +++ b/crates/zakura-network/src/protocol/external/types.rs @@ -128,9 +128,11 @@ impl Version { (Mainnet, Nu6_3) => 170_160, (Testnet(params), Nu7) if params.is_default_testnet() || params.is_regtest() => 170_170, (Mainnet, Nu7) => 170_180, + #[cfg(zcash_unstable = "nutachyon")] (Testnet(params), NuTachyon) if params.is_default_testnet() || params.is_regtest() => { 170_190 } + #[cfg(zcash_unstable = "nutachyon")] (Mainnet, NuTachyon) => 170_190, // It should be fine to reject peers with earlier network protocol versions on custom testnets for now. @@ -223,6 +225,13 @@ mod test { let _init_guard = zakura_test::init(); let highest_network_upgrade = NetworkUpgrade::current(network, block::Height::MAX); + #[cfg(not(zcash_unstable = "nutachyon"))] + assert!( + matches!(highest_network_upgrade, Nu6 | Nu6_1 | Nu6_2 | Nu6_3 | Nu7), + "expected coverage of all network upgrades: \ + add the new network upgrade to the list in this test" + ); + #[cfg(zcash_unstable = "nutachyon")] assert!( matches!( highest_network_upgrade, @@ -245,6 +254,7 @@ mod test { Nu6_2, Nu6_3, Nu7, + #[cfg(zcash_unstable = "nutachyon")] NuTachyon, ] { let height = network_upgrade.activation_height(network); @@ -258,6 +268,7 @@ mod test { } #[test] + #[cfg(zcash_unstable = "nutachyon")] fn nu_tachyon_protocol_versions_match_current_version() { let _init_guard = zakura_test::init(); @@ -278,6 +289,21 @@ mod test { ); } + #[test] + #[cfg(not(zcash_unstable = "nutachyon"))] + fn nu63_protocol_versions_match_current_version() { + let _init_guard = zakura_test::init(); + + assert_eq!( + Version::min_specified_for_upgrade(&Mainnet, Nu6_3), + CURRENT_NETWORK_PROTOCOL_VERSION + ); + assert_eq!( + Version::min_specified_for_upgrade(&Network::new_default_testnet(), Nu6_3), + CURRENT_NETWORK_PROTOCOL_VERSION + ); + } + #[test] fn peer_services_p2p_v2_bit_and_unknown_bits_are_compatible() { let _init_guard = zakura_test::init(); diff --git a/crates/zakura-network/src/zakura/block_sync/peer_registry.rs b/crates/zakura-network/src/zakura/block_sync/peer_registry.rs index 47ec6878db..b9cefd4d88 100644 --- a/crates/zakura-network/src/zakura/block_sync/peer_registry.rs +++ b/crates/zakura-network/src/zakura/block_sync/peer_registry.rs @@ -563,12 +563,15 @@ impl PeerRegistry { return SessionAdmission::Parked; } + // Rust 1.97 replaces this API with `try_update`. + // Zakura supports Rust 1.91. + #[allow(deprecated)] let generation = self .next_generation - .try_update( + .fetch_update( std::sync::atomic::Ordering::Relaxed, std::sync::atomic::Ordering::Relaxed, - |generation: u64| generation.checked_add(1), + |generation| generation.checked_add(1), ) .unwrap_or_else(|_| panic!("block-sync routine generation counter is exhausted")); peers diff --git a/crates/zakura-network/src/zakura/block_sync/tests.rs b/crates/zakura-network/src/zakura/block_sync/tests.rs index f2b9830de0..ae305c543d 100644 --- a/crates/zakura-network/src/zakura/block_sync/tests.rs +++ b/crates/zakura-network/src/zakura/block_sync/tests.rs @@ -107,7 +107,9 @@ fn fake_block_at_height(template: &Arc, height: block::Height) -> | Transaction::V3 { inputs, .. } | Transaction::V4 { inputs, .. } | Transaction::V5 { inputs, .. } => &mut inputs[0], - Transaction::V6 { inputs, .. } | Transaction::V7 { inputs, .. } => &mut inputs[0], + Transaction::V6 { inputs, .. } => &mut inputs[0], + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { inputs, .. } => &mut inputs[0], }; match input { transparent::Input::Coinbase { diff --git a/crates/zakura-network/src/zakura/testkit/cluster.rs b/crates/zakura-network/src/zakura/testkit/cluster.rs index ebb37d1b8d..a9c8440bc7 100644 --- a/crates/zakura-network/src/zakura/testkit/cluster.rs +++ b/crates/zakura-network/src/zakura/testkit/cluster.rs @@ -925,6 +925,7 @@ mod tests { nu6_2: None, nu6_3: None, nu7: None, + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, #[cfg(zcash_unstable = "zfuture")] zfuture: None, diff --git a/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs b/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs index 5bf61b7709..bdfd2fef69 100644 --- a/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs +++ b/crates/zakura-network/src/zakura/testkit/mock_blocksync.rs @@ -738,7 +738,9 @@ fn synthetic_block_at_height( | Transaction::V3 { inputs, .. } | Transaction::V4 { inputs, .. } | Transaction::V5 { inputs, .. } => &mut inputs[0], - Transaction::V6 { inputs, .. } | Transaction::V7 { inputs, .. } => &mut inputs[0], + Transaction::V6 { inputs, .. } => &mut inputs[0], + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { inputs, .. } => &mut inputs[0], }; match input { transparent::Input::Coinbase { diff --git a/crates/zakura-rpc/Cargo.toml b/crates/zakura-rpc/Cargo.toml index e31dcff4f1..18f00a5208 100644 --- a/crates/zakura-rpc/Cargo.toml +++ b/crates/zakura-rpc/Cargo.toml @@ -107,7 +107,6 @@ zcash_primitives = { workspace = true, features = ["transparent-inputs", "non-st zcash_protocol = { workspace = true } zcash_script = { workspace = true } zcash_transparent = { workspace = true } -zcash_tachyon = { workspace = true } zakura-chain = { path = "../zakura-chain", version = "6.0.0", features = [ "json-conversion", @@ -124,6 +123,12 @@ tokio-rustls = { version = "0.26.4", default-features = false, features = ["logg # Only used to read the validity dates of the configured RPC TLS certificates. der = "0.7.10" +[target.'cfg(zcash_unstable = "nutachyon")'.dependencies] +zcash_tachyon = { workspace = true } + +[target.'cfg(zcash_unstable = "nutachyon")'.dev-dependencies] +ragu = { workspace = true } + [dev-dependencies] blake2b_simd = { workspace = true } bytes = { workspace = true } @@ -134,7 +139,6 @@ tempfile = { workspace = true } toml = { workspace = true } proptest = { workspace = true } -ragu = { workspace = true } tokio = { workspace = true, features = ["full", "tracing", "test-util"] } diff --git a/crates/zakura-rpc/src/methods.rs b/crates/zakura-rpc/src/methods.rs index 4f7684b4ad..9beae0cec6 100644 --- a/crates/zakura-rpc/src/methods.rs +++ b/crates/zakura-rpc/src/methods.rs @@ -2730,6 +2730,7 @@ where #[cfg(not(test))] let selected_txs = mempool_txs; + #[cfg(zcash_unstable = "nutachyon")] let selected_txs = types::get_block_template::tachyon::aggregate_transactions( self.network.clone(), height, diff --git a/crates/zakura-rpc/src/methods/tests/utils.rs b/crates/zakura-rpc/src/methods/tests/utils.rs index 69b149350a..26ae2c7af4 100644 --- a/crates/zakura-rpc/src/methods/tests/utils.rs +++ b/crates/zakura-rpc/src/methods/tests/utils.rs @@ -22,6 +22,7 @@ pub fn fake_history_tree(network: &Network) -> Arc { &first_sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .unwrap(); diff --git a/crates/zakura-rpc/src/methods/types/get_block_template.rs b/crates/zakura-rpc/src/methods/types/get_block_template.rs index 97082d88ea..13ba7fcd15 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template.rs @@ -3,6 +3,7 @@ pub mod constants; pub mod parameters; pub mod proposal; +#[cfg(zcash_unstable = "nutachyon")] pub mod tachyon; pub mod zip317; diff --git a/crates/zakura-state/Cargo.toml b/crates/zakura-state/Cargo.toml index f6a20a828a..4fdcf81505 100644 --- a/crates/zakura-state/Cargo.toml +++ b/crates/zakura-state/Cargo.toml @@ -72,7 +72,6 @@ tower = { workspace = true, features = ["buffer", "util"] } tracing = { workspace = true } sapling-crypto = { workspace = true } -zcash_tachyon = { workspace = true } zakura-assets = { workspace = true } zakura-node-services = { path = "../zakura-node-services", version = "3.2.1" } @@ -88,6 +87,9 @@ proptest-derive = { workspace = true, optional = true } derive-getters.workspace = true derive-new.workspace = true +[target.'cfg(zcash_unstable = "nutachyon")'.dependencies] +zcash_tachyon = { workspace = true } + [dev-dependencies] zakura-header-chain = { path = "../zakura-header-chain", version = "1.0.0", features = ["test-support"] } color-eyre = { workspace = true } diff --git a/crates/zakura-state/src/constants.rs b/crates/zakura-state/src/constants.rs index 377073d7cd..b65aa3607b 100644 --- a/crates/zakura-state/src/constants.rs +++ b/crates/zakura-state/src/constants.rs @@ -95,6 +95,9 @@ pub const MAX_PRUNE_HEIGHTS_PER_COMMIT: u32 = 100; /// /// Instead of using this constant directly, use [`constants::state_database_format_version_in_code()`] /// or [`config::database_format_version_on_disk()`] to get the full semantic format version. +#[cfg(not(zcash_unstable = "nutachyon"))] +const DATABASE_FORMAT_VERSION: u64 = 28; +#[cfg(zcash_unstable = "nutachyon")] const DATABASE_FORMAT_VERSION: u64 = 29; /// The database format minor version, incremented each time the on-disk database format has a @@ -110,10 +113,16 @@ const DATABASE_FORMAT_VERSION: u64 = 29; /// balance from 48 to 56 bytes, and widens history entries for the V4 history-node fields. /// Existing value-balance and history rows remain readable, so version 29 can reuse a version /// 28 database without a row migration. +#[cfg(not(zcash_unstable = "nutachyon"))] +const DATABASE_FORMAT_MINOR_VERSION: u64 = 1; +#[cfg(zcash_unstable = "nutachyon")] const DATABASE_FORMAT_MINOR_VERSION: u64 = 0; /// The database format patch version, incremented each time the on-disk database format has a /// significant format compatibility fix. +#[cfg(not(zcash_unstable = "nutachyon"))] +const DATABASE_FORMAT_PATCH_VERSION: u64 = 5; +#[cfg(zcash_unstable = "nutachyon")] const DATABASE_FORMAT_PATCH_VERSION: u64 = 0; /// Returns the full semantic version of the currently running state database format code. diff --git a/crates/zakura-state/src/error.rs b/crates/zakura-state/src/error.rs index 477695417c..48e1d09e48 100644 --- a/crates/zakura-state/src/error.rs +++ b/crates/zakura-state/src/error.rs @@ -768,6 +768,7 @@ pub enum ValidateContextError { NoteCommitmentTreeError(#[from] zakura_chain::parallel::tree::NoteCommitmentTreeError), #[error("error advancing the Tachyon anchor: {0}")] + #[cfg(zcash_unstable = "nutachyon")] TachyonAnchorError(#[source] Arc), #[error("error building the history tree: {0}")] @@ -825,6 +826,7 @@ pub enum ValidateContextError { }, #[error("unknown Tachyon anchor {anchor:?}")] + #[cfg(zcash_unstable = "nutachyon")] #[non_exhaustive] UnknownTachyonAnchor { anchor: zakura_chain::tachyon::Anchor, @@ -834,6 +836,7 @@ pub enum ValidateContextError { }, #[error("duplicate Tachyon Tachygram {tachygram:?}, in finalized state: {in_finalized_state}")] + #[cfg(zcash_unstable = "nutachyon")] #[non_exhaustive] DuplicateTachyonTachygram { tachygram: zakura_chain::tachyon::Tachygram, @@ -841,6 +844,7 @@ pub enum ValidateContextError { }, } +#[cfg(zcash_unstable = "nutachyon")] impl From for ValidateContextError { fn from(error: zcash_tachyon::AnchorError) -> Self { Self::TachyonAnchorError(Arc::new(error)) @@ -875,10 +879,13 @@ impl ValidateContextError { Self::VctSproutHandoffRootMismatch { .. } | Self::CumulativeWorkOverflow { .. } | Self::NoteCommitmentTreeError(_) - | Self::TachyonAnchorError(_) | Self::HistoryTreeError(_) => { BodyVerificationClass::Retryable(TransientBodyFailureKind::Storage) } + #[cfg(zcash_unstable = "nutachyon")] + Self::TachyonAnchorError(_) => { + BodyVerificationClass::Retryable(TransientBodyFailureKind::Storage) + } Self::InvalidBlockCommitment(error) => { let kind = match error { CommitmentError::InvalidAuthDataRoot { .. } => BodyCommitmentKind::AuthDataRoot, @@ -968,7 +975,9 @@ impl ValidateContextError { Self::UnknownSaplingAnchor { .. } => consensus("context.unknown_sapling_anchor"), Self::UnknownOrchardAnchor { .. } => consensus("context.unknown_orchard_anchor"), Self::UnknownIronwoodAnchor { .. } => consensus("context.unknown_ironwood_anchor"), + #[cfg(zcash_unstable = "nutachyon")] Self::UnknownTachyonAnchor { .. } => consensus("context.unknown_tachyon_anchor"), + #[cfg(zcash_unstable = "nutachyon")] Self::DuplicateTachyonTachygram { .. } => { consensus("context.duplicate_tachyon_tachygram") } @@ -994,7 +1003,6 @@ impl ValidateContextError { | ValidateContextError::DuplicateSaplingNullifier { .. } | ValidateContextError::DuplicateOrchardNullifier { .. } | ValidateContextError::DuplicateIronwoodNullifier { .. } - | ValidateContextError::DuplicateTachyonTachygram { .. } | ValidateContextError::NegativeRemainingTransactionValue { .. } | ValidateContextError::AddValuePool { .. } | ValidateContextError::InvalidBlockCommitment(_) @@ -1002,6 +1010,9 @@ impl ValidateContextError { | ValidateContextError::UnknownSaplingAnchor { .. } | ValidateContextError::UnknownOrchardAnchor { .. } | ValidateContextError::UnknownIronwoodAnchor { .. } => 100, + #[cfg(zcash_unstable = "nutachyon")] + ValidateContextError::DuplicateTachyonTachygram { .. } => 100, + #[cfg(zcash_unstable = "nutachyon")] ValidateContextError::UnknownTachyonAnchor { .. } => 100, // Residual arithmetic failures in our own value summation, not @@ -1032,8 +1043,9 @@ impl ValidateContextError { | ValidateContextError::CumulativeWorkOverflow { .. } | ValidateContextError::OrphanedBlock { .. } | ValidateContextError::NoteCommitmentTreeError(_) - | ValidateContextError::TachyonAnchorError(_) | ValidateContextError::HistoryTreeError(_) => 0, + #[cfg(zcash_unstable = "nutachyon")] + ValidateContextError::TachyonAnchorError(_) => 0, } } diff --git a/crates/zakura-state/src/lib.rs b/crates/zakura-state/src/lib.rs index dba706ecb8..d30177183f 100644 --- a/crates/zakura-state/src/lib.rs +++ b/crates/zakura-state/src/lib.rs @@ -64,9 +64,11 @@ pub use request::{ #[cfg(feature = "indexer")] pub use request::Spend; +#[cfg(zcash_unstable = "nutachyon")] +pub use response::TachyonMiningData; pub use response::{ AnyTx, BlockSyncBodyMetadata, GetBlockTemplateChainInfo, KnownBlock, MinedTx, - NonFinalizedBlocksListener, ReadResponse, Response, TachyonMiningData, + NonFinalizedBlocksListener, ReadResponse, Response, }; #[cfg(any(test, feature = "header-fuzz"))] pub use service::finalized_state::{replay_recovery_rows_bytes, RecoveryRowsReplaySummary}; diff --git a/crates/zakura-state/src/request.rs b/crates/zakura-state/src/request.rs index 6305d0ea27..79abbe2df0 100644 --- a/crates/zakura-state/src/request.rs +++ b/crates/zakura-state/src/request.rs @@ -11,6 +11,8 @@ use std::{ }; use tower::{BoxError, Service, ServiceExt}; +#[cfg(zcash_unstable = "nutachyon")] +use zakura_chain::tachyon; use zakura_chain::{ amount::{DeferredPoolBalanceChange, NegativeAllowed}, block::{ @@ -26,7 +28,6 @@ use zakura_chain::{ serialization::SerializationError, sprout, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeIndex}, - tachyon, transaction::{self, UnminedTx}, transparent, value_balance::{ValueBalance, ValueBalanceError}, @@ -371,8 +372,10 @@ impl Treestate { sapling_subtree: Option>, orchard_subtree: Option>, ironwood_subtree: Option>, - tachyon_anchor: zakura_chain::tachyon::Anchor, - tachyon_epoch_anchor: Option, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: zakura_chain::tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_epoch_anchor: Option< + zakura_chain::tachyon::Anchor, + >, history_tree: Arc, ) -> Self { Self { @@ -384,7 +387,9 @@ impl Treestate { orchard_subtree, ironwood, ironwood_subtree, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_epoch_anchor, }, history_tree, @@ -1471,6 +1476,7 @@ pub enum ReadRequest { /// tip. Unknown anchors are omitted. For every epoch represented by a known anchor, the /// response also contains the blocks between the earliest and latest requested anchors and /// any requested tachygrams already revealed within the candidate's two-epoch window. + #[cfg(zcash_unstable = "nutachyon")] TachyonMiningData { /// Anchors referenced by the selected autonome transactions. anchors: HashSet, @@ -1958,6 +1964,7 @@ impl ReadRequest { ReadRequest::Tip => "tip", ReadRequest::FinalizedTip => "finalized_tip", ReadRequest::TipPoolValues => "tip_pool_values", + #[cfg(zcash_unstable = "nutachyon")] ReadRequest::TachyonMiningData { .. } => "tachyon_mining_data", ReadRequest::BlockInfo(_) => "block_info", ReadRequest::Depth(_) => "depth", diff --git a/crates/zakura-state/src/response.rs b/crates/zakura-state/src/response.rs index 52ab0844d2..46371db069 100644 --- a/crates/zakura-state/src/response.rs +++ b/crates/zakura-state/src/response.rs @@ -1,12 +1,16 @@ //! State [`tower::Service`] response types. use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{BTreeMap, HashSet}, sync::Arc, }; use chrono::{DateTime, Utc}; +#[cfg(zcash_unstable = "nutachyon")] +use std::collections::HashMap; +#[cfg(zcash_unstable = "nutachyon")] +use zakura_chain::tachyon; use zakura_chain::{ amount::{Amount, NonNegative}, block::{self, Block, ChainHistoryMmrRootHash}, @@ -14,7 +18,6 @@ use zakura_chain::{ ironwood, orchard, sapling, serialization::DateTime32, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, - tachyon, transaction::{self, Transaction}, transparent, value_balance::ValueBalance, @@ -38,6 +41,7 @@ mod tests; /// Best-chain data used to aggregate selected autonome Tachyon transactions. #[derive(Clone, Debug, PartialEq, Eq)] +#[cfg(zcash_unstable = "nutachyon")] pub struct TachyonMiningData { /// Heights that created the requested anchors. Unknown anchors are omitted. pub anchor_heights: HashMap, @@ -443,6 +447,7 @@ pub enum ReadResponse { /// Response to [`ReadRequest::TachyonMiningData`], or `None` when the requested chain tip is /// no longer current. + #[cfg(zcash_unstable = "nutachyon")] TachyonMiningData(Option), /// Response to [`ReadRequest::BlockInfo`] with @@ -719,7 +724,6 @@ impl TryFrom for Response { | ReadResponse::PruningInfo { .. } | ReadResponse::BlockRoots(_) | ReadResponse::TipPoolValues { .. } - | ReadResponse::TachyonMiningData(_) | ReadResponse::BlockInfo(_) | ReadResponse::TransactionIdsForBlock(_) | ReadResponse::AnyChainTransactionIdsForBlock(_) @@ -748,6 +752,11 @@ impl TryFrom for Response { Err("there is no corresponding Response for this ReadResponse") } + #[cfg(zcash_unstable = "nutachyon")] + ReadResponse::TachyonMiningData(_) => { + Err("there is no corresponding Response for this ReadResponse") + } + #[cfg(feature = "indexer")] ReadResponse::TransactionId(_) | ReadResponse::RawBlocks(_) => { Err("there is no corresponding Response for this ReadResponse") diff --git a/crates/zakura-state/src/service.rs b/crates/zakura-state/src/service.rs index f0826c9f57..04c5622be0 100644 --- a/crates/zakura-state/src/service.rs +++ b/crates/zakura-state/src/service.rs @@ -2499,6 +2499,7 @@ impl Service for ReadStateService { }) } + #[cfg(zcash_unstable = "nutachyon")] ReadRequest::TachyonMiningData { anchors, tachygrams, diff --git a/crates/zakura-state/src/service/arbitrary.rs b/crates/zakura-state/src/service/arbitrary.rs index 57bd272151..721ac8a4e1 100644 --- a/crates/zakura-state/src/service/arbitrary.rs +++ b/crates/zakura-state/src/service/arbitrary.rs @@ -175,6 +175,7 @@ impl Strategy for PreparedChain { &Default::default(), &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("history tree should be created"); diff --git a/crates/zakura-state/src/service/check/anchors.rs b/crates/zakura-state/src/service/check/anchors.rs index a48ffea6f2..7954bc0522 100644 --- a/crates/zakura-state/src/service/check/anchors.rs +++ b/crates/zakura-state/src/service/check/anchors.rs @@ -154,6 +154,7 @@ fn sapling_orchard_ironwood_anchors_refer_to_final_treestates( ); } + #[cfg(zcash_unstable = "nutachyon")] if let Some(tachyon_shielded_data) = transaction.tachyon_shielded_data() { if let zcash_tachyon::TachyonBundle::Proven(bundle) = &tachyon_shielded_data.0 { let anchor = zakura_chain::tachyon::Anchor::from(bundle.stamp.anchor); diff --git a/crates/zakura-state/src/service/check/nullifier.rs b/crates/zakura-state/src/service/check/nullifier.rs index 9bf3bcb572..0fe34f5729 100644 --- a/crates/zakura-state/src/service/check/nullifier.rs +++ b/crates/zakura-state/src/service/check/nullifier.rs @@ -62,7 +62,9 @@ pub(crate) fn no_duplicates_in_finalized_chain( } } + #[cfg(zcash_unstable = "nutachyon")] let network = finalized_state.network(); + #[cfg(zcash_unstable = "nutachyon")] for transaction in &semantically_verified.block.transactions { for tachygram in transaction.tachyon_tachygrams() { if finalized_state diff --git a/crates/zakura-state/src/service/check/tests/vectors.rs b/crates/zakura-state/src/service/check/tests/vectors.rs index be207f3365..9293d1d266 100644 --- a/crates/zakura-state/src/service/check/tests/vectors.rs +++ b/crates/zakura-state/src/service/check/tests/vectors.rs @@ -89,6 +89,7 @@ fn block_commitment_uses_the_precomputed_auth_data_root() { &sapling_root, &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("NU5 parent builds a history tree"); diff --git a/crates/zakura-state/src/service/finalized_state.rs b/crates/zakura-state/src/service/finalized_state.rs index 0605bf36b9..00822e4d26 100644 --- a/crates/zakura-state/src/service/finalized_state.rs +++ b/crates/zakura-state/src/service/finalized_state.rs @@ -214,9 +214,13 @@ pub const STATE_COLUMN_FAMILIES_IN_CODE: &[&str] = &[ "ironwood_anchors", "ironwood_note_commitment_tree", "ironwood_note_commitment_subtree", + #[cfg(zcash_unstable = "nutachyon")] "tachyon_anchors", + #[cfg(zcash_unstable = "nutachyon")] "tachyon_anchor_by_height", + #[cfg(zcash_unstable = "nutachyon")] "tachyon_epoch_anchor_by_epoch", + #[cfg(zcash_unstable = "nutachyon")] "tachyon_tachygrams", // Chain "history_tree", @@ -903,6 +907,7 @@ impl FinalizedState { // The roots-only VCT payload does not carry Tachyon anchors or transaction // counts, so NuTachyon blocks must use the body-derived commitment path. + #[cfg(zcash_unstable = "nutachyon")] let vct_roots = vct_roots.filter(|_| { NetworkUpgrade::current(&network, height) < NetworkUpgrade::NuTachyon }); @@ -1038,6 +1043,7 @@ impl FinalizedState { commitment_aux_verify::verify_commitment_roots( &network, (*history_tree).clone(), + #[cfg(zcash_unstable = "nutachyon")] prev_note_commitment_trees.tachyon_anchor, verification_items, ) @@ -1182,7 +1188,9 @@ impl FinalizedState { orchard_subtree: None, ironwood: ironwood_frontier, ironwood_subtree: None, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: note_commitment_trees.tachyon_anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_epoch_anchor: None, }; @@ -1269,6 +1277,7 @@ impl FinalizedState { commitment_result.expect("scope has already finished")?; // Update the history tree (depends on both operations above). + #[cfg(zcash_unstable = "nutachyon")] if let Some(pool_height) = zakura_chain::tachyon::pool_height(&network, checkpoint_verified.height) { @@ -1284,6 +1293,7 @@ impl FinalizedState { let sapling_root = note_commitment_trees.sapling.root(); let orchard_root = note_commitment_trees.orchard.root(); let ironwood_root = note_commitment_trees.ironwood.root(); + #[cfg(zcash_unstable = "nutachyon")] let tachyon_anchor = note_commitment_trees.tachyon_anchor; history_tree_mut .push( @@ -1292,6 +1302,7 @@ impl FinalizedState { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &tachyon_anchor, ) .map_err(Arc::new) diff --git a/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs b/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs index 83a97ac3bb..354163c331 100644 --- a/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs +++ b/crates/zakura-state/src/service/finalized_state/commitment_aux_verify.rs @@ -246,7 +246,7 @@ pub(crate) fn verify_supplied_ironwood_root_below_nu6_3( pub(crate) fn verify_commitment_roots( network: &Network, mut history_tree: HistoryTree, - mut tachyon_anchor: zakura_chain::tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] mut tachyon_anchor: zakura_chain::tachyon::Anchor, blocks_to_verify: I, ) -> Result where @@ -296,6 +296,7 @@ where auth_data_root, ) .map_err(|error| match error { + #[cfg(zcash_unstable = "nutachyon")] SuppliedRootsError::TachyonDataUnavailable => { ValidateContextError::HistoryTreeError(Arc::new( zakura_chain::history_tree::HistoryTreeError::InvalidCachedTree { @@ -336,6 +337,7 @@ where verify_supplied_ironwood_root_below_nu6_3(network, height, &ironwood_root) .map_err(|error| CommitmentRootVerificationError::CurrentRoots { height, error })?; + #[cfg(zcash_unstable = "nutachyon")] if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, height) { tachyon_anchor = tachyon_anchor .advance_with_block(pool_height, &block) @@ -353,6 +355,7 @@ where &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &tachyon_anchor, ) .map_err(Arc::new) @@ -390,6 +393,7 @@ mod tests { &Default::default(), &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("empty history tree for a pre-Heartwood block") @@ -726,8 +730,14 @@ mod tests { Some(next_block.auth_data_root()), ), ]; - verify_commitment_roots(&Mainnet, empty_history_tree(), Default::default(), ok_items) - .expect("real roots verify against the headers"); + verify_commitment_roots( + &Mainnet, + empty_history_tree(), + #[cfg(zcash_unstable = "nutachyon")] + Default::default(), + ok_items, + ) + .expect("real roots verify against the headers"); // Negative + lag: a wrong root at the activation height (here, the next // block's root, which is a valid but different root) is only caught when the @@ -744,6 +754,7 @@ mod tests { let failure = verify_commitment_roots( &Mainnet, empty_history_tree(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), bad_items, ) @@ -853,8 +864,14 @@ mod tests { let items: Vec<_> = (start..=end + 1).map(item_at).collect(); // Positive: every supplied root in the range is confirmed by the V2 headers. - verify_commitment_roots(&Mainnet, seed.clone(), Default::default(), items.clone()) - .expect("real NU5 roots verify against the headers"); + verify_commitment_roots( + &Mainnet, + seed.clone(), + #[cfg(zcash_unstable = "nutachyon")] + Default::default(), + items.clone(), + ) + .expect("real NU5 roots verify against the headers"); eprintln!("VCT NU5 positive: {} blocks verified", items.len()); // Negative + lag: corrupt one root mid-range with a distinct valid root (the @@ -877,8 +894,14 @@ mod tests { .as_mut() .expect("test verification item has roots") .0 = wrong_root; - let failure = verify_commitment_roots(&Mainnet, seed, Default::default(), bad_items) - .expect_err("a wrong NU5 root must be rejected"); + let failure = verify_commitment_roots( + &Mainnet, + seed, + #[cfg(zcash_unstable = "nutachyon")] + Default::default(), + bad_items, + ) + .expect_err("a wrong NU5 root must be rejected"); assert_eq!( failure.height().0, bad_height + 1, @@ -1089,14 +1112,20 @@ mod tests { .0; for &(start, end) in ranges.iter().filter(|(start, _)| *start < heartwood) { let items = (start..=end).map(|height| verification_item_at(Height(height))); - verify_commitment_roots(&Mainnet, empty_history_tree(), Default::default(), items) - .unwrap_or_else(|failure| { - panic!( - "pre-Heartwood roots failed at {:?}: {}", - failure.height(), - failure.error() - ) - }); + verify_commitment_roots( + &Mainnet, + empty_history_tree(), + #[cfg(zcash_unstable = "nutachyon")] + Default::default(), + items, + ) + .unwrap_or_else(|failure| { + panic!( + "pre-Heartwood roots failed at {:?}: {}", + failure.height(), + failure.error() + ) + }); eprintln!("validated direct pre-Heartwood commitments for {start}..={end}"); } @@ -1132,6 +1161,7 @@ mod tests { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("network-upgrade activation starts a history-tree epoch"); @@ -1141,15 +1171,21 @@ mod tests { let items = (activation + 1..=confirm_end).map(|height| verification_item_at(Height(height))); - verify_commitment_roots(&Mainnet, history_tree, Default::default(), items) - .unwrap_or_else(|failure| { - panic!( - "{upgrade:?} MMR linkage failed at {:?} while validating through \ + verify_commitment_roots( + &Mainnet, + history_tree, + #[cfg(zcash_unstable = "nutachyon")] + Default::default(), + items, + ) + .unwrap_or_else(|failure| { + panic!( + "{upgrade:?} MMR linkage failed at {:?} while validating through \ {confirm_end}: {}", - failure.height(), - failure.error() - ) - }); + failure.height(), + failure.error() + ) + }); eprintln!( "validated {upgrade:?} MMR linkage for {activation}..={end} \ (confirmed by {confirm_end})" diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs b/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs index 96ca1a4290..00ee22145c 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/chain.rs @@ -58,6 +58,9 @@ pub enum HistoryTreeDecodeError { } impl IntoDisk for ValueBalance { + #[cfg(not(zcash_unstable = "nutachyon"))] + type Bytes = [u8; 48]; + #[cfg(zcash_unstable = "nutachyon")] type Bytes = [u8; 56]; fn as_bytes(&self) -> Self::Bytes { @@ -263,10 +266,12 @@ impl FromDisk for BlockInfo { // We have different DB formats for NU6.1, Ironwood, and NuTachyon onward. const NU6_1_VALUE_BALANCE_LEN: usize = 40; const IRONWOOD_VALUE_BALANCE_LEN: usize = 48; + #[cfg(zcash_unstable = "nutachyon")] const TACHYON_VALUE_BALANCE_LEN: usize = 56; const BLOCK_SIZE_LEN: usize = 4; const NU6_1_BLOCK_INFO_LEN: usize = NU6_1_VALUE_BALANCE_LEN + BLOCK_SIZE_LEN; const IRONWOOD_BLOCK_INFO_LEN: usize = IRONWOOD_VALUE_BALANCE_LEN + BLOCK_SIZE_LEN; + #[cfg(zcash_unstable = "nutachyon")] const TACHYON_BLOCK_INFO_LEN: usize = TACHYON_VALUE_BALANCE_LEN + BLOCK_SIZE_LEN; let bytes = bytes.as_ref(); @@ -274,6 +279,7 @@ impl FromDisk for BlockInfo { // We want to be forward-compatible, so this must work even if the // size of the buffer is larger than expected. match bytes.len() { + #[cfg(zcash_unstable = "nutachyon")] TACHYON_BLOCK_INFO_LEN.. => { let value_pools = ValueBalance::::from_bytes(&bytes[..TACHYON_VALUE_BALANCE_LEN]) diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs b/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs index e95525112e..103ba19953 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/chain/tests.rs @@ -40,10 +40,12 @@ fn valid_history_tree() -> NonEmptyHistoryTree { sapling_root: &sapling_root, orchard_root: &orchard_root, ironwood_root: &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: &Default::default(), sapling_tx: 0, orchard_tx: 0, ironwood_tx: 0, + #[cfg(zcash_unstable = "nutachyon")] tachyon_tx: 0, }, ) diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs b/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs index 3870308147..38dfa64eff 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/shielded.rs @@ -7,11 +7,12 @@ use bincode::Options; +#[cfg(zcash_unstable = "nutachyon")] +use zakura_chain::tachyon; use zakura_chain::{ block::{merkle::AuthDataRoot, Height}, ironwood, orchard, sapling, sprout, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, - tachyon, }; use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk}; @@ -88,6 +89,7 @@ impl FromDisk for orchard::tree::Root { } } +#[cfg(zcash_unstable = "nutachyon")] impl IntoDisk for tachyon::Anchor { type Bytes = [u8; 32]; @@ -96,6 +98,7 @@ impl IntoDisk for tachyon::Anchor { } } +#[cfg(zcash_unstable = "nutachyon")] impl FromDisk for tachyon::Anchor { fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { let bytes: [u8; 32] = bytes @@ -106,6 +109,7 @@ impl FromDisk for tachyon::Anchor { } } +#[cfg(zcash_unstable = "nutachyon")] impl IntoDisk for tachyon::Tachygram { type Bytes = [u8; 32]; @@ -114,6 +118,7 @@ impl IntoDisk for tachyon::Tachygram { } } +#[cfg(zcash_unstable = "nutachyon")] impl FromDisk for tachyon::Tachygram { fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { let bytes: [u8; 32] = bytes @@ -126,8 +131,10 @@ impl FromDisk for tachyon::Tachygram { /// An on-disk Tachyon epoch index, encoded big-endian for sort order. #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg(zcash_unstable = "nutachyon")] pub struct TachyonEpoch(pub u32); +#[cfg(zcash_unstable = "nutachyon")] impl IntoDisk for TachyonEpoch { type Bytes = [u8; 4]; @@ -136,6 +143,7 @@ impl IntoDisk for TachyonEpoch { } } +#[cfg(zcash_unstable = "nutachyon")] impl FromDisk for TachyonEpoch { fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { let bytes: [u8; 4] = bytes diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs b/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs index 7147444b1e..ad3c933b94 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/tests/prop.rs @@ -576,6 +576,7 @@ fn roundtrip_value_balance() { } #[test] +#[cfg(zcash_unstable = "nutachyon")] fn roundtrip_block_info_with_tachyon_value_pool() { let _init_guard = zakura_test::init(); @@ -604,6 +605,7 @@ fn block_info_decodes_ironwood_value_pools() { let block_info = BlockInfo::from_bytes(ironwood_bytes); assert_eq!(block_info.value_pools().ironwood_amount(), ironwood_amount); + #[cfg(zcash_unstable = "nutachyon")] assert_eq!( block_info.value_pools().tachyon_amount(), Amount::::zero() diff --git a/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs b/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs index b94b1e363c..34b92a560f 100644 --- a/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs +++ b/crates/zakura-state/src/service/finalized_state/disk_format/upgrade.rs @@ -111,8 +111,8 @@ fn format_upgrades( let min_version = move || min_version.clone().unwrap_or(Version::new(0, 0, 0)); // Note: Disk format upgrades must be run in order of database version. - ([ - Box::new(block_info_and_address_received::Upgrade), + vec![ + Box::new(block_info_and_address_received::Upgrade) as Box, Box::new(no_migration::NoMigration::new( "add pruning metadata column family", Version::new(27, 1, 0), @@ -144,17 +144,19 @@ fn format_upgrades( )), Box::new(drop_header_root_auth_frontier::Upgrade), Box::new(unauthenticated_commitment_roots::Upgrade), + #[cfg(zcash_unstable = "nutachyon")] Box::new(no_migration::NoMigration::new( "widen history tree entries for NuTachyon", Version::new(28, 2, 5), )), + #[cfg(zcash_unstable = "nutachyon")] Box::new(no_migration::NoMigration::new( "add Tachyon state and widen chain value balance and history entries", Version::new(29, 0, 0), )), - ] as [Box; 12]) - .into_iter() - .filter(move |upgrade| upgrade.version() > min_version()) + ] + .into_iter() + .filter(move |upgrade| upgrade.version() > min_version()) } /// Returns a list of all the major db format versions that can restored from the diff --git a/crates/zakura-state/src/service/finalized_state/tests/prop.rs b/crates/zakura-state/src/service/finalized_state/tests/prop.rs index 4d4aadb418..6bf942e27d 100644 --- a/crates/zakura-state/src/service/finalized_state/tests/prop.rs +++ b/crates/zakura-state/src/service/finalized_state/tests/prop.rs @@ -315,6 +315,7 @@ fn vct_generated_final_frontier_bytes_are_node_loader_compatible() -> Result<()> nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -431,6 +432,7 @@ fn all_upgrades_and_wrong_commitments_with_fake_activation_heights() -> Result<( nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -553,6 +555,7 @@ fn vct_fast_path_matches_legacy_and_rejects_wrong_roots() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -776,6 +779,7 @@ fn vct_frozen_frontier_hole_refuses_instead_of_recomputing() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -882,6 +886,7 @@ fn vct_retryable_root_miss_keeps_checkpoint_response_pending() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -984,6 +989,7 @@ fn vct_untrusted_source_defers_unverifiable_tip_root_until_successor() -> Result nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -1230,6 +1236,7 @@ fn vct_untrusted_source_bad_root_replacement_commits_same_height() -> Result<()> nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -1384,6 +1391,7 @@ fn vct_frozen_frontier_survives_reopen() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -1562,6 +1570,7 @@ fn vct_fast_sync_handoff_marks_database_and_resumes() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -2098,6 +2107,7 @@ fn vct_mode_switches_continue_from_safe_boundaries() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -2304,6 +2314,7 @@ fn vct_dedup_skips_redundant_check_and_guards_stale_cache() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -2577,6 +2588,7 @@ fn vct_clear_prevalidation_cache_disarms_skip_then_dedup_resumes() -> Result<()> nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -2686,6 +2698,7 @@ fn vct_db_produced_payload_round_trips_to_byte_identical_state() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -3084,6 +3097,7 @@ fn vct_untrusted_fixture_drives_byte_identical_state() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") @@ -3259,6 +3273,7 @@ fn vct_read_service_serves_or_refuses_absent_band_treestates() -> Result<()> { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("failed to set activation heights") diff --git a/crates/zakura-state/src/service/finalized_state/tests/rollback.rs b/crates/zakura-state/src/service/finalized_state/tests/rollback.rs index c15916126b..a004ba84e8 100644 --- a/crates/zakura-state/src/service/finalized_state/tests/rollback.rs +++ b/crates/zakura-state/src/service/finalized_state/tests/rollback.rs @@ -870,6 +870,7 @@ fn modern_rollback_network() -> Network { nu6_2: Some(10), nu6_3: Some(11), nu7: Some(12), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("configured activation heights are valid") diff --git a/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs b/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs index f67a945995..bc6eb5a0c0 100644 --- a/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs +++ b/crates/zakura-state/src/service/finalized_state/vct_fast_sync_fixture.rs @@ -125,6 +125,7 @@ impl VctFastSyncedChain { nu6_2: Some(47), nu6_3: Some(48), nu7: Some(50), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("the fixture activation heights are ordered") diff --git a/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs b/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs index 2210d3507b..355a6b899d 100644 --- a/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs +++ b/crates/zakura-state/src/service/finalized_state/zakura_db/rollback.rs @@ -240,6 +240,7 @@ pub enum RollbackFinalizedStateError { /// Rebuilding the Tachyon anchor failed. #[error("failed to rebuild the Tachyon anchor")] + #[cfg(zcash_unstable = "nutachyon")] TachyonAnchor(#[from] zcash_tachyon::AnchorError), /// Computing a block subsidy failed. @@ -610,7 +611,9 @@ fn rebuild_history_tree_from_upgrade_activation( let (block, sapling_root, orchard_root, ironwood_root) = history_rebuild_inputs_at_height(db, start_height)?; + #[cfg(zcash_unstable = "nutachyon")] let mut tachyon_anchor = zakura_chain::tachyon::Anchor::default(); + #[cfg(zcash_unstable = "nutachyon")] if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, start_height) { tachyon_anchor = tachyon_anchor .advance_with_block(pool_height, &block)? @@ -622,6 +625,7 @@ fn rebuild_history_tree_from_upgrade_activation( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &tachyon_anchor, )?; @@ -629,6 +633,7 @@ fn rebuild_history_tree_from_upgrade_activation( let (block, sapling_root, orchard_root, ironwood_root) = history_rebuild_inputs_at_height(db, height)?; + #[cfg(zcash_unstable = "nutachyon")] if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, height) { tachyon_anchor = tachyon_anchor .advance_with_block(pool_height, &block)? @@ -641,6 +646,7 @@ fn rebuild_history_tree_from_upgrade_activation( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &tachyon_anchor, )?; } @@ -708,6 +714,7 @@ fn rebuild_treestate_to_height( .ok_or(RollbackFinalizedStateError::MissingBlock { height })?; note_commitment_trees.update_trees_parallel(&block)?; + #[cfg(zcash_unstable = "nutachyon")] if let Some(pool_height) = zakura_chain::tachyon::pool_height(network, height) { let advance = note_commitment_trees .tachyon_anchor @@ -726,6 +733,7 @@ fn rebuild_treestate_to_height( &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] ¬e_commitment_trees.tachyon_anchor, )?; } diff --git a/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs b/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs index f3a747995c..841de476ea 100644 --- a/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs +++ b/crates/zakura-state/src/service/finalized_state/zakura_db/shielded.rs @@ -17,21 +17,24 @@ use std::{ sync::Arc, }; +#[cfg(zcash_unstable = "nutachyon")] +use zakura_chain::tachyon; use zakura_chain::{ block::Height, ironwood, orchard, parallel::{commitment_aux::BlockCommitmentRoots, tree::NoteCommitmentTrees}, sapling, sprout, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, - tachyon, transaction::Transaction, }; +#[cfg(zcash_unstable = "nutachyon")] +use crate::service::finalized_state::disk_format::shielded::TachyonEpoch; use crate::{ request::{FinalizedBlock, Treestate}, service::finalized_state::{ disk_db::{DiskWriteBatch, ReadDisk, WriteDisk}, - disk_format::{shielded::TachyonEpoch, RawBytes}, + disk_format::RawBytes, vct::VctWriteData, zakura_db::ZakuraDb, }, @@ -185,12 +188,14 @@ impl ZakuraDb { /// Returns the finalized height that created `tachyon_anchor` while it remains retained. #[allow(clippy::unwrap_in_result)] + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_anchor_height(&self, tachyon_anchor: &tachyon::Anchor) -> Option { let tachyon_anchors = self.db.cf_handle("tachyon_anchors").unwrap(); self.db.zs_get(&tachyon_anchors, tachyon_anchor) } /// Returns the Tachyon pool anchor after the finalized tip. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_anchor_for_tip(&self) -> tachyon::Anchor { let anchors_by_height = self.db.cf_handle("tachyon_anchor_by_height").unwrap(); self.db @@ -201,6 +206,7 @@ impl ZakuraDb { /// Returns the finalized height that revealed `tachygram` while it remains retained. #[allow(clippy::unwrap_in_result)] + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_tachygram_revealed_height( &self, tachygram: &tachyon::Tachygram, @@ -211,6 +217,7 @@ impl ZakuraDb { /// Returns the boundary anchor for a finalized Tachyon epoch. #[allow(clippy::unwrap_in_result)] + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_epoch_anchor(&self, epoch: u32) -> Option { let anchors = self.db.cf_handle("tachyon_epoch_anchor_by_epoch").unwrap(); self.db.zs_get(&anchors, &TachyonEpoch(epoch)) @@ -218,6 +225,7 @@ impl ZakuraDb { /// Returns the latest finalized Tachyon epoch. #[allow(clippy::unwrap_in_result)] + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_current_epoch(&self) -> Option { let anchors = self.db.cf_handle("tachyon_epoch_anchor_by_epoch").unwrap(); self.db @@ -759,7 +767,9 @@ impl ZakuraDb { orchard_subtree: self.orchard_subtree_for_tip(), ironwood: self.ironwood_tree_for_tip(), ironwood_subtree: self.ironwood_subtree_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: self.tachyon_anchor_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_epoch_anchor: None, }) } @@ -791,10 +801,12 @@ impl DiskWriteBatch { self.prepare_nullifier_batch(zakura_db, transaction); } + #[cfg(zcash_unstable = "nutachyon")] self.prepare_tachyon_tachygram_batch(zakura_db, finalized); } /// Stores this block's Tachygrams and prunes data outside the two-epoch scan window. + #[cfg(zcash_unstable = "nutachyon")] pub fn prepare_tachyon_tachygram_batch( &mut self, zakura_db: &ZakuraDb, @@ -922,13 +934,16 @@ impl DiskWriteBatch { let orchard_tx = finalized.block.orchard_transactions_count(); let ironwood_tx = finalized.block.ironwood_transactions_count(); + #[cfg(zcash_unstable = "nutachyon")] let previous_tachyon_anchor = prev_note_commitment_trees.as_ref().map_or_else( || zakura_db.tachyon_anchor_for_tip(), |trees| trees.tachyon_anchor, ); + #[cfg(zcash_unstable = "nutachyon")] if previous_tachyon_anchor != note_commitment_trees.tachyon_anchor { self.create_tachyon_anchor(zakura_db, height, ¬e_commitment_trees.tachyon_anchor); } + #[cfg(zcash_unstable = "nutachyon")] if let Some(epoch_anchor) = note_commitment_trees.tachyon_epoch_anchor { let epoch = tachyon::epoch(&zakura_db.network(), *height) .expect("a block carrying a Tachyon epoch anchor is NuTachyon-onward"); @@ -1212,6 +1227,7 @@ impl DiskWriteBatch { } /// Stores an end-of-block Tachyon anchor by value and height. + #[cfg(zcash_unstable = "nutachyon")] pub fn create_tachyon_anchor( &mut self, zakura_db: &ZakuraDb, diff --git a/crates/zakura-state/src/service/non_finalized_state.rs b/crates/zakura-state/src/service/non_finalized_state.rs index f90273b448..7a24d31695 100644 --- a/crates/zakura-state/src/service/non_finalized_state.rs +++ b/crates/zakura-state/src/service/non_finalized_state.rs @@ -473,6 +473,7 @@ impl NonFinalizedState { finalized_state.sapling_tree_for_tip(), finalized_state.orchard_tree_for_tip(), finalized_state.ironwood_tree_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] finalized_state.tachyon_anchor_for_tip(), finalized_state.history_tree(), finalized_state.finalized_value_pool(), @@ -539,6 +540,7 @@ impl NonFinalizedState { finalized_state.sapling_tree_for_tip(), finalized_state.orchard_tree_for_tip(), finalized_state.ironwood_tree_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] finalized_state.tachyon_anchor_for_tip(), finalized_state.history_tree(), finalized_state.finalized_value_pool(), diff --git a/crates/zakura-state/src/service/non_finalized_state/chain.rs b/crates/zakura-state/src/service/non_finalized_state/chain.rs index 8c2692a413..2af19409bf 100644 --- a/crates/zakura-state/src/service/non_finalized_state/chain.rs +++ b/crates/zakura-state/src/service/non_finalized_state/chain.rs @@ -11,6 +11,8 @@ use std::{ use chrono::{DateTime, Utc}; use tracing::instrument; +#[cfg(zcash_unstable = "nutachyon")] +use zakura_chain::tachyon; use zakura_chain::{ amount::{Amount, NegativeAllowed, NonNegative}, block::{self, Height}, @@ -24,7 +26,6 @@ use zakura_chain::{ serialization::ZcashSerialize as _, sprout, subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, - tachyon, transaction::{ self, Transaction::{self, *}, @@ -219,14 +220,19 @@ pub struct ChainInner { pub(crate) ironwood_anchors_by_height: BTreeMap, /// Tachyon end-of-block anchors and the heights that created them. + #[cfg(zcash_unstable = "nutachyon")] pub(crate) tachyon_anchors: HashMap>, /// Tachyon end-of-block anchors indexed by height. + #[cfg(zcash_unstable = "nutachyon")] pub(crate) tachyon_anchors_by_height: BTreeMap, /// Tachyon epoch-boundary anchors indexed by epoch. + #[cfg(zcash_unstable = "nutachyon")] pub(crate) tachyon_epoch_anchors_by_epoch: BTreeMap, /// Tachygrams and every non-finalized height that revealed them. + #[cfg(zcash_unstable = "nutachyon")] pub(crate) tachyon_tachygrams: HashMap>, /// Tachygrams revealed by each non-finalized block. + #[cfg(zcash_unstable = "nutachyon")] pub(crate) tachyon_tachygrams_by_height: BTreeMap>, // Nullifiers @@ -281,7 +287,7 @@ impl Chain { sapling_note_commitment_tree: Arc, orchard_note_commitment_tree: Arc, ironwood_note_commitment_tree: Arc, - finalized_tip_tachyon_anchor: tachyon::Anchor, + #[cfg(zcash_unstable = "nutachyon")] finalized_tip_tachyon_anchor: tachyon::Anchor, history_tree: Arc, finalized_tip_chain_value_pools: ValueBalance, ) -> Self { @@ -307,10 +313,15 @@ impl Chain { ironwood_anchors_by_height: Default::default(), ironwood_trees_by_height: Default::default(), ironwood_subtrees: Default::default(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchors: Default::default(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchors_by_height: Default::default(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_epoch_anchors_by_epoch: Default::default(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_tachygrams: Default::default(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_tachygrams_by_height: Default::default(), sprout_nullifiers: Default::default(), sapling_nullifiers: Default::default(), @@ -337,6 +348,7 @@ impl Chain { // Only seed a real Tachyon anchor: the default anchor means the pool has not // started (pre-NuTachyon finalized tip), and only NuTachyon-onward anchors are tracked. + #[cfg(zcash_unstable = "nutachyon")] if finalized_tip_tachyon_anchor != tachyon::Anchor::default() { chain.add_tachyon_anchor(finalized_tip_height, finalized_tip_tachyon_anchor); } @@ -1341,6 +1353,7 @@ impl Chain { } /// Returns the Tachyon anchor after `hash_or_height`. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_anchor(&self, hash_or_height: HashOrHeight) -> Option { let height = hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).copied())?; @@ -1354,6 +1367,7 @@ impl Chain { } /// Returns the Tachyon anchor after this chain's tip. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_anchor_for_tip(&self) -> tachyon::Anchor { self.tachyon_anchors_by_height .last_key_value() @@ -1361,6 +1375,7 @@ impl Chain { .unwrap_or_default() } + #[cfg(zcash_unstable = "nutachyon")] fn add_tachyon_anchor(&mut self, height: Height, anchor: tachyon::Anchor) { assert_eq!( self.tachyon_anchors_by_height.insert(height, anchor), @@ -1370,6 +1385,7 @@ impl Chain { self.tachyon_anchors.entry(anchor).or_default().push(height); } + #[cfg(zcash_unstable = "nutachyon")] fn remove_tachyon_anchor(&mut self, position: RevertPosition, height: Height) { let removed: Vec<_> = if position == RevertPosition::Root { let removed = self @@ -1406,16 +1422,19 @@ impl Chain { } /// Returns the boundary anchor for `epoch`. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_epoch_anchor(&self, epoch: u32) -> Option { self.tachyon_epoch_anchors_by_epoch.get(&epoch).copied() } /// Returns the Tachyon epoch containing this chain's tip. + #[cfg(zcash_unstable = "nutachyon")] pub fn tachyon_current_epoch(&self) -> Option { let tip_height = (!self.is_empty()).then(|| self.non_finalized_tip_height())?; tachyon::epoch(&self.network, tip_height) } + #[cfg(zcash_unstable = "nutachyon")] fn add_tachyon_epoch_anchor(&mut self, epoch: u32, anchor: tachyon::Anchor) { assert_eq!( self.tachyon_epoch_anchors_by_epoch.insert(epoch, anchor), @@ -1424,6 +1443,7 @@ impl Chain { ); } + #[cfg(zcash_unstable = "nutachyon")] fn remove_tachyon_epoch_anchor(&mut self, position: RevertPosition, height: Height) { let Some(pool_height) = tachyon::pool_height(&self.network, height) else { return; @@ -1444,6 +1464,7 @@ impl Chain { } } + #[cfg(zcash_unstable = "nutachyon")] fn add_tachyon_tachygrams( &mut self, height: Height, @@ -1471,6 +1492,7 @@ impl Chain { Ok(()) } + #[cfg(zcash_unstable = "nutachyon")] fn remove_tachyon_tachygrams(&mut self, position: RevertPosition, height: Height) { let removed_heights: Vec<_> = if position == RevertPosition::Root { self.tachyon_tachygrams_by_height @@ -1578,9 +1600,12 @@ impl Chain { let sapling_subtree = self.sapling_subtree(hash_or_height); let orchard_subtree = self.orchard_subtree(hash_or_height); let ironwood_subtree = self.ironwood_subtree(hash_or_height); + #[cfg(zcash_unstable = "nutachyon")] let tachyon_anchor = self.tachyon_anchor(hash_or_height)?; + #[cfg(zcash_unstable = "nutachyon")] let height = hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).copied())?; + #[cfg(zcash_unstable = "nutachyon")] let tachyon_epoch_anchor = tachyon::pool_height(&self.network, height) .filter(|&pool_height| tachyon::is_epoch_first(pool_height)) .and_then(|pool_height| { @@ -1595,7 +1620,9 @@ impl Chain { sapling_subtree, orchard_subtree, ironwood_subtree, + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor, + #[cfg(zcash_unstable = "nutachyon")] tachyon_epoch_anchor, history_tree, )) @@ -1884,7 +1911,9 @@ impl Chain { orchard_subtree: self.orchard_subtree_for_tip(), ironwood: self.ironwood_note_commitment_tree_for_tip(), ironwood_subtree: self.ironwood_subtree_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_anchor: self.tachyon_anchor_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] tachyon_epoch_anchor: None, }; @@ -1935,32 +1964,35 @@ impl Chain { // // In builds without tachyon support V7 transactions cannot be parsed, so a NuTachyon // chain cannot be synced and the anchor legitimately stays at its default. - if let Some(pool_height) = zakura_chain::tachyon::pool_height(&self.network, height) { - let advance = nct - .tachyon_anchor - .advance_with_block(pool_height, &contextually_valid.block)?; - nct.tachyon_anchor = advance.post_block; - nct.tachyon_epoch_anchor = advance.epoch_boundary; - self.add_tachyon_anchor(height, advance.post_block); - if let Some(boundary_anchor) = advance.epoch_boundary { - self.add_tachyon_epoch_anchor( - zakura_chain::tachyon::epoch_of_pool_height(pool_height), - boundary_anchor, - ); + #[cfg(zcash_unstable = "nutachyon")] + { + if let Some(pool_height) = zakura_chain::tachyon::pool_height(&self.network, height) { + let advance = nct + .tachyon_anchor + .advance_with_block(pool_height, &contextually_valid.block)?; + nct.tachyon_anchor = advance.post_block; + nct.tachyon_epoch_anchor = advance.epoch_boundary; + self.add_tachyon_anchor(height, advance.post_block); + if let Some(boundary_anchor) = advance.epoch_boundary { + self.add_tachyon_epoch_anchor( + zakura_chain::tachyon::epoch_of_pool_height(pool_height), + boundary_anchor, + ); + } } + // Add this block's tachygrams to the epoch working set, rejecting same-epoch + // duplicates within this chain. + let block_tachygrams: Vec<_> = contextually_valid + .block + .transactions + .iter() + .flat_map(|transaction| transaction.tachyon_tachygrams()) + .collect(); + self.add_tachyon_tachygrams(height, block_tachygrams)?; } + #[cfg(zcash_unstable = "nutachyon")] let tachyon_anchor = nct.tachyon_anchor; - // Add this block's tachygrams to the epoch working set, rejecting same-epoch - // duplicates within this chain. - let block_tachygrams: Vec<_> = contextually_valid - .block - .transactions - .iter() - .flat_map(|transaction| transaction.tachyon_tachygrams()) - .collect(); - self.add_tachyon_tachygrams(height, block_tachygrams)?; - // TODO: update the history trees in a rayon thread, if they show up in CPU profiles let mut history_tree = self.history_block_commitment_tree(); let history_tree_mut = Arc::make_mut(&mut history_tree); @@ -1971,6 +2003,7 @@ impl Chain { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &tachyon_anchor, ) .map_err(Arc::new)?; @@ -2085,16 +2118,24 @@ impl Chain { orchard_shielded_data, ironwood_shielded_data, .. - } - | V7 { + } => ( + inputs, + outputs, + &None, + &None, + sapling_shielded_data, + orchard_shielded_data, + ironwood_shielded_data, + ), + #[cfg(zcash_unstable = "nutachyon")] + V7 { inputs, outputs, sapling_shielded_data, orchard_shielded_data, ironwood_shielded_data, .. - } - => ( + } => ( inputs, outputs, &None, @@ -2303,8 +2344,17 @@ impl UpdateWith for Chain { orchard_shielded_data, ironwood_shielded_data, .. - } - | V7 { + } => ( + inputs, + outputs, + &None, + &None, + sapling_shielded_data, + orchard_shielded_data, + ironwood_shielded_data, + ), + #[cfg(zcash_unstable = "nutachyon")] + V7 { inputs, outputs, sapling_shielded_data, @@ -2364,9 +2414,12 @@ impl UpdateWith for Chain { self.remove_sapling_tree_and_anchor(position, height); self.remove_orchard_tree_and_anchor(position, height); self.remove_ironwood_tree_and_anchor(position, height); - self.remove_tachyon_anchor(position, height); - self.remove_tachyon_epoch_anchor(position, height); - self.remove_tachyon_tachygrams(position, height); + #[cfg(zcash_unstable = "nutachyon")] + { + self.remove_tachyon_anchor(position, height); + self.remove_tachyon_epoch_anchor(position, height); + self.remove_tachyon_tachygrams(position, height); + } // TODO: move this to the history tree UpdateWith.revert...()? self.remove_history_tree(position, height); @@ -2978,7 +3031,7 @@ impl Chain { } } -#[cfg(test)] +#[cfg(all(test, zcash_unstable = "nutachyon"))] mod tachyon_tests { use zakura_chain::parameters::testnet::ConfiguredActivationHeights; diff --git a/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs b/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs index 4ca8ed0f21..8aaa60254d 100644 --- a/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs +++ b/crates/zakura-state/src/service/non_finalized_state/tests/prop.rs @@ -71,7 +71,7 @@ fn push_genesis_chain() -> Result<()> { |((chain, count, network, empty_tree) in PreparedChain::default())| { prop_assert!(empty_tree.is_none()); - let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), empty_tree, ValueBalance::zero()); + let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), empty_tree, ValueBalance::zero()); // contains the block value pool changes and chain value pool balances for each height let mut chain_values = BTreeMap::new(); @@ -124,7 +124,7 @@ fn push_history_tree_chain() -> Result<()> { let count = std::cmp::min(count, chain.len() - 1); let chain = &chain[1..]; - let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, ValueBalance::zero()); + let mut only_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), finalized_tree, ValueBalance::zero()); for block in chain .iter() @@ -173,6 +173,7 @@ fn forked_equals_pushed_genesis() -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), empty_tree.clone(), ValueBalance::zero(), @@ -196,6 +197,7 @@ fn forked_equals_pushed_genesis() -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), empty_tree, ValueBalance::zero(), @@ -295,8 +297,8 @@ fn forked_equals_pushed_history_tree() -> Result<()> { // use `fork_at_count` as the fork tip let fork_tip_hash = chain[fork_at_count - 1].hash; - let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree.clone(), ValueBalance::zero()); - let mut partial_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, ValueBalance::zero()); + let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), finalized_tree.clone(), ValueBalance::zero()); + let mut partial_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), finalized_tree, ValueBalance::zero()); for block in chain .iter() @@ -363,7 +365,7 @@ fn finalized_equals_pushed_genesis() -> Result<()> { let fake_value_pool = ValueBalance::::fake_populated_pool(); - let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), empty_tree, fake_value_pool); + let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), empty_tree, fake_value_pool); for block in chain .clone() .take(finalized_count) { @@ -377,6 +379,7 @@ fn finalized_equals_pushed_genesis() -> Result<()> { full_chain.sapling_note_commitment_tree_for_tip(), full_chain.orchard_note_commitment_tree_for_tip(), full_chain.ironwood_note_commitment_tree_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), full_chain.history_block_commitment_tree(), full_chain.chain_value_pools, @@ -442,7 +445,7 @@ fn finalized_equals_pushed_history_tree() -> Result<()> { let fake_value_pool = ValueBalance::::fake_populated_pool(); - let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree, fake_value_pool); + let mut full_chain = Chain::new(&network, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), finalized_tree, fake_value_pool); for block in chain .iter() .take(finalized_count) @@ -457,6 +460,7 @@ fn finalized_equals_pushed_history_tree() -> Result<()> { full_chain.sapling_note_commitment_tree_for_tip(), full_chain.orchard_note_commitment_tree_for_tip(), full_chain.ironwood_note_commitment_tree_for_tip(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), full_chain.history_block_commitment_tree(), full_chain.chain_value_pools, @@ -633,6 +637,7 @@ fn different_blocks_different_chains() -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ).unwrap().into() ) @@ -647,6 +652,7 @@ fn different_blocks_different_chains() -> Result<()> { &Default::default(), &Default::default(), &Default::default(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ).unwrap().into() ) @@ -654,8 +660,8 @@ fn different_blocks_different_chains() -> Result<()> { Default::default() }; - let chain1 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree1, ValueBalance::fake_populated_pool()); - let chain2 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), Default::default(), finalized_tree2, ValueBalance::fake_populated_pool()); + let chain1 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), finalized_tree1, ValueBalance::fake_populated_pool()); + let chain2 = Chain::new(&Network::Mainnet, Height(0), Default::default(), Default::default(), Default::default(), Default::default(), #[cfg(zcash_unstable = "nutachyon")] Default::default(), finalized_tree2, ValueBalance::fake_populated_pool()); let block1 = vec1[1].clone().prepare().test_with_zero_spent_utxos(); let block2 = vec2[1].clone().prepare().test_with_zero_spent_utxos(); diff --git a/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs b/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs index 0ef6e54220..fcbd2d00bc 100644 --- a/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs +++ b/crates/zakura-state/src/service/non_finalized_state/tests/vectors.rs @@ -35,6 +35,7 @@ fn construct_empty() { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), Default::default(), ValueBalance::zero(), @@ -54,6 +55,7 @@ fn construct_single() -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), Default::default(), ValueBalance::fake_populated_pool(), @@ -90,6 +92,7 @@ fn construct_many() -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), Default::default(), ValueBalance::fake_populated_pool(), @@ -119,6 +122,7 @@ fn ord_matches_work() -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), Default::default(), ValueBalance::fake_populated_pool(), @@ -132,6 +136,7 @@ fn ord_matches_work() -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), Default::default(), ValueBalance::zero(), @@ -244,6 +249,7 @@ fn finalize_drops_empty_side_chain_for_network(network: Network) -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), Default::default(), ValueBalance::fake_populated_pool(), @@ -992,6 +998,7 @@ fn history_tree_is_updated_for_network_upgrade( &chain.sapling_note_commitment_tree_for_tip().root(), &chain.orchard_note_commitment_tree_for_tip().root(), &chain.ironwood_note_commitment_tree_for_tip().root(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .unwrap(); @@ -1073,6 +1080,7 @@ fn commitment_is_validated_for_network_upgrade(network: Network, network_upgrade &chain.sapling_note_commitment_tree_for_tip().root(), &chain.orchard_note_commitment_tree_for_tip().root(), &chain.ironwood_note_commitment_tree_for_tip().root(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .unwrap(); @@ -1180,6 +1188,7 @@ fn fork_drops_subtrees_above_fork_point() -> Result<()> { Default::default(), Default::default(), Default::default(), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), Default::default(), ValueBalance::fake_populated_pool(), diff --git a/crates/zakura-state/src/service/write/tests/full_state_coherence.rs b/crates/zakura-state/src/service/write/tests/full_state_coherence.rs index ec1bcc01ac..3f0f69948c 100644 --- a/crates/zakura-state/src/service/write/tests/full_state_coherence.rs +++ b/crates/zakura-state/src/service/write/tests/full_state_coherence.rs @@ -176,6 +176,7 @@ fn generated_nu5_graph_matches_full_state_before_finalization() { nu6_2: Some(120), nu6_3: Some(130), nu7: Some(140), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("the compressed activation schedule is ordered") @@ -316,6 +317,7 @@ fn generated_nu5_graph_matches_full_state_before_finalization() { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("the deterministic history tree advances"); diff --git a/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs b/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs index a279a9de00..d33029596c 100644 --- a/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs +++ b/crates/zakura-state/src/service/write/vct_authentication_sweep/tests.rs @@ -108,6 +108,7 @@ fn parameters(checkpoint_blocks: Option<&[Arc]>) -> Network { nu6_2: Some(NU6_2), nu6_3: Some(NU6_3), nu7: Some(NU7), + #[cfg(zcash_unstable = "nutachyon")] nu_tachyon: None, }) .expect("the compressed activation schedule is ordered") @@ -193,8 +194,17 @@ fn generate_chain(network: &Network) -> Vec> { | NetworkUpgrade::Nu6_1 | NetworkUpgrade::Nu6_2 | NetworkUpgrade::Nu6_3 - | NetworkUpgrade::Nu7 - | NetworkUpgrade::NuTachyon => Transaction::V5 { + | NetworkUpgrade::Nu7 => Transaction::V5 { + network_upgrade: upgrade, + lock_time: LockTime::unlocked(), + expiry_height: height, + inputs: vec![input], + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + }, + #[cfg(zcash_unstable = "nutachyon")] + NetworkUpgrade::NuTachyon => Transaction::V5 { network_upgrade: upgrade, lock_time: LockTime::unlocked(), expiry_height: height, @@ -253,6 +263,7 @@ fn generate_chain(network: &Network) -> Vec> { &sapling_root, &orchard_root, &ironwood_root, + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("the deterministic history tree advances"); @@ -636,6 +647,7 @@ impl Fixture { &empty_sapling_root(), &empty_orchard_root(), &empty_ironwood_root(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("the generated history tree advances"); @@ -662,6 +674,7 @@ impl Fixture { verify_commitment_roots( &self.network, self.history_tree_through(parent), + #[cfg(zcash_unstable = "nutachyon")] Default::default(), vec![ CommitmentRootVerification::with_roots( @@ -1082,6 +1095,7 @@ fn fold_placement_is_checked_before_every_history_tree_push() { &empty_sapling_root(), &empty_orchard_root(), &empty_ironwood_root(), + #[cfg(zcash_unstable = "nutachyon")] &Default::default(), ) .expect("the activation block creates the tree"); diff --git a/crates/zakura-state/src/tests.rs b/crates/zakura-state/src/tests.rs index c11b963d06..2ecbfe0eeb 100644 --- a/crates/zakura-state/src/tests.rs +++ b/crates/zakura-state/src/tests.rs @@ -36,7 +36,9 @@ impl FakeChainHelper for Arc { Transaction::V3 { inputs, .. } => &mut inputs[0], Transaction::V4 { inputs, .. } => &mut inputs[0], Transaction::V5 { inputs, .. } => &mut inputs[0], - Transaction::V6 { inputs, .. } | Transaction::V7 { inputs, .. } => &mut inputs[0], + Transaction::V6 { inputs, .. } => &mut inputs[0], + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { inputs, .. } => &mut inputs[0], }; match input { diff --git a/crates/zakurad/Cargo.toml b/crates/zakurad/Cargo.toml index 1680b3cebc..2f6e113ad8 100644 --- a/crates/zakurad/Cargo.toml +++ b/crates/zakurad/Cargo.toml @@ -24,7 +24,7 @@ categories = ["command-line-utilities", "cryptography::cryptocurrencies"] edition.workspace = true # Zakura can have a higher MSRV than the other crates since it is a binary. -rust-version = "1.97" +rust-version = "1.91" # Settings that impact runtime behaviour diff --git a/crates/zakurad/src/components/mempool/storage/tests/prop.rs b/crates/zakurad/src/components/mempool/storage/tests/prop.rs index d88864324c..ac03f3fcdb 100644 --- a/crates/zakurad/src/components/mempool/storage/tests/prop.rs +++ b/crates/zakurad/src/components/mempool/storage/tests/prop.rs @@ -660,7 +660,9 @@ impl SpendConflictTestInput { // No JoinSplits Transaction::V1 { .. } | Transaction::V5 { .. } => {} - Transaction::V6 { .. } | Transaction::V7 { .. } => {} + Transaction::V6 { .. } => {} + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { .. } => {} } } } @@ -734,8 +736,11 @@ impl SpendConflictTestInput { Transaction::V6 { sapling_shielded_data, .. + } => { + Self::remove_sapling_transfers_with_conflicts(sapling_shielded_data, &conflicts) } - | Transaction::V7 { + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { sapling_shielded_data, .. } => { @@ -816,8 +821,9 @@ impl SpendConflictTestInput { Transaction::V6 { orchard_shielded_data, .. - } - | Transaction::V7 { + } => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts), + #[cfg(zcash_unstable = "nutachyon")] + Transaction::V7 { orchard_shielded_data, .. } => Self::remove_orchard_actions_with_conflicts(orchard_shielded_data, &conflicts), diff --git a/docker/Dockerfile b/docker/Dockerfile index 79793a65dc..8de50716bc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,7 +13,7 @@ # We first set default values for build arguments used across the stages. # Each stage must define the build arguments (ARGs) it uses. -ARG RUST_VERSION=1.97.0 +ARG RUST_VERSION=1.91.0 ARG FEATURES="default-release-binaries" ARG SHORT_SHA diff --git a/docker/ubuntu-package.Dockerfile b/docker/ubuntu-package.Dockerfile index 7a83452a60..f9c144b86c 100644 --- a/docker/ubuntu-package.Dockerfile +++ b/docker/ubuntu-package.Dockerfile @@ -4,7 +4,7 @@ ARG UBUNTU_IMAGE=ubuntu:22.04 FROM ${UBUNTU_IMAGE} AS build ARG DEBIAN_FRONTEND=noninteractive -ARG RUST_VERSION=1.97 +ARG RUST_VERSION=1.91 ARG FEATURES="default-release-binaries" RUN apt-get update && \ diff --git a/docs/changelog/unreleased/795.md b/docs/changelog/unreleased/795.md index 33bfae47bd..cc2a8827ad 100644 --- a/docs/changelog/unreleased/795.md +++ b/docs/changelog/unreleased/795.md @@ -3,8 +3,3 @@ - Added NuTachyon network-upgrade and V7 transaction support, including Tachyon bundle serialization, consensus validation, chain state, and block-template aggregation ([#795](https://github.com/zakura-core/zakura/pull/795)). - -## Changed - -- Raised the minimum supported Rust version to 1.97 for the Tachyon and Ragu dependencies - ([#795](https://github.com/zakura-core/zakura/pull/795)). diff --git a/scripts/pack-assets-crate.py b/scripts/pack-assets-crate.py index b036953123..c23baf9caa 100755 --- a/scripts/pack-assets-crate.py +++ b/scripts/pack-assets-crate.py @@ -234,7 +234,7 @@ def setUp(self) -> None: (self.crate / "src").mkdir(parents=True) (self.crate / "Cargo.toml").write_text( '[package]\nname = "example-assets"\nversion = "0.0.0"\n' - 'rust-version = "1.97"\n', + 'rust-version = "1.91"\n', encoding="utf-8", ) self.grid_path = Path(self.temp.name) / GRID_NAME @@ -259,7 +259,7 @@ def test_packs_a_valid_grid_and_derives_its_version(self) -> None: ) # The stamp must not disturb the neighbouring rust-version key. self.assertIn( - 'rust-version = "1.97"', + 'rust-version = "1.91"', (self.crate / "Cargo.toml").read_text(encoding="utf-8"), ) From ccb7eae468b6685861a61a15b39a47d9d44b1adb Mon Sep 17 00:00:00 2001 From: c-node Date: Tue, 1 Sep 2026 16:33:38 -0400 Subject: [PATCH 09/15] fix: keep cfg-off block template selection unchanged --- crates/zakura-rpc/src/methods.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/zakura-rpc/src/methods.rs b/crates/zakura-rpc/src/methods.rs index 9beae0cec6..7104673ad5 100644 --- a/crates/zakura-rpc/src/methods.rs +++ b/crates/zakura-rpc/src/methods.rs @@ -2725,9 +2725,9 @@ where mempool_tx_deps, ); - #[cfg(test)] + #[cfg(all(test, zcash_unstable = "nutachyon"))] let (dependency_depths, selected_txs): (Vec<_>, Vec<_>) = mempool_txs.into_iter().unzip(); - #[cfg(not(test))] + #[cfg(all(not(test), zcash_unstable = "nutachyon"))] let selected_txs = mempool_txs; #[cfg(zcash_unstable = "nutachyon")] @@ -2740,9 +2740,9 @@ where ) .await; - #[cfg(test)] + #[cfg(all(test, zcash_unstable = "nutachyon"))] let mempool_txs: Vec<_> = dependency_depths.into_iter().zip(selected_txs).collect(); - #[cfg(not(test))] + #[cfg(all(not(test), zcash_unstable = "nutachyon"))] let mempool_txs = selected_txs; tracing::debug!( From 06fc9b5cbce6fe4d353175c44ab2e55ac1bb1cc2 Mon Sep 17 00:00:00 2001 From: c-node Date: Fri, 4 Sep 2026 12:35:51 -0400 Subject: [PATCH 10/15] test: add V7 serialization vectors --- Cargo.lock | 37 +-- Cargo.toml | 30 +-- crates/zakura-chain/Cargo.toml | 3 + .../zakura-chain/src/transaction/serialize.rs | 13 +- crates/zakura-chain/src/transaction/tests.rs | 2 + .../src/transaction/tests/v7_vectors.rs | 255 ++++++++++++++++++ .../src/transaction/tests/vectors.rs | 140 ---------- 7 files changed, 303 insertions(+), 177 deletions(-) create mode 100644 crates/zakura-chain/src/transaction/tests/v7_vectors.rs diff --git a/Cargo.lock b/Cargo.lock index e53f23edcd..d4d749c765 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7799,7 +7799,7 @@ checksum = "1518d74211e0ae2422cab4560acdc67f72ae8fcdb871294eb03476a5a4586a74" [[package]] name = "zakura-bellman" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "bitvec", "blake2s_simd", @@ -7815,7 +7815,7 @@ dependencies = [ [[package]] name = "zakura-bls12-381" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "ff", "group", @@ -7854,6 +7854,7 @@ dependencies = [ "primitive-types", "proptest", "proptest-derive", + "ragu", "rand 0.10.2", "rand 0.8.6", "rand_chacha 0.10.0", @@ -7948,7 +7949,7 @@ dependencies = [ [[package]] name = "zakura-halo2-gadgets" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "arrayvec", "ff", @@ -7963,12 +7964,12 @@ dependencies = [ [[package]] name = "zakura-halo2-legacy-pdqsort" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" [[package]] name = "zakura-halo2-poseidon" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "bitvec", "ff", @@ -7979,7 +7980,7 @@ dependencies = [ [[package]] name = "zakura-halo2-proofs" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "blake2b_simd", "ff", @@ -8010,7 +8011,7 @@ dependencies = [ [[package]] name = "zakura-history" version = "1.0.0" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "blake2b_simd", "corez", @@ -8033,7 +8034,7 @@ dependencies = [ [[package]] name = "zakura-jubjub" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "bitvec", "ff", @@ -8046,7 +8047,7 @@ dependencies = [ [[package]] name = "zakura-keys" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "bech32", "blake2b_simd", @@ -8130,7 +8131,7 @@ dependencies = [ [[package]] name = "zakura-orchard" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "aes", "bitvec", @@ -8165,7 +8166,7 @@ dependencies = [ [[package]] name = "zakura-pairing" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "group", ] @@ -8173,7 +8174,7 @@ dependencies = [ [[package]] name = "zakura-pasta-curves" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "blake2b_simd", "cc", @@ -8189,7 +8190,7 @@ dependencies = [ [[package]] name = "zakura-primitives" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "blake2b_simd", "block-buffer 0.11.0-rc.3", @@ -8220,7 +8221,7 @@ dependencies = [ [[package]] name = "zakura-proofs" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "blake2b_simd", "document-features", @@ -8239,7 +8240,7 @@ dependencies = [ [[package]] name = "zakura-reddsa" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "blake2b_simd", "frost-rerandomized", @@ -8256,7 +8257,7 @@ dependencies = [ [[package]] name = "zakura-redjubjub" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "rand_core 0.10.1", "serde", @@ -8339,7 +8340,7 @@ dependencies = [ [[package]] name = "zakura-sapling-crypto" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "aes", "bitvec", @@ -8390,7 +8391,7 @@ dependencies = [ [[package]] name = "zakura-sinsemilla" version = "1.0.1" -source = "git+https://github.com/zakura-core/common.git?rev=69d1bc64b8ef3bbb885a94f70d66725fa6cc028b#69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" +source = "git+https://github.com/zakura-core/common.git?rev=2873e2a102d1459229a90bee117dcb2d2ce8c4a3#2873e2a102d1459229a90bee117dcb2d2ce8c4a3" dependencies = [ "group", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index c9daf5e649..a2f3aa26bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,13 +49,13 @@ edition = "2021" # gate refers to `zakura-assets` and nothing else. zakura-assets = { version = "=0.3462171.0" } incrementalmerkletree = { version = "0.8.2", features = ["legacy-api"] } -orchard = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-orchard" } -sapling-crypto = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-sapling-crypto" } +orchard = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-orchard" } +sapling-crypto = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-sapling-crypto" } zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } -zcash_history = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-history" } -zcash_keys = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-keys" } -zcash_primitives = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-primitives" } -zcash_proofs = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-proofs" } +zcash_history = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-history" } +zcash_keys = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-keys" } +zcash_primitives = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-primitives" } +zcash_proofs = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-proofs" } zcash_transparent = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "c7235b1fc4b4296b8af2e26bf2f6adb24f873f56" } zcash_tachyon = { git = "https://github.com/tachyon-zcash/tachyon", rev = "2dc7010c3e7771c539fd1c03a06bc6be1a7316c7", version = "0.0.0", default-features = false } @@ -63,14 +63,14 @@ ragu = { git = "https://github.com/tachyon-zcash/ragu", rev = "f4f6315b10ed96944 abscissa_core = { version = "0.7", default-features = false } base64 = "0.22.1" bech32 = "0.11.0" -bellman = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-bellman" } +bellman = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-bellman" } bincode = "1.3" bitflags = "2.9" bitflags-serde-legacy = "0.1.1" bitvec = "1.0" blake2b_simd = "1.0" blake2s_simd = "1.0" -bls12_381 = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-bls12-381" } +bls12_381 = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-bls12-381" } bs58 = "0.5" byteorder = "1.5" bytes = "1.10" @@ -89,7 +89,7 @@ futures = "0.3.31" futures-core = "0.3.31" futures-util = "0.3.31" group = "0.14" -halo2 = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-halo2-proofs" } +halo2 = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-halo2-proofs" } hex = "0.4.3" hex-literal = "0.4" howudoin = "0.1" @@ -108,7 +108,7 @@ itertools = "0.14" jsonrpsee = "0.24.8" jsonrpsee-proc-macros = "0.24.9" jsonrpsee-types = "0.24.9" -jubjub = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-jubjub" } +jubjub = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-jubjub" } lazy_static = "1.4" libzcash_script = "0.1" log = "0.4.27" @@ -131,8 +131,8 @@ rand_10 = { package = "rand", version = "0.10" } rand_chacha_10 = { package = "rand_chacha", version = "0.10" } rand_core_10 = { package = "rand_core", version = "0.10" } rayon = "1.10" -reddsa = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-reddsa" } -redjubjub = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-redjubjub" } +reddsa = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-reddsa" } +redjubjub = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-redjubjub" } regex = "1.11" reqwest = { version = "0.12", default-features = false } ripemd = "0.1" @@ -147,7 +147,7 @@ serde_json = "1.0.140" serde_with = "3.12" sha2 = "0.10" schemars = "1" -sinsemilla = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b", package = "zakura-sinsemilla" } +sinsemilla = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3", package = "zakura-sinsemilla" } spandoc = "0.2" anyhow = "1.0" strum = "0.27" @@ -186,8 +186,8 @@ zcash_script = "0.4.5" config = { version = "0.15.14", default-features = false, features = ["toml"] } [patch.crates-io] -zakura-pasta-curves = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" } -zakura-reddsa = { git = "https://github.com/zakura-core/common.git", rev = "69d1bc64b8ef3bbb885a94f70d66725fa6cc028b" } +zakura-pasta-curves = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3" } +zakura-reddsa = { git = "https://github.com/zakura-core/common.git", rev = "2873e2a102d1459229a90bee117dcb2d2ce8c4a3" } # All cargo-release configuration lives in this table. Do not add a root # release.toml: it would silently shadow every setting here (cargo-release diff --git a/crates/zakura-chain/Cargo.toml b/crates/zakura-chain/Cargo.toml index 85c23f215f..f3cd57d1d6 100644 --- a/crates/zakura-chain/Cargo.toml +++ b/crates/zakura-chain/Cargo.toml @@ -153,6 +153,9 @@ tokio = { workspace = true, features = ["full", "tracing", "test-util"] } zakura-test = { path = "../zakura-test/", version = "2.1.0" } +[target.'cfg(zcash_unstable = "nutachyon")'.dev-dependencies] +ragu = { workspace = true } + [[bench]] name = "block" harness = false diff --git a/crates/zakura-chain/src/transaction/serialize.rs b/crates/zakura-chain/src/transaction/serialize.rs index 4539e6ee49..9cb246913a 100644 --- a/crates/zakura-chain/src/transaction/serialize.rs +++ b/crates/zakura-chain/src/transaction/serialize.rs @@ -917,8 +917,11 @@ impl ZcashSerialize for Transaction { &mut writer, ALLOW_CROSS_ADDRESS_BIT, )?; + let tachyon_bundle = tachyon_shielded_data + .as_ref() + .map_or(&zcash_tachyon::TachyonBundle::NoBundle, |bundle| &bundle.0); zcash_primitives::transaction::components::tachyon::write_v7_bundle( - tachyon_shielded_data.as_ref().map(|bundle| &bundle.0), + tachyon_bundle, &mut writer, )?; } @@ -1315,10 +1318,12 @@ impl ZcashDeserialize for Transaction { ALLOW_CROSS_ADDRESS_BIT, )?; let tachyon_shielded_data = - zcash_primitives::transaction::components::tachyon::read_v7_bundle( + match zcash_primitives::transaction::components::tachyon::read_v7_bundle( &mut limited_reader, - )? - .map(TachyonShieldedData::from); + )? { + zcash_tachyon::TachyonBundle::NoBundle => None, + bundle => Some(TachyonShieldedData::from(bundle)), + }; Ok(Transaction::V7 { network_upgrade, lock_time, diff --git a/crates/zakura-chain/src/transaction/tests.rs b/crates/zakura-chain/src/transaction/tests.rs index ccb1591e09..83d84a24d8 100644 --- a/crates/zakura-chain/src/transaction/tests.rs +++ b/crates/zakura-chain/src/transaction/tests.rs @@ -3,4 +3,6 @@ mod ironwood_v6_tx_hash; mod preallocate; mod prop; +#[cfg(zcash_unstable = "nutachyon")] +mod v7_vectors; mod vectors; diff --git a/crates/zakura-chain/src/transaction/tests/v7_vectors.rs b/crates/zakura-chain/src/transaction/tests/v7_vectors.rs new file mode 100644 index 0000000000..6eb3dcf639 --- /dev/null +++ b/crates/zakura-chain/src/transaction/tests/v7_vectors.rs @@ -0,0 +1,255 @@ +//! V7 transaction fixtures and cross-implementation serialization vectors. + +use group::{ + ff::{FromUniformBytes, PrimeField}, + CurveAffine, GroupEncoding, +}; +use halo2::pasta::pallas; +use reddsa::{orchard::SpendAuth, SigningKey, VerificationKey}; +use zcash_tachyon::{ + Action, Anchor, Bundle, PointerStamp, ProofStamp, Tachygram, TachygramSetPoly, TachyonBundle, +}; + +use crate::{ + block::Height, + parameters::{NetworkUpgrade, TX_V7_VERSION_GROUP_ID}, + serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}, + transaction::{LockTime, TachyonShieldedData, Transaction}, +}; + +fn action_with_seed(seed: u8, signature_byte: u8) -> Action { + let signing_key = SigningKey::::try_from( + pallas::Scalar::from_uniform_bytes(&[seed; 64]).to_repr(), + ) + .expect("the reduced scalar is a valid signing key"); + let verification_key = VerificationKey::::from(&signing_key); + let verification_key = Option::::from(pallas::Affine::from_bytes( + &<[u8; 32]>::from(verification_key), + )) + .expect("the verification key is a valid curve point"); + + Action { + cv: zcash_tachyon::value::Commitment::from(pallas::Affine::generator()), + rk: zcash_tachyon::keys::public::ActionVerificationKey::try_from(verification_key) + .expect("a non-identity verification key is valid"), + sig: zcash_tachyon::action::Signature::read(&[signature_byte; 64][..]) + .expect("test signature bytes are canonical"), + } +} + +fn default_anchor() -> Anchor { + Anchor::read(&[0; 64][..]).expect("64 zero bytes encode the default Tachyon anchor") +} + +fn tachygram(seed: u8) -> Tachygram { + Tachygram::from(pallas::Base::from_uniform_bytes(&[seed; 64])) +} + +fn transaction_with_tachyon_bundle(bundle: TachyonBundle) -> Transaction { + let tachyon_shielded_data = (!bundle.is_no_bundle()).then_some(TachyonShieldedData(bundle)); + + Transaction::V7 { + network_upgrade: NetworkUpgrade::NuTachyon, + lock_time: LockTime::unlocked(), + expiry_height: Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data, + } +} + +fn empty_transaction() -> Transaction { + transaction_with_tachyon_bundle(TachyonBundle::NoBundle) +} + +fn adjunct_transaction() -> Transaction { + transaction_with_tachyon_bundle(TachyonBundle::Adjunct(Bundle { + actions: vec![action_with_seed(0x42, 0x01)], + value_balance: zcash_tachyon::value::Balance::ZERO, + binding_sig: zcash_tachyon::bundle::Signature::read(&[0x02; 64][..]) + .expect("test signature bytes are canonical"), + memo: Vec::new(), + stamp: PointerStamp::try_from([0xEE; 64]) + .expect("a 64-byte witnessed transaction ID is valid"), + })) +} + +fn proven_transaction() -> Transaction { + let tachygram = tachygram(0xAA); + + transaction_with_tachyon_bundle(TachyonBundle::Proven(Bundle { + actions: vec![action_with_seed(0x42, 0x01)], + value_balance: zcash_tachyon::value::Balance::try_from(100i64) + .expect("100 is a valid Tachyon value balance"), + binding_sig: zcash_tachyon::bundle::Signature::read(&[0x02; 64][..]) + .expect("test signature bytes are canonical"), + memo: Vec::new(), + stamp: ProofStamp { + coverage: [0; 32], + tachygram_set: [tachygram] + .into_iter() + .collect::() + .commit(), + tachygrams: [tachygram].into_iter().collect(), + anchor: default_anchor(), + proof: Box::new(ragu::Proof::trivial()), + }, + })) +} + +fn multi_action_proven_transaction() -> Transaction { + let tachygrams = [tachygram(0xAA), tachygram(0xCC), tachygram(0xDD)]; + + transaction_with_tachyon_bundle(TachyonBundle::Proven(Bundle { + actions: vec![action_with_seed(0x42, 0x01), action_with_seed(0x43, 0x03)], + value_balance: zcash_tachyon::value::Balance::try_from(300i64) + .expect("300 is a valid Tachyon value balance"), + binding_sig: zcash_tachyon::bundle::Signature::read(&[0x02; 64][..]) + .expect("test signature bytes are canonical"), + memo: Vec::new(), + stamp: ProofStamp { + coverage: [0; 32], + tachygram_set: tachygrams + .into_iter() + .collect::() + .commit(), + tachygrams: tachygrams.into_iter().collect(), + anchor: default_anchor(), + proof: Box::new(ragu::Proof::trivial()), + }, + })) +} + +fn fixtures() -> [(&'static str, Transaction); 4] { + [ + ("V7_EMPTY", empty_transaction()), + ("V7_TACHYON_ADJUNCT", adjunct_transaction()), + ("V7_TACHYON_PROVEN", proven_transaction()), + ( + "V7_TACHYON_MULTI_ACTION_PROVEN", + multi_action_proven_transaction(), + ), + ] +} + +#[test] +fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { + let _init_guard = zakura_test::init(); + + assert_eq!( + TX_V7_VERSION_GROUP_ID, + zcash_protocol::constants::V7_VERSION_GROUP_ID, + ); + assert_eq!( + TX_V7_VERSION_GROUP_ID, + zcash_primitives::transaction::TxVersion::V7.version_group_id(), + ); + + let pre_nu_tachyon = Transaction::V7 { + network_upgrade: NetworkUpgrade::Nu6_3, + lock_time: LockTime::unlocked(), + expiry_height: Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data: None, + }; + pre_nu_tachyon + .zcash_serialize_to_vec() + .expect_err("V7 must not serialize before NuTachyon"); + + let transaction = empty_transaction(); + let bytes = transaction + .zcash_serialize_to_vec() + .expect("an empty NuTachyon V7 transaction has a valid wire encoding"); + assert_eq!(&bytes[0..4], &[0x07, 0x00, 0x00, 0x80]); + assert_eq!(&bytes[4..8], &TX_V7_VERSION_GROUP_ID.to_le_bytes()); + assert_eq!(&bytes[8..12], &0xffff_fffcu32.to_le_bytes()); + assert_eq!(transaction.version_group_id(), Some(TX_V7_VERSION_GROUP_ID)); + + let decoded = Transaction::zcash_deserialize(&bytes[..]) + .expect("Zakura must deserialize its NuTachyon V7 encoding"); + assert_eq!(decoded, transaction); + let librustzcash_transaction = transaction + .to_librustzcash(NetworkUpgrade::NuTachyon) + .expect("librustzcash must accept Zakura's NuTachyon V7 encoding"); + assert_eq!( + transaction.hash().0, + *librustzcash_transaction.txid().as_ref() + ); + let librustzcash_auth_digest: [u8; 32] = librustzcash_transaction + .auth_commitment() + .as_ref() + .try_into() + .expect("the authorizing-data digest is 32 bytes"); + assert_eq!( + transaction + .auth_digest() + .expect("V7 transactions have an authorizing-data digest") + .0, + librustzcash_auth_digest, + ); + + let mut pre_nu_tachyon_bytes = bytes; + let nu6_3_branch_id = u32::from( + NetworkUpgrade::Nu6_3 + .branch_id() + .expect("NU6.3 has a branch ID"), + ); + pre_nu_tachyon_bytes[8..12].copy_from_slice(&nu6_3_branch_id.to_le_bytes()); + let error = Transaction::zcash_deserialize(&pre_nu_tachyon_bytes[..]) + .expect_err("V7 must not deserialize with a pre-NuTachyon branch ID"); + assert!(matches!( + error, + SerializationError::Parse(message) if message.contains("NuTachyon") + )); +} + +#[test] +fn v7_tachyon_fixtures_round_trip() { + let _init_guard = zakura_test::init(); + + for (name, transaction) in fixtures() { + let bytes = transaction + .zcash_serialize_to_vec() + .unwrap_or_else(|error| panic!("{name} serialization failed: {error}")); + let decoded = Transaction::zcash_deserialize(&bytes[..]) + .unwrap_or_else(|error| panic!("{name} deserialization failed: {error}")); + let reencoded = decoded + .zcash_serialize_to_vec() + .unwrap_or_else(|error| panic!("{name} reserialization failed: {error}")); + + assert_eq!(decoded, transaction, "{name} round-trip mismatch"); + assert_eq!(reencoded, bytes, "{name} reserialization differs"); + assert_eq!(decoded.hash(), transaction.hash(), "{name} txid differs"); + assert_eq!( + decoded.auth_digest(), + transaction.auth_digest(), + "{name} authorization digest differs" + ); + } +} + +/// Prints the canonical encodings consumed by `zakura-primitives` tests. +#[test] +#[ignore = "prints V7 serialization vectors for zakura-primitives"] +#[allow(clippy::print_stdout)] +fn generate_v7_tachyon_serialization_vectors() { + let _init_guard = zakura_test::init(); + + for (name, transaction) in fixtures() { + let bytes = transaction + .zcash_serialize_to_vec() + .unwrap_or_else(|error| panic!("{name} serialization failed: {error}")); + let decoded = Transaction::zcash_deserialize(&bytes[..]) + .unwrap_or_else(|error| panic!("{name} deserialization failed: {error}")); + + assert_eq!(decoded, transaction, "{name} round-trip mismatch"); + println!("{name}={}", hex::encode(bytes)); + } +} diff --git a/crates/zakura-chain/src/transaction/tests/vectors.rs b/crates/zakura-chain/src/transaction/tests/vectors.rs index bb8c2e4253..4834df0304 100644 --- a/crates/zakura-chain/src/transaction/tests/vectors.rs +++ b/crates/zakura-chain/src/transaction/tests/vectors.rs @@ -1857,146 +1857,6 @@ fn v6_version_group_id_matches_librustzcash_and_wire_format() { )); } -#[test] -#[cfg(zcash_unstable = "nutachyon")] -fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { - use crate::parameters::TX_V7_VERSION_GROUP_ID; - - let _init_guard = zakura_test::init(); - - assert_eq!( - TX_V7_VERSION_GROUP_ID, - zcash_protocol::constants::V7_VERSION_GROUP_ID, - ); - assert_eq!( - TX_V7_VERSION_GROUP_ID, - zcash_primitives::transaction::TxVersion::V7.version_group_id(), - ); - - let empty_v7 = |network_upgrade| Transaction::V7 { - network_upgrade, - lock_time: LockTime::unlocked(), - expiry_height: Height(0), - inputs: Vec::new(), - outputs: Vec::new(), - sapling_shielded_data: None, - orchard_shielded_data: None, - ironwood_shielded_data: None, - tachyon_shielded_data: None, - }; - - empty_v7(NetworkUpgrade::Nu6_3) - .zcash_serialize_to_vec() - .expect_err("V7 must not serialize before NuTachyon"); - - let tx = empty_v7(NetworkUpgrade::NuTachyon); - let tx_bytes = tx - .zcash_serialize_to_vec() - .expect("an empty NuTachyon V7 transaction has a valid wire encoding"); - assert_eq!(&tx_bytes[0..4], &[0x07, 0x00, 0x00, 0x80]); - assert_eq!(&tx_bytes[4..8], &TX_V7_VERSION_GROUP_ID.to_le_bytes()); - assert_eq!(&tx_bytes[8..12], &0xffff_fffcu32.to_le_bytes()); - assert_eq!(tx.version_group_id(), Some(TX_V7_VERSION_GROUP_ID)); - - let decoded = Transaction::zcash_deserialize(&tx_bytes[..]) - .expect("Zakura must deserialize its NuTachyon V7 encoding"); - assert_eq!(decoded, tx); - let librustzcash_tx = tx - .to_librustzcash(NetworkUpgrade::NuTachyon) - .expect("librustzcash must accept Zakura's NuTachyon V7 encoding"); - assert_eq!(tx.hash().0, *librustzcash_tx.txid().as_ref()); - let librustzcash_auth_digest: [u8; 32] = librustzcash_tx - .auth_commitment() - .as_ref() - .try_into() - .expect("the authorizing-data digest is 32 bytes"); - assert_eq!( - tx.auth_digest() - .expect("V7 transactions have an authorizing-data digest") - .0, - librustzcash_auth_digest, - ); - - let mut pre_nu_tachyon = tx_bytes; - let nu6_3_branch_id = u32::from( - NetworkUpgrade::Nu6_3 - .branch_id() - .expect("NU6.3 has a branch ID"), - ); - pre_nu_tachyon[8..12].copy_from_slice(&nu6_3_branch_id.to_le_bytes()); - let error = Transaction::zcash_deserialize(&pre_nu_tachyon[..]) - .expect_err("V7 must not deserialize with a pre-NuTachyon branch ID"); - assert!(matches!( - error, - SerializationError::Parse(message) if message.contains("NuTachyon") - )); -} - -#[test] -#[cfg(zcash_unstable = "nutachyon")] -fn v7_tachyon_bundle_round_trips() { - use group::{ - ff::{FromUniformBytes, PrimeField}, - CurveAffine, GroupEncoding, - }; - use halo2::pasta::pallas; - use reddsa::{orchard::SpendAuth, SigningKey, VerificationKey}; - - let _init_guard = zakura_test::init(); - - let signing_key = SigningKey::::try_from( - pallas::Scalar::from_uniform_bytes(&[0x42; 64]).to_repr(), - ) - .expect("the reduced scalar is a valid signing key"); - let verification_key = VerificationKey::::from(&signing_key); - let verification_key = Option::::from(pallas::Affine::from_bytes( - &<[u8; 32]>::from(verification_key), - )) - .expect("the verification key is a valid curve point"); - - let bundle = zcash_tachyon::TachyonBundle::Adjunct(zcash_tachyon::Bundle { - actions: vec![zcash_tachyon::Action { - cv: zcash_tachyon::value::Commitment::from(pallas::Affine::generator()), - rk: zcash_tachyon::keys::public::ActionVerificationKey::try_from(verification_key) - .expect("a non-identity verification key is valid"), - sig: zcash_tachyon::action::Signature::read(&[0x01; 64][..]) - .expect("test signature bytes are canonical"), - }], - value_balance: zcash_tachyon::value::Balance::try_from(100i64) - .expect("100 is a valid Tachyon value balance"), - binding_sig: zcash_tachyon::bundle::Signature::read(&[0x02; 64][..]) - .expect("test signature bytes are canonical"), - memo: vec![0xAB; 5], - stamp: zcash_tachyon::PointerStamp::try_from([0xEE; 64]) - .expect("a 64-byte witnessed transaction ID is valid"), - }); - let tx = Transaction::V7 { - network_upgrade: NetworkUpgrade::NuTachyon, - lock_time: LockTime::unlocked(), - expiry_height: Height(0), - inputs: Vec::new(), - outputs: Vec::new(), - sapling_shielded_data: None, - orchard_shielded_data: None, - ironwood_shielded_data: None, - tachyon_shielded_data: Some(TachyonShieldedData(bundle)), - }; - - let bytes = tx - .zcash_serialize_to_vec() - .expect("a V7 transaction with a Tachyon bundle serializes"); - let decoded = Transaction::zcash_deserialize(&bytes[..]) - .expect("a serialized V7 Tachyon transaction deserializes"); - - assert_eq!(decoded, tx); - assert_eq!(decoded.hash(), tx.hash()); - assert_eq!(decoded.auth_digest(), tx.auth_digest()); - assert_eq!( - decoded.tachyon_value_balance().tachyon_amount(), - Amount::::try_from(100).expect("100 zatoshi is valid"), - ); -} - #[test] fn v6_transactions_accept_nu6_3_and_later_branch_ids() { use crate::parameters::TX_V6_VERSION_GROUP_ID; From b9083c6cd778b7672a437a8519071ca684742a09 Mon Sep 17 00:00:00 2001 From: c-node Date: Fri, 4 Sep 2026 13:14:31 -0400 Subject: [PATCH 11/15] test: name zakura-primitives interoperability accurately --- .../zakura-chain/src/transaction/tests/v7_vectors.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/zakura-chain/src/transaction/tests/v7_vectors.rs b/crates/zakura-chain/src/transaction/tests/v7_vectors.rs index 6eb3dcf639..82a4e5f2a0 100644 --- a/crates/zakura-chain/src/transaction/tests/v7_vectors.rs +++ b/crates/zakura-chain/src/transaction/tests/v7_vectors.rs @@ -136,7 +136,7 @@ fn fixtures() -> [(&'static str, Transaction); 4] { } #[test] -fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { +fn v7_is_nu_tachyon_gated_and_matches_zakura_primitives() { let _init_guard = zakura_test::init(); assert_eq!( @@ -175,14 +175,14 @@ fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { let decoded = Transaction::zcash_deserialize(&bytes[..]) .expect("Zakura must deserialize its NuTachyon V7 encoding"); assert_eq!(decoded, transaction); - let librustzcash_transaction = transaction + let zakura_primitives_transaction = transaction .to_librustzcash(NetworkUpgrade::NuTachyon) - .expect("librustzcash must accept Zakura's NuTachyon V7 encoding"); + .expect("zakura-primitives must accept Zakura's NuTachyon V7 encoding"); assert_eq!( transaction.hash().0, - *librustzcash_transaction.txid().as_ref() + *zakura_primitives_transaction.txid().as_ref() ); - let librustzcash_auth_digest: [u8; 32] = librustzcash_transaction + let zakura_primitives_auth_digest: [u8; 32] = zakura_primitives_transaction .auth_commitment() .as_ref() .try_into() @@ -192,7 +192,7 @@ fn v7_is_nu_tachyon_gated_and_matches_librustzcash() { .auth_digest() .expect("V7 transactions have an authorizing-data digest") .0, - librustzcash_auth_digest, + zakura_primitives_auth_digest, ); let mut pre_nu_tachyon_bytes = bytes; From d5fce97d1b57c33eec0f7af36ac7b359befb0628 Mon Sep 17 00:00:00 2001 From: c-node Date: Mon, 7 Sep 2026 00:16:28 -0400 Subject: [PATCH 12/15] fix(rpc): build NuTachyon proposal blocks --- .../types/get_block_template/proposal.rs | 2 + .../methods/types/get_block_template/tests.rs | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/crates/zakura-rpc/src/methods/types/get_block_template/proposal.rs b/crates/zakura-rpc/src/methods/types/get_block_template/proposal.rs index fef79555c2..59cc0e9edd 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template/proposal.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template/proposal.rs @@ -218,6 +218,8 @@ pub fn proposal_block_from_template( | NetworkUpgrade::Nu6_2 | NetworkUpgrade::Nu6_3 | NetworkUpgrade::Nu7 => block_commitments_hash.bytes_in_serialized_order(), + #[cfg(zcash_unstable = "nutachyon")] + NetworkUpgrade::NuTachyon => block_commitments_hash.bytes_in_serialized_order(), _ => Err(SerializationError::Parse( "Zakura does not support generating pre-Canopy block templates", ))?, diff --git a/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs b/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs index 307455e43f..9744e625bf 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs @@ -126,6 +126,77 @@ fn local_genesis_activation_coinbase_includes_lockbox_marker() -> anyhow::Result Ok(()) } +/// A NuTachyon template must convert into a mineable proposal block. +#[cfg(zcash_unstable = "nutachyon")] +#[test] +fn nu_tachyon_template_converts_to_proposal_block() -> anyhow::Result<()> { + use crate::methods::types::{ + get_block_template::proposal::proposal_block_from_template, long_poll::LongPollInput, + }; + use zakura_chain::{ + block::ChainHistoryMmrRootHash, + serialization::{BytesInDisplayOrder, DateTime32}, + work::difficulty::{CompactDifficulty, ExpandedDifficulty, U256}, + }; + + let net = testnet::Parameters::build() + .with_activation_heights(ConfiguredActivationHeights { + canopy: Some(1), + nu5: Some(2), + nu6: Some(3), + nu6_1: Some(4), + nu6_2: Some(5), + nu6_3: Some(6), + nu_tachyon: Some(7), + ..Default::default() + })? + .clear_funding_streams() + .to_network()?; + let height = NetworkUpgrade::NuTachyon + .activation_height(&net) + .ok_or(anyhow!("NuTachyon activation height must be configured"))?; + let tip_height = height.previous()?; + let miner_params = MinerParams::from( + Address::decode( + &net, + default_miner_address(net.kind(), &MinerAddressType::Transparent), + ) + .ok_or(anyhow!("hard-coded transparent address must be valid"))?, + ); + let now = DateTime32::now(); + let chain_info = zakura_state::GetBlockTemplateChainInfo { + tip_hash: net.genesis_hash(), + tip_height, + chain_history_root: Some(ChainHistoryMmrRootHash::default()), + expected_difficulty: CompactDifficulty::from(ExpandedDifficulty::from(U256::one())), + cur_time: now, + min_time: now, + max_time: now, + }; + let long_poll_id = LongPollInput::new(tip_height, chain_info.tip_hash, now, []).generate_id(); + let template = super::BlockTemplateResponse::new_internal( + &net, + None, + &miner_params, + &chain_info, + long_poll_id, + vec![], + None, + ); + + let block = proposal_block_from_template(&template, None, &net)?; + + assert_eq!( + block.header.commitment_bytes.0, + template + .default_roots + .block_commitments_hash + .bytes_in_serialized_order(), + ); + + Ok(()) +} + /// The Zakura marker is always prepended, and `extra_coinbase_data` can't exceed /// the limit. #[test] From 96cea46a7a6bc7c9c992d7e9a69bb12d512bd0b8 Mon Sep 17 00:00:00 2001 From: c-node Date: Mon, 7 Sep 2026 11:10:37 -0400 Subject: [PATCH 13/15] fix(rpc): report Tachyon value pool --- crates/zakura-rpc/src/methods.rs | 47 ++++++++++---- ...k_verbose_hash_verbosity_1@mainnet_10.snap | 10 ++- ...k_verbose_hash_verbosity_1@testnet_10.snap | 10 ++- ...k_verbose_hash_verbosity_2@mainnet_10.snap | 10 ++- ...k_verbose_hash_verbosity_2@testnet_10.snap | 10 ++- ...ose_hash_verbosity_default@mainnet_10.snap | 10 ++- ...ose_hash_verbosity_default@testnet_10.snap | 10 ++- ...verbose_height_verbosity_1@mainnet_10.snap | 10 ++- ...verbose_height_verbosity_1@testnet_10.snap | 10 ++- ...verbose_height_verbosity_2@mainnet_10.snap | 10 ++- ...verbose_height_verbosity_2@testnet_10.snap | 10 ++- ...e_height_verbosity_default@mainnet_10.snap | 10 ++- ...e_height_verbosity_default@testnet_10.snap | 10 ++- .../get_blockchain_info@mainnet_10.snap | 8 ++- .../get_blockchain_info@testnet_10.snap | 8 ++- ..._info_future_nu6_height@nu6testnet_10.snap | 8 ++- .../src/methods/types/get_blockchain_info.rs | 61 ++++++++++++++++--- .../zakura-rpc/tests/serialization_tests.rs | 25 ++++++++ 18 files changed, 244 insertions(+), 33 deletions(-) diff --git a/crates/zakura-rpc/src/methods.rs b/crates/zakura-rpc/src/methods.rs index 5741a10b54..39abe564f7 100644 --- a/crates/zakura-rpc/src/methods.rs +++ b/crates/zakura-rpc/src/methods.rs @@ -139,6 +139,14 @@ use types::{ z_validate_address::ZValidateAddressResponse, }; +/// Value pool balances returned by the blockchain RPCs. +#[cfg(not(zcash_unstable = "nutachyon"))] +pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 6]; + +/// Value pool balances returned by the blockchain RPCs. +#[cfg(zcash_unstable = "nutachyon")] +pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 7]; + /// Calls a Tower service and maps readiness or call errors to /// [`server::error::LegacyCode::Misc`]. async fn call_service(service: S, request: Request) -> Result @@ -3668,9 +3676,6 @@ pub struct EndOfService { estimated_time: i64, } -/// Type alias for the array of `GetBlockchainInfoBalance` objects -pub type BlockchainValuePoolBalances = [GetBlockchainInfoBalance; 6]; - fn deserialize_blockchain_value_pool_balances<'de, D>( deserializer: D, ) -> std::result::Result @@ -3698,20 +3703,40 @@ fn blockchain_value_pool_balances_from_vec( where E: serde::de::Error, { + let missing_pool_delta = value_pools + .iter() + .any(|pool| pool.value_delta().is_some() || pool.value_delta_zat().is_some()) + .then(Amount::zero); + match value_pools.len() { 5 => { - let ironwood_delta = value_pools - .iter() - .any(|pool| pool.value_delta().is_some() || pool.value_delta_zat().is_some()) - .then(Amount::zero); - value_pools.push(GetBlockchainInfoBalance::ironwood( Amount::zero(), - ironwood_delta, + missing_pool_delta, + )); + #[cfg(zcash_unstable = "nutachyon")] + value_pools.push(GetBlockchainInfoBalance::tachyon( + Amount::zero(), + missing_pool_delta, + )); + } + 6 => { + #[cfg(zcash_unstable = "nutachyon")] + value_pools.push(GetBlockchainInfoBalance::tachyon( + Amount::zero(), + missing_pool_delta, )); } - 6 => {} - len => return Err(E::invalid_length(len, &"five or six value pool balances")), + #[cfg(zcash_unstable = "nutachyon")] + 7 => {} + len => { + #[cfg(not(zcash_unstable = "nutachyon"))] + let expected = &"five or six value pool balances"; + #[cfg(zcash_unstable = "nutachyon")] + let expected = &"five, six, or seven value pool balances"; + + return Err(E::invalid_length(len, expected)); + } } value_pools diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap index 49b5c9decc..0c39f9a326 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@mainnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap index cb0bcbb39f..4f100179e1 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_1@testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap index 82a48b36fa..31b291622e 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@mainnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -128,6 +128,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap index 9652f62dc2..04e6350c28 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_2@testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -128,6 +128,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap index 49b5c9decc..0c39f9a326 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@mainnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap index cb0bcbb39f..4f100179e1 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_hash_verbosity_default@testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap index 49b5c9decc..0c39f9a326 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@mainnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap index cb0bcbb39f..4f100179e1 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_1@testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap index 82a48b36fa..31b291622e 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@mainnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -128,6 +128,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap index 9652f62dc2..04e6350c28 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_2@testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -128,6 +128,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap index 49b5c9decc..0c39f9a326 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@mainnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap index cb0bcbb39f..4f100179e1 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_block_verbose_height_verbosity_default@testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: block --- { @@ -73,6 +73,14 @@ expression: block "monitored": false, "valueDelta": 0.0, "valueDeltaZat": 0 + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false, + "valueDelta": 0.0, + "valueDeltaZat": 0 } ], "trees": {}, diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap index 78eb8be8ea..9e875dfdaa 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@mainnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: info --- { @@ -55,6 +55,12 @@ expression: info "chainValue": 0.0, "chainValueZat": 0, "monitored": false + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false } ], "upgrades": { diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap index 5e65a23dec..68e6dd9de6 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info@testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: info --- { @@ -55,6 +55,12 @@ expression: info "chainValue": 0.0, "chainValueZat": 0, "monitored": false + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false } ], "upgrades": { diff --git a/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info_future_nu6_height@nu6testnet_10.snap b/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info_future_nu6_height@nu6testnet_10.snap index 897d41b14c..63cb87889f 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info_future_nu6_height@nu6testnet_10.snap +++ b/crates/zakura-rpc/src/methods/tests/snapshots/get_blockchain_info_future_nu6_height@nu6testnet_10.snap @@ -1,5 +1,5 @@ --- -source: zebra-rpc/src/methods/tests/snapshot.rs +source: crates/zakura-rpc/src/methods/tests/snapshot.rs expression: info --- { @@ -55,6 +55,12 @@ expression: info "chainValue": 0.0, "chainValueZat": 0, "monitored": false + }, + { + "id": "tachyon", + "chainValue": 0.0, + "chainValueZat": 0, + "monitored": false } ], "upgrades": { diff --git a/crates/zakura-rpc/src/methods/types/get_blockchain_info.rs b/crates/zakura-rpc/src/methods/types/get_blockchain_info.rs index 227e76b47d..9c351aafc7 100644 --- a/crates/zakura-rpc/src/methods/types/get_blockchain_info.rs +++ b/crates/zakura-rpc/src/methods/types/get_blockchain_info.rs @@ -9,6 +9,8 @@ use zakura_chain::{ use zec::Zec; +use crate::methods::BlockchainValuePoolBalances; + use super::*; /// A value pool's balance in Zec and Zatoshis @@ -37,9 +39,8 @@ pub struct GetBlockchainInfoBalance { } impl GetBlockchainInfoBalance { - /// Returns zero-value transparent, Sprout, Sapling, Orchard, Lockbox, and - /// Ironwood value pools. - pub fn zero_pools() -> [Self; 6] { + /// Returns zero-value entries for every tracked value pool. + pub fn zero_pools() -> BlockchainValuePoolBalances { Self::value_pools(Default::default(), None) } @@ -88,18 +89,22 @@ impl GetBlockchainInfoBalance { Self::new_internal("ironwood", amount, delta) } + /// Creates a [`GetBlockchainInfoBalance`] for the Tachyon pool. + #[cfg(zcash_unstable = "nutachyon")] + pub fn tachyon(amount: Amount, delta: Option>) -> Self { + Self::new_internal("tachyon", amount, delta) + } + /// Creates a [`GetBlockchainInfoBalance`] for the Lockbox pool. pub fn deferred(amount: Amount, delta: Option>) -> Self { Self::new_internal("lockbox", amount, delta) } - /// Converts a [`ValueBalance`] to transparent, Sprout, Sapling, Orchard, - /// Lockbox, and Ironwood value pool entries, keeping the pre-Ironwood pool - /// indexes stable. + /// Converts a [`ValueBalance`] to RPC value pool entries in activation order. pub fn value_pools( value_balance: ValueBalance, delta_balance: Option>, - ) -> [Self; 6] { + ) -> BlockchainValuePoolBalances { [ Self::transparent( value_balance.transparent_amount(), @@ -125,6 +130,11 @@ impl GetBlockchainInfoBalance { value_balance.ironwood_amount(), delta_balance.map(|b| b.ironwood_amount()), ), + #[cfg(zcash_unstable = "nutachyon")] + Self::tachyon( + value_balance.tachyon_amount(), + delta_balance.map(|b| b.tachyon_amount()), + ), ] } @@ -143,3 +153,40 @@ impl GetBlockchainInfoBalance { .expect("at least one pool") } } + +#[cfg(all(test, zcash_unstable = "nutachyon"))] +mod tests { + use super::*; + + #[test] + fn tachyon_pool_is_included_in_rpc_balances_and_chain_supply() { + let tachyon_amount = Amount::try_from(123_456_u64).expect("test amount is valid"); + let value_balance = ValueBalance::from_tachyon_amount(tachyon_amount); + + let pools = GetBlockchainInfoBalance::value_pools(value_balance, None); + + assert_eq!(pools.len(), 7); + assert_eq!(pools[6].id().as_str(), "tachyon"); + assert_eq!(pools[6].chain_value_zat(), tachyon_amount); + assert_eq!( + GetBlockchainInfoBalance::chain_supply(value_balance).chain_value_zat(), + tachyon_amount, + ); + } + + #[test] + fn legacy_rpc_balances_decode_with_an_empty_tachyon_pool() { + let legacy_pools = GetBlockchainInfoBalance::value_pools(ValueBalance::zero(), None) + .into_iter() + .take(6) + .collect(); + + let pools = crate::methods::blockchain_value_pool_balances_from_vec::< + serde::de::value::Error, + >(legacy_pools) + .expect("six-pool RPC balances remain compatible"); + + assert_eq!(pools[6].id().as_str(), "tachyon"); + assert_eq!(pools[6].chain_value_zat(), Amount::::zero()); + } +} diff --git a/crates/zakura-rpc/tests/serialization_tests.rs b/crates/zakura-rpc/tests/serialization_tests.rs index 2d9b89bee0..0e32b44b52 100644 --- a/crates/zakura-rpc/tests/serialization_tests.rs +++ b/crates/zakura-rpc/tests/serialization_tests.rs @@ -236,12 +236,22 @@ fn test_get_blockchain_info_accepts_legacy_value_pools() -> Result<(), Box Result<(), Box Result<(), Box Date: Mon, 7 Sep 2026 12:20:25 -0400 Subject: [PATCH 14/15] feat(mining): generate Tachyon reward workload --- crates/zakura-rpc/src/config/mining.rs | 14 +- crates/zakura-rpc/src/methods.rs | 36 +- .../zakura-rpc/src/methods/tests/snapshot.rs | 2 + .../zakura-rpc/src/methods/tests/vectors.rs | 4 + .../src/methods/types/get_block_template.rs | 57 ++- .../types/get_block_template/tachyon.rs | 4 + .../get_block_template/tachyon_workload.rs | 431 ++++++++++++++++++ .../methods/types/get_block_template/tests.rs | 53 +++ .../src/methods/types/transaction.rs | 22 + crates/zakura-state/src/request.rs | 5 +- crates/zakura-state/src/response.rs | 3 + crates/zakura-state/src/service.rs | 6 + 12 files changed, 631 insertions(+), 6 deletions(-) create mode 100644 crates/zakura-rpc/src/methods/types/get_block_template/tachyon_workload.rs diff --git a/crates/zakura-rpc/src/config/mining.rs b/crates/zakura-rpc/src/config/mining.rs index 15583080d5..44ceca1134 100644 --- a/crates/zakura-rpc/src/config/mining.rs +++ b/crates/zakura-rpc/src/config/mining.rs @@ -57,12 +57,22 @@ pub struct Config { /// Mine blocks using Zakura's internal miner, without an external mining pool or equihash solver. /// - /// This experimental feature is only supported on regtest as it uses null solutions and skips checking - /// for a valid Proof of Work. + /// This experimental feature is only supported on proof-of-work-disabled test networks because + /// it uses null solutions. /// /// The internal miner is off by default. #[serde(default)] pub internal_miner: bool, + + /// Generate a deterministic Tachyon transaction workload for every internally mined block. + /// + /// This test-only workload replaces externally submitted mempool transactions. It pays miner + /// rewards to anyone-can-spend outputs, then shields each output into an independently derived + /// Tachyon recipient as soon as the coinbase matures. It is only accepted together with + /// [`Self::internal_miner`] on a proof-of-work-disabled test network. + #[cfg(zcash_unstable = "nutachyon")] + #[serde(default)] + pub tachyon_workload: bool, } impl Config { diff --git a/crates/zakura-rpc/src/methods.rs b/crates/zakura-rpc/src/methods.rs index 39abe564f7..1a1cbb042f 100644 --- a/crates/zakura-rpc/src/methods.rs +++ b/crates/zakura-rpc/src/methods.rs @@ -2762,6 +2762,14 @@ where precomputed_coinbase = wait_for_new_tip => { let chain_info = fetch_chain_info(read_state.clone()).await?; + // Each mature coinbase reward has one deterministic workload at its first + // spendable height. Do not let the internal miner race that full template + // with the usual empty, provisional tip-change template. + #[cfg(zcash_unstable = "nutachyon")] + if miner_params.tachyon_workload() { + continue; + } + let server_long_poll_id = LongPollInput::new( chain_info.tip_height, chain_info.tip_hash, @@ -2829,7 +2837,33 @@ where let height = chain_info.tip_height.next().map_misc_error()?; - // Randomly select some mempool transactions. + // Randomly select some mempool transactions, or replace them with the internal miner's + // self-funded Tachyon workload. + #[cfg(zcash_unstable = "nutachyon")] + let mempool_txs = if miner_params.tachyon_workload() { + let generated = types::get_block_template::generate_tachyon_workload_transactions( + &self.network, + height, + chain_info.tip_hash, + read_state.clone(), + ) + .await; + + #[cfg(test)] + let generated = generated.into_iter().map(|tx| (0, tx)).collect(); + + generated + } else { + select_mempool_transactions( + &self.network, + height, + miner_params, + mempool_txs, + mempool_tx_deps, + ) + }; + + #[cfg(not(zcash_unstable = "nutachyon"))] let mempool_txs = select_mempool_transactions( &self.network, height, diff --git a/crates/zakura-rpc/src/methods/tests/snapshot.rs b/crates/zakura-rpc/src/methods/tests/snapshot.rs index 5d05edac44..ee2cbbe78b 100644 --- a/crates/zakura-rpc/src/methods/tests/snapshot.rs +++ b/crates/zakura-rpc/src/methods/tests/snapshot.rs @@ -1020,6 +1020,8 @@ pub async fn test_mining_rpcs( miner_memo: None, // TODO: Use default field values when optional features are enabled in tests #8183 internal_miner: true, + #[cfg(zcash_unstable = "nutachyon")] + tachyon_workload: false, }; // nu5 block height diff --git a/crates/zakura-rpc/src/methods/tests/vectors.rs b/crates/zakura-rpc/src/methods/tests/vectors.rs index 9f0d30e3b4..773d19a69f 100644 --- a/crates/zakura-rpc/src/methods/tests/vectors.rs +++ b/crates/zakura-rpc/src/methods/tests/vectors.rs @@ -3094,6 +3094,8 @@ async fn gbt_with(net: Network, addr: ZcashAddress) { extra_coinbase_data: None, miner_memo: None, internal_miner: true, + #[cfg(zcash_unstable = "nutachyon")] + tachyon_workload: false, }; // nu5 block height @@ -3784,6 +3786,8 @@ async fn rpc_getdifficulty() { extra_coinbase_data: None, miner_memo: None, internal_miner: true, + #[cfg(zcash_unstable = "nutachyon")] + tachyon_workload: false, }; // nu5 block height diff --git a/crates/zakura-rpc/src/methods/types/get_block_template.rs b/crates/zakura-rpc/src/methods/types/get_block_template.rs index 13ba7fcd15..da64eb0b31 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template.rs @@ -5,6 +5,8 @@ pub mod parameters; pub mod proposal; #[cfg(zcash_unstable = "nutachyon")] pub mod tachyon; +#[cfg(zcash_unstable = "nutachyon")] +mod tachyon_workload; pub mod zip317; #[cfg(test)] @@ -66,6 +68,11 @@ pub use parameters::{ GetBlockTemplateCapability, GetBlockTemplateParameters, GetBlockTemplateRequestMode, }; pub use proposal::{BlockProposalResponse, BlockTemplateTimeSource}; +#[cfg(zcash_unstable = "nutachyon")] +pub(crate) use tachyon_workload::{ + generate_transactions as generate_tachyon_workload_transactions, REDEEM_SCRIPT_HASH, + TRANSACTIONS_PER_BLOCK, +}; /// An alias to indicate that a usize value represents the depth of in-block dependencies of a /// transaction. @@ -444,6 +451,10 @@ pub struct MinerParams { /// /// Applies only if [`Self::addr`] contains a shielded component. memo: Option, + + /// Generate the internal miner's Tachyon test workload. + #[cfg(zcash_unstable = "nutachyon")] + tachyon_workload: bool, } /// Builds the coinbase input data for a block Zakura constructs. @@ -465,6 +476,30 @@ impl MinerParams { // (`extra_coinbase_data` is length-validated), not a recoverable error. #[allow(clippy::unwrap_in_result)] pub fn new(net: &Network, conf: config::mining::Config) -> Result { + #[cfg(zcash_unstable = "nutachyon")] + if conf.tachyon_workload && !conf.internal_miner { + return Err(MinerParamsError::TachyonWorkloadNeedsInternalMiner); + } + #[cfg(zcash_unstable = "nutachyon")] + if conf.tachyon_workload && (!net.is_a_test_network() || !net.disable_pow()) { + return Err(MinerParamsError::UnsupportedTachyonWorkloadNetwork); + } + + #[cfg(zcash_unstable = "nutachyon")] + let workload_enabled = conf.tachyon_workload; + + #[cfg(zcash_unstable = "nutachyon")] + let addr = if workload_enabled { + Address::Transparent(zcash_transparent::address::TransparentAddress::ScriptHash( + REDEEM_SCRIPT_HASH, + )) + } else { + conf.miner_address + .map(|addr| Address::try_from_zcash_address(net, addr)) + .ok_or(MinerParamsError::MissingAddr)?? + }; + + #[cfg(not(zcash_unstable = "nutachyon"))] let addr = conf .miner_address .map(|addr| Address::try_from_zcash_address(net, addr)) @@ -485,7 +520,13 @@ impl MinerParams { .map(|memo| MemoBytes::from_bytes(memo.as_bytes())) .transpose()?; - Ok(Self { addr, data, memo }) + Ok(Self { + addr, + data, + memo, + #[cfg(zcash_unstable = "nutachyon")] + tachyon_workload: workload_enabled, + }) } /// Returns the miner address. @@ -503,6 +544,12 @@ impl MinerParams { self.memo.as_ref() } + /// Is the internal miner's Tachyon workload enabled? + #[cfg(zcash_unstable = "nutachyon")] + pub fn tachyon_workload(&self) -> bool { + self.tachyon_workload + } + /// Randomizes the memo. pub fn randomize_memo(&mut self) { let mut random = [0u8; 512]; @@ -524,6 +571,8 @@ impl From
for MinerParams { addr, data: None, memo: None, + #[cfg(zcash_unstable = "nutachyon")] + tachyon_workload: false, } } } @@ -537,6 +586,12 @@ pub enum MinerParamsError { InvalidAddr(zcash_address::ConversionError<&'static str>), #[error(transparent)] InvalidMemo(#[from] zcash_protocol::memo::Error), + #[cfg(zcash_unstable = "nutachyon")] + #[error("tachyon_workload requires internal_miner = true")] + TachyonWorkloadNeedsInternalMiner, + #[cfg(zcash_unstable = "nutachyon")] + #[error("tachyon_workload is only supported on proof-of-work-disabled test networks")] + UnsupportedTachyonWorkloadNetwork, } impl From> for MinerParamsError { diff --git a/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs index b7e41c3371..332b1dd13f 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs @@ -446,6 +446,7 @@ mod tests { .map(|tx| tx.transaction.id().mined_id()) .collect::>(); let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(anchor), anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), blocks: BTreeMap::new(), revealed_tachygrams: HashSet::new(), @@ -485,6 +486,7 @@ mod tests { .expect("hardcoded genesis block deserializes"); intervening_block.transactions = vec![intervening_transaction]; let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(target_anchor), anchor_heights: HashMap::from([ (tachyon::Anchor::from(start_anchor), Height(10)), (tachyon::Anchor::from(target_anchor), Height(11)), @@ -518,6 +520,7 @@ mod tests { let child = verified_dependent_transaction(parent.transaction.id().mined_id()); let transactions = vec![parent, child]; let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(anchor), anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), blocks: BTreeMap::new(), revealed_tachygrams: HashSet::new(), @@ -544,6 +547,7 @@ mod tests { .copied() .expect("test stamp has tachygrams"); let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(anchor), anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), blocks: BTreeMap::new(), revealed_tachygrams: HashSet::from([tachyon::Tachygram::from(revealed)]), diff --git a/crates/zakura-rpc/src/methods/types/get_block_template/tachyon_workload.rs b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon_workload.rs new file mode 100644 index 0000000000..932aac90a7 --- /dev/null +++ b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon_workload.rs @@ -0,0 +1,431 @@ +//! Internally generated Tachyon transactions for proof-of-work-disabled test networks. + +use std::{collections::HashSet, sync::Arc, time::Duration}; + +use rand_10::{rngs::StdRng, SeedableRng}; +use tokio::time::timeout; +use tower::{BoxError, Service, ServiceExt}; +use zcash_tachyon::{ + action, bundle, + entropy::ActionEntropy, + keys::private, + note::{CommitmentTrapdoor, Note}, + nullifier, value, TachyonBundle, +}; + +use zakura_chain::{ + amount::Amount, + block::{self, Height}, + parameters::{Network, NetworkUpgrade}, + transaction::{HashType, LockTime, Transaction, UnminedTx, VerifiedUnminedTx}, + transparent::{self, MIN_TRANSPARENT_COINBASE_MATURITY}, +}; +use zakura_state::{ReadRequest, ReadResponse}; + +/// The number of independently proof-stamped transactions generated in each eligible block. +pub const TRANSACTIONS_PER_BLOCK: usize = 3; + +/// HASH160 of the one-byte `OP_TRUE` redeem script used by the workload's coinbase outputs. +pub const REDEEM_SCRIPT_HASH: [u8; 20] = [ + 0xda, 0x17, 0x45, 0xe9, 0xb5, 0x49, 0xbd, 0x0b, 0xfa, 0x1a, 0x56, 0x99, 0x71, 0xc7, 0x7e, 0xba, + 0x30, 0xcd, 0x5a, 0x4b, +]; + +const OP_TRUE: u8 = 0x51; +const WORKLOAD_FEE: u64 = 50_000; +const STATE_QUERY_TIMEOUT: Duration = Duration::from_secs(5); + +/// Generate transactions that shield the first newly mature internal-miner reward into Tachyon. +/// +/// The miner reward at each height is split into [`TRANSACTIONS_PER_BLOCK`] outputs. Exactly when +/// that reward matures, this function spends every matching output into a separately generated +/// Tachyon recipient. Recipient randomness and mock-Ragu proof randomness are seeded by the source +/// outpoint, so repeated templates at the same height contain identical transactions. +pub async fn generate_transactions( + network: &Network, + candidate_height: Height, + tip_hash: block::Hash, + read_state: S, +) -> Vec +where + S: Service + Send + Clone + 'static, + S::Future: Send + 'static, +{ + if NetworkUpgrade::current(network, candidate_height) != NetworkUpgrade::NuTachyon { + return Vec::new(); + } + + let Some(source_height) = candidate_height + .0 + .checked_sub(MIN_TRANSPARENT_COINBASE_MATURITY) + .map(Height) + .filter(|height| *height > Height::MIN) + else { + return Vec::new(); + }; + + let block_request = read_state + .clone() + .oneshot(ReadRequest::Block(source_height.into())); + let mining_request = read_state.oneshot(ReadRequest::TachyonMiningData { + anchors: HashSet::new(), + tachygrams: HashSet::new(), + tip_hash, + candidate_height, + }); + + let responses = timeout(STATE_QUERY_TIMEOUT, async { + tokio::try_join!(block_request, mining_request) + }) + .await; + + let (source_block, tip_anchor) = match responses { + Ok(Ok(( + ReadResponse::Block(Some(source_block)), + ReadResponse::TachyonMiningData(Some(mining_data)), + ))) => (source_block, mining_data.tip_anchor), + Ok(Ok((_block, _mining_data))) => { + tracing::warn!(?source_height, "could not load Tachyon workload state"); + return Vec::new(); + } + Ok(Err(error)) => { + tracing::warn!(?error, "could not query Tachyon workload state"); + return Vec::new(); + } + Err(_) => { + tracing::warn!("timed out querying Tachyon workload state"); + return Vec::new(); + } + }; + + let Some(coinbase) = source_block.transactions.first() else { + tracing::warn!( + ?source_height, + "Tachyon workload source block has no coinbase" + ); + return Vec::new(); + }; + + let source_hash = coinbase.hash(); + let workload_script = workload_address(network).script(); + let mut transactions = Vec::with_capacity(TRANSACTIONS_PER_BLOCK); + + for (index, output) in coinbase.outputs().iter().enumerate() { + if output.lock_script != workload_script { + continue; + } + + let outpoint = transparent::OutPoint::from_usize(source_hash, index); + match build_transaction(candidate_height, outpoint, output.clone(), tip_anchor) { + Ok(transaction) => transactions.push(transaction), + Err(error) => { + tracing::warn!( + ?source_height, + index, + %error, + "could not build Tachyon workload transaction" + ); + return Vec::new(); + } + } + } + + if transactions.len() != TRANSACTIONS_PER_BLOCK { + tracing::warn!( + ?source_height, + expected = TRANSACTIONS_PER_BLOCK, + actual = transactions.len(), + "mature coinbase does not have the expected Tachyon workload outputs", + ); + return Vec::new(); + } + + transactions +} + +fn workload_address(network: &Network) -> transparent::Address { + transparent::Address::from_script_hash(network.t_addr_kind(), REDEEM_SCRIPT_HASH) +} + +fn build_transaction( + candidate_height: Height, + outpoint: transparent::OutPoint, + source_output: transparent::Output, + anchor: zakura_chain::tachyon::Anchor, +) -> Result { + let input_value = u64::try_from(source_output.value.zatoshis()) + .map_err(|error| format!("invalid source value: {error}"))?; + let output_value = input_value + .checked_sub(WORKLOAD_FEE) + .ok_or_else(|| "miner reward part is smaller than the workload fee".to_string())?; + + let mut seed: [u8; 32] = outpoint.hash.into(); + for (seed_byte, index_byte) in seed + .iter_mut() + .zip(outpoint.index.to_le_bytes().iter().cycle()) + { + *seed_byte ^= index_byte; + } + let mut rng = StdRng::from_seed(seed); + + let spending_key = private::SpendingKey::random(&mut rng); + let note = Note { + pk: spending_key.derive_payment_key(), + value: value::Positive::try_from(output_value) + .map_err(|error| format!("invalid Tachyon note value: {error}"))?, + psi: nullifier::Trapdoor::random(&mut rng), + rcm: CommitmentTrapdoor::random(&mut rng), + }; + let output_plan = action::Plan::output( + note, + ActionEntropy::random(&mut rng), + value::Trapdoor::random(&mut rng), + ); + let plan = bundle::Plan::new(Vec::new(), vec![output_plan]); + let proof_key = spending_key.derive_proof_private(); + let auth_key = spending_key.derive_auth_private(); + let anchor_bytes: [u8; 32] = anchor.into(); + let anchor = zcash_tachyon::Anchor::read(&anchor_bytes[..]) + .map_err(|error| format!("invalid stored Tachyon anchor: {error}"))?; + let stamp = plan + .clone() + .stamp_plan(anchor) + .prove(&mut rng, &proof_key, Vec::new()) + .map_err(|error| format!("could not create mock Ragu proof: {error}"))?; + + let placeholder_bundle = plan + .sign(&mut rng, &[0; 32], &auth_key) + .map_err(|error| format!("could not create placeholder signatures: {error}"))? + .stamp(stamp.clone()); + let placeholder = transaction( + candidate_height, + outpoint, + TachyonBundle::Proven(placeholder_bundle), + ); + let spent_outputs = Arc::new(vec![source_output]); + let sighash = placeholder + .sighash( + NetworkUpgrade::NuTachyon, + HashType::ALL, + spent_outputs.clone(), + None, + ) + .map_err(|error| format!("could not compute V7 sighash: {error}"))?; + + let bundle = plan + .sign(&mut rng, sighash.as_ref(), &auth_key) + .map_err(|error| format!("could not sign Tachyon bundle: {error}"))? + .stamp(stamp); + let transaction = transaction(candidate_height, outpoint, TachyonBundle::Proven(bundle)); + let fee = Amount::try_from(WORKLOAD_FEE) + .map_err(|error| format!("invalid Tachyon workload fee: {error}"))?; + + VerifiedUnminedTx::new(UnminedTx::from(transaction), fee, 0, 0, spent_outputs) + .map_err(|error| format!("invalid generated transaction: {error}")) +} + +fn transaction( + candidate_height: Height, + outpoint: transparent::OutPoint, + bundle: TachyonBundle, +) -> Transaction { + Transaction::V7 { + network_upgrade: NetworkUpgrade::NuTachyon, + lock_time: LockTime::min_lock_time_timestamp(), + expiry_height: candidate_height, + inputs: vec![transparent::Input::PrevOut { + outpoint, + // Push the one-byte OP_TRUE redeem script onto the P2SH script stack. + unlock_script: transparent::Script::new(&[1, OP_TRUE]), + sequence: u32::MAX, + }], + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + ironwood_shielded_data: None, + tachyon_shielded_data: Some(bundle.into()), + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use zakura_chain::{ + amount::NonNegative, + parameters::testnet::ConfiguredActivationHeights, + serialization::{ZcashDeserialize, ZcashDeserializeInto}, + transaction::Hash, + }; + use zakura_node_services::BoxError; + use zakura_state::TachyonMiningData; + use zakura_test::mock_service::MockService; + + use super::*; + + #[test] + fn generated_transaction_has_a_valid_tachyon_bundle() { + let network = Network::new_regtest(Default::default()); + let source_output = transparent::Output::new( + Amount::::try_from(100_000_000u64).expect("test value is valid"), + workload_address(&network).script(), + ); + let outpoint = transparent::OutPoint { + hash: Hash([7; 32]), + index: 2, + }; + + let generated = build_transaction( + Height(200), + outpoint, + source_output, + zakura_chain::tachyon::Anchor::default(), + ) + .expect("output-only mock proof can be generated"); + let transaction = generated.transaction.transaction(); + let TachyonBundle::Proven(bundle) = &transaction + .tachyon_shielded_data() + .expect("generated transaction has Tachyon data") + .0 + else { + panic!("generated transaction must carry its own proof"); + }; + + assert!(bundle.is_autonome()); + assert_eq!(transaction.outputs().len(), 0); + assert_eq!(transaction.inputs().len(), 1); + assert_eq!( + generated.miner_fee, + Amount::::try_from(WORKLOAD_FEE).unwrap() + ); + bundle + .verify_coverage(&[]) + .expect("proof stamp covers the generated action"); + } + + #[tokio::test] + async fn mature_reward_generates_and_aggregates_three_transactions() { + let network = Network::new_regtest( + ConfiguredActivationHeights { + canopy: Some(1), + nu5: Some(2), + nu6: Some(3), + nu6_1: Some(4), + nu6_2: Some(5), + nu6_3: Some(6), + nu7: Some(8), + nu_tachyon: Some(10), + ..Default::default() + } + .into(), + ); + let candidate_height = Height(200); + let source_height = Height(candidate_height.0 - MIN_TRANSPARENT_COINBASE_MATURITY); + let miner_params = super::super::MinerParams::new( + &network, + crate::config::mining::Config { + internal_miner: true, + tachyon_workload: true, + ..Default::default() + }, + ) + .expect("test network supports the workload"); + let coinbase = crate::methods::types::transaction::TransactionTemplate::new_coinbase( + &network, + source_height, + &miner_params, + Amount::zero(), + ) + .expect("workload coinbase can be built"); + let coinbase = coinbase + .data + .as_ref() + .zcash_deserialize_into() + .expect("workload coinbase deserializes"); + let mut source_block = + block::Block::zcash_deserialize(&zakura_test::vectors::BLOCK_MAINNET_GENESIS_BYTES[..]) + .expect("hardcoded genesis block deserializes"); + source_block.transactions = vec![Arc::new(coinbase)]; + let tip_hash = source_block.hash(); + let tip_anchor = zakura_chain::tachyon::Anchor::default(); + + let mut read_state: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + let generation = + generate_transactions(&network, candidate_height, tip_hash, read_state.clone()); + let responses = async { + read_state + .expect_request(ReadRequest::Block(source_height.into())) + .await + .respond(ReadResponse::Block(Some(Arc::new(source_block)))); + read_state + .expect_request(ReadRequest::TachyonMiningData { + anchors: HashSet::new(), + tachygrams: HashSet::new(), + tip_hash, + candidate_height, + }) + .await + .respond(ReadResponse::TachyonMiningData(Some(TachyonMiningData { + tip_anchor, + anchor_heights: HashMap::new(), + blocks: Default::default(), + revealed_tachygrams: HashSet::new(), + }))); + }; + let (generated, ()) = tokio::join!(generation, responses); + + assert_eq!(generated.len(), TRANSACTIONS_PER_BLOCK); + assert!(generated.iter().all(|transaction| matches!( + &transaction + .transaction + .transaction() + .tachyon_shielded_data() + .expect("generated transaction has Tachyon data") + .0, + TachyonBundle::Proven(bundle) if bundle.is_autonome() + ))); + + let anchor_heights = HashMap::from([(tip_anchor, Height(candidate_height.0 - 1))]); + let aggregation = super::super::tachyon::aggregate_transactions( + network, + candidate_height, + tip_hash, + read_state.clone(), + generated, + ); + let aggregation_response = async { + read_state + .expect_request_that(|request| { + matches!(request, ReadRequest::TachyonMiningData { .. }) + }) + .await + .respond(ReadResponse::TachyonMiningData(Some(TachyonMiningData { + tip_anchor, + anchor_heights, + blocks: Default::default(), + revealed_tachygrams: HashSet::new(), + }))); + }; + let (aggregated, ()) = tokio::join!(aggregation, aggregation_response); + + assert_eq!(aggregated.len(), TRANSACTIONS_PER_BLOCK); + assert!(matches!( + &aggregated[0] + .transaction + .transaction() + .tachyon_shielded_data() + .expect("aggregate has Tachyon data") + .0, + TachyonBundle::Proven(bundle) if bundle.is_aggregate() + )); + assert!(aggregated[1..].iter().all(|transaction| matches!( + transaction + .transaction + .transaction() + .tachyon_shielded_data() + .expect("adjunct has Tachyon data") + .0, + TachyonBundle::Adjunct(_) + ))); + } +} diff --git a/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs b/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs index 9744e625bf..081d1e23ef 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template/tests.rs @@ -126,6 +126,59 @@ fn local_genesis_activation_coinbase_includes_lockbox_marker() -> anyhow::Result Ok(()) } +/// The Tachyon workload is limited to internal mining on proof-of-work-disabled test networks and +/// splits the miner reward into independently spendable outputs. +#[cfg(zcash_unstable = "nutachyon")] +#[test] +fn tachyon_workload_coinbase_outputs() { + use crate::{ + config::mining::Config, + methods::types::get_block_template::{REDEEM_SCRIPT_HASH, TRANSACTIONS_PER_BLOCK}, + }; + + let network = Network::new_regtest(Default::default()); + let config = Config { + internal_miner: true, + tachyon_workload: true, + ..Default::default() + }; + let miner_params = MinerParams::new(&network, config.clone()) + .expect("proof-of-work-disabled test networks support the Tachyon workload"); + let height = Height(20); + let template = + TransactionTemplate::new_coinbase(&network, height, &miner_params, Amount::zero()) + .expect("workload coinbase can be built"); + let coinbase: Transaction = template + .data() + .as_ref() + .zcash_deserialize_into() + .expect("workload coinbase deserializes"); + let workload_script = + transparent::Address::from_script_hash(network.t_addr_kind(), REDEEM_SCRIPT_HASH).script(); + + assert_eq!( + coinbase + .outputs() + .iter() + .filter(|output| output.lock_script == workload_script) + .count(), + TRANSACTIONS_PER_BLOCK, + ); + + let mut missing_internal_miner = config; + missing_internal_miner.internal_miner = false; + assert!(MinerParams::new(&network, missing_internal_miner).is_err()); + assert!(MinerParams::new( + &Network::Mainnet, + Config { + internal_miner: true, + tachyon_workload: true, + ..Default::default() + } + ) + .is_err()); +} + /// A NuTachyon template must convert into a mineable proposal block. #[cfg(zcash_unstable = "nutachyon")] #[test] diff --git a/crates/zakura-rpc/src/methods/types/transaction.rs b/crates/zakura-rpc/src/methods/types/transaction.rs index 7fc8dd9bc4..a2209b0ac8 100644 --- a/crates/zakura-rpc/src/methods/types/transaction.rs +++ b/crates/zakura-rpc/src/methods/types/transaction.rs @@ -220,6 +220,28 @@ impl TransactionTemplate { }; let add_transparent_reward = |builder: &mut Builder<_, _>, addr| { + #[cfg(zcash_unstable = "nutachyon")] + if miner_params.tachyon_workload() { + let output_count = super::get_block_template::TRANSACTIONS_PER_BLOCK; + let output_count_u64 = + u64::try_from(output_count).expect("the fixed workload count fits in u64"); + let total = miner_reward.into_u64(); + let quotient = total / output_count_u64; + let remainder = total % output_count_u64; + + return (0..output_count) + .try_for_each(|index| { + let value = quotient + u64::from(index == 0) * remainder; + builder.add_transparent_output( + addr, + Zatoshis::from_u64(value) + .expect("parts of a valid miner reward remain valid"), + ) + }) + .map_err(|err| tracing::error!("Failed to add transparent output: {err}")) + .ok(); + } + trace_err!( builder.add_transparent_output(addr, miner_reward), "transparent" diff --git a/crates/zakura-state/src/request.rs b/crates/zakura-state/src/request.rs index 15e7e79e70..2728582331 100644 --- a/crates/zakura-state/src/request.rs +++ b/crates/zakura-state/src/request.rs @@ -1477,8 +1477,9 @@ pub enum ReadRequest { /// /// Returns [`ReadResponse::TachyonMiningData`] if `tip_hash` is still the current best-chain /// tip. Unknown anchors are omitted. For every epoch represented by a known anchor, the - /// response also contains the blocks between the earliest and latest requested anchors and - /// any requested tachygrams already revealed within the candidate's two-epoch window. + /// response also contains the current tip anchor, the blocks between the earliest and latest + /// requested anchors, and any requested tachygrams already revealed within the candidate's + /// two-epoch window. #[cfg(zcash_unstable = "nutachyon")] TachyonMiningData { /// Anchors referenced by the selected autonome transactions. diff --git a/crates/zakura-state/src/response.rs b/crates/zakura-state/src/response.rs index 53322309f1..2578f6d1e1 100644 --- a/crates/zakura-state/src/response.rs +++ b/crates/zakura-state/src/response.rs @@ -43,6 +43,9 @@ mod tests; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg(zcash_unstable = "nutachyon")] pub struct TachyonMiningData { + /// The Tachyon anchor at the current best-chain tip. + pub tip_anchor: tachyon::Anchor, + /// Heights that created the requested anchors. Unknown anchors are omitted. pub anchor_heights: HashMap, diff --git a/crates/zakura-state/src/service.rs b/crates/zakura-state/src/service.rs index 9f236b6cbb..6cbaf4c6ef 100644 --- a/crates/zakura-state/src/service.rs +++ b/crates/zakura-state/src/service.rs @@ -2528,6 +2528,11 @@ impl Service for ReadStateService { }) .collect(); + let tip_anchor = best_chain.as_ref().map_or_else( + || state.db.tachyon_anchor_for_tip(), + |chain| chain.tachyon_anchor_for_tip(), + ); + let mut epoch_ranges = HashMap::new(); for &height in anchor_heights.values() { let Some(epoch) = zakura_chain::tachyon::epoch(&state.network, height) else { @@ -2583,6 +2588,7 @@ impl Service for ReadStateService { Ok(ReadResponse::TachyonMiningData(Some( crate::response::TachyonMiningData { + tip_anchor, anchor_heights, blocks, revealed_tachygrams, From a871092d07d48cd008a7bef5045cc5f7070854de Mon Sep 17 00:00:00 2001 From: c-node Date: Mon, 7 Sep 2026 12:28:08 -0400 Subject: [PATCH 15/15] feat(state): expose Tachyon tip anchor for mining --- .../src/methods/types/get_block_template/tachyon.rs | 4 ++++ crates/zakura-state/src/request.rs | 5 +++-- crates/zakura-state/src/response.rs | 3 +++ crates/zakura-state/src/service.rs | 6 ++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs index b7e41c3371..332b1dd13f 100644 --- a/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs +++ b/crates/zakura-rpc/src/methods/types/get_block_template/tachyon.rs @@ -446,6 +446,7 @@ mod tests { .map(|tx| tx.transaction.id().mined_id()) .collect::>(); let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(anchor), anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), blocks: BTreeMap::new(), revealed_tachygrams: HashSet::new(), @@ -485,6 +486,7 @@ mod tests { .expect("hardcoded genesis block deserializes"); intervening_block.transactions = vec![intervening_transaction]; let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(target_anchor), anchor_heights: HashMap::from([ (tachyon::Anchor::from(start_anchor), Height(10)), (tachyon::Anchor::from(target_anchor), Height(11)), @@ -518,6 +520,7 @@ mod tests { let child = verified_dependent_transaction(parent.transaction.id().mined_id()); let transactions = vec![parent, child]; let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(anchor), anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), blocks: BTreeMap::new(), revealed_tachygrams: HashSet::new(), @@ -544,6 +547,7 @@ mod tests { .copied() .expect("test stamp has tachygrams"); let mining_data = TachyonMiningData { + tip_anchor: tachyon::Anchor::from(anchor), anchor_heights: HashMap::from([(tachyon::Anchor::from(anchor), Height(10))]), blocks: BTreeMap::new(), revealed_tachygrams: HashSet::from([tachyon::Tachygram::from(revealed)]), diff --git a/crates/zakura-state/src/request.rs b/crates/zakura-state/src/request.rs index 15e7e79e70..2728582331 100644 --- a/crates/zakura-state/src/request.rs +++ b/crates/zakura-state/src/request.rs @@ -1477,8 +1477,9 @@ pub enum ReadRequest { /// /// Returns [`ReadResponse::TachyonMiningData`] if `tip_hash` is still the current best-chain /// tip. Unknown anchors are omitted. For every epoch represented by a known anchor, the - /// response also contains the blocks between the earliest and latest requested anchors and - /// any requested tachygrams already revealed within the candidate's two-epoch window. + /// response also contains the current tip anchor, the blocks between the earliest and latest + /// requested anchors, and any requested tachygrams already revealed within the candidate's + /// two-epoch window. #[cfg(zcash_unstable = "nutachyon")] TachyonMiningData { /// Anchors referenced by the selected autonome transactions. diff --git a/crates/zakura-state/src/response.rs b/crates/zakura-state/src/response.rs index 53322309f1..2578f6d1e1 100644 --- a/crates/zakura-state/src/response.rs +++ b/crates/zakura-state/src/response.rs @@ -43,6 +43,9 @@ mod tests; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg(zcash_unstable = "nutachyon")] pub struct TachyonMiningData { + /// The Tachyon anchor at the current best-chain tip. + pub tip_anchor: tachyon::Anchor, + /// Heights that created the requested anchors. Unknown anchors are omitted. pub anchor_heights: HashMap, diff --git a/crates/zakura-state/src/service.rs b/crates/zakura-state/src/service.rs index 9f236b6cbb..6cbaf4c6ef 100644 --- a/crates/zakura-state/src/service.rs +++ b/crates/zakura-state/src/service.rs @@ -2528,6 +2528,11 @@ impl Service for ReadStateService { }) .collect(); + let tip_anchor = best_chain.as_ref().map_or_else( + || state.db.tachyon_anchor_for_tip(), + |chain| chain.tachyon_anchor_for_tip(), + ); + let mut epoch_ranges = HashMap::new(); for &height in anchor_heights.values() { let Some(epoch) = zakura_chain::tachyon::epoch(&state.network, height) else { @@ -2583,6 +2588,7 @@ impl Service for ReadStateService { Ok(ReadResponse::TachyonMiningData(Some( crate::response::TachyonMiningData { + tip_anchor, anchor_heights, blocks, revealed_tachygrams,