-
Notifications
You must be signed in to change notification settings - Fork 107
feat(AggLayer): B2AGG note consumption check
#2334
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
base: agglayer
Are you sure you want to change the base?
Changes from all commits
181b25d
a89766f
841f839
5b855fc
bec68d1
b00c976
2aba29c
dfb75bc
4b2f3fa
e3a4af1
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 |
|---|---|---|
|
|
@@ -12,8 +12,9 @@ const B2AGG_NOTE_NUM_STORAGE_ITEMS=6 | |
| # ERRORS | ||
| # ================================================================================================= | ||
| const ERR_B2AGG_WRONG_NUMBER_OF_ASSETS="B2AGG script requires exactly 1 note asset" | ||
|
|
||
| const ERR_B2AGG_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS="B2AGG script expects exactly 6 note storage items" | ||
| const ERR_B2AGG_TARGET_ACCOUNT_MISMATCH="B2AGG note attachment target account does not match consuming account" | ||
|
|
||
|
|
||
| #! Bridge-to-AggLayer (B2AGG) note script: bridges assets from Miden to an AggLayer-connected chain. | ||
| #! | ||
|
|
@@ -34,10 +35,13 @@ const ERR_B2AGG_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS="B2AGG script expects exactly | |
| #! - destination_address_2: bytes 8-11 | ||
| #! - destination_address_3: bytes 12-15 | ||
| #! - destination_address_4: bytes 16-19 | ||
| #! Note attachment is constructed from a NetworkAccountTarget standard: | ||
| #! - [0, 0, target_id_prefix, target_id_suffix] | ||
| #! | ||
| #! Panics if: | ||
| #! - The note does not contain exactly 6 storage items. | ||
| #! - The note does not contain exactly 1 asset. | ||
| #! - The note attachment does not target the consuming account. | ||
| #! | ||
| begin | ||
| dropw | ||
|
|
@@ -58,6 +62,26 @@ begin | |
| exec.basic_wallet::add_assets_to_account | ||
| # => [pad(16)] | ||
| else | ||
| # Ensure note attachment targets the consuming bridge account. | ||
| exec.active_note::get_metadata | ||
| # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] | ||
|
|
||
| # TODO simplify once https://github.com/0xMiden/miden-base/pull/2338 lands | ||
|
|
||
| swapw dropw | ||
| # => [NOTE_ATTACHMENT, pad(12)] | ||
|
|
||
| # Reorder attachment word to [target_id_prefix, target_id_suffix]. | ||
| drop drop | ||
| # => [target_id_prefix, target_id_suffix, pad(14)] (auto-padding) | ||
|
Comment on lines
+65
to
+76
Contributor
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. |
||
|
|
||
| exec.active_account::get_id | ||
| # => [account_id_prefix, account_id_suffix, target_id_prefix, target_id_suffix, pad(14)] | ||
|
|
||
| exec.account_id::is_equal | ||
| assert.err=ERR_B2AGG_TARGET_ACCOUNT_MISMATCH | ||
| # => [pad(16)] | ||
|
|
||
| # Store note storage -> mem[8..14] | ||
| push.8 exec.active_note::get_storage | ||
| # => [num_storage_items, dest_ptr, pad(16)] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| use miden::agglayer::bridge_in | ||
| use miden::protocol::active_note | ||
| use miden::protocol::active_account | ||
| use miden::protocol::account_id | ||
|
|
||
| # CONSTANTS | ||
| # ================================================================================================= | ||
|
|
@@ -10,6 +12,7 @@ const STORAGE_PTR_GER_UPPER = 4 | |
| # ERRORS | ||
| # ================================================================================================= | ||
| const ERR_UPDATE_GER_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS = "UPDATE_GER script expects exactly 8 note storage items" | ||
| const ERR_UPDATE_GER_TARGET_ACCOUNT_MISMATCH = "UPDATE_GER note attachment target account does not match consuming account" | ||
|
|
||
| #! Agglayer Bridge UPDATE_GER script: updates the GER by calling the bridge_in::update_ger function. | ||
|
Contributor
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. nit: let's add |
||
| #! | ||
|
|
@@ -33,6 +36,28 @@ begin | |
| dropw | ||
| # => [pad(16)] | ||
|
|
||
| # Ensure note attachment targets the consuming bridge account. | ||
| exec.active_note::get_metadata | ||
| # => [NOTE_ATTACHMENT, METADATA_HEADER, pad(8)] | ||
|
|
||
| # TODO simplify once https://github.com/0xMiden/miden-base/pull/2338 lands | ||
|
|
||
| swapw dropw | ||
| # => [NOTE_ATTACHMENT, pad(12)] | ||
|
|
||
| # Reorder attachment word to [target_id_prefix, target_id_suffix]. | ||
| drop drop | ||
| # => [target_id_prefix, target_id_suffix, pad(14)] (auto-padding) | ||
|
|
||
| exec.active_account::get_id | ||
| # => [account_id_prefix, account_id_suffix, target_id_prefix, target_id_suffix, pad(14)] | ||
|
|
||
| exec.account_id::is_equal | ||
| assert.err=ERR_UPDATE_GER_TARGET_ACCOUNT_MISMATCH | ||
| # => [pad(16)] | ||
|
|
||
| # proceed with the GER update logic | ||
|
|
||
| push.STORAGE_PTR_GER_LOWER exec.active_note::get_storage | ||
| # => [num_storage_items, dest_ptr, pad(16)] | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| //! Bridge Out note creation utilities. | ||
| //! | ||
| //! This module provides helpers for creating B2AGG (Bridge to AggLayer) notes, | ||
| //! which are used to bridge assets out from Miden to the AggLayer network. | ||
| use alloc::string::ToString; | ||
| use alloc::vec::Vec; | ||
|
|
||
| use miden_core::Felt; | ||
| use miden_protocol::account::AccountId; | ||
| use miden_protocol::crypto::rand::FeltRng; | ||
| use miden_protocol::errors::NoteError; | ||
| use miden_protocol::note::{ | ||
| Note, | ||
| NoteAssets, | ||
| NoteAttachment, | ||
| NoteExecutionHint, | ||
| NoteMetadata, | ||
| NoteRecipient, | ||
| NoteStorage, | ||
| NoteTag, | ||
| NoteType, | ||
| }; | ||
| use miden_standards::note::NetworkAccountTarget; | ||
|
|
||
| use crate::{EthAddressFormat, b2agg_script}; | ||
|
|
||
| // B2AGG NOTE STRUCTURES | ||
| // ================================================================================================ | ||
|
|
||
| /// Storage data for B2AGG note creation. | ||
| /// | ||
| /// Contains the destination network and address information required | ||
| /// for bridging assets to the AggLayer network. | ||
| #[derive(Debug, Clone)] | ||
|
Comment on lines
+33
to
+35
Contributor
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. nit: comment wrapping. |
||
| pub struct B2AggNoteStorage { | ||
| /// Destination network identifier (AggLayer-assigned network ID) | ||
| pub destination_network: u32, | ||
| /// Destination Ethereum address (20 bytes) | ||
| pub destination_address: EthAddressFormat, | ||
|
Contributor
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. nit (and not related to this PR): I think we should call it just |
||
| } | ||
|
|
||
| impl B2AggNoteStorage { | ||
| /// Creates a new B2AGG note storage with the specified destination. | ||
| pub fn new(destination_network: u32, destination_address: EthAddressFormat) -> Self { | ||
| Self { destination_network, destination_address } | ||
| } | ||
|
|
||
| /// Converts the storage data to a vector of field elements for note storage. | ||
| /// | ||
| /// The layout is: | ||
| /// - 1 felt: destination_network | ||
| /// - 5 felts: destination_address (20 bytes as 5 u32 values) | ||
| pub fn to_elements(&self) -> Vec<Felt> { | ||
| let mut elements = Vec::with_capacity(6); | ||
|
|
||
| // Destination network | ||
| elements.push(Felt::new(self.destination_network as u64)); | ||
|
|
||
| // Destination address (5 u32 felts) | ||
| elements.extend(self.destination_address.to_elements()); | ||
|
|
||
| elements | ||
| } | ||
| } | ||
|
|
||
| impl TryFrom<B2AggNoteStorage> for NoteStorage { | ||
| type Error = NoteError; | ||
|
|
||
| fn try_from(storage: B2AggNoteStorage) -> Result<Self, Self::Error> { | ||
| NoteStorage::new(storage.to_elements()) | ||
| } | ||
| } | ||
|
|
||
| // B2AGG NOTE CREATION | ||
| // ================================================================================================ | ||
|
|
||
| /// Generates a B2AGG (Bridge to AggLayer) note. | ||
| /// | ||
| /// This note is used to bridge assets from Miden to another network via the AggLayer. | ||
| /// When consumed by a bridge account, the assets are burned and a corresponding | ||
| /// claim can be made on the destination network. | ||
| /// | ||
| /// # Parameters | ||
| /// - `storage`: The destination network and address information | ||
| /// - `assets`: The assets to bridge (must be fungible assets from a network faucet) | ||
| /// - `target_account_id`: The account ID that will consume this note (bridge account) | ||
| /// - `sender_account_id`: The account ID of the note creator | ||
| /// - `note_type`: The type of note (Public or Private) | ||
| /// - `rng`: Random number generator for creating the note serial number | ||
|
Comment on lines
+84
to
+90
Contributor
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. Do we need |
||
| /// | ||
| /// # Errors | ||
| /// Returns an error if note creation fails. | ||
| pub fn create_b2agg_note<R: FeltRng>( | ||
| storage: B2AggNoteStorage, | ||
| assets: NoteAssets, | ||
| target_account_id: AccountId, | ||
| sender_account_id: AccountId, | ||
| note_type: NoteType, | ||
| rng: &mut R, | ||
| ) -> Result<Note, NoteError> { | ||
| let note_storage = NoteStorage::try_from(storage)?; | ||
|
|
||
| let tag = NoteTag::new(0); | ||
|
|
||
| let attachment = NoteAttachment::from( | ||
| NetworkAccountTarget::new(target_account_id, NoteExecutionHint::None) | ||
|
Contributor
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. I think we should use |
||
| .map_err(|e| NoteError::other(e.to_string()))?, | ||
| ); | ||
|
|
||
| let metadata = NoteMetadata::new(sender_account_id, note_type, tag).with_attachment(attachment); | ||
|
|
||
| let b2agg_script = b2agg_script(); | ||
| let recipient = NoteRecipient::new( | ||
| rng.draw_word(), | ||
| miden_protocol::note::NoteScript::new(b2agg_script), | ||
| note_storage, | ||
| ); | ||
|
|
||
| Ok(Note::new(assets, metadata, recipient)) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,12 +24,14 @@ use miden_standards::account::auth::NoAuth; | |
| use miden_standards::account::faucets::NetworkFungibleFaucet; | ||
| use miden_utils_sync::LazyLock; | ||
|
|
||
| pub mod bridge_out; | ||
| pub mod claim_note; | ||
| pub mod errors; | ||
| pub mod eth_types; | ||
| pub mod update_ger_note; | ||
| pub mod utils; | ||
|
|
||
| pub use bridge_out::{B2AggNoteStorage, create_b2agg_note}; | ||
| pub use claim_note::{ | ||
| ClaimNoteStorage, | ||
| ExitRoot, | ||
|
|
@@ -254,21 +256,26 @@ pub fn create_agglayer_faucet_component( | |
|
|
||
| /// Creates a complete bridge account builder with the standard configuration. | ||
| pub fn create_bridge_account_builder(seed: Word) -> AccountBuilder { | ||
| // Create the "bridge_in" component | ||
| let ger_upper_storage_slot_name = StorageSlotName::new("miden::agglayer::bridge::ger_upper") | ||
| .expect("Bridge storage slot name should be valid"); | ||
| let ger_lower_storage_slot_name = StorageSlotName::new("miden::agglayer::bridge::ger_lower") | ||
| .expect("Bridge storage slot name should be valid"); | ||
| let bridge_storage_slots = vec![ | ||
| let bridge_in_storage_slots = vec![ | ||
| StorageSlot::with_value(ger_upper_storage_slot_name, Word::empty()), | ||
| StorageSlot::with_value(ger_lower_storage_slot_name, Word::empty()), | ||
| ]; | ||
|
|
||
| let bridge_in_component = bridge_in_component(bridge_storage_slots); | ||
| let bridge_in_component = bridge_in_component(bridge_in_storage_slots); | ||
|
|
||
| let bridge_out_component = bridge_out_component(vec![]); | ||
| // Create the "bridge_out" component | ||
| let let_storage_slot_name = StorageSlotName::new("miden::agglayer::let").unwrap(); | ||
|
Contributor
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. Maybe not for this PR, but we should normalize storage slot naming. For example, this should be |
||
| let bridge_out_storage_slots = vec![StorageSlot::with_empty_map(let_storage_slot_name)]; | ||
| let bridge_out_component = bridge_out_component(bridge_out_storage_slots); | ||
|
|
||
| // Combine the components into a single account(builder) | ||
| Account::builder(seed.into()) | ||
| .storage_mode(AccountStorageMode::Public) | ||
| .storage_mode(AccountStorageMode::Network) | ||
| .with_component(bridge_out_component) | ||
| .with_component(bridge_in_component) | ||
| } | ||
|
|
||
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.
nit: let's add
NOTE SCRIPTsection header above this line.