mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-05-18 15:09:51 +00:00
feat(token): verify definition ownership via self_program_id in initialize and mint
Pass `ctx.self_program_id` from `ProgramContext` into `initialize_account` and `mint`, which now assert that the token definition account is owned by the token program. This prevents callers from supplying a foreign-owned account as the definition. See https://github.com/logos-co/spel/issues/172
This commit is contained in:
parent
ceb8a4b597
commit
8005c74e26
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -2970,7 +2970,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2985,7 +2985,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-macros"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@ -5,7 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3", features = ["host"] }
|
||||
spel-framework-macros = { git = "https://github.com/logos-co/spel.git", rev = "ba6e87d086ed85c5ac095325d8a28f02e3d33ca2", package = "spel-framework-macros" }
|
||||
spel-framework-macros = { git = "https://github.com/logos-co/spel.git", rev = "6473ab4c400bc59bac8db83a286faaeafa7d1999", package = "spel-framework-macros" }
|
||||
token_core = { path = "../../token/core" }
|
||||
borsh = { version = "1.5", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@ -26,7 +26,7 @@ pub enum Instruction {
|
||||
/// - Vault Holding Account for Token A
|
||||
/// - Vault Holding Account for Token B
|
||||
/// - Pool Liquidity Token Definition
|
||||
/// - LP Lock Holding Account, derived as `compute_lp_lock_holding_pda(amm_program_id,
|
||||
/// - LP Lock Holding Account, derived as `compute_lp_lock_holding_pda(self_program_id,
|
||||
/// pool.account_id)`
|
||||
/// - User Holding Account for Token A (authorized)
|
||||
/// - User Holding Account for Token B (authorized)
|
||||
@ -35,7 +35,6 @@ pub enum Instruction {
|
||||
token_a_amount: u128,
|
||||
token_b_amount: u128,
|
||||
fees: u128,
|
||||
amm_program_id: ProgramId,
|
||||
/// Unix timestamp (milliseconds) after which this transaction is invalid.
|
||||
deadline: u64,
|
||||
},
|
||||
|
||||
6
amm/methods/guest/Cargo.lock
generated
6
amm/methods/guest/Cargo.lock
generated
@ -2892,7 +2892,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2904,7 +2904,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2919,7 +2919,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-macros"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@ -10,7 +10,7 @@ name = "amm"
|
||||
path = "src/bin/amm.rs"
|
||||
|
||||
[dependencies]
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "ba6e87d086ed85c5ac095325d8a28f02e3d33ca2", package = "spel-framework" }
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "6473ab4c400bc59bac8db83a286faaeafa7d1999", package = "spel-framework" }
|
||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3" }
|
||||
risc0-zkvm = { version = "=3.0.5", default-features = false }
|
||||
amm_core = { path = "../../core" }
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
use std::num::NonZeroU128;
|
||||
|
||||
use spel_framework::prelude::*;
|
||||
use spel_framework::context::ProgramContext;
|
||||
use nssa_core::{
|
||||
account::{AccountId, AccountWithMetadata},
|
||||
program::ProgramId,
|
||||
};
|
||||
|
||||
risc0_zkvm::guest::entry!(main);
|
||||
@ -19,6 +19,7 @@ mod amm {
|
||||
/// A fresh user LP holding must be explicitly authorized by the caller.
|
||||
#[instruction]
|
||||
pub fn new_definition(
|
||||
ctx: ProgramContext,
|
||||
pool: AccountWithMetadata,
|
||||
vault_a: AccountWithMetadata,
|
||||
vault_b: AccountWithMetadata,
|
||||
@ -30,7 +31,6 @@ mod amm {
|
||||
token_a_amount: u128,
|
||||
token_b_amount: u128,
|
||||
fees: u128,
|
||||
amm_program_id: ProgramId,
|
||||
deadline: u64,
|
||||
) -> SpelResult {
|
||||
let (post_states, chained_calls) = amm_program::new_definition::new_definition(
|
||||
@ -45,7 +45,7 @@ mod amm {
|
||||
NonZeroU128::new(token_a_amount).expect("token_a_amount must be nonzero"),
|
||||
NonZeroU128::new(token_b_amount).expect("token_b_amount must be nonzero"),
|
||||
fees,
|
||||
amm_program_id,
|
||||
ctx.self_program_id,
|
||||
);
|
||||
Ok(spel_framework::SpelOutput::execute(post_states, chained_calls)
|
||||
.with_timestamp_validity_window(..deadline))
|
||||
|
||||
@ -67,10 +67,6 @@
|
||||
"name": "fees",
|
||||
"type": "u128"
|
||||
},
|
||||
{
|
||||
"name": "amm_program_id",
|
||||
"type": "program_id"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "u64"
|
||||
|
||||
@ -24,12 +24,7 @@
|
||||
"init": false
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "ata_program_id",
|
||||
"type": "program_id"
|
||||
}
|
||||
]
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "transfer",
|
||||
@ -54,10 +49,6 @@
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "ata_program_id",
|
||||
"type": "program_id"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u128"
|
||||
@ -87,10 +78,6 @@
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
{
|
||||
"name": "ata_program_id",
|
||||
"type": "program_id"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "u128"
|
||||
|
||||
@ -16,7 +16,7 @@ pub enum Instruction {
|
||||
/// - Associated token account (default/uninitialized, or already initialized)
|
||||
///
|
||||
/// `token_program_id` is derived from `token_definition.account.program_owner`.
|
||||
Create { ata_program_id: ProgramId },
|
||||
Create,
|
||||
|
||||
/// Transfer tokens FROM owner's ATA to a recipient token holding account.
|
||||
/// Uses ATA PDA seeds to authorize the chained Token::Transfer call.
|
||||
@ -27,10 +27,7 @@ pub enum Instruction {
|
||||
/// - Recipient token holding (must be initialized)
|
||||
///
|
||||
/// `token_program_id` is derived from `sender_ata.account.program_owner`.
|
||||
Transfer {
|
||||
ata_program_id: ProgramId,
|
||||
amount: u128,
|
||||
},
|
||||
Transfer { amount: u128 },
|
||||
|
||||
/// Burn tokens FROM owner's ATA.
|
||||
/// Uses PDA seeds to authorize the ATA in the chained Token::Burn call.
|
||||
@ -41,10 +38,7 @@ pub enum Instruction {
|
||||
/// - Token definition account
|
||||
///
|
||||
/// `token_program_id` is derived from `holder_ata.account.program_owner`.
|
||||
Burn {
|
||||
ata_program_id: ProgramId,
|
||||
amount: u128,
|
||||
},
|
||||
Burn { amount: u128 },
|
||||
}
|
||||
|
||||
pub fn compute_ata_seed(owner_id: AccountId, definition_id: AccountId) -> PdaSeed {
|
||||
|
||||
6
ata/methods/guest/Cargo.lock
generated
6
ata/methods/guest/Cargo.lock
generated
@ -2890,7 +2890,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2902,7 +2902,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2917,7 +2917,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-macros"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@ -10,7 +10,7 @@ name = "ata"
|
||||
path = "src/bin/ata.rs"
|
||||
|
||||
[dependencies]
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "ba6e87d086ed85c5ac095325d8a28f02e3d33ca2", package = "spel-framework" }
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "6473ab4c400bc59bac8db83a286faaeafa7d1999", package = "spel-framework" }
|
||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3" }
|
||||
risc0-zkvm = { version = "=3.0.5", default-features = false }
|
||||
ata_core = { path = "../../core" }
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
#![no_main]
|
||||
|
||||
use spel_framework::prelude::*;
|
||||
use nssa_core::{account::AccountWithMetadata, program::ProgramId};
|
||||
use spel_framework::context::ProgramContext;
|
||||
use nssa_core::account::AccountWithMetadata;
|
||||
|
||||
risc0_zkvm::guest::entry!(main);
|
||||
|
||||
@ -14,16 +15,16 @@ mod ata {
|
||||
/// Idempotent: no-op if the account already exists.
|
||||
#[instruction]
|
||||
pub fn create(
|
||||
ctx: ProgramContext,
|
||||
owner: AccountWithMetadata,
|
||||
token_definition: AccountWithMetadata,
|
||||
ata_account: AccountWithMetadata,
|
||||
ata_program_id: ProgramId,
|
||||
) -> SpelResult {
|
||||
let (post_states, chained_calls) = ata_program::create::create_associated_token_account(
|
||||
owner,
|
||||
token_definition,
|
||||
ata_account,
|
||||
ata_program_id,
|
||||
ctx.self_program_id,
|
||||
);
|
||||
Ok(spel_framework::SpelOutput::execute(post_states, chained_calls))
|
||||
}
|
||||
@ -32,10 +33,10 @@ mod ata {
|
||||
/// The recipient holding account must already be initialized.
|
||||
#[instruction]
|
||||
pub fn transfer(
|
||||
ctx: ProgramContext,
|
||||
owner: AccountWithMetadata,
|
||||
sender_ata: AccountWithMetadata,
|
||||
recipient: AccountWithMetadata,
|
||||
ata_program_id: ProgramId,
|
||||
amount: u128,
|
||||
) -> SpelResult {
|
||||
let (post_states, chained_calls) =
|
||||
@ -43,7 +44,7 @@ mod ata {
|
||||
owner,
|
||||
sender_ata,
|
||||
recipient,
|
||||
ata_program_id,
|
||||
ctx.self_program_id,
|
||||
amount,
|
||||
);
|
||||
Ok(spel_framework::SpelOutput::execute(post_states, chained_calls))
|
||||
@ -52,10 +53,10 @@ mod ata {
|
||||
/// Burn tokens FROM owner's ATA.
|
||||
#[instruction]
|
||||
pub fn burn(
|
||||
ctx: ProgramContext,
|
||||
owner: AccountWithMetadata,
|
||||
holder_ata: AccountWithMetadata,
|
||||
token_definition: AccountWithMetadata,
|
||||
ata_program_id: ProgramId,
|
||||
amount: u128,
|
||||
) -> SpelResult {
|
||||
let (post_states, chained_calls) =
|
||||
@ -63,7 +64,7 @@ mod ata {
|
||||
owner,
|
||||
holder_ata,
|
||||
token_definition,
|
||||
ata_program_id,
|
||||
ctx.self_program_id,
|
||||
amount,
|
||||
);
|
||||
Ok(spel_framework::SpelOutput::execute(post_states, chained_calls))
|
||||
|
||||
@ -965,7 +965,6 @@ fn try_execute_new_definition(
|
||||
token_a_amount: Balances::vault_a_init(),
|
||||
token_b_amount: Balances::vault_b_init(),
|
||||
fees,
|
||||
amm_program_id: Ids::amm_program(),
|
||||
deadline: u64::MAX,
|
||||
};
|
||||
|
||||
@ -1844,7 +1843,6 @@ fn amm_new_definition_rejects_expired_deadline() {
|
||||
token_a_amount: Balances::vault_a_init(),
|
||||
token_b_amount: Balances::vault_b_init(),
|
||||
fees: amm_core::FEE_TIER_BPS_30,
|
||||
amm_program_id: Ids::amm_program(),
|
||||
deadline: deadline_ms,
|
||||
};
|
||||
|
||||
|
||||
@ -144,9 +144,7 @@ fn ata_create() {
|
||||
deploy_programs(&mut state);
|
||||
state.force_insert_account(Ids::token_definition(), Accounts::token_definition_init());
|
||||
|
||||
let instruction = ata_core::Instruction::Create {
|
||||
ata_program_id: Ids::ata_program(),
|
||||
};
|
||||
let instruction = ata_core::Instruction::Create;
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::ata_program(),
|
||||
@ -179,9 +177,7 @@ fn ata_create() {
|
||||
fn ata_create_is_idempotent() {
|
||||
let mut state = state_for_ata_tests();
|
||||
|
||||
let instruction = ata_core::Instruction::Create {
|
||||
ata_program_id: Ids::ata_program(),
|
||||
};
|
||||
let instruction = ata_core::Instruction::Create;
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::ata_program(),
|
||||
@ -216,7 +212,6 @@ fn ata_transfer() {
|
||||
let mut state = state_for_ata_tests_with_precreated_recipient_ata();
|
||||
|
||||
let instruction = ata_core::Instruction::Transfer {
|
||||
ata_program_id: Ids::ata_program(),
|
||||
amount: 400_000_u128,
|
||||
};
|
||||
|
||||
@ -265,7 +260,6 @@ fn ata_burn() {
|
||||
let mut state = state_for_ata_tests();
|
||||
|
||||
let instruction = ata_core::Instruction::Burn {
|
||||
ata_program_id: Ids::ata_program(),
|
||||
amount: 300_000_u128,
|
||||
};
|
||||
|
||||
@ -337,9 +331,7 @@ fn ata_create_from_private_owner() {
|
||||
);
|
||||
let ata_pre = AccountWithMetadata::new(Account::default(), false, owner_ata_id);
|
||||
|
||||
let instruction = ata_core::Instruction::Create {
|
||||
ata_program_id: Ids::ata_program(),
|
||||
};
|
||||
let instruction = ata_core::Instruction::Create;
|
||||
let instruction_data = Program::serialize_instruction(instruction).unwrap();
|
||||
|
||||
// Ephemeral key for encrypting the private owner's post-state
|
||||
|
||||
@ -36,6 +36,10 @@ impl Ids {
|
||||
token_methods::TOKEN_ID
|
||||
}
|
||||
|
||||
fn foreign_token_program() -> nssa_core::program::ProgramId {
|
||||
[0xfeed_u32; 8]
|
||||
}
|
||||
|
||||
fn token_definition() -> AccountId {
|
||||
AccountId::from(&PublicKey::new_from_private_key(&Keys::def_key()))
|
||||
}
|
||||
@ -63,6 +67,19 @@ impl Accounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn token_definition_foreign_owner() -> Account {
|
||||
Account {
|
||||
program_owner: Ids::foreign_token_program(),
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("Gold"),
|
||||
total_supply: 1_000_000_u128,
|
||||
metadata_id: None,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn holder_init() -> Account {
|
||||
Account {
|
||||
program_owner: Ids::token_program(),
|
||||
@ -167,6 +184,78 @@ fn token_new_fungible_definition() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_initialize_account_succeeds_for_canonical_definition() {
|
||||
let mut state = state_for_token_tests_without_recipient();
|
||||
|
||||
let instruction = token_core::Instruction::InitializeAccount;
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::token_program(),
|
||||
vec![Ids::token_definition(), Ids::recipient()],
|
||||
vec![Nonce(0)],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set =
|
||||
public_transaction::WitnessSet::for_message(&message, &[&Keys::recipient_key()]);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
state.transition_from_public_transaction(&tx, 0, 0).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::token_definition()),
|
||||
Accounts::token_definition_init()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::recipient()),
|
||||
Account {
|
||||
program_owner: Ids::token_program(),
|
||||
balance: 0_u128,
|
||||
data: Data::from(&TokenHolding::Fungible {
|
||||
definition_id: Ids::token_definition(),
|
||||
balance: 0_u128,
|
||||
}),
|
||||
nonce: Nonce(1),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_initialize_account_rejects_foreign_owned_definition() {
|
||||
let mut state = state_for_token_tests_without_recipient();
|
||||
state.force_insert_account(
|
||||
Ids::token_definition(),
|
||||
Accounts::token_definition_foreign_owner(),
|
||||
);
|
||||
|
||||
let instruction = token_core::Instruction::InitializeAccount;
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::token_program(),
|
||||
vec![Ids::token_definition(), Ids::recipient()],
|
||||
vec![Nonce(0)],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set =
|
||||
public_transaction::WitnessSet::for_message(&message, &[&Keys::recipient_key()]);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
assert!(state.transition_from_public_transaction(&tx, 0, 0).is_err());
|
||||
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::token_definition()),
|
||||
Accounts::token_definition_foreign_owner()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::recipient()),
|
||||
Account::default()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_transfer() {
|
||||
let mut state = state_for_token_tests();
|
||||
@ -395,6 +484,44 @@ fn token_mint() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_mint_rejects_foreign_owned_definition() {
|
||||
let mut state = state_for_token_tests_without_recipient();
|
||||
state.force_insert_account(
|
||||
Ids::token_definition(),
|
||||
Accounts::token_definition_foreign_owner(),
|
||||
);
|
||||
|
||||
let instruction = token_core::Instruction::Mint {
|
||||
amount_to_mint: 500_000_u128,
|
||||
};
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
Ids::token_program(),
|
||||
vec![Ids::token_definition(), Ids::recipient()],
|
||||
vec![Nonce(0), Nonce(0)],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = public_transaction::WitnessSet::for_message(
|
||||
&message,
|
||||
&[&Keys::def_key(), &Keys::recipient_key()],
|
||||
);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
assert!(state.transition_from_public_transaction(&tx, 0, 0).is_err());
|
||||
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::token_definition()),
|
||||
Accounts::token_definition_foreign_owner()
|
||||
);
|
||||
assert_eq!(
|
||||
state.get_account_by_id(Ids::recipient()),
|
||||
Account::default()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn token_mint_fresh_public_recipient_requires_authorization() {
|
||||
let mut state = state_for_token_tests_without_recipient();
|
||||
|
||||
6
stablecoin/methods/guest/Cargo.lock
generated
6
stablecoin/methods/guest/Cargo.lock
generated
@ -2857,7 +2857,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2869,7 +2869,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2884,7 +2884,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-macros"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@ -10,7 +10,7 @@ name = "stablecoin"
|
||||
path = "src/bin/stablecoin.rs"
|
||||
|
||||
[dependencies]
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "ba6e87d086ed85c5ac095325d8a28f02e3d33ca2", package = "spel-framework" }
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "6473ab4c400bc59bac8db83a286faaeafa7d1999", package = "spel-framework" }
|
||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3" }
|
||||
risc0-zkvm = { version = "=3.0.5", default-features = false }
|
||||
stablecoin_core = { path = "../../core" }
|
||||
|
||||
@ -5,6 +5,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3", features = ["host"] }
|
||||
spel-framework-macros = { git = "https://github.com/logos-co/spel.git", rev = "ba6e87d086ed85c5ac095325d8a28f02e3d33ca2", package = "spel-framework-macros" }
|
||||
spel-framework-macros = { git = "https://github.com/logos-co/spel.git", rev = "6473ab4c400bc59bac8db83a286faaeafa7d1999", package = "spel-framework-macros" }
|
||||
borsh = { version = "1.5", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
6
token/methods/guest/Cargo.lock
generated
6
token/methods/guest/Cargo.lock
generated
@ -2857,7 +2857,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2869,7 +2869,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"nssa_core",
|
||||
@ -2884,7 +2884,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "spel-framework-macros"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=ba6e87d086ed85c5ac095325d8a28f02e3d33ca2#ba6e87d086ed85c5ac095325d8a28f02e3d33ca2"
|
||||
source = "git+https://github.com/logos-co/spel.git?rev=6473ab4c400bc59bac8db83a286faaeafa7d1999#6473ab4c400bc59bac8db83a286faaeafa7d1999"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@ -10,7 +10,7 @@ name = "token"
|
||||
path = "src/bin/token.rs"
|
||||
|
||||
[dependencies]
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "ba6e87d086ed85c5ac095325d8a28f02e3d33ca2", package = "spel-framework" }
|
||||
spel-framework = { git = "https://github.com/logos-co/spel.git", rev = "6473ab4c400bc59bac8db83a286faaeafa7d1999", package = "spel-framework" }
|
||||
nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc3" }
|
||||
risc0-zkvm = { version = "=3.0.5", default-features = false }
|
||||
token_core = { path = "../../core" }
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#![no_main]
|
||||
|
||||
use spel_framework::prelude::*;
|
||||
use spel_framework::context::ProgramContext;
|
||||
use nssa_core::account::AccountWithMetadata;
|
||||
|
||||
risc0_zkvm::guest::entry!(main);
|
||||
@ -71,6 +72,7 @@ mod token {
|
||||
/// The holding target must be uninitialized and authorized.
|
||||
#[instruction]
|
||||
pub fn initialize_account(
|
||||
ctx: ProgramContext,
|
||||
definition_account: AccountWithMetadata,
|
||||
account_to_initialize: AccountWithMetadata,
|
||||
) -> SpelResult {
|
||||
@ -78,6 +80,7 @@ mod token {
|
||||
token_program::initialize::initialize_account(
|
||||
definition_account,
|
||||
account_to_initialize,
|
||||
ctx.self_program_id,
|
||||
),
|
||||
vec![],
|
||||
))
|
||||
@ -101,6 +104,7 @@ mod token {
|
||||
/// Fresh public holders must be explicitly authorized in the same transaction.
|
||||
#[instruction]
|
||||
pub fn mint(
|
||||
ctx: ProgramContext,
|
||||
definition_account: AccountWithMetadata,
|
||||
user_holding_account: AccountWithMetadata,
|
||||
amount_to_mint: u128,
|
||||
@ -109,6 +113,7 @@ mod token {
|
||||
definition_account,
|
||||
user_holding_account,
|
||||
amount_to_mint,
|
||||
ctx.self_program_id,
|
||||
), vec![]))
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
use nssa_core::{
|
||||
account::{Account, AccountWithMetadata, Data},
|
||||
program::{AccountPostState, Claim},
|
||||
program::{AccountPostState, Claim, ProgramId},
|
||||
};
|
||||
use token_core::{TokenDefinition, TokenHolding};
|
||||
|
||||
pub fn initialize_account(
|
||||
definition_account: AccountWithMetadata,
|
||||
account_to_initialize: AccountWithMetadata,
|
||||
token_program_id: ProgramId,
|
||||
) -> Vec<AccountPostState> {
|
||||
assert_eq!(
|
||||
account_to_initialize.account,
|
||||
@ -17,11 +18,11 @@ pub fn initialize_account(
|
||||
account_to_initialize.is_authorized,
|
||||
"Account to initialize must be authorized"
|
||||
);
|
||||
assert_eq!(
|
||||
definition_account.account.program_owner, token_program_id,
|
||||
"Token definition must be owned by token program"
|
||||
);
|
||||
|
||||
// TODO: #212 We should check that this is an account owned by the token program.
|
||||
// This check can't be done here since the ID of the program is known only after compiling it
|
||||
//
|
||||
// Check definition account is valid
|
||||
let definition = TokenDefinition::try_from(&definition_account.account.data)
|
||||
.expect("Definition account must be valid");
|
||||
let holding =
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use nssa_core::{
|
||||
account::{Account, AccountWithMetadata, Data},
|
||||
program::{AccountPostState, Claim},
|
||||
program::{AccountPostState, Claim, ProgramId},
|
||||
};
|
||||
use token_core::{TokenDefinition, TokenHolding};
|
||||
|
||||
@ -8,11 +8,16 @@ pub fn mint(
|
||||
definition_account: AccountWithMetadata,
|
||||
user_holding_account: AccountWithMetadata,
|
||||
amount_to_mint: u128,
|
||||
token_program_id: ProgramId,
|
||||
) -> Vec<AccountPostState> {
|
||||
assert!(
|
||||
definition_account.is_authorized,
|
||||
"Definition authorization is missing"
|
||||
);
|
||||
assert_eq!(
|
||||
definition_account.account.program_owner, token_program_id,
|
||||
"Token definition must be owned by token program"
|
||||
);
|
||||
|
||||
let mut definition = TokenDefinition::try_from(&definition_account.account.data)
|
||||
.expect("Token Definition account must be valid");
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
use nssa_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata, Data, Nonce},
|
||||
program::Claim,
|
||||
program::{Claim, ProgramId},
|
||||
};
|
||||
use token_core::{
|
||||
MetadataStandard, NewTokenDefinition, NewTokenMetadata, TokenDefinition, TokenHolding,
|
||||
@ -25,11 +25,31 @@ struct IdForTests;
|
||||
|
||||
struct AccountForTests;
|
||||
|
||||
const TOKEN_PROGRAM_ID: ProgramId = [5u32; 8];
|
||||
const FOREIGN_TOKEN_PROGRAM_ID: ProgramId = [6u32; 8];
|
||||
|
||||
impl AccountForTests {
|
||||
fn definition_account_auth() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
total_supply: BalanceForTests::init_supply(),
|
||||
metadata_id: None,
|
||||
}),
|
||||
nonce: Nonce(0),
|
||||
},
|
||||
is_authorized: true,
|
||||
account_id: IdForTests::pool_definition_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn definition_account_foreign_owner() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: FOREIGN_TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
@ -46,7 +66,7 @@ impl AccountForTests {
|
||||
fn definition_account_without_auth() -> AccountWithMetadata {
|
||||
AccountWithMetadata {
|
||||
account: Account {
|
||||
program_owner: [5u32; 8],
|
||||
program_owner: TOKEN_PROGRAM_ID,
|
||||
balance: 0u128,
|
||||
data: Data::from(&TokenDefinition::Fungible {
|
||||
name: String::from("test"),
|
||||
@ -757,7 +777,7 @@ fn test_transfer_with_default_recipient_claims_recipient() {
|
||||
fn test_token_initialize_account_succeeds() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_account_uninit_auth();
|
||||
let post_states = initialize_account(definition_account, holding_account);
|
||||
let post_states = initialize_account(definition_account, holding_account, TOKEN_PROGRAM_ID);
|
||||
let [definition_post, holding_post] = post_states.try_into().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
@ -785,7 +805,15 @@ fn test_token_initialize_account_succeeds() {
|
||||
fn test_token_initialize_account_requires_authorization() {
|
||||
let definition_account = AccountForTests::definition_account_auth();
|
||||
let holding_account = AccountForTests::holding_account_uninit();
|
||||
let _post_states = initialize_account(definition_account, holding_account);
|
||||
let _post_states = initialize_account(definition_account, holding_account, TOKEN_PROGRAM_ID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Token definition must be owned by token program")]
|
||||
fn test_token_initialize_account_rejects_foreign_owned_definition() {
|
||||
let definition_account = AccountForTests::definition_account_foreign_owner();
|
||||
let holding_account = AccountForTests::holding_account_uninit_auth();
|
||||
let _post_states = initialize_account(definition_account, holding_account, TOKEN_PROGRAM_ID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -868,6 +896,7 @@ fn test_mint_not_valid_holding_account() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
@ -880,6 +909,7 @@ fn test_mint_not_valid_definition_account() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
@ -892,6 +922,20 @@ fn test_mint_missing_authorization() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Token definition must be owned by token program")]
|
||||
fn test_mint_rejects_foreign_owned_definition() {
|
||||
let definition_account = AccountForTests::definition_account_foreign_owner();
|
||||
let holding_account = AccountForTests::holding_account_uninit();
|
||||
let _post_states = mint(
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
@ -904,6 +948,7 @@ fn test_mint_mismatched_token_definition() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
@ -915,6 +960,7 @@ fn test_mint_success() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
|
||||
let [def_post, holding_post] = post_states.try_into().unwrap();
|
||||
@ -939,6 +985,7 @@ fn test_mint_uninit_holding_success() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
|
||||
let [def_post, holding_post] = post_states.try_into().unwrap();
|
||||
@ -964,6 +1011,7 @@ fn test_mint_total_supply_overflow() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_overflow(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
@ -976,6 +1024,7 @@ fn test_mint_holding_account_overflow() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_overflow(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
@ -988,6 +1037,7 @@ fn test_mint_cannot_mint_unmintable_tokens() {
|
||||
definition_account,
|
||||
holding_account,
|
||||
BalanceForTests::mint_success(),
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ name = "idl-gen"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
spel-framework-core = { git = "https://github.com/logos-co/spel.git", rev = "ba6e87d086ed85c5ac095325d8a28f02e3d33ca2", features = [
|
||||
spel-framework-core = { git = "https://github.com/logos-co/spel.git", rev = "6473ab4c400bc59bac8db83a286faaeafa7d1999", features = [
|
||||
"idl-gen",
|
||||
] }
|
||||
serde_json = "1.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user