Anchor 계정 Discriminator 이해하기
Anchor 프로그램은 모든 계정 데이터에 8바이트 discriminator를 앞에 추가합니다. 이 discriminator는 계정 유형 이름의 해시값으로, 계정 데이터가 예상된 유형과 일치하는지 검증하는 데 사용됩니다.
┌─────────────────────────────────────────────────┐│ Account Data Layout │├────────────────┬────────────────────────────────┤│ Discriminator │ Account Fields ││ (8 bytes) │ (variable size) │└────────────────┴────────────────────────────────┘
anchor-litesvm 계정 유틸리티는 이 discriminator를 자동으로 처리합니다.
Anchor 계정 가져오기
기본 계정 가져오기
use anchor_litesvm::AnchorLiteSVM;use anchor_lang::prelude::*;use solana_signer::Signer;use solana_program::pubkey::Pubkey;#[account]pub struct UserAccount {pub name: String,pub balance: u64,pub authority: Pubkey,}#[test]fn test_fetch_account() {let mut ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));let user = ctx.create_funded_account(10_000_000_000).unwrap();// ... initialize the account ...let (user_pda, _) = Pubkey::find_program_address(&[b"user", user.pubkey().as_ref()],&PROGRAM_ID,);// Fetch and deserialize the accountlet account: UserAccount = ctx.get_account(&user_pda).unwrap();assert_eq!(account.name, "Alice");assert_eq!(account.authority, user.pubkey());}
get_account<T>()는 계정 데이터가 예상된 유형과 일치하는지 확인하기 위해 8바이트 discriminator를 자동으로 검증합니다.
비검증 역직렬화
discriminator 검증이 실패할 수 있는 PDA 또는 커스텀 계정 레이아웃의 경우,
get_account_unchecked를 사용하세요:
use anchor_litesvm::AnchorLiteSVM;#[test]fn test_fetch_unchecked() {let ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));// Fetch without discriminator validationlet account: UserAccount = ctx.get_account_unchecked(&some_pda).unwrap();}
get_account_unchecked는 신중하게 사용하세요. discriminator 검증 없이는
잘못된 데이터를 역직렬화할 수 있습니다. 계정 유형을 확실히 알고 있을 때만
사용하세요.
계정 모듈 직접 사용하기
계정 역직렬화 함수를 직접 사용할 수도 있습니다:
use anchor_litesvm::{get_anchor_account, get_anchor_account_unchecked};use litesvm::LiteSVM;#[test]fn test_direct_account_fetch() {let mut svm = LiteSVM::new();// ... setup ...// Fetch with discriminator checklet account: UserAccount = get_anchor_account(&svm, &account_pubkey).unwrap();// Fetch without discriminator checklet account: UserAccount = get_anchor_account_unchecked(&svm, &account_pubkey).unwrap();}
계정 오류
AccountError 열거형은 상세한 오류 정보를 제공합니다:
use anchor_litesvm::AccountError;#[test]fn test_handle_errors() {let ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));let result: Result<UserAccount, AccountError> = ctx.get_account(&nonexistent_pubkey);match result {Ok(account) => println!("Found account: {}", account.name),Err(AccountError::AccountNotFound(pubkey)) => {println!("Account not found: {}", pubkey);}Err(AccountError::DiscriminatorMismatch) => {println!("Wrong account type!");}Err(AccountError::DeserializationError(msg)) => {println!("Failed to deserialize: {}", msg);}}}
| 오류 | 설명 |
|---|---|
AccountNotFound(Pubkey) | 지정된 주소에 계정이 존재하지 않음 |
DiscriminatorMismatch | 계정 discriminator가 예상된 유형과 일치하지 않음 |
DeserializationError(String) | 계정 데이터 역직렬화 실패 |
여러 계정 유형 다루기
다양한 계정 유형 가져오기
use anchor_litesvm::AnchorLiteSVM;use anchor_lang::prelude::*;#[account]pub struct Config {pub admin: Pubkey,pub fee_bps: u16,}#[account]pub struct UserProfile {pub owner: Pubkey,pub username: String,pub created_at: i64,}#[account]pub struct Order {pub user: Pubkey,pub amount: u64,pub status: OrderStatus,}#[derive(AnchorSerialize, AnchorDeserialize, Clone)]pub enum OrderStatus {Pending,Completed,Cancelled,}#[test]fn test_multiple_account_types() {let ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));// Each account type is deserialized correctlylet config: Config = ctx.get_account(&config_pda).unwrap();let profile: UserProfile = ctx.get_account(&profile_pda).unwrap();let order: Order = ctx.get_account(&order_pda).unwrap();assert_eq!(config.fee_bps, 100);assert_eq!(profile.username, "alice");assert!(matches!(order.status, OrderStatus::Pending));}
가져오기 전 계정 존재 여부 확인
use anchor_litesvm::AnchorLiteSVM;#[test]fn test_check_before_fetch() {let ctx = AnchorLiteSVM::build_with_program(PROGRAM_ID, include_bytes!("../target/deploy/your_program.so"));let user_pda = Pubkey::new_unique();// Check existence firstif ctx.account_exists(&user_pda) {let account: UserAccount = ctx.get_account(&user_pda).unwrap();println!("Found: {}", account.name);} else {println!("Account not initialized yet");}}
완전한 예제
계정 관리를 보여주는 포괄적인 예제입니다:
use anchor_litesvm::AnchorLiteSVM;use anchor_litesvm::TestHelpers;use anchor_lang::prelude::*;use solana_signer::Signer;use solana_program::pubkey::Pubkey;anchor_lang::declare_program!(counter);#[test]fn test_counter_workflow() {let mut ctx = AnchorLiteSVM::build_with_program(id(),include_bytes!("../target/deploy/counter.so"),);let authority = ctx.svm.create_funded_account(10_000_000_000).unwrap();// Derive the counter PDAlet (counter_pda, bump) = Pubkey::find_program_address(&[b"counter", authority.pubkey().as_ref()],&id(),);// Verify counter doesn't exist yetassert!(!ctx.account_exists(&counter_pda));// Initialize using generated client typeslet init_ix = ctx.program().accounts(counter::client::accounts::Initialize {authority: authority.pubkey(),counter: counter_pda,system_program: anchor_lang::system_program::ID,}).args(counter::client::args::Initialize {}).instruction().unwrap();ctx.execute_instruction(init_ix, &[&authority]).unwrap();// Verify counter exists and has correct initial stateassert!(ctx.account_exists(&counter_pda));let counter: counter::Counter = ctx.get_account(&counter_pda).unwrap();assert_eq!(counter.count, 0);assert_eq!(counter.authority, authority.pubkey());assert_eq!(counter.bump, bump);// Increment using generated client typeslet increment_ix = ctx.program().accounts(counter::client::accounts::Increment {authority: authority.pubkey(),counter: counter_pda,}).args(counter::client::args::Increment {}).instruction().unwrap();ctx.execute_instruction(increment_ix, &[&authority]).unwrap();// Verify incrementedlet counter: counter::Counter = ctx.get_account(&counter_pda).unwrap();assert_eq!(counter.count, 1);// Increment againctx.execute_instruction(increment_ix.clone(), &[&authority]).unwrap();let counter: counter::Counter = ctx.get_account(&counter_pda).unwrap();assert_eq!(counter.count, 2);println!("Counter test passed! Final count: {}", counter.count);}
Is this page helpful?