-
Notifications
You must be signed in to change notification settings - Fork 4
fix(token): reject foreign-owned definitions on initialize #82
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: main
Are you sure you want to change the base?
Changes from all commits
90a2ab0
beda1d1
9a186de
d9337d5
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 |
|---|---|---|
| @@ -1,17 +1,134 @@ | ||
| #![no_main] | ||
| #![cfg_attr(not(test), no_main)] | ||
|
|
||
| use nssa_core::{ | ||
| account::AccountWithMetadata, | ||
| program::{AccountPostState, ProgramId}, | ||
| }; | ||
| #[cfg(not(test))] | ||
| use nssa_core::program::{ProgramInput, ProgramOutput}; | ||
| #[cfg(test)] | ||
| use spel_framework::prelude::*; | ||
| use nssa_core::account::AccountWithMetadata; | ||
| use token_core::Instruction as TokenInstruction; | ||
|
|
||
| #[cfg(not(test))] | ||
| risc0_zkvm::guest::entry!(main); | ||
|
|
||
| #[cfg(not(test))] | ||
| fn main() { | ||
| let ( | ||
| ProgramInput { | ||
| self_program_id, | ||
| caller_program_id, | ||
| pre_states, | ||
| instruction, | ||
| }, | ||
| instruction_words, | ||
| ) = nssa_core::program::read_nssa_inputs::<TokenInstruction>(); | ||
| let pre_states_clone = pre_states.clone(); | ||
|
|
||
| let post_states = dispatch(instruction, pre_states, self_program_id); | ||
|
|
||
| ProgramOutput::new( | ||
| self_program_id, | ||
| caller_program_id, | ||
| instruction_words, | ||
| pre_states_clone, | ||
| post_states, | ||
| ) | ||
| .write(); | ||
| } | ||
|
|
||
| #[cfg_attr(test, allow(dead_code))] | ||
| fn dispatch( | ||
| instruction: TokenInstruction, | ||
| pre_states: Vec<AccountWithMetadata>, | ||
| self_program_id: ProgramId, | ||
| ) -> Vec<AccountPostState> { | ||
| match instruction { | ||
| TokenInstruction::Transfer { amount_to_transfer } => { | ||
| let [sender, recipient] = expect_accounts(pre_states); | ||
|
|
||
| token_program::transfer::transfer(sender, recipient, amount_to_transfer) | ||
| } | ||
| TokenInstruction::NewFungibleDefinition { name, total_supply } => { | ||
| let [definition_target_account, holding_target_account] = | ||
| expect_accounts(pre_states); | ||
| token_program::new_definition::new_fungible_definition( | ||
| definition_target_account, | ||
| holding_target_account, | ||
| name, | ||
| total_supply, | ||
| ) | ||
| } | ||
| TokenInstruction::NewDefinitionWithMetadata { | ||
| new_definition, | ||
| metadata, | ||
| } => { | ||
| let [ | ||
| definition_target_account, | ||
| holding_target_account, | ||
| metadata_target_account, | ||
| ] = expect_accounts(pre_states); | ||
| token_program::new_definition::new_definition_with_metadata( | ||
| definition_target_account, | ||
| holding_target_account, | ||
| metadata_target_account, | ||
| new_definition, | ||
| *metadata, | ||
| ) | ||
|
Comment on lines
+62
to
+77
|
||
| } | ||
| TokenInstruction::InitializeAccount => { | ||
| let [definition_account, account_to_initialize] = expect_accounts(pre_states); | ||
| token_program::initialize::initialize_account( | ||
| definition_account, | ||
| account_to_initialize, | ||
| self_program_id, | ||
| ) | ||
| } | ||
| TokenInstruction::Burn { amount_to_burn } => { | ||
| let [definition_account, user_holding_account] = expect_accounts(pre_states); | ||
| token_program::burn::burn( | ||
| definition_account, | ||
| user_holding_account, | ||
| amount_to_burn, | ||
| ) | ||
| } | ||
| TokenInstruction::Mint { amount_to_mint } => { | ||
| let [definition_account, user_holding_account] = expect_accounts(pre_states); | ||
| token_program::mint::mint( | ||
| definition_account, | ||
| user_holding_account, | ||
| amount_to_mint, | ||
| self_program_id, | ||
| ) | ||
| } | ||
| TokenInstruction::PrintNft => { | ||
| let [master_account, printed_account] = expect_accounts(pre_states); | ||
|
|
||
| token_program::print_nft::print_nft(master_account, printed_account) | ||
| } | ||
| } | ||
|
Comment on lines
+41
to
+108
|
||
| } | ||
|
|
||
| #[cfg_attr(test, allow(dead_code))] | ||
| fn expect_accounts<const N: usize>( | ||
| pre_states: Vec<AccountWithMetadata>, | ||
| ) -> [AccountWithMetadata; N] { | ||
| let actual = pre_states.len(); | ||
| pre_states.try_into().unwrap_or_else(|_| { | ||
| panic!("Account count mismatch: expected {N}, got {actual}"); | ||
| }) | ||
| } | ||
|
|
||
| // Kept for `idl-gen`, which parses source annotations directly. | ||
| #[cfg(test)] | ||
| #[allow(dead_code)] | ||
|
Collaborator
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 looks like a workaround. We should rather add support for
Collaborator
Author
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. Agreed. The explicit guest dispatch was only there to avoid adding a caller-supplied program id to the public token instruction, but the cleaner fix is for SPEL to expose the trusted
Collaborator
Author
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 opened logos-co/spel#172 to track support for SPEL's |
||
| #[lez_program(instruction = "token_core::Instruction")] | ||
|
3esmit marked this conversation as resolved.
|
||
| mod token { | ||
| #[allow(unused_imports)] | ||
| use super::*; | ||
|
|
||
| /// Transfer tokens from sender to recipient. | ||
| /// Fresh public recipients must be explicitly authorized in the same transaction. | ||
| #[allow(dead_code, clippy::boxed_local)] | ||
| #[instruction] | ||
| pub fn transfer( | ||
| sender: AccountWithMetadata, | ||
|
|
@@ -27,6 +144,7 @@ mod token { | |
|
|
||
| /// Create a new fungible token definition without metadata. | ||
| /// Definition and holding targets must be uninitialized and authorized. | ||
| #[allow(dead_code, clippy::boxed_local)] | ||
| #[instruction] | ||
| pub fn new_fungible_definition( | ||
| definition_target_account: AccountWithMetadata, | ||
|
|
@@ -46,6 +164,7 @@ mod token { | |
|
|
||
| /// Create a new fungible or non-fungible token definition with metadata. | ||
| /// Definition, holding, and metadata targets must be uninitialized and authorized. | ||
| #[allow(dead_code, clippy::boxed_local)] | ||
| #[instruction] | ||
| pub fn new_definition_with_metadata( | ||
| definition_target_account: AccountWithMetadata, | ||
|
|
@@ -67,6 +186,7 @@ mod token { | |
|
|
||
| /// Initialize a token holding account for a given token definition. | ||
| /// The holding target must be uninitialized and authorized. | ||
| #[allow(dead_code, clippy::boxed_local)] | ||
| #[instruction] | ||
| pub fn initialize_account( | ||
| definition_account: AccountWithMetadata, | ||
|
|
@@ -76,11 +196,13 @@ mod token { | |
| token_program::initialize::initialize_account( | ||
| definition_account, | ||
| account_to_initialize, | ||
| nssa_core::program::DEFAULT_PROGRAM_ID, | ||
| ), | ||
| )) | ||
| } | ||
|
|
||
| /// Burn tokens from the holder's account. | ||
| #[allow(dead_code, clippy::boxed_local)] | ||
| #[instruction] | ||
| pub fn burn( | ||
| definition_account: AccountWithMetadata, | ||
|
|
@@ -96,6 +218,7 @@ mod token { | |
|
|
||
| /// Mint new tokens to the holder's account. | ||
| /// Fresh public holders must be explicitly authorized in the same transaction. | ||
| #[allow(dead_code, clippy::boxed_local)] | ||
| #[instruction] | ||
| pub fn mint( | ||
| definition_account: AccountWithMetadata, | ||
|
|
@@ -106,11 +229,13 @@ mod token { | |
| definition_account, | ||
| user_holding_account, | ||
| amount_to_mint, | ||
| nssa_core::program::DEFAULT_PROGRAM_ID, | ||
| ))) | ||
| } | ||
|
|
||
| /// Print a new NFT from the master copy. | ||
| /// The printed copy target must be uninitialized and authorized. | ||
| #[allow(dead_code, clippy::boxed_local)] | ||
| #[instruction] | ||
| pub fn print_nft( | ||
| master_account: AccountWithMetadata, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.