Adds issued assets to the finalized state
This commit is contained in:
parent
1af120ea98
commit
cc8bc0da97
|
@ -13,4 +13,4 @@ mod issuance;
|
|||
pub(crate) use burn::{Burn, BurnItem, NoBurn};
|
||||
pub(crate) use issuance::IssueData;
|
||||
|
||||
pub use asset_state::{AssetBase, AssetState, AssetStateChange, IssuedAssetsChange};
|
||||
pub use asset_state::{AssetBase, AssetState, AssetStateChange, IssuedAssets, IssuedAssetsChange};
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::block::Block;
|
|||
use super::BurnItem;
|
||||
|
||||
/// The circulating supply and whether that supply has been finalized.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct AssetState {
|
||||
/// Indicates whether the asset is finalized such that no more of it can be issued.
|
||||
pub is_finalized: bool,
|
||||
|
@ -29,6 +29,17 @@ pub struct AssetStateChange {
|
|||
pub supply_change: i128,
|
||||
}
|
||||
|
||||
impl AssetState {
|
||||
fn with_change(mut self, change: AssetStateChange) -> Self {
|
||||
self.is_finalized |= change.is_finalized;
|
||||
self.total_supply = self
|
||||
.total_supply
|
||||
.checked_add_signed(change.supply_change)
|
||||
.expect("burn amounts must not be greater than initial supply");
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetStateChange {
|
||||
fn from_note(is_finalized: bool, note: orchard::Note) -> (AssetBase, Self) {
|
||||
(
|
||||
|
@ -77,6 +88,33 @@ impl std::ops::AddAssign for AssetStateChange {
|
|||
}
|
||||
}
|
||||
|
||||
/// An `issued_asset` map
|
||||
// TODO: Reference ZIP
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct IssuedAssets(HashMap<AssetBase, AssetState>);
|
||||
|
||||
impl IssuedAssets {
|
||||
fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
}
|
||||
|
||||
fn update<'a>(&mut self, issued_assets: impl Iterator<Item = (AssetBase, AssetState)> + 'a) {
|
||||
for (asset_base, asset_state) in issued_assets {
|
||||
self.0.insert(asset_base, asset_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for IssuedAssets {
|
||||
type Item = (AssetBase, AssetState);
|
||||
|
||||
type IntoIter = std::collections::hash_map::IntoIter<AssetBase, AssetState>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// A map of changes to apply to the issued assets map.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct IssuedAssetsChange(HashMap<AssetBase, AssetStateChange>);
|
||||
|
@ -109,4 +147,35 @@ impl IssuedAssetsChange {
|
|||
|
||||
(burn_change, issuance_change)
|
||||
}
|
||||
|
||||
/// Consumes self and accepts a closure for looking up previous asset states.
|
||||
///
|
||||
/// Applies changes in self to the previous asset state.
|
||||
///
|
||||
/// Returns an [`IssuedAssets`] with the updated asset states.
|
||||
pub fn apply_with(self, f: impl Fn(AssetBase) -> AssetState) -> IssuedAssets {
|
||||
let mut issued_assets = IssuedAssets::new();
|
||||
|
||||
issued_assets.update(
|
||||
self.0
|
||||
.into_iter()
|
||||
.map(|(asset_base, change)| (asset_base, f(asset_base).with_change(change))),
|
||||
);
|
||||
|
||||
issued_assets
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for IssuedAssetsChange {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, mut rhs: Self) -> Self {
|
||||
if self.0.len() > rhs.0.len() {
|
||||
self.update(rhs.0.into_iter());
|
||||
self
|
||||
} else {
|
||||
rhs.update(self.0.into_iter());
|
||||
rhs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ use zebra_chain::{
|
|||
transparent,
|
||||
work::equihash,
|
||||
};
|
||||
use zebra_state as zs;
|
||||
use zebra_state::{self as zs, IssuedAssetsOrChanges};
|
||||
|
||||
use crate::{error::*, transaction as tx, BoxError};
|
||||
|
||||
|
@ -315,8 +315,7 @@ where
|
|||
let new_outputs = Arc::into_inner(known_utxos)
|
||||
.expect("all verification tasks using known_utxos are complete");
|
||||
|
||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
||||
IssuedAssetsChange::from_block(&block);
|
||||
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||
let prepared_block = zs::SemanticallyVerifiedBlock {
|
||||
block,
|
||||
hash,
|
||||
|
@ -324,8 +323,10 @@ where
|
|||
new_outputs,
|
||||
transaction_hashes,
|
||||
deferred_balance: Some(expected_deferred_amount),
|
||||
issued_assets_burns_change,
|
||||
issued_assets_issuance_change,
|
||||
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||
burns,
|
||||
issuance,
|
||||
},
|
||||
};
|
||||
|
||||
// Return early for proposal requests when getblocktemplate-rpcs feature is enabled
|
||||
|
|
|
@ -12,7 +12,8 @@ use zebra_chain::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
request::ContextuallyVerifiedBlock, service::chain_tip::ChainTipBlock,
|
||||
request::{ContextuallyVerifiedBlock, IssuedAssetsOrChanges},
|
||||
service::chain_tip::ChainTipBlock,
|
||||
SemanticallyVerifiedBlock,
|
||||
};
|
||||
|
||||
|
@ -31,8 +32,7 @@ impl Prepare for Arc<Block> {
|
|||
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_burns_change, issued_assets_issuance_change) =
|
||||
IssuedAssetsChange::from_block(&block);
|
||||
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||
|
||||
SemanticallyVerifiedBlock {
|
||||
block,
|
||||
|
@ -41,8 +41,10 @@ impl Prepare for Arc<Block> {
|
|||
new_outputs,
|
||||
transaction_hashes,
|
||||
deferred_balance: None,
|
||||
issued_assets_burns_change,
|
||||
issued_assets_issuance_change,
|
||||
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||
burns,
|
||||
issuance,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,8 +119,7 @@ impl ContextuallyVerifiedBlock {
|
|||
new_outputs,
|
||||
transaction_hashes,
|
||||
deferred_balance: _,
|
||||
issued_assets_burns_change: _,
|
||||
issued_assets_issuance_change: _,
|
||||
issued_assets_changes: _,
|
||||
} = block.into();
|
||||
|
||||
Self {
|
||||
|
|
|
@ -42,7 +42,8 @@ pub use error::{
|
|||
ValidateContextError,
|
||||
};
|
||||
pub use request::{
|
||||
CheckpointVerifiedBlock, HashOrHeight, ReadRequest, Request, SemanticallyVerifiedBlock,
|
||||
CheckpointVerifiedBlock, HashOrHeight, IssuedAssetsOrChanges, ReadRequest, Request,
|
||||
SemanticallyVerifiedBlock,
|
||||
};
|
||||
pub use response::{KnownBlock, MinedTx, ReadResponse, Response};
|
||||
pub use service::{
|
||||
|
|
|
@ -11,7 +11,7 @@ use zebra_chain::{
|
|||
block::{self, Block},
|
||||
history_tree::HistoryTree,
|
||||
orchard,
|
||||
orchard_zsa::IssuedAssetsChange,
|
||||
orchard_zsa::{IssuedAssets, IssuedAssetsChange},
|
||||
parallel::tree::NoteCommitmentTrees,
|
||||
sapling,
|
||||
serialization::SerializationError,
|
||||
|
@ -166,10 +166,7 @@ pub struct SemanticallyVerifiedBlock {
|
|||
pub deferred_balance: Option<Amount<NonNegative>>,
|
||||
/// A map of burns to be applied to the issued assets map.
|
||||
// TODO: Reference ZIP.
|
||||
pub issued_assets_burns_change: IssuedAssetsChange,
|
||||
/// A map of issuance to be applied to the issued assets map.
|
||||
// TODO: Reference ZIP.
|
||||
pub issued_assets_issuance_change: IssuedAssetsChange,
|
||||
pub issued_assets_changes: IssuedAssetsOrChanges,
|
||||
}
|
||||
|
||||
/// A block ready to be committed directly to the finalized state with
|
||||
|
@ -300,6 +297,48 @@ pub struct FinalizedBlock {
|
|||
pub(super) treestate: Treestate,
|
||||
/// This block's contribution to the deferred pool.
|
||||
pub(super) deferred_balance: Option<Amount<NonNegative>>,
|
||||
/// 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
|
||||
/// asset states for those asset bases.
|
||||
pub issued_assets: IssuedAssetsOrChanges,
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// asset states for those asset bases.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum IssuedAssetsOrChanges {
|
||||
/// A map of updated issued assets.
|
||||
State(IssuedAssets),
|
||||
|
||||
/// A map of changes to apply to the issued assets map.
|
||||
Change(IssuedAssetsChange),
|
||||
|
||||
/// A map of changes from burns and issuance to apply to the issued assets map.
|
||||
BurnAndIssuanceChanges {
|
||||
/// A map of changes from burns to apply to the issued assets map.
|
||||
burns: IssuedAssetsChange,
|
||||
/// A map of changes from issuance to apply to the issued assets map.
|
||||
issuance: IssuedAssetsChange,
|
||||
},
|
||||
}
|
||||
|
||||
impl IssuedAssetsOrChanges {
|
||||
/// Combines fields in the `BurnAndIssuanceChanges` variant then returns a `Change` variant, or
|
||||
/// returns self unmodified.
|
||||
pub fn combine(self) -> Self {
|
||||
let Self::BurnAndIssuanceChanges { burns, issuance } = self else {
|
||||
return self;
|
||||
};
|
||||
|
||||
Self::Change(burns + issuance)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IssuedAssetsChange> for IssuedAssetsOrChanges {
|
||||
fn from(change: IssuedAssetsChange) -> Self {
|
||||
Self::Change(change)
|
||||
}
|
||||
}
|
||||
|
||||
impl FinalizedBlock {
|
||||
|
@ -326,6 +365,7 @@ impl FinalizedBlock {
|
|||
transaction_hashes: block.transaction_hashes,
|
||||
treestate,
|
||||
deferred_balance: block.deferred_balance,
|
||||
issued_assets: block.issued_assets_changes.combine(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -399,8 +439,7 @@ impl ContextuallyVerifiedBlock {
|
|||
new_outputs,
|
||||
transaction_hashes,
|
||||
deferred_balance,
|
||||
issued_assets_burns_change: _,
|
||||
issued_assets_issuance_change: _,
|
||||
issued_assets_changes: _,
|
||||
} = semantically_verified;
|
||||
|
||||
// This is redundant for the non-finalized state,
|
||||
|
@ -454,8 +493,7 @@ impl SemanticallyVerifiedBlock {
|
|||
.expect("semantically verified block should have a coinbase height");
|
||||
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
||||
let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
|
||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
||||
IssuedAssetsChange::from_block(&block);
|
||||
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||
|
||||
Self {
|
||||
block,
|
||||
|
@ -464,8 +502,11 @@ impl SemanticallyVerifiedBlock {
|
|||
new_outputs,
|
||||
transaction_hashes,
|
||||
deferred_balance: None,
|
||||
issued_assets_burns_change,
|
||||
issued_assets_issuance_change,
|
||||
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||
burns,
|
||||
issuance,
|
||||
}
|
||||
.combine(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -490,8 +531,7 @@ impl From<Arc<Block>> for SemanticallyVerifiedBlock {
|
|||
.expect("semantically verified block should have a coinbase height");
|
||||
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
||||
let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
|
||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
||||
IssuedAssetsChange::from_block(&block);
|
||||
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||
|
||||
Self {
|
||||
block,
|
||||
|
@ -500,16 +540,17 @@ impl From<Arc<Block>> for SemanticallyVerifiedBlock {
|
|||
new_outputs,
|
||||
transaction_hashes,
|
||||
deferred_balance: None,
|
||||
issued_assets_burns_change,
|
||||
issued_assets_issuance_change,
|
||||
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||
burns,
|
||||
issuance,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
|
||||
fn from(valid: ContextuallyVerifiedBlock) -> Self {
|
||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
||||
IssuedAssetsChange::from_block(&valid.block);
|
||||
let (burns, issuance) = IssuedAssetsChange::from_block(&valid.block);
|
||||
|
||||
Self {
|
||||
block: valid.block,
|
||||
|
@ -524,16 +565,17 @@ impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
|
|||
.constrain::<NonNegative>()
|
||||
.expect("deferred balance in a block must me non-negative"),
|
||||
),
|
||||
issued_assets_burns_change,
|
||||
issued_assets_issuance_change,
|
||||
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||
burns,
|
||||
issuance,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FinalizedBlock> for SemanticallyVerifiedBlock {
|
||||
fn from(finalized: FinalizedBlock) -> Self {
|
||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
||||
IssuedAssetsChange::from_block(&finalized.block);
|
||||
let (burns, issuance) = IssuedAssetsChange::from_block(&finalized.block);
|
||||
|
||||
Self {
|
||||
block: finalized.block,
|
||||
|
@ -542,8 +584,10 @@ impl From<FinalizedBlock> for SemanticallyVerifiedBlock {
|
|||
new_outputs: finalized.new_outputs,
|
||||
transaction_hashes: finalized.transaction_hashes,
|
||||
deferred_balance: finalized.deferred_balance,
|
||||
issued_assets_burns_change,
|
||||
issued_assets_issuance_change,
|
||||
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||
burns,
|
||||
issuance,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,8 +116,7 @@ impl From<SemanticallyVerifiedBlock> for ChainTipBlock {
|
|||
new_outputs: _,
|
||||
transaction_hashes,
|
||||
deferred_balance: _,
|
||||
issued_assets_burns_change: _,
|
||||
issued_assets_issuance_change: _,
|
||||
issued_assets_changes: _,
|
||||
} = prepared;
|
||||
|
||||
Self {
|
||||
|
|
|
@ -9,7 +9,9 @@ use bincode::Options;
|
|||
|
||||
use zebra_chain::{
|
||||
block::Height,
|
||||
orchard, sapling, sprout,
|
||||
orchard,
|
||||
orchard_zsa::{AssetBase, AssetState},
|
||||
sapling, sprout,
|
||||
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
|
||||
};
|
||||
|
||||
|
@ -207,3 +209,46 @@ impl<Node: FromDisk> FromDisk for NoteCommitmentSubtreeData<Node> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace `.unwrap()`s with `.expect()`s
|
||||
|
||||
impl IntoDisk for AssetState {
|
||||
type Bytes = [u8; 9];
|
||||
|
||||
fn as_bytes(&self) -> Self::Bytes {
|
||||
[
|
||||
vec![self.is_finalized as u8],
|
||||
self.total_supply.to_be_bytes().to_vec(),
|
||||
]
|
||||
.concat()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDisk for AssetState {
|
||||
fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
|
||||
let (&is_finalized_byte, bytes) = bytes.as_ref().split_first().unwrap();
|
||||
let (&total_supply_bytes, _bytes) = bytes.split_first_chunk().unwrap();
|
||||
|
||||
Self {
|
||||
is_finalized: is_finalized_byte != 0,
|
||||
total_supply: u64::from_be_bytes(total_supply_bytes).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDisk for AssetBase {
|
||||
type Bytes = [u8; 32];
|
||||
|
||||
fn as_bytes(&self) -> Self::Bytes {
|
||||
self.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDisk for AssetBase {
|
||||
fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
|
||||
let (asset_base_bytes, _) = bytes.as_ref().split_first_chunk().unwrap();
|
||||
Self::from_bytes(asset_base_bytes).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -463,7 +463,7 @@ impl DiskWriteBatch {
|
|||
// which is already present from height 1 to the first shielded transaction.
|
||||
//
|
||||
// In Zebra we include the nullifiers and note commitments in the genesis block because it simplifies our code.
|
||||
self.prepare_shielded_transaction_batch(db, finalized)?;
|
||||
self.prepare_shielded_transaction_batch(zebra_db, finalized)?;
|
||||
self.prepare_trees_batch(zebra_db, finalized, prev_note_commitment_trees)?;
|
||||
|
||||
// # Consensus
|
||||
|
|
|
@ -29,7 +29,7 @@ use zebra_test::vectors::{MAINNET_BLOCKS, TESTNET_BLOCKS};
|
|||
|
||||
use crate::{
|
||||
constants::{state_database_format_version_in_code, STATE_DATABASE_KIND},
|
||||
request::{FinalizedBlock, Treestate},
|
||||
request::{FinalizedBlock, IssuedAssetsOrChanges, Treestate},
|
||||
service::finalized_state::{disk_db::DiskWriteBatch, ZebraDb, STATE_COLUMN_FAMILIES_IN_CODE},
|
||||
CheckpointVerifiedBlock, Config, SemanticallyVerifiedBlock,
|
||||
};
|
||||
|
@ -130,8 +130,7 @@ 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_burns_change, issued_assets_issuance_change) =
|
||||
IssuedAssetsChange::from_block(&original_block);
|
||||
let (burns, issuance) = IssuedAssetsChange::from_block(&original_block);
|
||||
|
||||
CheckpointVerifiedBlock(SemanticallyVerifiedBlock {
|
||||
block: original_block.clone(),
|
||||
|
@ -140,8 +139,10 @@ fn test_block_db_round_trip_with(
|
|||
new_outputs,
|
||||
transaction_hashes,
|
||||
deferred_balance: None,
|
||||
issued_assets_burns_change,
|
||||
issued_assets_issuance_change,
|
||||
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||
burns,
|
||||
issuance,
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ use std::{
|
|||
|
||||
use zebra_chain::{
|
||||
block::Height,
|
||||
orchard,
|
||||
orchard::{self},
|
||||
orchard_zsa::{AssetBase, AssetState},
|
||||
parallel::tree::NoteCommitmentTrees,
|
||||
sapling, sprout,
|
||||
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
|
||||
|
@ -33,14 +34,31 @@ use crate::{
|
|||
disk_format::RawBytes,
|
||||
zebra_db::ZebraDb,
|
||||
},
|
||||
BoxError,
|
||||
BoxError, IssuedAssetsOrChanges, TypedColumnFamily,
|
||||
};
|
||||
|
||||
// Doc-only items
|
||||
#[allow(unused_imports)]
|
||||
use zebra_chain::subtree::NoteCommitmentSubtree;
|
||||
|
||||
/// The name of the chain value pools column family.
|
||||
///
|
||||
/// This constant should be used so the compiler can detect typos.
|
||||
pub const ISSUED_ASSETS: &str = "orchard_issued_assets";
|
||||
|
||||
/// The type for reading value pools from the database.
|
||||
///
|
||||
/// This constant should be used so the compiler can detect incorrectly typed accesses to the
|
||||
/// column family.
|
||||
pub type IssuedAssetsCf<'cf> = TypedColumnFamily<'cf, AssetBase, AssetState>;
|
||||
|
||||
impl ZebraDb {
|
||||
/// Returns a typed handle to the `history_tree` column family.
|
||||
pub(crate) fn issued_assets_cf(&self) -> IssuedAssetsCf {
|
||||
IssuedAssetsCf::new(&self.db, ISSUED_ASSETS)
|
||||
.expect("column family was created when database was created")
|
||||
}
|
||||
|
||||
// Read shielded methods
|
||||
|
||||
/// Returns `true` if the finalized state contains `sprout_nullifier`.
|
||||
|
@ -410,6 +428,11 @@ impl ZebraDb {
|
|||
Some(subtree_data.with_index(index))
|
||||
}
|
||||
|
||||
/// Get the orchard issued asset state for the finalized tip.
|
||||
pub fn issued_asset(&self, asset_base: &AssetBase) -> Option<AssetState> {
|
||||
self.issued_assets_cf().zs_get(asset_base)
|
||||
}
|
||||
|
||||
/// Returns the shielded note commitment trees of the finalized tip
|
||||
/// or the empty trees if the state is empty.
|
||||
/// Additionally, returns the sapling and orchard subtrees for the finalized tip if
|
||||
|
@ -437,16 +460,18 @@ impl DiskWriteBatch {
|
|||
/// - Propagates any errors from updating note commitment trees
|
||||
pub fn prepare_shielded_transaction_batch(
|
||||
&mut self,
|
||||
db: &DiskDb,
|
||||
zebra_db: &ZebraDb,
|
||||
finalized: &FinalizedBlock,
|
||||
) -> Result<(), BoxError> {
|
||||
let FinalizedBlock { block, .. } = finalized;
|
||||
|
||||
// Index each transaction's shielded data
|
||||
for transaction in &block.transactions {
|
||||
self.prepare_nullifier_batch(db, transaction)?;
|
||||
self.prepare_nullifier_batch(&zebra_db.db, transaction)?;
|
||||
}
|
||||
|
||||
self.prepare_issued_assets_batch(zebra_db, &finalized.issued_assets)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -480,6 +505,36 @@ impl DiskWriteBatch {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare a database batch containing `finalized.block`'s asset issuance
|
||||
/// and return it (without actually writing anything).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - This method doesn't currently return any errors, but it might in future
|
||||
#[allow(clippy::unwrap_in_result)]
|
||||
pub fn prepare_issued_assets_batch(
|
||||
&mut self,
|
||||
zebra_db: &ZebraDb,
|
||||
issued_assets_or_changes: &IssuedAssetsOrChanges,
|
||||
) -> 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().combine() {
|
||||
IssuedAssetsOrChanges::State(issued_assets) => issued_assets,
|
||||
IssuedAssetsOrChanges::Change(issued_assets_change) => issued_assets_change
|
||||
.apply_with(|asset_base| zebra_db.issued_asset(&asset_base).unwrap_or_default()),
|
||||
IssuedAssetsOrChanges::BurnAndIssuanceChanges { .. } => {
|
||||
panic!("unexpected variant returned from `combine()`")
|
||||
}
|
||||
};
|
||||
|
||||
for (asset_base, updated_issued_asset_state) in updated_issued_assets {
|
||||
batch = batch.zs_insert(&asset_base, &updated_issued_asset_state);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare a database batch containing the note commitment and history tree updates
|
||||
/// from `finalized.block`, and return it (without actually writing anything).
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue