-
Notifications
You must be signed in to change notification settings - Fork 165
feat(standards): add the pass-through transaction script #3713
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: next
Are you sure you want to change the base?
Changes from all commits
b1f583e
84ed47a
5671c34
a179be1
fba243c
c45dc12
a6fda68
09ccc2a
66e81ec
62799d8
8ea966d
49dec91
35d81ca
6fe84a3
c66cf89
d4a98dd
18807a1
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 |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| [package] | ||
| name = "miden-standards-pass-through" | ||
| version.workspace = true | ||
|
|
||
| [lib] | ||
| kind = "account-component" | ||
| namespace = "miden::standards::components::pass_through" | ||
| path = "pass_through.masm" | ||
|
|
||
| [dependencies] | ||
| miden-core.workspace = true | ||
| miden-protocol.workspace = true | ||
| miden-standards.workspace = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # The MASM code of the Pass Through Account Component. | ||
| # | ||
| # See the `PassThrough` Rust type's documentation for more details. | ||
|
|
||
| pub use {sweep_asset_to_note} from miden::standards::pass_through | ||
| pub use {assert_vault_unchanged} from miden::standards::pass_through |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| # The account procedures a pass-through transaction needs. | ||
| # | ||
| # See the `PassThrough` Rust type's documentation for more details. | ||
|
|
||
| use miden::core::word | ||
| use miden::protocol::active_account | ||
| use miden::protocol::native_account | ||
| use miden::protocol::output_note | ||
| use {AssetId} from miden::protocol::types | ||
|
|
||
| # ERRORS | ||
| # ================================================================================================= | ||
|
|
||
| const ERR_PASS_THROUGH_ACCOUNT_ALREADY_HELD_ASSET = "a pass-through account must not hold the swept asset before the transaction" | ||
|
|
||
| const ERR_PASS_THROUGH_ACCOUNT_VAULT_CHANGED = "the account's vault is not the one it had at the start of the pass-through transaction" | ||
|
|
||
| # PUBLIC INTERFACE | ||
| # ================================================================================================= | ||
|
|
||
| #! Moves the account's entire balance of the given asset into the output note at `note_idx`. | ||
| #! | ||
| #! Moving the whole balance is what makes a pass-through transaction independent of how many notes | ||
| #! it consumes: whatever the input notes deposited is a single balance by the time this runs, so | ||
| #! one call per asset drains it regardless of the number of notes. | ||
| #! | ||
| #! Does nothing if the account holds none of the asset. | ||
| #! | ||
| #! Unlike wallets::basic::move_asset_to_note, which makes the caller name the amount, this | ||
| #! procedure reads the balance itself, so it needs no prior knowledge of what the vault holds. | ||
| #! ERR_PASS_THROUGH_ACCOUNT_ALREADY_HELD_ASSET bounds it to what the transaction deposited, but | ||
| #! nothing bounds who moves that: any note script the account consumes can call this and redirect | ||
| #! what earlier notes deposited, and on an account whose auth procedure authenticates nobody - | ||
| #! which is what keeps a pass-through account's commitment unchanged - any third party can execute | ||
| #! a transaction as the account and name themselves as the destination. | ||
| #! | ||
| #! Assets passing through are therefore only safe if the input note's own script constrains where | ||
| #! they go, or if they were already unrestricted before they arrived. TX_FEE notes are the latter: | ||
| #! any account may consume one, so routing them through a pass-through account takes nothing away. | ||
| #! Routing a destination-restricted note through one instead destroys that restriction, since the | ||
| #! assets become claimable by whoever executes the next transaction as the account. | ||
| #! | ||
| #! Inputs: [ASSET_ID, note_idx, pad(11)] | ||
| #! Outputs: [pad(16)] | ||
| #! | ||
| #! Where: | ||
| #! - ASSET_ID is the ID of the asset to move. | ||
| #! - note_idx is the index of the output note to move the balance into. | ||
| #! | ||
| #! Panics if: | ||
| #! - the account held the asset when the transaction started. | ||
| #! - the maximum number of assets per note is exceeded. | ||
| #! | ||
| #! Invocation: call | ||
| @account_procedure | ||
| pub proc sweep_asset_to_note(asset_id: AssetId, note_idx: u16) | ||
| # only what this transaction deposited may be moved out, so the account must have started | ||
| # without the asset | ||
| dupw exec.native_account::get_initial_asset exec.word::eqz | ||
| assert.err=ERR_PASS_THROUGH_ACCOUNT_ALREADY_HELD_ASSET | ||
| # => [ASSET_ID, note_idx, pad(11)] | ||
|
|
||
| dupw exec.active_account::get_asset | ||
| # => [ASSET_VALUE, ASSET_ID, note_idx, pad(11)] | ||
|
|
||
| dupw exec.word::eqz | ||
| # => [is_absent, ASSET_VALUE, ASSET_ID, note_idx, pad(11)] | ||
|
|
||
| if.true | ||
| # the account holds none of the asset, so there is nothing to move | ||
| dropw dropw drop | ||
| # => [pad(16)] | ||
|
Comment on lines
+69
to
+72
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.
Is this desired? If you call If you call this procedure now with an asset you don't have, the call to this procedure would succeed, but it would have no side effects. |
||
| else | ||
| swapw | ||
| # => [ASSET_ID, ASSET_VALUE, note_idx, pad(11)] | ||
|
|
||
| # remove the balance from the vault and add it to the note | ||
| # (see basic_wallet::move_asset_to_note for the same sequence) | ||
| dupw.1 dupw.1 | ||
| # => [ASSET_ID, ASSET_VALUE, ASSET_ID, ASSET_VALUE, note_idx, pad(11)] | ||
|
|
||
| exec.native_account::remove_asset dropw | ||
| # => [ASSET_ID, ASSET_VALUE, note_idx, pad(11)] | ||
|
|
||
| exec.output_note::add_asset | ||
| # => [pad(16)] | ||
| end | ||
| end | ||
|
|
||
| #! Asserts that the account's vault is the one it had at the start of the transaction. | ||
| #! | ||
| #! A pass-through transaction script calls this once it has moved everything out, so that an asset | ||
| #! it failed to name fails the transaction rather than silently changing the account. | ||
| #! | ||
| #! Inputs: [pad(16)] | ||
| #! Outputs: [pad(16)] | ||
| #! | ||
| #! Panics if: | ||
| #! - the account's vault root differs from its initial one. | ||
| #! | ||
| #! Invocation: call | ||
| @account_procedure | ||
| pub proc assert_vault_unchanged | ||
| exec.native_account::get_initial_vault_root | ||
| # => [INITIAL_VAULT_ROOT, pad(16)] | ||
|
|
||
| exec.active_account::get_vault_root | ||
| # => [VAULT_ROOT, INITIAL_VAULT_ROOT, pad(16)] | ||
|
|
||
| assert_eqw.err=ERR_PASS_THROUGH_ACCOUNT_VAULT_CHANGED | ||
| # => [pad(16)] | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| pub mod expiration | ||
| pub mod pass_through | ||
| pub mod send_notes |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # The canonical pass-through transaction scripts. | ||
| # | ||
| # What happens to the assets moved out depends on the exact script used (e.g. move all notes' | ||
| # assets into a single P2ID, move each note's assets into its own P2ID, etc.). | ||
|
|
||
| pub mod single_p2id |
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: I'd move this comment wording to the procedure comment above or delete it since this is already mentioned in the procedure comment