Comprensión de los Discriminadores de Cuentas Anchor
Los programas Anchor anteponen un discriminador de 8 bytes a todos los datos de la cuenta. Este discriminador es un hash del nombre del tipo de cuenta y se utiliza para verificar que los datos de la cuenta coincidan con el tipo esperado.
┌─────────────────────────────────────────────────┐│ Account Data Layout │├────────────────┬────────────────────────────────┤│ Discriminator │ Account Fields ││ (8 bytes) │ (variable size) │└────────────────┴────────────────────────────────┘
Las utilidades de cuenta anchor-litesvm gestionan este discriminador
automáticamente.
Obtención de Cuentas Anchor
Obtención Básica de Cuentas
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>() valida automáticamente el discriminador de 8 bytes para garantizar que los datos de la cuenta coincidan con el tipo esperado.
Deserialización sin Verificación
Para PDAs o diseños de cuentas personalizados donde la validación del
discriminador podría fallar, use 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();}
Use get_account_unchecked con precaución. Sin la validación del
discriminador, podría deserializar datos inválidos. Utilícelo solo cuando
tenga certeza sobre el tipo de cuenta.
Uso Directo del Módulo de Cuentas
También puede utilizar las funciones de deserialización de cuentas directamente:
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();}
Errores de Cuenta
El enum AccountError proporciona información detallada sobre los errores:
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);}}}
| Error | Descripción |
|---|---|
AccountNotFound(Pubkey) | No existe ninguna cuenta en la dirección indicada |
DiscriminatorMismatch | El discriminador de la cuenta no coincide con el tipo esperado |
DeserializationError(String) | Error al deserializar los datos de la cuenta |
Trabajo con Múltiples Tipos de Cuentas
Obtención de Diferentes Tipos de Cuentas
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));}
Verificación de Existencia de Cuenta Antes de Obtenerla
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");}}
Ejemplo completo
Aquí hay un ejemplo completo que demuestra la gestión de cuentas:
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?