diff --git a/zebra-chain/src/orchard_zsa/asset_state.rs b/zebra-chain/src/orchard_zsa/asset_state.rs index 1ecf4a76091..264951bfd52 100644 --- a/zebra-chain/src/orchard_zsa/asset_state.rs +++ b/zebra-chain/src/orchard_zsa/asset_state.rs @@ -372,6 +372,17 @@ impl std::ops::Add for IssuedAssetsChange { } } } + +impl From> for IssuedAssetsChange { + fn from(change: Arc<[IssuedAssetsChange]>) -> Self { + change + .iter() + .cloned() + .reduce(|a, b| a + b) + .unwrap_or_default() + } +} + /// Used in snapshot test for `getassetstate` RPC method. // TODO: Replace with `AssetBase::random()` or a known value. pub trait RandomAssetBase { diff --git a/zebra-consensus/src/block.rs b/zebra-consensus/src/block.rs index aa4bf94c72f..207a202f6ea 100644 --- a/zebra-consensus/src/block.rs +++ b/zebra-consensus/src/block.rs @@ -15,7 +15,7 @@ use std::{ }; use chrono::Utc; -use futures::stream::FuturesOrdered; +use futures::stream::FuturesUnordered; use futures_util::FutureExt; use thiserror::Error; use tower::{Service, ServiceExt}; @@ -226,7 +226,7 @@ where tx::check::coinbase_outputs_are_decryptable(&coinbase_tx, &network, height)?; // Send transactions to the transaction verifier to be checked - let mut async_checks = FuturesOrdered::new(); + let mut async_checks = FuturesUnordered::new(); let known_utxos = Arc::new(transparent::new_ordered_outputs( &block, @@ -243,7 +243,7 @@ where height, time: block.header.time, }); - async_checks.push_back(rsp); + async_checks.push(rsp); } tracing::trace!(len = async_checks.len(), "built async tx checks"); @@ -252,7 +252,6 @@ where // Sum up some block totals from the transaction responses. let mut legacy_sigop_count = 0; let mut block_miner_fees = Ok(Amount::zero()); - let mut issued_assets_changes = Vec::new(); use futures::StreamExt; while let Some(result) = async_checks.next().await { @@ -261,7 +260,6 @@ where tx_id: _, miner_fee, legacy_sigop_count: tx_legacy_sigop_count, - issued_assets_change, } = result .map_err(Into::into) .map_err(VerifyBlockError::Transaction)? @@ -276,8 +274,6 @@ where if let Some(miner_fee) = miner_fee { block_miner_fees += miner_fee; } - - issued_assets_changes.push(issued_assets_change); } // Check the summed block totals @@ -327,7 +323,6 @@ where new_outputs, transaction_hashes, deferred_balance: Some(expected_deferred_amount), - issued_assets_changes: issued_assets_changes.into(), }; // Return early for proposal requests when getblocktemplate-rpcs feature is enabled diff --git a/zebra-consensus/src/checkpoint.rs b/zebra-consensus/src/checkpoint.rs index f6520ba5564..039ea6e33e3 100644 --- a/zebra-consensus/src/checkpoint.rs +++ b/zebra-consensus/src/checkpoint.rs @@ -42,7 +42,7 @@ use crate::{ Progress::{self, *}, TargetHeight::{self, *}, }, - error::{BlockError, SubsidyError, TransactionError}, + error::{BlockError, SubsidyError}, funding_stream_values, BoxError, ParameterCheckpoint as _, }; @@ -619,8 +619,7 @@ where }; // don't do precalculation until the block passes basic difficulty checks - let block = CheckpointVerifiedBlock::new(block, Some(hash), expected_deferred_amount) - .ok_or_else(|| VerifyBlockError::from(TransactionError::InvalidAssetIssuanceOrBurn))?; + let block = CheckpointVerifiedBlock::new(block, Some(hash), expected_deferred_amount); crate::block::check::merkle_root_validity( &self.network, diff --git a/zebra-consensus/src/error.rs b/zebra-consensus/src/error.rs index 9aa41103910..8fe14c62d52 100644 --- a/zebra-consensus/src/error.rs +++ b/zebra-consensus/src/error.rs @@ -239,9 +239,6 @@ pub enum TransactionError { #[error("failed to verify ZIP-317 transaction rules, transaction was not inserted to mempool")] #[cfg_attr(any(test, feature = "proptest-impl"), proptest(skip))] Zip317(#[from] zebra_chain::transaction::zip317::Error), - - #[error("failed to validate asset issuance and/or burns")] - InvalidAssetIssuanceOrBurn, } impl From for TransactionError { diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index ca06395b68c..8c5a6d69c92 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -19,7 +19,6 @@ use tracing::Instrument; use zebra_chain::{ amount::{Amount, NonNegative}, block, orchard, - orchard_zsa::IssuedAssetsChange, parameters::{Network, NetworkUpgrade}, primitives::Groth16Proof, sapling, @@ -144,10 +143,6 @@ pub enum Response { /// The number of legacy signature operations in this transaction's /// transparent inputs and outputs. legacy_sigop_count: u64, - - /// The changes to the issued assets map that should be applied for - /// this transaction. - issued_assets_change: IssuedAssetsChange, }, /// A response to a mempool transaction verification request. @@ -485,7 +480,6 @@ where tx_id, miner_fee, legacy_sigop_count, - issued_assets_change: IssuedAssetsChange::from_transaction(&tx).ok_or(TransactionError::InvalidAssetIssuanceOrBurn)?, }, Request::Mempool { transaction, .. } => { let transaction = VerifiedUnminedTx::new( diff --git a/zebra-state/src/arbitrary.rs b/zebra-state/src/arbitrary.rs index 183567b5794..352ad550159 100644 --- a/zebra-state/src/arbitrary.rs +++ b/zebra-state/src/arbitrary.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use zebra_chain::{ amount::Amount, block::{self, Block}, - orchard_zsa::IssuedAssetsChange, transaction::Transaction, transparent, value_balance::ValueBalance, @@ -31,8 +30,6 @@ impl Prepare for Arc { let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect(); let new_outputs = transparent::new_ordered_outputs_with_height(&block, height, &transaction_hashes); - let issued_assets_changes = IssuedAssetsChange::from_transactions(&block.transactions) - .expect("prepared blocks should be semantically valid"); SemanticallyVerifiedBlock { block, @@ -41,7 +38,6 @@ impl Prepare for Arc { new_outputs, transaction_hashes, deferred_balance: None, - issued_assets_changes, } } } @@ -120,7 +116,6 @@ impl ContextuallyVerifiedBlock { new_outputs, transaction_hashes, deferred_balance: _, - issued_assets_changes: _, } = block.into(); Self { diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index ae223802131..cd71173caae 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -164,9 +164,6 @@ pub struct SemanticallyVerifiedBlock { pub transaction_hashes: Arc<[transaction::Hash]>, /// This block's contribution to the deferred pool. pub deferred_balance: Option>, - /// A precomputed list of the [`IssuedAssetsChange`]s for the transactions in this block, - /// in the same order as `block.transactions`. - pub issued_assets_changes: Arc<[IssuedAssetsChange]>, } /// A block ready to be committed directly to the finalized state with @@ -301,10 +298,9 @@ pub struct FinalizedBlock { pub(super) treestate: Treestate, /// This block's contribution to the deferred pool. pub(super) deferred_balance: Option>, - /// Either changes to be applied to the previous `issued_assets` map for the finalized tip, or - /// updates asset states to be inserted into the finalized state, replacing the previous + /// Updated asset states to be inserted into the finalized state, replacing the previous /// asset states for those asset bases. - pub issued_assets: IssuedAssetsOrChange, + pub issued_assets: Option, } /// Either changes to be applied to the previous `issued_assets` map for the finalized tip, or @@ -319,18 +315,6 @@ pub enum IssuedAssetsOrChange { Change(IssuedAssetsChange), } -impl From> for IssuedAssetsOrChange { - fn from(change: Arc<[IssuedAssetsChange]>) -> Self { - Self::Change( - change - .iter() - .cloned() - .reduce(|a, b| a + b) - .unwrap_or_default(), - ) - } -} - impl From for IssuedAssetsOrChange { fn from(updated_issued_assets: IssuedAssets) -> Self { Self::Updated(updated_issued_assets) @@ -340,13 +324,7 @@ impl From for IssuedAssetsOrChange { impl FinalizedBlock { /// Constructs [`FinalizedBlock`] from [`CheckpointVerifiedBlock`] and its [`Treestate`]. pub fn from_checkpoint_verified(block: CheckpointVerifiedBlock, treestate: Treestate) -> Self { - let issued_assets = block.issued_assets_changes.clone().into(); - - Self::from_semantically_verified( - SemanticallyVerifiedBlock::from(block), - treestate, - issued_assets, - ) + Self::from_semantically_verified(SemanticallyVerifiedBlock::from(block), treestate, None) } /// Constructs [`FinalizedBlock`] from [`ContextuallyVerifiedBlock`] and its [`Treestate`]. @@ -354,7 +332,7 @@ impl FinalizedBlock { block: ContextuallyVerifiedBlock, treestate: Treestate, ) -> Self { - let issued_assets = block.issued_assets.clone().into(); + let issued_assets = Some(block.issued_assets.clone()); Self::from_semantically_verified( SemanticallyVerifiedBlock::from(block), treestate, @@ -366,7 +344,7 @@ impl FinalizedBlock { fn from_semantically_verified( block: SemanticallyVerifiedBlock, treestate: Treestate, - issued_assets: IssuedAssetsOrChange, + issued_assets: Option, ) -> Self { Self { block: block.block, @@ -451,7 +429,6 @@ impl ContextuallyVerifiedBlock { new_outputs, transaction_hashes, deferred_balance, - issued_assets_changes: _, } = semantically_verified; // This is redundant for the non-finalized state, @@ -483,12 +460,10 @@ impl CheckpointVerifiedBlock { block: Arc, hash: Option, deferred_balance: Option>, - ) -> Option { - let issued_assets_change = IssuedAssetsChange::from_transactions(&block.transactions)?; + ) -> Self { let mut block = Self::with_hash(block.clone(), hash.unwrap_or(block.hash())); block.deferred_balance = deferred_balance; - block.issued_assets_changes = issued_assets_change; - Some(block) + block } /// Creates a block that's ready to be committed to the finalized state, @@ -517,7 +492,6 @@ impl SemanticallyVerifiedBlock { new_outputs, transaction_hashes, deferred_balance: None, - issued_assets_changes: Arc::new([]), } } @@ -550,7 +524,6 @@ impl From> for SemanticallyVerifiedBlock { new_outputs, transaction_hashes, deferred_balance: None, - issued_assets_changes: Arc::new([]), } } } @@ -570,7 +543,6 @@ impl From for SemanticallyVerifiedBlock { .constrain::() .expect("deferred balance in a block must me non-negative"), ), - issued_assets_changes: Arc::new([]), } } } diff --git a/zebra-state/src/service/chain_tip.rs b/zebra-state/src/service/chain_tip.rs index 8a0ed517766..04ea61d6982 100644 --- a/zebra-state/src/service/chain_tip.rs +++ b/zebra-state/src/service/chain_tip.rs @@ -116,7 +116,6 @@ impl From for ChainTipBlock { new_outputs: _, transaction_hashes, deferred_balance: _, - issued_assets_changes: _, } = prepared; Self { diff --git a/zebra-state/src/service/check/issuance.rs b/zebra-state/src/service/check/issuance.rs index 98df3c4ed9f..472ffd61fd8 100644 --- a/zebra-state/src/service/check/issuance.rs +++ b/zebra-state/src/service/check/issuance.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc}; -use zebra_chain::orchard_zsa::{AssetBase, AssetState, IssuedAssets}; +use zebra_chain::orchard_zsa::{AssetBase, AssetState, IssuedAssets, IssuedAssetsChange}; use crate::{service::read, SemanticallyVerifiedBlock, ValidateContextError, ZebraDb}; @@ -17,11 +17,10 @@ pub fn valid_burns_and_issuance( // Burns need to be checked and asset state changes need to be applied per tranaction, in case // the asset being burned was also issued in an earlier transaction in the same block. - for (issued_assets_change, transaction) in semantically_verified - .issued_assets_changes - .iter() - .zip(&semantically_verified.block.transactions) - { + for transaction in &semantically_verified.block.transactions { + let issued_assets_change = IssuedAssetsChange::from_transaction(transaction) + .ok_or(ValidateContextError::InvalidIssuance)?; + // Check that no burn item attempts to burn more than the issued supply for an asset for burn in transaction.orchard_burns() { let asset_base = burn.asset(); diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs b/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs index d7df21fda0e..194f2202a87 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs @@ -20,7 +20,6 @@ use zebra_chain::{ }, Block, Height, }, - orchard_zsa::IssuedAssetsChange, parameters::Network::{self, *}, serialization::{ZcashDeserializeInto, ZcashSerialize}, transparent::new_ordered_outputs_with_height, @@ -130,9 +129,6 @@ fn test_block_db_round_trip_with( .collect(); let new_outputs = new_ordered_outputs_with_height(&original_block, Height(0), &transaction_hashes); - let issued_assets_changes = - IssuedAssetsChange::from_transactions(&original_block.transactions) - .expect("issued assets should be valid"); CheckpointVerifiedBlock(SemanticallyVerifiedBlock { block: original_block.clone(), @@ -141,7 +137,6 @@ fn test_block_db_round_trip_with( new_outputs, transaction_hashes, deferred_balance: None, - issued_assets_changes, }) }; diff --git a/zebra-state/src/service/finalized_state/zebra_db/shielded.rs b/zebra-state/src/service/finalized_state/zebra_db/shielded.rs index 1ca7e9cd3dc..7e5664f80ea 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/shielded.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/shielded.rs @@ -20,7 +20,7 @@ use std::{ use zebra_chain::{ block::Height, orchard::{self}, - orchard_zsa::{AssetBase, AssetState}, + orchard_zsa::{AssetBase, AssetState, IssuedAssetsChange}, parallel::tree::NoteCommitmentTrees, sapling, sprout, subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex}, @@ -34,7 +34,7 @@ use crate::{ disk_format::RawBytes, zebra_db::ZebraDb, }, - BoxError, IssuedAssetsOrChange, TypedColumnFamily, + BoxError, TypedColumnFamily, }; // Doc-only items @@ -470,7 +470,7 @@ impl DiskWriteBatch { self.prepare_nullifier_batch(&zebra_db.db, transaction)?; } - self.prepare_issued_assets_batch(zebra_db, &finalized.issued_assets)?; + self.prepare_issued_assets_batch(zebra_db, finalized)?; Ok(()) } @@ -515,18 +515,23 @@ impl DiskWriteBatch { pub fn prepare_issued_assets_batch( &mut self, zebra_db: &ZebraDb, - issued_assets_or_changes: &IssuedAssetsOrChange, + finalized: &FinalizedBlock, ) -> Result<(), BoxError> { let mut batch = zebra_db.issued_assets_cf().with_batch_for_writing(self); - let updated_issued_assets = match issued_assets_or_changes.clone() { - IssuedAssetsOrChange::Updated(issued_assets) => issued_assets, - IssuedAssetsOrChange::Change(issued_assets_change) => issued_assets_change - .apply_with(|asset_base| zebra_db.issued_asset(&asset_base).unwrap_or_default()), - }; - - for (asset_base, updated_issued_asset_state) in updated_issued_assets { - batch = batch.zs_insert(&asset_base, &updated_issued_asset_state); + let updated_issued_assets = + if let Some(updated_issued_assets) = finalized.issued_assets.as_ref() { + updated_issued_assets + } else { + &IssuedAssetsChange::from( + IssuedAssetsChange::from_transactions(&finalized.block.transactions) + .ok_or(BoxError::from("invalid issued assets changes"))?, + ) + .apply_with(|asset_base| zebra_db.issued_asset(&asset_base).unwrap_or_default()) + }; + + for (asset_base, updated_issued_asset_state) in updated_issued_assets.iter() { + batch = batch.zs_insert(asset_base, updated_issued_asset_state); } Ok(())