413 lines
12 KiB
Rust
413 lines
12 KiB
Rust
// Mark this test as SBF-only due to current `ProgramTest` limitations when CPIing into the system program
|
|
#![cfg(feature = "test-sbf")]
|
|
|
|
use {
|
|
solana_program_test::{
|
|
processor,
|
|
tokio::{self, sync::Mutex},
|
|
ProgramTest, ProgramTestContext,
|
|
},
|
|
solana_sdk::{
|
|
instruction::{AccountMeta, InstructionError},
|
|
pubkey::Pubkey,
|
|
signature::Signer,
|
|
signer::keypair::Keypair,
|
|
transaction::{Transaction, TransactionError},
|
|
},
|
|
spl_token_client::{
|
|
client::{
|
|
ProgramBanksClient, ProgramBanksClientProcessTransaction, ProgramClient,
|
|
SendTransaction,
|
|
},
|
|
token::Token,
|
|
},
|
|
spl_token_upgrade::{
|
|
error::TokenUpgradeError, get_token_upgrade_authority_address, instruction::exchange,
|
|
},
|
|
std::sync::Arc,
|
|
test_case::test_case,
|
|
};
|
|
|
|
fn keypair_clone(kp: &Keypair) -> Keypair {
|
|
Keypair::from_bytes(&kp.to_bytes()).expect("failed to copy keypair")
|
|
}
|
|
|
|
async fn setup() -> (
|
|
Arc<Mutex<ProgramTestContext>>,
|
|
Arc<dyn ProgramClient<ProgramBanksClientProcessTransaction>>,
|
|
Arc<Keypair>,
|
|
) {
|
|
let mut program_test = ProgramTest::new(
|
|
"spl_token_upgrade",
|
|
spl_token_upgrade::id(),
|
|
processor!(spl_token_upgrade::processor::process),
|
|
);
|
|
|
|
program_test.prefer_bpf(false); // simplicity in the build
|
|
|
|
program_test.add_program(
|
|
"spl_token_2022",
|
|
spl_token_2022::id(),
|
|
processor!(spl_token_2022::processor::Processor::process),
|
|
);
|
|
program_test.add_program(
|
|
"spl_token",
|
|
spl_token::id(),
|
|
processor!(spl_token::processor::Processor::process),
|
|
);
|
|
|
|
let context = program_test.start_with_context().await;
|
|
let payer = Arc::new(keypair_clone(&context.payer));
|
|
let context = Arc::new(Mutex::new(context));
|
|
|
|
let client: Arc<dyn ProgramClient<ProgramBanksClientProcessTransaction>> =
|
|
Arc::new(ProgramBanksClient::new_from_context(
|
|
Arc::clone(&context),
|
|
ProgramBanksClientProcessTransaction,
|
|
));
|
|
(context, client, payer)
|
|
}
|
|
|
|
async fn setup_mint<T: SendTransaction>(
|
|
program_id: &Pubkey,
|
|
mint_authority: &Pubkey,
|
|
decimals: u8,
|
|
payer: Arc<Keypair>,
|
|
client: Arc<dyn ProgramClient<T>>,
|
|
) -> Token<T> {
|
|
let mint_account = Keypair::new();
|
|
let token = Token::new(client, program_id, &mint_account.pubkey(), payer);
|
|
token
|
|
.create_mint(mint_authority, None, decimals, vec![], &[&mint_account])
|
|
.await
|
|
.unwrap();
|
|
token
|
|
}
|
|
|
|
#[test_case(spl_token::id(), spl_token_2022::id() ; "upgrade to token-2022")]
|
|
#[test_case(spl_token_2022::id(), spl_token::id() ; "downgrade to token")]
|
|
#[test_case(spl_token::id(), spl_token::id() ; "token to token")]
|
|
#[test_case(spl_token_2022::id(), spl_token_2022::id() ; "token-2022 to token-2022")]
|
|
#[tokio::test]
|
|
async fn success(original_program_id: Pubkey, new_program_id: Pubkey) {
|
|
let (context, client, payer) = setup().await;
|
|
|
|
let wallet = Keypair::new();
|
|
let mint_authority = Keypair::new();
|
|
let mint_authority_pubkey = mint_authority.pubkey();
|
|
|
|
let decimals = 2;
|
|
let original_token = setup_mint(
|
|
&original_program_id,
|
|
&mint_authority_pubkey,
|
|
decimals,
|
|
payer.clone(),
|
|
client.clone(),
|
|
)
|
|
.await;
|
|
let new_token = setup_mint(
|
|
&new_program_id,
|
|
&mint_authority_pubkey,
|
|
decimals,
|
|
payer.clone(),
|
|
client.clone(),
|
|
)
|
|
.await;
|
|
|
|
let program_escrow = get_token_upgrade_authority_address(
|
|
original_token.get_address(),
|
|
new_token.get_address(),
|
|
&spl_token_upgrade::id(),
|
|
);
|
|
|
|
original_token
|
|
.create_associated_token_account(&wallet.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let original_account = original_token.get_associated_token_address(&wallet.pubkey());
|
|
let token_amount = 1_000_000_000_000;
|
|
original_token
|
|
.mint_to(
|
|
&original_account,
|
|
&mint_authority_pubkey,
|
|
token_amount,
|
|
Some(decimals),
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
new_token
|
|
.create_associated_token_account(&wallet.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let new_account = new_token.get_associated_token_address(&wallet.pubkey());
|
|
new_token
|
|
.create_associated_token_account(&program_escrow)
|
|
.await
|
|
.unwrap();
|
|
let escrow_account = new_token.get_associated_token_address(&program_escrow);
|
|
new_token
|
|
.mint_to(
|
|
&escrow_account,
|
|
&mint_authority_pubkey,
|
|
token_amount,
|
|
Some(decimals),
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
{
|
|
let mut context = context.lock().await;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[exchange(
|
|
&spl_token_upgrade::id(),
|
|
&original_account,
|
|
original_token.get_address(),
|
|
&escrow_account,
|
|
&new_account,
|
|
new_token.get_address(),
|
|
&original_program_id,
|
|
&new_program_id,
|
|
&wallet.pubkey(),
|
|
&[],
|
|
)],
|
|
Some(&context.payer.pubkey()),
|
|
&[&context.payer, &wallet],
|
|
context.last_blockhash,
|
|
);
|
|
context
|
|
.banks_client
|
|
.process_transaction(transaction)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
let original_mint = original_token.get_mint_info().await.unwrap();
|
|
assert_eq!(original_mint.base.supply, 0);
|
|
let original_account_info = original_token
|
|
.get_account_info(&original_account)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(original_account_info.base.amount, 0);
|
|
let new_account_info = new_token.get_account_info(&new_account).await.unwrap();
|
|
assert_eq!(new_account_info.base.amount, token_amount);
|
|
let escrow_info = new_token.get_account_info(&escrow_account).await.unwrap();
|
|
assert_eq!(escrow_info.base.amount, 0);
|
|
}
|
|
|
|
#[test_case(spl_token::id(), spl_token_2022::id() ; "fail upgrade to token-2022")]
|
|
#[tokio::test]
|
|
async fn fail_incorrect_escrow_derivation(original_program_id: Pubkey, new_program_id: Pubkey) {
|
|
let (context, client, payer) = setup().await;
|
|
|
|
let wallet = Keypair::new();
|
|
let mint_authority = Keypair::new();
|
|
let mint_authority_pubkey = mint_authority.pubkey();
|
|
|
|
let decimals = 2;
|
|
let original_token = setup_mint(
|
|
&original_program_id,
|
|
&mint_authority_pubkey,
|
|
decimals,
|
|
payer.clone(),
|
|
client.clone(),
|
|
)
|
|
.await;
|
|
let new_token = setup_mint(
|
|
&new_program_id,
|
|
&mint_authority_pubkey,
|
|
decimals,
|
|
payer.clone(),
|
|
client.clone(),
|
|
)
|
|
.await;
|
|
|
|
// backwards derivation
|
|
let program_escrow = get_token_upgrade_authority_address(
|
|
new_token.get_address(),
|
|
original_token.get_address(),
|
|
&spl_token_upgrade::id(),
|
|
);
|
|
|
|
original_token
|
|
.create_associated_token_account(&wallet.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let original_account = original_token.get_associated_token_address(&wallet.pubkey());
|
|
let token_amount = 1_000_000_000_000;
|
|
original_token
|
|
.mint_to(
|
|
&original_account,
|
|
&mint_authority_pubkey,
|
|
token_amount,
|
|
Some(decimals),
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
new_token
|
|
.create_associated_token_account(&wallet.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let new_account = new_token.get_associated_token_address(&wallet.pubkey());
|
|
new_token
|
|
.create_associated_token_account(&program_escrow)
|
|
.await
|
|
.unwrap();
|
|
let escrow_account = new_token.get_associated_token_address(&program_escrow);
|
|
new_token
|
|
.mint_to(
|
|
&escrow_account,
|
|
&mint_authority_pubkey,
|
|
token_amount,
|
|
Some(decimals),
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut instruction = exchange(
|
|
&spl_token_upgrade::id(),
|
|
&original_account,
|
|
original_token.get_address(),
|
|
&escrow_account,
|
|
&new_account,
|
|
new_token.get_address(),
|
|
&original_program_id,
|
|
&new_program_id,
|
|
&wallet.pubkey(),
|
|
&[],
|
|
);
|
|
instruction.accounts[5] = AccountMeta::new_readonly(program_escrow, false);
|
|
let mut context = context.lock().await;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[instruction],
|
|
Some(&context.payer.pubkey()),
|
|
&[&context.payer, &wallet],
|
|
context.last_blockhash,
|
|
);
|
|
let error = context
|
|
.banks_client
|
|
.process_transaction(transaction)
|
|
.await
|
|
.unwrap_err()
|
|
.unwrap();
|
|
assert_eq!(
|
|
error,
|
|
TransactionError::InstructionError(
|
|
0,
|
|
InstructionError::Custom(TokenUpgradeError::InvalidOwner as u32)
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test_case(spl_token::id(), spl_token_2022::id() ; "fail upgrade to token-2022")]
|
|
#[tokio::test]
|
|
async fn fail_decimals_mismatch(original_program_id: Pubkey, new_program_id: Pubkey) {
|
|
let (context, client, payer) = setup().await;
|
|
|
|
let wallet = Keypair::new();
|
|
let mint_authority = Keypair::new();
|
|
let mint_authority_pubkey = mint_authority.pubkey();
|
|
|
|
// different decimals
|
|
let original_decimals = 2;
|
|
let new_decimals = 3;
|
|
|
|
let original_token = setup_mint(
|
|
&original_program_id,
|
|
&mint_authority_pubkey,
|
|
original_decimals,
|
|
payer.clone(),
|
|
client.clone(),
|
|
)
|
|
.await;
|
|
let new_token = setup_mint(
|
|
&new_program_id,
|
|
&mint_authority_pubkey,
|
|
new_decimals,
|
|
payer.clone(),
|
|
client.clone(),
|
|
)
|
|
.await;
|
|
|
|
let program_escrow = get_token_upgrade_authority_address(
|
|
original_token.get_address(),
|
|
new_token.get_address(),
|
|
&spl_token_upgrade::id(),
|
|
);
|
|
|
|
original_token
|
|
.create_associated_token_account(&wallet.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let original_account = original_token.get_associated_token_address(&wallet.pubkey());
|
|
let token_amount = 1_000_000_000_000;
|
|
original_token
|
|
.mint_to(
|
|
&original_account,
|
|
&mint_authority_pubkey,
|
|
token_amount,
|
|
Some(original_decimals),
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
new_token
|
|
.create_associated_token_account(&wallet.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let new_account = new_token.get_associated_token_address(&wallet.pubkey());
|
|
new_token
|
|
.create_associated_token_account(&program_escrow)
|
|
.await
|
|
.unwrap();
|
|
let escrow_account = new_token.get_associated_token_address(&program_escrow);
|
|
new_token
|
|
.mint_to(
|
|
&escrow_account,
|
|
&mint_authority_pubkey,
|
|
token_amount,
|
|
Some(new_decimals),
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut context = context.lock().await;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[exchange(
|
|
&spl_token_upgrade::id(),
|
|
&original_account,
|
|
original_token.get_address(),
|
|
&escrow_account,
|
|
&new_account,
|
|
new_token.get_address(),
|
|
&original_program_id,
|
|
&new_program_id,
|
|
&wallet.pubkey(),
|
|
&[],
|
|
)],
|
|
Some(&context.payer.pubkey()),
|
|
&[&context.payer, &wallet],
|
|
context.last_blockhash,
|
|
);
|
|
let error = context
|
|
.banks_client
|
|
.process_transaction(transaction)
|
|
.await
|
|
.unwrap_err()
|
|
.unwrap();
|
|
assert_eq!(
|
|
error,
|
|
TransactionError::InstructionError(
|
|
0,
|
|
InstructionError::Custom(TokenUpgradeError::DecimalsMismatch as u32)
|
|
)
|
|
);
|
|
}
|