Skip to content

Commit

Permalink
Adds a new column family tracking [AssetBase] -> [is_finalized, supply]
Browse files Browse the repository at this point in the history
  • Loading branch information
arya2 committed Nov 8, 2024
1 parent 9ad18a4 commit 1e24da0
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 13 deletions.
7 changes: 7 additions & 0 deletions zebra-chain/src/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ pub struct Amount<C = NegativeAllowed>(
PhantomData<C>,
);

impl Amount {
/// TODO: Use a u64 for burn amounts instead of an Amount and remove this method
pub fn as_i128(&self) -> i128 {
self.0.into()
}
}

impl<C> fmt::Display for Amount<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let zats = self.zatoshis();
Expand Down
2 changes: 2 additions & 0 deletions zebra-chain/src/orchard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ pub(crate) use shielded_data::ActionCommon;

#[cfg(feature = "tx-v6")]
pub use orchard_flavor_ext::OrchardZSA;

pub use orchard::note::AssetBase;
13 changes: 11 additions & 2 deletions zebra-chain/src/orchard/orchard_flavor_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use proptest_derive::Arbitrary;

use orchard::{note_encryption::OrchardDomainCommon, orchard_flavor};

use crate::serialization::{ZcashDeserialize, ZcashSerialize};
use crate::{
orchard_zsa,
serialization::{ZcashDeserialize, ZcashSerialize},
};

#[cfg(feature = "tx-v6")]
use crate::orchard_zsa::{Burn, NoBurn};
Expand Down Expand Up @@ -50,7 +53,13 @@ pub trait OrchardFlavorExt: Clone + Debug {

/// A type representing a burn field for this protocol version.
#[cfg(feature = "tx-v6")]
type BurnType: Clone + Debug + Default + ZcashDeserialize + ZcashSerialize + TestArbitrary;
type BurnType: Clone
+ Debug
+ Default
+ ZcashDeserialize
+ ZcashSerialize
+ TestArbitrary
+ AsRef<[orchard_zsa::BurnItem]>;
}

/// A structure representing a tag for Orchard protocol variant used for the transaction version `V5`.
Expand Down
4 changes: 2 additions & 2 deletions zebra-chain/src/orchard_zsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ mod common;
mod burn;
mod issuance;

pub(crate) use burn::{Burn, NoBurn};
pub(crate) use issuance::IssueData;
pub use burn::{Burn, BurnItem, NoBurn};
pub use issuance::{IssueData, Note};
26 changes: 25 additions & 1 deletion zebra-chain/src/orchard_zsa/burn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,21 @@ const AMOUNT_SIZE: u64 = 8;
const BURN_ITEM_SIZE: u64 = ASSET_BASE_SIZE + AMOUNT_SIZE;

/// Orchard ZSA burn item.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct BurnItem(AssetBase, Amount);

impl BurnItem {
/// Returns [`AssetBase`] being burned.
pub fn asset(&self) -> AssetBase {
self.0
}

/// Returns [`Amount`] being burned.
pub fn amount(&self) -> Amount {
self.1
}
}

// Convert from burn item type used in `orchard` crate
impl TryFrom<(AssetBase, NoteValue)> for BurnItem {
type Error = crate::amount::Error;
Expand Down Expand Up @@ -105,10 +117,22 @@ impl ZcashDeserialize for NoBurn {
}
}

impl AsRef<[BurnItem]> for NoBurn {
fn as_ref(&self) -> &[BurnItem] {
&[]
}
}

/// Orchard ZSA burn items (assets intended for burning)
#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct Burn(Vec<BurnItem>);

impl AsRef<[BurnItem]> for Burn {
fn as_ref(&self) -> &[BurnItem] {
&self.0
}
}

impl From<Vec<BurnItem>> for Burn {
fn from(inner: Vec<BurnItem>) -> Self {
Self(inner)
Expand Down
9 changes: 8 additions & 1 deletion zebra-chain/src/orchard_zsa/issuance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use orchard::{
note::{ExtractedNoteCommitment, RandomSeed, Rho},
primitives::redpallas::{SigType, Signature, SpendAuth},
value::NoteValue,
Address, Note,
Address,
};

use crate::{
Expand All @@ -33,6 +33,8 @@ use crate::{

use super::common::ASSET_BASE_SIZE;

pub use orchard::Note;

/// Wrapper for `IssueBundle` used in the context of Transaction V6. This allows the implementation of
/// a Serde serializer for unit tests within this crate.
#[derive(Clone, Debug, PartialEq, Eq)]
Expand All @@ -57,6 +59,11 @@ impl IssueData {
})
})
}

/// Returns issue actions
pub fn actions(&self) -> &NonEmpty<IssueAction> {
self.0.actions()
}
}

// Sizes of the serialized values for types in bytes (used for TrustedPreallocate impls)
Expand Down
50 changes: 50 additions & 0 deletions zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,56 @@ impl Transaction {
}
}

