From 9c4d4721d62598dfcb3560a9b8f0e6cf0475808f Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 22 Oct 2023 13:31:03 +0200 Subject: [PATCH] token 2022: add `GroupMemberPointer` extension --- token/client/src/token.rs | 10 -- .../tests/group_member_pointer.rs | 153 ++---------------- .../group_member_pointer/instruction.rs | 26 +-- .../src/extension/group_member_pointer/mod.rs | 5 +- .../group_member_pointer/processor.rs | 56 +------ 5 files changed, 24 insertions(+), 226 deletions(-) diff --git a/token/client/src/token.rs b/token/client/src/token.rs index 5a8a6b47a68..9f7fb2f7dc4 100644 --- a/token/client/src/token.rs +++ b/token/client/src/token.rs @@ -178,8 +178,6 @@ pub enum ExtensionInitializationParams { }, GroupMemberPointer { authority: Option, - group_update_authority: Pubkey, - group_address: Pubkey, member_address: Option, }, } @@ -303,15 +301,11 @@ impl ExtensionInitializationParams { ), Self::GroupMemberPointer { authority, - group_update_authority, - group_address, member_address, } => group_member_pointer::instruction::initialize( token_program_id, mint, authority, - &group_update_authority, - &group_address, member_address, ), } @@ -1724,8 +1718,6 @@ where pub async fn update_group_member_address( &self, authority: &Pubkey, - group_update_authority: &Pubkey, - group_address: &Pubkey, new_member_address: Option, signing_keypairs: &S, ) -> TokenResult { @@ -1737,9 +1729,7 @@ where &self.program_id, self.get_address(), authority, - group_update_authority, &multisig_signers, - group_address, new_member_address, )?], signing_keypairs, diff --git a/token/program-2022-test/tests/group_member_pointer.rs b/token/program-2022-test/tests/group_member_pointer.rs index 96599345f50..9d29cc7e92e 100644 --- a/token/program-2022-test/tests/group_member_pointer.rs +++ b/token/program-2022-test/tests/group_member_pointer.rs @@ -1,14 +1,13 @@ #![cfg(feature = "test-sbf")] -use { - solana_program::system_instruction, solana_program_test::tokio::sync::Mutex, - spl_token_2022::extension::ExtensionType, -}; - mod program_test; use { program_test::{TestContext, TokenContext}, - solana_program_test::{processor, tokio, ProgramTest, ProgramTestContext}, + solana_program_test::{ + processor, + tokio::{self, sync::Mutex}, + ProgramTest, ProgramTestContext, + }, solana_sdk::{ account::Account as SolanaAccount, instruction::InstructionError, @@ -21,14 +20,10 @@ use { spl_token_2022::{ error::TokenError, extension::{ - group_member_pointer::{ - instruction::{initialize, update}, - GroupMemberPointer, - }, + group_member_pointer::{instruction::update, GroupMemberPointer}, BaseStateWithExtensions, }, processor::Processor, - state::Mint, }, spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError}, std::{convert::TryInto, sync::Arc}, @@ -91,9 +86,7 @@ async fn setup_token_group( async fn setup_member_mint( context: Arc>, mint: Keypair, - group_address: &Pubkey, authority: &Pubkey, - group_update_authority: &Keypair, ) -> TestContext { let mut context = TestContext { context, @@ -101,16 +94,13 @@ async fn setup_member_mint( }; let member_address = Some(mint.pubkey()); context - .init_token_with_mint_keypair_and_freeze_authority_and_additional_signers( + .init_token_with_mint_keypair_and_freeze_authority( mint, vec![ExtensionInitializationParams::GroupMemberPointer { authority: Some(*authority), - group_address: *group_address, - group_update_authority: group_update_authority.pubkey(), member_address, }], None, - &[&group_update_authority], ) .await .unwrap(); @@ -158,9 +148,7 @@ async fn success_init() { let member_token = setup_member_mint( context, member_mint.insecure_clone(), - &group_mint.pubkey(), &member_authority.pubkey(), - &group_update_authority, ) .await .token_context @@ -175,7 +163,6 @@ async fn success_init() { extension.authority, Some(member_authority.pubkey()).try_into().unwrap() ); - assert_eq!(extension.group_address, group_mint.pubkey()); assert_eq!( extension.member_address, Some(member_mint.pubkey()).try_into().unwrap() @@ -187,8 +174,6 @@ async fn fail_init() { let payer = Keypair::new(); let group_mint = Keypair::new(); let group_update_authority = Keypair::new(); - let member_mint = Keypair::new(); - let member_authority = Keypair::new(); let program_test = setup_program_test(); let mut context = program_test.start_with_context().await; @@ -226,16 +211,13 @@ async fn fail_init() { token_context: None, }; let err = context - .init_token_with_mint_keypair_and_freeze_authority_and_additional_signers( + .init_token_with_mint_keypair_and_freeze_authority( Keypair::new(), vec![ExtensionInitializationParams::GroupMemberPointer { authority: None, - group_address: group_mint.pubkey(), - group_update_authority: group_update_authority.pubkey(), member_address: None, }], None, - &[&group_update_authority], ) .await .unwrap_err(); @@ -248,53 +230,6 @@ async fn fail_init() { ) ))) ); - - // fail missing group update authority signature - let mut context = context.context.lock().await; - let space = - ExtensionType::try_calculate_account_len::(&[ExtensionType::GroupMemberPointer]) - .unwrap(); - let lamports = context - .banks_client - .get_rent() - .await - .unwrap() - .minimum_balance(space); - let mut instruction = initialize( - &spl_token_2022::id(), - &member_mint.pubkey(), - Some(member_authority.pubkey()), - &group_update_authority.pubkey(), - &group_mint.pubkey(), - Some(member_mint.pubkey()), - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - let transaction = Transaction::new_signed_with_payer( - &[ - system_instruction::create_account( - &payer.pubkey(), - &member_mint.pubkey(), - lamports, - space as u64, - &spl_token_2022::id(), - ), - instruction, - ], - Some(&payer.pubkey()), - &[&payer, &member_mint], - context.last_blockhash, - ); - let error = context - .banks_client - .process_transaction(transaction) - .await - .unwrap_err() - .unwrap(); - assert_eq!( - error, - TransactionError::InstructionError(1, InstructionError::MissingRequiredSignature,) - ); } #[tokio::test] @@ -338,9 +273,7 @@ async fn success_update() { let member_token = setup_member_mint( context.clone(), member_mint.insecure_clone(), - &group_mint.pubkey(), &member_authority.pubkey(), - &group_update_authority, ) .await .token_context @@ -354,8 +287,6 @@ async fn success_update() { member_token .update_group_member_address( &member_authority.pubkey(), - &group_update_authority.pubkey(), - &group_mint.pubkey(), Some(new_member_address), &[&group_update_authority, &member_authority], ) @@ -368,7 +299,6 @@ async fn success_update() { extension.authority, Some(member_authority.pubkey()).try_into().unwrap() ); - assert_eq!(extension.group_address, group_mint.pubkey()); assert_eq!( extension.member_address, Some(new_member_address).try_into().unwrap() @@ -378,8 +308,6 @@ async fn success_update() { member_token .update_group_member_address( &member_authority.pubkey(), - &group_update_authority.pubkey(), - &group_mint.pubkey(), None, &[&group_update_authority, &member_authority], ) @@ -392,7 +320,6 @@ async fn success_update() { extension.authority, Some(member_authority.pubkey()).try_into().unwrap() ); - assert_eq!(extension.group_address, group_mint.pubkey()); assert_eq!(extension.member_address, None.try_into().unwrap()); } @@ -437,9 +364,7 @@ async fn fail_update() { let member_token = setup_member_mint( context.clone(), member_mint.insecure_clone(), - &group_mint.pubkey(), &member_authority.pubkey(), - &group_update_authority, ) .await .token_context @@ -450,33 +375,10 @@ async fn fail_update() { let wrong = Keypair::new(); let new_member_address = Pubkey::new_unique(); - // fail, wrong signature for group update authority - let err = member_token - .update_group_member_address( - &member_authority.pubkey(), - &wrong.pubkey(), - &group_mint.pubkey(), - Some(new_member_address), - &[&wrong, &member_authority], - ) - .await - .unwrap_err(); - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::OwnerMismatch as u32) - ) - ))) - ); - - // fail, wrong signature for member pointer authority + // fail, wrong signature for authority let err = member_token .update_group_member_address( &wrong.pubkey(), - &group_update_authority.pubkey(), - &group_mint.pubkey(), Some(new_member_address), &[&group_update_authority, &wrong], ) @@ -494,51 +396,20 @@ async fn fail_update() { let mut context = context.lock().await; - // fail, missing group update authority signature + // fail, missing authority signature let mut instruction = update( &spl_token_2022::id(), &member_mint.pubkey(), &member_authority.pubkey(), - &group_update_authority.pubkey(), - &[], - &group_mint.pubkey(), - Some(member_mint.pubkey()), - ) - .unwrap(); - instruction.accounts[3].is_signer = false; - let transaction = Transaction::new_signed_with_payer( - &[instruction], - Some(&payer.pubkey()), - &[&payer, &member_authority], - context.last_blockhash, - ); - let error = context - .banks_client - .process_transaction(transaction) - .await - .unwrap_err() - .unwrap(); - assert_eq!( - error, - TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature,) - ); - - // fail, missing member pointer authority signature - let mut instruction = update( - &spl_token_2022::id(), - &member_mint.pubkey(), - &member_authority.pubkey(), - &group_update_authority.pubkey(), &[], - &group_mint.pubkey(), Some(member_mint.pubkey()), ) .unwrap(); - instruction.accounts[2].is_signer = false; + instruction.accounts[1].is_signer = false; let transaction = Transaction::new_signed_with_payer( &[instruction], Some(&payer.pubkey()), - &[&payer, &group_update_authority], + &[&payer], context.last_blockhash, ); let error = context diff --git a/token/program-2022/src/extension/group_member_pointer/instruction.rs b/token/program-2022/src/extension/group_member_pointer/instruction.rs index 931aad18dd5..c8fc28019b3 100644 --- a/token/program-2022/src/extension/group_member_pointer/instruction.rs +++ b/token/program-2022/src/extension/group_member_pointer/instruction.rs @@ -34,8 +34,6 @@ pub enum GroupMemberPointerInstruction { /// Accounts expected by this instruction: /// /// 0. `[writable]` The mint to initialize. - /// 1. `[]` The group mint. - /// 2. `[signer]` The group's update authority. /// /// Data expected by this instruction: /// `crate::extension::group_member_pointer::instruction::InitializeInstructionData` @@ -47,15 +45,12 @@ pub enum GroupMemberPointerInstruction { /// /// * Single authority /// 0. `[writable]` The mint. - /// 1. `[]` The group mint. - /// 2. `[signer]` The group's update authority. + /// 1. `[signer]` The group member pointer authority. /// /// * Multisignature authority /// 0. `[writable]` The mint. - /// 1. `[]` The group mint. - /// 2. `[]` The mint's group member pointer authority. - /// 3. `[signer]` The group's update authority. - /// 4. ..4+M `[signer]` M signer accounts. + /// 1. `[]` The group member pointer authority. + /// 2. ..2+M `[signer]` M signer accounts. /// /// Data expected by this instruction: /// `crate::extension::group_member_pointer::instruction::UpdateInstructionData` @@ -70,8 +65,6 @@ pub enum GroupMemberPointerInstruction { pub struct InitializeInstructionData { /// The public key for the account that can update the group address pub authority: OptionalNonZeroPubkey, - /// The account address that holds the group - pub group_address: Pubkey, /// The account address that holds the member pub member_address: OptionalNonZeroPubkey, } @@ -91,16 +84,10 @@ pub fn initialize( token_program_id: &Pubkey, mint: &Pubkey, authority: Option, - group_update_authority: &Pubkey, - group_address: &Pubkey, member_address: Option, ) -> Result { check_program_account(token_program_id)?; - let accounts = vec![ - AccountMeta::new(*mint, false), - AccountMeta::new(*group_address, false), - AccountMeta::new(*group_update_authority, true), - ]; + let accounts = vec![AccountMeta::new(*mint, false)]; Ok(encode_instruction( token_program_id, accounts, @@ -108,7 +95,6 @@ pub fn initialize( GroupMemberPointerInstruction::Initialize, &InitializeInstructionData { authority: authority.try_into()?, - group_address: *group_address, member_address: member_address.try_into()?, }, )) @@ -119,17 +105,13 @@ pub fn update( token_program_id: &Pubkey, mint: &Pubkey, authority: &Pubkey, - group_update_authority: &Pubkey, signers: &[&Pubkey], - group_address: &Pubkey, member_address: Option, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ AccountMeta::new(*mint, false), - AccountMeta::new(*group_address, false), AccountMeta::new_readonly(*authority, signers.is_empty()), - AccountMeta::new_readonly(*group_update_authority, true), ]; for signer_pubkey in signers.iter() { accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); diff --git a/token/program-2022/src/extension/group_member_pointer/mod.rs b/token/program-2022/src/extension/group_member_pointer/mod.rs index 2495fb4f8a9..175f869a7a6 100644 --- a/token/program-2022/src/extension/group_member_pointer/mod.rs +++ b/token/program-2022/src/extension/group_member_pointer/mod.rs @@ -3,7 +3,6 @@ use serde::{Deserialize, Serialize}; use { crate::extension::{Extension, ExtensionType}, bytemuck::{Pod, Zeroable}, - solana_program::pubkey::Pubkey, spl_pod::optional_keys::OptionalNonZeroPubkey, }; @@ -17,10 +16,8 @@ pub mod processor; #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] pub struct GroupMemberPointer { - /// Authority that can set the group address + /// Authority that can set the member address pub authority: OptionalNonZeroPubkey, - /// Account address that holds the group - pub group_address: Pubkey, /// Account address that holds the member pub member_address: OptionalNonZeroPubkey, } diff --git a/token/program-2022/src/extension/group_member_pointer/processor.rs b/token/program-2022/src/extension/group_member_pointer/processor.rs index b493db470be..87fd530deb6 100644 --- a/token/program-2022/src/extension/group_member_pointer/processor.rs +++ b/token/program-2022/src/extension/group_member_pointer/processor.rs @@ -9,7 +9,7 @@ use { }, GroupMemberPointer, }, - BaseStateWithExtensions, StateWithExtensions, StateWithExtensionsMut, + StateWithExtensionsMut, }, instruction::{decode_instruction_data, decode_instruction_type}, processor::Processor, @@ -19,49 +19,20 @@ use { account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, }, spl_pod::optional_keys::OptionalNonZeroPubkey, - spl_token_group_interface::state::TokenGroup, }; -fn check_group_update_authority( - group_info: &AccountInfo, - group_update_authority_info: &AccountInfo, -) -> Result<(), ProgramError> { - if !group_update_authority_info.is_signer { - msg!("Group update authority must be a signer"); - Err(ProgramError::MissingRequiredSignature)?; - } - let group_data = group_info.data.borrow(); - let group_mint = StateWithExtensions::::unpack(&group_data)?; - let group_state = group_mint.get_extension::()?; - if Option::::from(group_state.update_authority) - != Some(*group_update_authority_info.key) - { - msg!("Incorrect update authority for group"); - Err(TokenError::OwnerMismatch)?; - } - Ok(()) -} - fn process_initialize( _program_id: &Pubkey, accounts: &[AccountInfo], authority: &OptionalNonZeroPubkey, - group_address: &Pubkey, member_address: &OptionalNonZeroPubkey, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); - let mint_info = next_account_info(account_info_iter)?; - let group_info = next_account_info(account_info_iter)?; - let group_update_authority_info = next_account_info(account_info_iter)?; - - // Group update authority checks - check_group_update_authority(group_info, group_update_authority_info)?; - - let mut mint_data = mint_info.data.borrow_mut(); + let mint_account_info = next_account_info(account_info_iter)?; + let mut mint_data = mint_account_info.data.borrow_mut(); let mut mint = StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data)?; if Option::::from(*authority).is_none() @@ -76,7 +47,6 @@ fn process_initialize( let extension = mint.init_extension::(true)?; extension.authority = *authority; - extension.group_address = *group_address; extension.member_address = *member_address; Ok(()) } @@ -87,21 +57,16 @@ fn process_update( new_member_address: &OptionalNonZeroPubkey, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); - let mint_info = next_account_info(account_info_iter)?; - let group_info = next_account_info(account_info_iter)?; + let mint_account_info = next_account_info(account_info_iter)?; let owner_info = next_account_info(account_info_iter)?; - let group_update_authority_info = next_account_info(account_info_iter)?; - - // Group update authority checks - check_group_update_authority(group_info, group_update_authority_info)?; + let owner_info_data_len = owner_info.data_len(); - let mut mint_data = mint_info.data.borrow_mut(); + let mut mint_data = mint_account_info.data.borrow_mut(); let mut mint = StateWithExtensionsMut::::unpack(&mut mint_data)?; let extension = mint.get_extension_mut::()?; let authority = Option::::from(extension.authority).ok_or(TokenError::NoAuthorityExists)?; - let owner_info_data_len = owner_info.data_len(); Processor::validate_owner( program_id, &authority, @@ -125,16 +90,9 @@ pub(crate) fn process_instruction( msg!("GroupMemberPointerInstruction::Initialize"); let InitializeInstructionData { authority, - group_address, member_address, } = decode_instruction_data(input)?; - process_initialize( - program_id, - accounts, - authority, - group_address, - member_address, - ) + process_initialize(program_id, accounts, authority, member_address) } GroupMemberPointerInstruction::Update => { msg!("GroupMemberPointerInstruction::Update");