mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-03 05:29:50 +00:00
fix(token): owner-guard set_authority; align demo script to token IDL
Second review round on PR #125 (LP-0013): - set_authority now rejects foreign-owned definitions. It takes the ProgramContext and asserts definition_account.program_owner == self_program_id, matching mint and initialize_account. Without this a foreign-owned account with token-shaped data could have its authority field rewritten. Added test_set_authority_rejects_foreign_owned_definition. - demo-full-flow.sh now calls instruction and flag names that exist in the regenerated token IDL: new-fungible-definition (was the nonexistent new-fungible-definition-with-authority), --total-supply (was --initial-supply), and drops --authority-account for the self-authority mint/set-authority path (the rest account is --authority-accounts and is empty when the definition is its own authority). - Stripped a trailing-space lint nit in docs/LP-0013-README.md.
This commit is contained in:
parent
160ff8ee4a
commit
686a7d066a
@ -15,7 +15,7 @@ The `lez-authority` crate provides a reusable, program-agnostic authority librar
|
||||
|
||||
## Architecture
|
||||
|
||||
### Authority Model
|
||||
### Authority Model
|
||||
|
||||
`mint_authority: Option<[u8; 32]>` is added to `TokenDefinition::Fungible`:
|
||||
- `Some(key)` — the key holder can mint and rotate/revoke
|
||||
|
||||
@ -148,6 +148,7 @@ mod token {
|
||||
/// The definition account must be authorized as the current mint authority.
|
||||
#[instruction]
|
||||
pub fn set_authority(
|
||||
ctx: ProgramContext,
|
||||
definition_account: AccountWithMetadata,
|
||||
authority_accounts: Vec<AccountWithMetadata>,
|
||||
new_authority: Option<AccountId>,
|
||||
@ -157,6 +158,7 @@ mod token {
|
||||
definition_account,
|
||||
new_authority,
|
||||
authority_accounts,
|
||||
ctx.self_program_id,
|
||||
),
|
||||
vec![],
|
||||
))
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use lez_authority::Ownable;
|
||||
use nssa_core::{
|
||||
account::{AccountId, AccountWithMetadata, Data},
|
||||
program::AccountPostState,
|
||||
program::{AccountPostState, ProgramId},
|
||||
};
|
||||
use token_core::TokenDefinition;
|
||||
|
||||
@ -9,7 +9,13 @@ pub fn set_authority(
|
||||
definition_account: AccountWithMetadata,
|
||||
new_authority: Option<AccountId>,
|
||||
authority_accounts: Vec<AccountWithMetadata>,
|
||||
token_program_id: ProgramId,
|
||||
) -> Vec<AccountPostState> {
|
||||
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");
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ use crate::{
|
||||
mint::mint,
|
||||
new_definition::{new_definition_with_metadata, new_fungible_definition},
|
||||
print_nft::print_nft,
|
||||
set_authority::set_authority,
|
||||
transfer::transfer,
|
||||
};
|
||||
|
||||
@ -960,6 +961,20 @@ fn test_mint_rejects_foreign_owned_definition() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Token definition must be owned by token program")]
|
||||
fn test_set_authority_rejects_foreign_owned_definition() {
|
||||
// A foreign-owned account carrying token-shaped data must not be able to
|
||||
// rotate or revoke its authority through the token program.
|
||||
let definition_account = AccountForTests::definition_account_foreign_owner();
|
||||
let _post_states = set_authority(
|
||||
definition_account,
|
||||
Some(AccountId::new([7_u8; 32])),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Mismatch Token Definition and Token Holding")]
|
||||
fn test_mint_mismatched_token_definition() {
|
||||
@ -1534,13 +1549,19 @@ mod authority_tests {
|
||||
def_with_authority(),
|
||||
Some(AccountId::new([0u8; 32])),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_authority_rotates_to_new_key() {
|
||||
let new_key = AccountId::new([7_u8; 32]);
|
||||
let post_states = set_authority(def_with_authority(), Some(new_key), vec![]);
|
||||
let post_states = set_authority(
|
||||
def_with_authority(),
|
||||
Some(new_key),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
let [def_post] = post_states.try_into().unwrap();
|
||||
|
||||
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
||||
@ -1553,7 +1574,7 @@ mod authority_tests {
|
||||
|
||||
#[test]
|
||||
fn set_authority_revokes_permanently() {
|
||||
let post_states = set_authority(def_with_authority(), None, vec![]);
|
||||
let post_states = set_authority(def_with_authority(), None, vec![], TOKEN_PROGRAM_ID);
|
||||
let [def_post] = post_states.try_into().unwrap();
|
||||
|
||||
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
||||
@ -1571,6 +1592,7 @@ mod authority_tests {
|
||||
def_with_authority_revoked(),
|
||||
Some(AccountId::new([7_u8; 32])),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1579,7 +1601,12 @@ mod authority_tests {
|
||||
fn set_authority_without_is_authorized_fails() {
|
||||
let mut def = def_with_authority();
|
||||
def.is_authorized = false;
|
||||
let _ = set_authority(def, Some(AccountId::new([7_u8; 32])), vec![]);
|
||||
let _ = set_authority(
|
||||
def,
|
||||
Some(AccountId::new([7_u8; 32])),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1589,13 +1616,19 @@ mod authority_tests {
|
||||
def_wrong_authority(),
|
||||
Some(AccountId::new([7_u8; 32])),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_authority_rotate_then_old_cannot_mint() {
|
||||
let new_key = AccountId::new([7_u8; 32]);
|
||||
let post_states = set_authority(def_with_authority(), Some(new_key), vec![]);
|
||||
let post_states = set_authority(
|
||||
def_with_authority(),
|
||||
Some(new_key),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
let [def_post] = post_states.try_into().unwrap();
|
||||
|
||||
let def = TokenDefinition::try_from(&def_post.account().data).unwrap();
|
||||
@ -1626,6 +1659,7 @@ mod authority_tests {
|
||||
def_with_authority(),
|
||||
Some(AccountId::new([7_u8; 32])),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
let [def_post] = rotate_post.try_into().unwrap();
|
||||
|
||||
@ -1670,6 +1704,7 @@ mod authority_tests {
|
||||
def_with_authority(),
|
||||
Some(AccountId::new([7_u8; 32])),
|
||||
vec![],
|
||||
TOKEN_PROGRAM_ID,
|
||||
);
|
||||
let [def_post] = rotate_post.try_into().unwrap();
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
# 1. Start a local LEZ sequencer
|
||||
# 2. Fund the wallet
|
||||
# 3. Create token accounts
|
||||
# 4. Submit NewFungibleDefinitionWithAuthority transaction
|
||||
# 4. Submit NewFungibleDefinition transaction (with mint authority)
|
||||
# 5. Submit Mint transaction (authority-gated)
|
||||
# 6. Submit SetAuthority (revoke) transaction
|
||||
# 7. Run unit tests to verify authority logic (60 tests)
|
||||
@ -101,11 +101,11 @@ echo "[4/7] Creating token with mint authority..."
|
||||
DEF_ID_HEX=$(b58_to_hex "$DEF_ID")
|
||||
NSSA_WALLET_HOME_DIR="$WALLET_DIR" \
|
||||
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
||||
-- new-fungible-definition-with-authority \
|
||||
-- new-fungible-definition \
|
||||
--definition-target-account "$DEF_ID" \
|
||||
--holding-target-account "$SUPPLY_ID" \
|
||||
--name "DemoCoin" \
|
||||
--initial-supply 1000000 \
|
||||
--total-supply 1000000 \
|
||||
--mint-authority "$DEF_ID_HEX"
|
||||
echo " Token 'DemoCoin' submitted. Initial supply: 1,000,000"
|
||||
sleep 2
|
||||
@ -115,7 +115,6 @@ NSSA_WALLET_HOME_DIR="$WALLET_DIR" \
|
||||
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
||||
-- mint \
|
||||
--definition-account "$DEF_ID" \
|
||||
--authority-account "$DEF_ID" \
|
||||
--user-holding-account "$RECIPIENT_ID" \
|
||||
--amount-to-mint 500000
|
||||
echo " Mint transaction submitted. New total supply: 1,500,000"
|
||||
@ -126,7 +125,6 @@ NSSA_WALLET_HOME_DIR="$WALLET_DIR" \
|
||||
${TIMEOUT:+$TIMEOUT 30} "$SPEL" --idl "$IDL" --program "$TOKEN_BIN" \
|
||||
-- set-authority \
|
||||
--definition-account "$DEF_ID" \
|
||||
--authority-account "$DEF_ID" \
|
||||
--new-authority none
|
||||
echo " Authority revoked. Supply permanently fixed at 1,500,000"
|
||||
sleep 2
|
||||
@ -139,7 +137,7 @@ echo ""
|
||||
echo "================================================================"
|
||||
echo " LP-0013 Demo Complete"
|
||||
echo " Summary:"
|
||||
echo " [1/4] NewFungibleDefinitionWithAuthority → supply=1,000,000"
|
||||
echo " [1/4] NewFungibleDefinition (with authority) → supply=1,000,000"
|
||||
echo " [2/4] Mint 500,000 → supply=1,500,000"
|
||||
echo " [3/4] SetAuthority (revoke) → supply fixed"
|
||||
echo " [4/4] Unit tests passing → all authority cases verified"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user