Contas de Dados & Layout
O estado do programa é um buffer de bytes no campo data de uma conta. Anchor 0.32.1 usa a serialização Borsh com um prefixo discriminador de 8 bytes. O dimensionamento correto do layout evita falhas de inicialização e explorações de desserialização.
Receita
#[account]
#[derive(InitSpace)]
pub struct Market {
pub authority: Pubkey,
pub price: u64,
pub name: String, // max 32 em InitSpace via #[max_len(32)]
}
// space = 8 + Market::INIT_SPACEQuando usar isso:
- Calculando o espaço de
initpara novos tipos de conta - Versionando esquemas de conta para atualizações
- Escolhendo Borsh vs zero-copy para trade-offs de CU e tamanho
- Analisando contas desconhecidas de RPC em clientes
Exemplo de Trabalho
use anchor_lang::prelude::*;
#[account]
#[derive(InitSpace)]
pub struct UserStats {
pub authority: Pubkey,
#[max_len(16)]
pub nickname: String,
pub wins: u32,
pub losses: u32,
}
#[derive(Accounts)]
pub struct InitStats<'info> {
#[account(
init,
payer = authority,
space = 8 + UserStats::INIT_SPACE,
seeds = [b"stats", authority.key().as_ref()],
bump,
)]
pub stats: Account<'info, UserStats>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[program]
pub mod stats {
use super::*;
pub fn init(ctx: Context<InitStats>, nickname: String) -> Result<()> {
require!(nickname.len() <= 16, StatsError::NameTooLong);
ctx.accounts.stats.nickname = nickname;
ctx.accounts.stats.wins = 0;
ctx.accounts.stats.losses = 0;
Ok(())
}
}O que isso demonstra:
InitSpacecomputa o tamanho Borsh, incluindo stringsmax_len- O discriminador de 8 bytes é adicionado automaticamente pelo Anchor
- O tempo de execução valida o comprimento da string em relação ao espaço alocado
Mergulho Profundo
Estrutura do Layout
[ 8 bytes discriminador ][ Campo Borsh 1 ][ Campo 2 ]...
Regras de Dimensionamento
- Tipos de tamanho fixo:
Pubkey32,u648,bool1 String/Vec: comprimento de 4 bytes + bytes de conteúdo máximo- Enums: índice de variante de 1 byte + tamanho da maior variante
Versionamento
- Adicione
version: u8como primeiro campo após o discriminador - Ou use um novo tipo de conta com um novo discriminador
- Nunca encolha contas sem migração
Armadilhas
- Omissão de
max_lenemString/Vec- erro de compilação ou tamanho ilimitado. Correção: sempre defina#[max_len(N)]no Anchor 0.32.1. - Erros de cálculo manual de espaço - erro de um caractere em enums. Correção: prefira o derive
InitSpace. - Reordenação de campos - quebra contas existentes. Correção: apenas adicione novos campos com versionamento.
- Desserialização de contas não confiáveis - o atacante cria bytes. Correção: valide o proprietário, o discriminador e os intervalos de campos.
- Contas enormes - custo de aluguel e de desserialização de CU. Correção: zero-copy ou divisão entre contas.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Borsh + Anchor | Estado padrão do programa | Conjuntos de dados de vários MB |
Zero-copy (AccountLoader) | Estruturas grandes, economia de CU | Estruturas pequenas e simples |
bytemuck POD | Layouts compactados de tamanho fixo | Strings de comprimento variável |
| Compressão externa | Dados em escala de NFT | Campos mutáveis "quentes" |
FAQs
O que é o discriminador de 8 bytes?
Os primeiros 8 bytes do SHA256("account:StructName") no Anchor - identifica o tipo da conta.
Como calculo o espaço manualmente?
Some os tamanhos Borsh + 8. Para String(max N): 4 + N bytes.
Posso alterar a ordem dos campos em um upgrade?
Não - quebra a desserialização de contas existentes.
O que é InitSpace?
Macro derive do Anchor 0.32.1 que calcula a constante INIT_SPACE para o atributo space.
Como os clientes decodificam os dados da conta?
Use clientes Codama/@solana/kit do IDL, ou leituras de buffer manuais que correspondam ao layout Borsh.
Qual é o tamanho máximo da conta?
10 MB por conta no Agave 4.1.1 - limites práticos são menores devido ao aluguel/CU.
Posso armazenar JSON em contas?
Desperdício e ilimitado. Use layouts binários tipados.
Como os enums afetam o espaço?
Borsh usa uma tag de variante de 1 byte + o tamanho da maior carga útil da variante.
O que é layout zero-copy?
#[account(zero_copy)] mapeia a struct diretamente para os bytes da conta sem alocação de desserialização.
Devo empacotar campos manualmente?
Apenas para otimização extrema de CU. O layout padrão Borsh é bom para a maioria dos programas.
Como lidar com campos opcionais?
Use Option<T> em Borsh (tag de 1 byte + T) ou valores sentinela.
Duas structs podem compartilhar um discriminador?
Não - cada tipo de conta Anchor tem um discriminador exclusivo.
Relacionado
- Aluguel & Isenção de Aluguel - o espaço impulsiona o aluguel
- Estado de Propriedade do Programa - onde os layouts vivem
- Criação e Fechamento de Contas -
reallocpara crescimento
Versões da Pilha: Esta página foi escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, e LiteSVM 0.6.x.