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 burn::{Burn, BurnItem, NoBurn};
|
||||||
pub(crate) use issuance::IssueData;
|
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;
|
use super::BurnItem;
|
||||||
|
|
||||||
/// The circulating supply and whether that supply has been finalized.
|
/// 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 {
|
pub struct AssetState {
|
||||||
/// Indicates whether the asset is finalized such that no more of it can be issued.
|
/// Indicates whether the asset is finalized such that no more of it can be issued.
|
||||||
pub is_finalized: bool,
|
pub is_finalized: bool,
|
||||||
|
@ -29,6 +29,17 @@ pub struct AssetStateChange {
|
||||||
pub supply_change: i128,
|
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 {
|
impl AssetStateChange {
|
||||||
fn from_note(is_finalized: bool, note: orchard::Note) -> (AssetBase, Self) {
|
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.
|
/// A map of changes to apply to the issued assets map.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct IssuedAssetsChange(HashMap<AssetBase, AssetStateChange>);
|
pub struct IssuedAssetsChange(HashMap<AssetBase, AssetStateChange>);
|
||||||
|
@ -109,4 +147,35 @@ impl IssuedAssetsChange {
|
||||||
|
|
||||||
(burn_change, issuance_change)
|
(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,
|
transparent,
|
||||||
work::equihash,
|
work::equihash,
|
||||||
};
|
};
|
||||||
use zebra_state as zs;
|
use zebra_state::{self as zs, IssuedAssetsOrChanges};
|
||||||
|
|
||||||
use crate::{error::*, transaction as tx, BoxError};
|
use crate::{error::*, transaction as tx, BoxError};
|
||||||
|
|
||||||
|
@ -315,8 +315,7 @@ where
|
||||||
let new_outputs = Arc::into_inner(known_utxos)
|
let new_outputs = Arc::into_inner(known_utxos)
|
||||||
.expect("all verification tasks using known_utxos are complete");
|
.expect("all verification tasks using known_utxos are complete");
|
||||||
|
|
||||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||||
IssuedAssetsChange::from_block(&block);
|
|
||||||
let prepared_block = zs::SemanticallyVerifiedBlock {
|
let prepared_block = zs::SemanticallyVerifiedBlock {
|
||||||
block,
|
block,
|
||||||
hash,
|
hash,
|
||||||
|
@ -324,8 +323,10 @@ where
|
||||||
new_outputs,
|
new_outputs,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance: Some(expected_deferred_amount),
|
deferred_balance: Some(expected_deferred_amount),
|
||||||
issued_assets_burns_change,
|
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||||
issued_assets_issuance_change,
|
burns,
|
||||||
|
issuance,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return early for proposal requests when getblocktemplate-rpcs feature is enabled
|
// Return early for proposal requests when getblocktemplate-rpcs feature is enabled
|
||||||
|
|
|
@ -12,7 +12,8 @@ use zebra_chain::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
request::ContextuallyVerifiedBlock, service::chain_tip::ChainTipBlock,
|
request::{ContextuallyVerifiedBlock, IssuedAssetsOrChanges},
|
||||||
|
service::chain_tip::ChainTipBlock,
|
||||||
SemanticallyVerifiedBlock,
|
SemanticallyVerifiedBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,8 +32,7 @@ impl Prepare for Arc<Block> {
|
||||||
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
||||||
let new_outputs =
|
let new_outputs =
|
||||||
transparent::new_ordered_outputs_with_height(&block, height, &transaction_hashes);
|
transparent::new_ordered_outputs_with_height(&block, height, &transaction_hashes);
|
||||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||||
IssuedAssetsChange::from_block(&block);
|
|
||||||
|
|
||||||
SemanticallyVerifiedBlock {
|
SemanticallyVerifiedBlock {
|
||||||
block,
|
block,
|
||||||
|
@ -41,8 +41,10 @@ impl Prepare for Arc<Block> {
|
||||||
new_outputs,
|
new_outputs,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance: None,
|
deferred_balance: None,
|
||||||
issued_assets_burns_change,
|
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||||
issued_assets_issuance_change,
|
burns,
|
||||||
|
issuance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,8 +119,7 @@ impl ContextuallyVerifiedBlock {
|
||||||
new_outputs,
|
new_outputs,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance: _,
|
deferred_balance: _,
|
||||||
issued_assets_burns_change: _,
|
issued_assets_changes: _,
|
||||||
issued_assets_issuance_change: _,
|
|
||||||
} = block.into();
|
} = block.into();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -42,7 +42,8 @@ pub use error::{
|
||||||
ValidateContextError,
|
ValidateContextError,
|
||||||
};
|
};
|
||||||
pub use request::{
|
pub use request::{
|
||||||
CheckpointVerifiedBlock, HashOrHeight, ReadRequest, Request, SemanticallyVerifiedBlock,
|
CheckpointVerifiedBlock, HashOrHeight, IssuedAssetsOrChanges, ReadRequest, Request,
|
||||||
|
SemanticallyVerifiedBlock,
|
||||||
};
|
};
|
||||||
pub use response::{KnownBlock, MinedTx, ReadResponse, Response};
|
pub use response::{KnownBlock, MinedTx, ReadResponse, Response};
|
||||||
pub use service::{
|
pub use service::{
|
||||||
|
|
|
@ -11,7 +11,7 @@ use zebra_chain::{
|
||||||
block::{self, Block},
|
block::{self, Block},
|
||||||
history_tree::HistoryTree,
|
history_tree::HistoryTree,
|
||||||
orchard,
|
orchard,
|
||||||
orchard_zsa::IssuedAssetsChange,
|
orchard_zsa::{IssuedAssets, IssuedAssetsChange},
|
||||||
parallel::tree::NoteCommitmentTrees,
|
parallel::tree::NoteCommitmentTrees,
|
||||||
sapling,
|
sapling,
|
||||||
serialization::SerializationError,
|
serialization::SerializationError,
|
||||||
|
@ -166,10 +166,7 @@ pub struct SemanticallyVerifiedBlock {
|
||||||
pub deferred_balance: Option<Amount<NonNegative>>,
|
pub deferred_balance: Option<Amount<NonNegative>>,
|
||||||
/// A map of burns to be applied to the issued assets map.
|
/// A map of burns to be applied to the issued assets map.
|
||||||
// TODO: Reference ZIP.
|
// TODO: Reference ZIP.
|
||||||
pub issued_assets_burns_change: IssuedAssetsChange,
|
pub issued_assets_changes: IssuedAssetsOrChanges,
|
||||||
/// A map of issuance to be applied to the issued assets map.
|
|
||||||
// TODO: Reference ZIP.
|
|
||||||
pub issued_assets_issuance_change: IssuedAssetsChange,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A block ready to be committed directly to the finalized state with
|
/// A block ready to be committed directly to the finalized state with
|
||||||
|
@ -300,6 +297,48 @@ pub struct FinalizedBlock {
|
||||||
pub(super) treestate: Treestate,
|
pub(super) treestate: Treestate,
|
||||||
/// This block's contribution to the deferred pool.
|
/// This block's contribution to the deferred pool.
|
||||||
pub(super) deferred_balance: Option<Amount<NonNegative>>,
|
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 {
|
impl FinalizedBlock {
|
||||||
|
@ -326,6 +365,7 @@ impl FinalizedBlock {
|
||||||
transaction_hashes: block.transaction_hashes,
|
transaction_hashes: block.transaction_hashes,
|
||||||
treestate,
|
treestate,
|
||||||
deferred_balance: block.deferred_balance,
|
deferred_balance: block.deferred_balance,
|
||||||
|
issued_assets: block.issued_assets_changes.combine(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,8 +439,7 @@ impl ContextuallyVerifiedBlock {
|
||||||
new_outputs,
|
new_outputs,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance,
|
deferred_balance,
|
||||||
issued_assets_burns_change: _,
|
issued_assets_changes: _,
|
||||||
issued_assets_issuance_change: _,
|
|
||||||
} = semantically_verified;
|
} = semantically_verified;
|
||||||
|
|
||||||
// This is redundant for the non-finalized state,
|
// This is redundant for the non-finalized state,
|
||||||
|
@ -454,8 +493,7 @@ impl SemanticallyVerifiedBlock {
|
||||||
.expect("semantically verified block should have a coinbase height");
|
.expect("semantically verified block should have a coinbase height");
|
||||||
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
||||||
let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
|
let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
|
||||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||||
IssuedAssetsChange::from_block(&block);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
block,
|
block,
|
||||||
|
@ -464,8 +502,11 @@ impl SemanticallyVerifiedBlock {
|
||||||
new_outputs,
|
new_outputs,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance: None,
|
deferred_balance: None,
|
||||||
issued_assets_burns_change,
|
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||||
issued_assets_issuance_change,
|
burns,
|
||||||
|
issuance,
|
||||||
|
}
|
||||||
|
.combine(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,8 +531,7 @@ impl From<Arc<Block>> for SemanticallyVerifiedBlock {
|
||||||
.expect("semantically verified block should have a coinbase height");
|
.expect("semantically verified block should have a coinbase height");
|
||||||
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
|
||||||
let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
|
let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
|
||||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
let (burns, issuance) = IssuedAssetsChange::from_block(&block);
|
||||||
IssuedAssetsChange::from_block(&block);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
block,
|
block,
|
||||||
|
@ -500,16 +540,17 @@ impl From<Arc<Block>> for SemanticallyVerifiedBlock {
|
||||||
new_outputs,
|
new_outputs,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance: None,
|
deferred_balance: None,
|
||||||
issued_assets_burns_change,
|
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||||
issued_assets_issuance_change,
|
burns,
|
||||||
|
issuance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
|
impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
|
||||||
fn from(valid: ContextuallyVerifiedBlock) -> Self {
|
fn from(valid: ContextuallyVerifiedBlock) -> Self {
|
||||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
let (burns, issuance) = IssuedAssetsChange::from_block(&valid.block);
|
||||||
IssuedAssetsChange::from_block(&valid.block);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
block: valid.block,
|
block: valid.block,
|
||||||
|
@ -524,16 +565,17 @@ impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
|
||||||
.constrain::<NonNegative>()
|
.constrain::<NonNegative>()
|
||||||
.expect("deferred balance in a block must me non-negative"),
|
.expect("deferred balance in a block must me non-negative"),
|
||||||
),
|
),
|
||||||
issued_assets_burns_change,
|
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||||
issued_assets_issuance_change,
|
burns,
|
||||||
|
issuance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FinalizedBlock> for SemanticallyVerifiedBlock {
|
impl From<FinalizedBlock> for SemanticallyVerifiedBlock {
|
||||||
fn from(finalized: FinalizedBlock) -> Self {
|
fn from(finalized: FinalizedBlock) -> Self {
|
||||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
let (burns, issuance) = IssuedAssetsChange::from_block(&finalized.block);
|
||||||
IssuedAssetsChange::from_block(&finalized.block);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
block: finalized.block,
|
block: finalized.block,
|
||||||
|
@ -542,8 +584,10 @@ impl From<FinalizedBlock> for SemanticallyVerifiedBlock {
|
||||||
new_outputs: finalized.new_outputs,
|
new_outputs: finalized.new_outputs,
|
||||||
transaction_hashes: finalized.transaction_hashes,
|
transaction_hashes: finalized.transaction_hashes,
|
||||||
deferred_balance: finalized.deferred_balance,
|
deferred_balance: finalized.deferred_balance,
|
||||||
issued_assets_burns_change,
|
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||||
issued_assets_issuance_change,
|
burns,
|
||||||
|
issuance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,8 +116,7 @@ impl From<SemanticallyVerifiedBlock> for ChainTipBlock {
|
||||||
new_outputs: _,
|
new_outputs: _,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance: _,
|
deferred_balance: _,
|
||||||
issued_assets_burns_change: _,
|
issued_assets_changes: _,
|
||||||
issued_assets_issuance_change: _,
|
|
||||||
} = prepared;
|
} = prepared;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -9,7 +9,9 @@ use bincode::Options;
|
||||||
|
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
block::Height,
|
block::Height,
|
||||||
orchard, sapling, sprout,
|
orchard,
|
||||||
|
orchard_zsa::{AssetBase, AssetState},
|
||||||
|
sapling, sprout,
|
||||||
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
|
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.
|
// 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.
|
// 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)?;
|
self.prepare_trees_batch(zebra_db, finalized, prev_note_commitment_trees)?;
|
||||||
|
|
||||||
// # Consensus
|
// # Consensus
|
||||||
|
|
|
@ -29,7 +29,7 @@ use zebra_test::vectors::{MAINNET_BLOCKS, TESTNET_BLOCKS};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::{state_database_format_version_in_code, STATE_DATABASE_KIND},
|
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},
|
service::finalized_state::{disk_db::DiskWriteBatch, ZebraDb, STATE_COLUMN_FAMILIES_IN_CODE},
|
||||||
CheckpointVerifiedBlock, Config, SemanticallyVerifiedBlock,
|
CheckpointVerifiedBlock, Config, SemanticallyVerifiedBlock,
|
||||||
};
|
};
|
||||||
|
@ -130,8 +130,7 @@ fn test_block_db_round_trip_with(
|
||||||
.collect();
|
.collect();
|
||||||
let new_outputs =
|
let new_outputs =
|
||||||
new_ordered_outputs_with_height(&original_block, Height(0), &transaction_hashes);
|
new_ordered_outputs_with_height(&original_block, Height(0), &transaction_hashes);
|
||||||
let (issued_assets_burns_change, issued_assets_issuance_change) =
|
let (burns, issuance) = IssuedAssetsChange::from_block(&original_block);
|
||||||
IssuedAssetsChange::from_block(&original_block);
|
|
||||||
|
|
||||||
CheckpointVerifiedBlock(SemanticallyVerifiedBlock {
|
CheckpointVerifiedBlock(SemanticallyVerifiedBlock {
|
||||||
block: original_block.clone(),
|
block: original_block.clone(),
|
||||||
|
@ -140,8 +139,10 @@ fn test_block_db_round_trip_with(
|
||||||
new_outputs,
|
new_outputs,
|
||||||
transaction_hashes,
|
transaction_hashes,
|
||||||
deferred_balance: None,
|
deferred_balance: None,
|
||||||
issued_assets_burns_change,
|
issued_assets_changes: IssuedAssetsOrChanges::BurnAndIssuanceChanges {
|
||||||
issued_assets_issuance_change,
|
burns,
|
||||||
|
issuance,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,8 @@ use std::{
|
||||||
|
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
block::Height,
|
block::Height,
|
||||||
orchard,
|
orchard::{self},
|
||||||
|
orchard_zsa::{AssetBase, AssetState},
|
||||||
parallel::tree::NoteCommitmentTrees,
|
parallel::tree::NoteCommitmentTrees,
|
||||||
sapling, sprout,
|
sapling, sprout,
|
||||||
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
|
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
|
||||||
|
@ -33,14 +34,31 @@ use crate::{
|
||||||
disk_format::RawBytes,
|
disk_format::RawBytes,
|
||||||
zebra_db::ZebraDb,
|
zebra_db::ZebraDb,
|
||||||
},
|
},
|
||||||
BoxError,
|
BoxError, IssuedAssetsOrChanges, TypedColumnFamily,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Doc-only items
|
// Doc-only items
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use zebra_chain::subtree::NoteCommitmentSubtree;
|
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 {
|
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
|
// Read shielded methods
|
||||||
|
|
||||||
/// Returns `true` if the finalized state contains `sprout_nullifier`.
|
/// Returns `true` if the finalized state contains `sprout_nullifier`.
|
||||||
|
@ -410,6 +428,11 @@ impl ZebraDb {
|
||||||
Some(subtree_data.with_index(index))
|
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
|
/// Returns the shielded note commitment trees of the finalized tip
|
||||||
/// or the empty trees if the state is empty.
|
/// or the empty trees if the state is empty.
|
||||||
/// Additionally, returns the sapling and orchard subtrees for the finalized tip if
|
/// 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
|
/// - Propagates any errors from updating note commitment trees
|
||||||
pub fn prepare_shielded_transaction_batch(
|
pub fn prepare_shielded_transaction_batch(
|
||||||
&mut self,
|
&mut self,
|
||||||
db: &DiskDb,
|
zebra_db: &ZebraDb,
|
||||||
finalized: &FinalizedBlock,
|
finalized: &FinalizedBlock,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let FinalizedBlock { block, .. } = finalized;
|
let FinalizedBlock { block, .. } = finalized;
|
||||||
|
|
||||||
// Index each transaction's shielded data
|
// Index each transaction's shielded data
|
||||||
for transaction in &block.transactions {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,6 +505,36 @@ impl DiskWriteBatch {
|
||||||
Ok(())
|
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
|
/// Prepare a database batch containing the note commitment and history tree updates
|
||||||
/// from `finalized.block`, and return it (without actually writing anything).
|
/// from `finalized.block`, and return it (without actually writing anything).
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in New Issue