-
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
Refactor AssetSupply to AssetRecords and update issuance verification functions #138
Conversation
… a finalization set, fix tests accordingly
… update verify_issue_bundle function to ensure the first asset issuance contains the reference note, update tests and docs accordingly
… verification only, update IssueAction::verify and return (AssetBase, NoteValue) from it instead of (AssetBase, AssetInfo<...>)
…ication test functions updated to check the result of the verify_issue_bundle directly
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.
Very good.
Added some semantic coments and one significant name change AssetInfo
-> AssetRecord
Also, for get_action_by_asset()
please fix comment to
/// # Returns
///
/// Returns `Some(&IssueAction)` if a single matching action is found.
/// Returns `None` if no action matches the given asset base.
(It is not very consise to start with If here)
Also, Add to the top of the file:
//! Issuance logic for Zcash Shielded Assets (ZSAs).
//!
//! This module defines structures and methods for creating, authorizing, and verifying
//! issuance bundles, which introduce new shielded assets into the Orchard protocol.
//!
//! The core components include:
//! - `IssueBundle`: Represents a collection of issuance actions with authorization states.
//! - `IssueAction`: Defines an individual issuance event, including asset details and notes.
//! - `IssueAuth` variants: Track issuance states from creation to finalization.
//! - `verify_issue_bundle`: Ensures issuance validity and prevents unauthorized asset creation.
//!
//! Errors related to issuance, such as invalid signatures or supply overflows,
//! are handled through the `Error` enum.
src/issuance.rs
Outdated
/// - **Signature Verification**: | ||
/// - Ensures the signature on the provided `sighash` matches the bundle’s authorization. | ||
/// - **IssueAction Verification**: | ||
/// - Checks that the asset description size is correct. |
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.
let's move this check (here and code) inside verify()
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.
Done.
src/issuance.rs
Outdated
})?; | ||
|
||
Ok(supply_info) | ||
Ok(verified_asset_states) |
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.
very good.
I did renames to simplify and adjust to the new record
terminology. Semantics are the same:
pub fn verify_issue_bundle(
bundle: &IssueBundle<Signed>,
sighash: [u8; 32],
get_global_records: impl Fn(&AssetBase) -> Option<AssetRecord>,
) -> Result<HashMap<AssetBase, AssetRecord>, Error> {
bundle
.ik()
.verify(&sighash, bundle.authorization().signature())
.map_err(|_| IssueBundleInvalidSignature)?;
let new_records =
bundle
.actions()
.iter()
.try_fold(HashMap::new(), |mut new_records, action| {
if !is_asset_desc_of_valid_size(action.asset_desc()) {
return Err(WrongAssetDescSize);
}
let (asset, amount) = action.verify(bundle.ik())?;
let is_finalized = action.is_finalized();
let ref_note = action.get_reference_note();
let new_asset_record = match new_records
.get(&asset)
.cloned()
.or_else(|| get_global_records(&asset))
{
// The first issuance of the asset
None => AssetRecord::new(
amount,
is_finalized,
*ref_note.ok_or(MissingReferenceNoteOnFirstIssuance)?,
),
// Subsequent issuances of the asset
Some(current_record) => {
let amount =
(current_record.amount + amount).ok_or(ValueOverflow)?;
if current_record.is_finalized {
return Err(IssueActionPreviouslyFinalizedAssetBase(asset));
}
AssetRecord::new(amount, is_finalized, current_record.reference_note)
}
};
new_records.insert(asset, new_asset_record);
Ok(new_records)
})?;
Ok(new_records)
}
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.
Done (updated the function).
src/issuance.rs
Outdated
let (rng, isk, ik, recipient, sighash, first_nullifier) = setup_params(); | ||
|
||
// A closure to build an issue bundle using parameters from `setup_params`. | ||
// Using a closure avoids passing `rng`, `ik`, etc. each time. | ||
let build_issue_bundle = |data: &[(&Vec<u8>, u64, bool, bool)]| -> IssueBundle<Signed> { |
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.
Consider defining a proper function here for clerity
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.
Fixed (converted it to a function).
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.
Good,
One more iteration is needed to finilize.
Also, please move is_reference_note()
down, under create_referance_note()
src/issuance.rs
Outdated
*ref_note.ok_or(MissingReferenceNoteOnFirstIssuance)?, | ||
), | ||
|
||
// Subsequent issuances of the asset |
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.
Subsequent issuance
(issuance is already plural)
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.
Fixed.
src/issuance.rs
Outdated
fn issue_bundle_verify_with_global_state() { | ||
type GlobalState = HashMap<AssetBase, AssetRecord>; | ||
|
||
fn first_note(bundle: &IssueBundle<Signed>, action_index: usize) -> Note { |
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.
consider
fn get_first_note(bundle: &IssueBundle<Signed>, action_index: usize) -> &Note {
bundle.actions()[action_index].notes().first().unwrap()
}
better naming + you are cloning the notes anyway later.
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.
Fixed (renamed).
src/issuance.rs
Outdated
bundle.actions()[action_index].notes()[0] | ||
} | ||
|
||
fn build_expected_global_state(data: &[(&AssetBase, u64, bool, Note)]) -> GlobalState { |
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.
consider &Note
. clone inside, if needed. Aslo for compatibility with get_first_note()->&Note
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.
also fn build_global_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.
I refactored the code and removed the build_expected_global_state function
. Now, I simply use HashMap::from([...]) instead, passing an array into it. To create the array elements, I added a simple
build_state_entryfunction (for
Note- it receives it by reference, i.e.
&Note`).
src/issuance.rs
Outdated
AssetRecord::new(NoteValue::from_raw(amount), is_finalized, reference_note), | ||
) | ||
}) | ||
.collect::<HashMap<_, _>>() |
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.
redundent, just collect()
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.
I refactored the code and use HashMap::from([...])
now, as mentioned above.
src/issuance.rs
Outdated
.map(|(asset_base, amount, is_finalized, reference_note)| { | ||
( | ||
asset_base.clone(), | ||
AssetRecord::new(NoteValue::from_raw(amount), is_finalized, reference_note), |
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.
*reference_note
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.
Fixed it (in new build_state_entry
function, see comments above).
src/issuance.rs
Outdated
[u8; 32], | ||
Nullifier, | ||
), | ||
data: &[(&Vec<u8>, u64, bool, bool)], |
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.
replace the tuple with a proper data struct and a constructor.
The bool,bool
is very confusing
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.
Fixed - added BundleTestData
struct for that with a constructor.
src/issuance.rs
Outdated
) -> IssueBundle<Signed> { | ||
let (rng, ref isk, ref ik, recipient, sighash, ref first_nullifier) = *params; | ||
|
||
let (asset_desc, amount, first_issuance, is_finalized) = data.first().unwrap().clone(); |
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.
we always do asset_desc, amount, is_finilized, ...
. Let's stick to this order
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.
Fixed (in the new BundleTestData
struct I mentioned above).
src/issuance.rs
Outdated
} | ||
|
||
fn build_issue_bundle( | ||
params: &( |
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.
why params
is needed? Let's just go with named input params.
fn build_issue_bundle(
rng: OsRng,
isk: IssuanceAuthorizingKey,
ik: IssuanceValidatingKey,
recipient: Address,
sighash: [u8; 32],
first_nullifier: Nullifier,
note_list: &[TestNoteList],
) -> IssueBundle<Signed> {
(it does not make sence to preserve the un-named tuple inside the function)
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.
I see that it is based on the old setup_params()
function but complexity has grown since then.
(sighash and first_nullifer cannot be identified by their type)
Two ways forward:
-
continue using the
setup_params()
but dcompose it into named params before calling the newbuild_issue_bundle()
-
change
setup_params
to return a struct and use the struct as an input to `build_issue_bundle.
Anyway we should have names to the params.
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.
Fixed - I introduced new struct called TestParams
with those fields, so setup_params
returns that struct now instead of a tuple. Updated all tests to use it (replaced tuple destructuring with struct destructuring).
…t as a return value of setup_params instead of a tuple
…refactor it according the #138 review comments
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.
Good restructure, added comments to finilize
…s issuance_global_state.rs, and use copies of setup_params and TestParams there. The code doesn't compile because some methods need to be public. Further fixes are required.
…b(crate) method to generate random keys
tests/issuance_global_state.rs
Outdated
@@ -29,18 +35,21 @@ struct TestParams { | |||
} | |||
|
|||
fn setup_params() -> TestParams { | |||
let mut rng = OsRng; | |||
use group::ff::{FromUniformBytes, PrimeField}; |
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.
Add a comment that these parameters are for testing global state only and should not be used in an actual setting.
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.
Done.
This PR modifies the
verify_issue_bundle
function and refactors thesupply_info.rs
module by removingSupplyInfo
and introducing a new generic type,AssetInfo
, in place of the previousAssetSupply
struct.AssetInfo
allows to properly distinguish the asset verification logic between reference notes required for first issuances versus optional references for subsequent issuances.Key Changes
Rename
AssetSupply
struct toAssetInfo
Rename
IssueAction::verify_supply
toIssueAction::verify
and update its return valueverify
method now returns only the sum of the action's note values instead ofAssetInfo
.is_finalized
andreference_note
action properties are now handled directly in theverify_issue_bundle
function.Revise
verify_issue_bundle
get_global_asset_state
callback instead of relying on afinalization
set.HashMap<AssetBase, AssetInfo>
(instead ofSupplyInfo
).Remove
supply_info.rs
SupplyInfo
struct and related tests, sinceverify_issue_bundle
now returns aHashMap
ofAssetInfo
objects.Test updates
verify_issue_bundle
signature and theget_global_asset_state
approach.Add issuance workflow test
issue_bundle_verify_with_global_state
that performs a series of bundle creations and verifications, with a global state simulation.Miscellaneous fixes
Error
variant imports inissuance.rs
by referencing the localError
enum directly.