-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ZSA integration (step 8): Merge zsa-issued-assets into zsa-integration-consensus with bug fixes #29
base: zsa-integration-consensus
Are you sure you want to change the base?
Changes from 10 commits
1af120e
cc8bc0d
c7116f3
bb62c67
3d00b81
2daf84f
c6c099b
9e0e043
8f26a89
e063729
6aad8da
f0b64ad
bc0c8e6
17f3ee6
3f96af0
5524480
8096da4
20fd58d
4932495
89be470
c3daec9
a8668d6
e31f24c
2a5aebd
f7b43a9
e35ae57
c278758
d144774
727e3e3
9a8c032
fb512d9
977af42
29af613
2b7926a
73c804f
6bd4284
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ pub struct AssetState { | |
pub struct AssetStateChange { | ||
/// Whether the asset should be finalized such that no more of it can be issued. | ||
pub should_finalize: bool, | ||
// FIXME: is this a correct comment? | ||
/// Whether the asset should be finalized such that no more of it can be issued. | ||
pub includes_issuance: bool, | ||
/// The change in supply from newly issued assets or burned assets, if any. | ||
|
@@ -50,6 +51,7 @@ impl Default for SupplyChange { | |
} | ||
} | ||
|
||
// FIXME: can we reuse some functions from orchard crate?s | ||
impl SupplyChange { | ||
/// Applies `self` to a provided `total_supply` of an asset. | ||
/// | ||
|
@@ -81,7 +83,8 @@ impl SupplyChange { | |
// Burn amounts MUST not be 0 | ||
// TODO: Reference ZIP | ||
0.. => signed.try_into().ok().map(Self::Issuance), | ||
..0 => signed.try_into().ok().map(Self::Burn), | ||
// FIXME: (-signed) - is this a correct fix? | ||
..0 => (-signed).try_into().ok().map(Self::Burn), | ||
}) | ||
{ | ||
*self = result; | ||
|
@@ -208,6 +211,7 @@ impl AssetStateChange { | |
}) | ||
.unwrap_or_default() | ||
.into_iter() | ||
// FIXME: We use 0 as a value - is that correct? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's correct but overly complicated, it should be refactored. |
||
.map(|asset_base| Self::new(asset_base, SupplyChange::Issuance(0), true)); | ||
|
||
supply_changes.chain(finalize_changes) | ||
|
@@ -238,7 +242,7 @@ impl AssetStateChange { | |
/// Accepts an [`BurnItem`] and returns an iterator of asset bases and issued asset state changes | ||
/// that should be applied to those asset bases when committing the provided burn to the chain state. | ||
fn from_burn(burn: &BurnItem) -> (AssetBase, Self) { | ||
Self::new(burn.asset(), SupplyChange::Burn(burn.amount()), false) | ||
Self::new(burn.asset(), SupplyChange::Burn(burn.raw_amount()), false) | ||
} | ||
|
||
/// Updates and returns self with the provided [`AssetStateChange`] if | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ use std::io; | |
use halo2::pasta::pallas; | ||
|
||
use crate::{ | ||
amount::Amount, | ||
block::MAX_BLOCK_BYTES, | ||
orchard::ValueCommitment, | ||
serialization::{ | ||
|
@@ -36,28 +37,27 @@ const AMOUNT_SIZE: u64 = 8; | |
// FIXME: is this a correct way to calculate (simple sum of sizes of components)? | ||
const BURN_ITEM_SIZE: u64 = ASSET_BASE_SIZE + AMOUNT_SIZE; | ||
|
||
// FIXME: Define BurnItem (or, even Burn/NoBurn) in Orchard and reuse it here? | ||
/// Orchard ZSA burn item. | ||
#[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
pub struct BurnItem(AssetBase, u64); | ||
pub struct BurnItem(AssetBase, NoteValue); | ||
|
||
impl BurnItem { | ||
/// Returns [`AssetBase`] being burned. | ||
pub fn asset(&self) -> AssetBase { | ||
self.0 | ||
} | ||
|
||
/// Returns [`u64`] representing amount being burned. | ||
pub fn amount(&self) -> u64 { | ||
self.1 | ||
/// Returns the raw [`u64`] amount being burned. | ||
pub fn raw_amount(&self) -> u64 { | ||
self.1.inner() | ||
} | ||
} | ||
|
||
// Convert from burn item type used in `orchard` crate | ||
impl TryFrom<(AssetBase, NoteValue)> for BurnItem { | ||
type Error = crate::amount::Error; | ||
|
||
fn try_from(item: (AssetBase, NoteValue)) -> Result<Self, Self::Error> { | ||
Ok(Self(item.0, item.1.inner())) | ||
impl From<(AssetBase, NoteValue)> for BurnItem { | ||
fn from(item: (AssetBase, NoteValue)) -> Self { | ||
Self(item.0, item.1) | ||
} | ||
} | ||
|
||
|
@@ -66,20 +66,18 @@ impl ZcashSerialize for BurnItem { | |
let BurnItem(asset_base, amount) = self; | ||
|
||
asset_base.zcash_serialize(&mut writer)?; | ||
writer.write_all(&amount.to_be_bytes())?; | ||
writer.write_all(&amount.to_bytes())?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl ZcashDeserialize for BurnItem { | ||
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> { | ||
let asset_base = AssetBase::zcash_deserialize(&mut reader)?; | ||
let mut amount_bytes = [0; 8]; | ||
reader.read_exact(&mut amount_bytes)?; | ||
Ok(Self( | ||
AssetBase::zcash_deserialize(&mut reader)?, | ||
u64::from_be_bytes(amount_bytes), | ||
)) | ||
Ok(Self(asset_base, NoteValue::from_bytes(amount_bytes))) | ||
} | ||
} | ||
|
||
|
@@ -98,7 +96,7 @@ impl serde::Serialize for BurnItem { | |
S: serde::Serializer, | ||
{ | ||
// FIXME: return a custom error with a meaningful description? | ||
(self.0.to_bytes(), &self.1).serialize(serializer) | ||
(self.0.to_bytes(), &self.1.inner()).serialize(serializer) | ||
} | ||
} | ||
|
||
|
@@ -114,7 +112,7 @@ impl<'de> serde::Deserialize<'de> for BurnItem { | |
// FIXME: duplicates the body of AssetBase::zcash_deserialize? | ||
Option::from(AssetBase::from_bytes(&asset_base_bytes)) | ||
.ok_or_else(|| serde::de::Error::custom("Invalid orchard_zsa AssetBase"))?, | ||
amount, | ||
NoteValue::from_raw(amount), | ||
)) | ||
} | ||
} | ||
|
@@ -166,7 +164,15 @@ impl From<Burn> for ValueCommitment { | |
burn.0 | ||
.into_iter() | ||
.map(|BurnItem(asset, amount)| { | ||
ValueCommitment::with_asset(pallas::Scalar::zero(), amount, &asset) | ||
ValueCommitment::with_asset( | ||
pallas::Scalar::zero(), | ||
// FIXME: consider to use TryFrom and return an error instead of using "expect" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be a u64 so the conversion should be infallible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense. But to make it work, the new |
||
amount | ||
.inner() | ||
.try_into() | ||
.expect("should convert Burn amount to i64"), | ||
&asset, | ||
) | ||
}) | ||
.sum() | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,8 @@ pub fn valid_burns_and_issuance( | |
) -> Result<IssuedAssets, ValidateContextError> { | ||
let mut issued_assets = HashMap::new(); | ||
|
||
// FIXME: Do all checks (for duplication, existence etc.) need to be performed per tranaction, not per | ||
// the entire block? | ||
for (issued_assets_change, transaction) in semantically_verified | ||
.issued_assets_changes | ||
.iter() | ||
|
@@ -41,7 +43,11 @@ pub fn valid_burns_and_issuance( | |
asset_state(finalized_state, parent_chain, &issued_assets, &asset_base) | ||
.ok_or(ValidateContextError::InvalidBurn)?; | ||
|
||
if asset_state.total_supply < burn.amount() { | ||
// FIXME: The check that we can't burn an asset before we issued it is implicit - | ||
// through the check it total_supply < burn.amount (the total supply is zero if the | ||
// asset is not issued). May be validation functions from the orcharfd crate need to be | ||
// reused in a some way? | ||
if asset_state.total_supply < burn.raw_amount() { | ||
return Err(ValidateContextError::InvalidBurn); | ||
} else { | ||
// Any burned asset bases in the transaction will also be present in the issued assets change, | ||
|
@@ -50,6 +56,13 @@ pub fn valid_burns_and_issuance( | |
} | ||
} | ||
|
||
// FIXME: Not sure: it looks like semantically_verified.issued_assets_changes is already | ||
// filled with burn and issuance items in zebra-consensus, see Verifier::call function in | ||
// zebra-consensus/src/transaction.rs (it uses from_burn and from_issue_action AssetStateChange | ||
// methods from ebra-chain/src/orchard_zsa/asset_state.rs). Can't it cause a duplication? | ||
// Can we collect all change items here, not in zebra-consensus (and so we don't need | ||
// SemanticallyVerifiedBlock::issued_assets_changes at all), or performing part of the | ||
// checks in zebra-consensus is important for the consensus checks order in a some way? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It wouldn't be duplicated since the previous loop isn't mutating the asset states, it's just loading them into memory.
Yep, I'll try to make this change. |
||
for (asset_base, change) in issued_assets_change.iter() { | ||
let asset_state = | ||
asset_state(finalized_state, parent_chain, &issued_assets, &asset_base) | ||
|
@@ -59,9 +72,13 @@ pub fn valid_burns_and_issuance( | |
.apply_change(change) | ||
.ok_or(ValidateContextError::InvalidIssuance)?; | ||
|
||
issued_assets | ||
.insert(asset_base, updated_asset_state) | ||
.expect("transactions must have only one burn item per asset base"); | ||
// FIXME: Is it correct to do nothing if the issued_assets aready has asset_base? Now it'd be | ||
// replaced with updated_asset_state in this case (where the duplicated value is added to | ||
// the supply). Block may have two burn records for the same asset but in different | ||
// transactions - it's allowed, that's why the check has been removed. On the other | ||
// hand, there needs to be a check that denies duplicated burn records for the same | ||
// asset inside the same transaction. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does need to check that there are no duplicate burns in a single transaction somewhere, but that could be validated during deserialization or semantic validation, and this should update the asset state correctly otherwise. I think we should update the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good idea to use a
In my opinion, we could continue using a |
||
issued_assets.insert(asset_base, updated_asset_state); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct, thank you for catching it!