/// Access the issuance actions in this transaction, if there are any,
/// regardless of version.
pub fn issue_actions(&self) -> impl Iterator<Item = &::orchard::issuance::IssueAction> {
let orchard_zsa_issue_data = match self {
Transaction::V1 { .. }
| Transaction::V2 { .. }
| Transaction::V3 { .. }
| Transaction::V4 { .. }
| Transaction::V5 { .. } => &None,

#[cfg(feature = "tx-v6")]
Transaction::V6 {
orchard_zsa_issue_data,
..
} => orchard_zsa_issue_data,
};

orchard_zsa_issue_data
.iter()
.flat_map(orchard_zsa::IssueData::actions)
}

/// Access the asset burns in this transaction, if there are any,
/// regardless of version.
#[cfg(feature = "tx-v6")]
pub fn burns(&self) -> Vec<orchard_zsa::BurnItem> {
match self {
#[cfg(feature = "tx-v6")]
Transaction::V5 {
orchard_shielded_data: Some(orchard_shielded_data),
..
} => orchard_shielded_data.burn.as_ref().to_vec(),
#[cfg(feature = "tx-v6")]
Transaction::V6 {
orchard_shielded_data,
..
} => orchard_shielded_data
.iter()
.flat_map(|data| data.burn.as_ref())
.copied()
.collect(),

Transaction::V1 { .. }
| Transaction::V2 { .. }
| Transaction::V3 { .. }
| Transaction::V4 { .. }
| Transaction::V5 { .. } => Vec::new(),
}
}

/// Access the [`orchard::Flags`] in this transaction, if there is any,
/// regardless of version.
pub fn orchard_flags(&self) -> Option<orchard::shielded_data::Flags> {
Expand Down
3 changes: 2 additions & 1 deletion zebra-state/src/service/finalized_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub use column_family::{TypedColumnFamily, WriteTypedBatch};
pub use disk_db::{DiskDb, DiskWriteBatch, ReadDisk, WriteDisk};
#[allow(unused_imports)]
pub use disk_format::{
FromDisk, IntoDisk, OutputIndex, OutputLocation, RawBytes, TransactionIndex,
AssetState, FromDisk, IntoDisk, OutputIndex, OutputLocation, RawBytes, TransactionIndex,
TransactionLocation, MAX_ON_DISK_HEIGHT,
};
pub use zebra_db::ZebraDb;
Expand Down Expand Up @@ -91,6 +91,7 @@ pub const STATE_COLUMN_FAMILIES_IN_CODE: &[&str] = &[
"orchard_anchors",
"orchard_note_commitment_tree",
"orchard_note_commitment_subtree",
"orchard_issued_assets",
// Chain
"history_tree",
"tip_chain_value_pool",
Expand Down
1 change: 1 addition & 0 deletions zebra-state/src/service/finalized_state/disk_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod scan;
mod tests;

pub use block::{TransactionIndex, TransactionLocation, MAX_ON_DISK_HEIGHT};
pub use shielded::AssetState;
pub use transparent::{OutputIndex, OutputLocation};

#[cfg(feature = "shielded-scan")]
Expand Down
100 changes: 99 additions & 1 deletion zebra-state/src/service/finalized_state/disk_format/shielded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,112 @@ use bincode::Options;

use zebra_chain::{
block::Height,
orchard, sapling, sprout,
orchard::{self, AssetBase},
orchard_zsa::{self, BurnItem},
sapling, sprout,
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
};

use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk};

use super::block::HEIGHT_DISK_BYTES;

/// The circulating supply and whether that supply has been finalized.
#[derive(Copy, Clone, Debug, 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,
/// The circulating supply that has been issued for an asset.
pub total_supply: i128,
}

impl AssetState {
/// Adds partial asset states
pub fn with_change(&self, change: Self) -> Self {
Self {
is_finalized: self.is_finalized || change.is_finalized,
total_supply: self
.total_supply
.checked_add(change.total_supply)
.expect("asset supply sum should not exceed u64 size"),
}
}

pub fn from_note(is_finalized: bool, note: orchard_zsa::Note) -> (AssetBase, Self) {
(
note.asset(),
Self {
is_finalized,
total_supply: note.value().inner().into(),
},
)
}

pub fn from_notes(
is_finalized: bool,
notes: &[orchard_zsa::Note],
) -> impl Iterator<Item = (AssetBase, Self)> + '_ {
notes
.iter()
.map(move |note| Self::from_note(is_finalized, *note))
}

pub fn from_burn(burn: BurnItem) -> (AssetBase, Self) {
(
burn.asset(),
Self {
is_finalized: false,
total_supply: -burn.amount().as_i128(),
},
)
}

pub fn from_burns(burns: Vec<BurnItem>) -> impl Iterator<Item = (AssetBase, Self)> {
burns.into_iter().map(Self::from_burn)
}
}

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 orchard::AssetBase {
type Bytes = [u8; 32];

fn as_bytes(&self) -> Self::Bytes {
self.to_bytes()
}
}

impl FromDisk for orchard::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()
}
}

impl IntoDisk for sprout::Nullifier {
type Bytes = [u8; 32];

Expand Down
2 changes: 1 addition & 1 deletion zebra-state/src/service/finalized_state/zebra_db/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 1e24da0

Please sign in to comment.