Pipeline de transações

Resumo

As transações passam por 8 estágios: receção, sigverify, sanitização, verificações de orçamento/idade, validação do pagador de taxas, carregamento de contas, execução de instruções e commit.

Pipeline de processamento de transações

Quando uma transação chega a um validator, ela passa por uma série de estágios de validação e execução. O seguinte descreve o pipeline completo desde a receção até o commit, com referências aos ficheiros de código-fonte no cliente validator agave.

1. Receção e desserialização

O validator recebe bytes de transação via UDP/QUIC. Os bytes brutos devem caber num único pacote (PACKET_DATA_SIZE = 1.232 bytes). Os bytes são desserializados numa VersionedTransaction, que contém o array de assinaturas e uma VersionedMessage (legacy ou v0).

2. Verificação de assinatura (sigverify)

As assinaturas são verificadas no estágio sigverify antes da transação entrar no estágio banking. Para cada assinatura no índice i, o verificador verifica Ed25519(signatures[i], account_keys[i], message_bytes). Se alguma assinatura for inválida, o pacote é descartado.

A verificação é paralelizada: o validator divide lotes de pacotes em blocos de VERIFY_PACKET_CHUNK_SIZE (128) e processa-os em paralelo.

3. Sanitização

A transação desserializada é sanitizada para produzir uma SanitizedTransaction (ou RuntimeTransaction). A sanitização valida invariantes estruturais:

  • O número de assinaturas corresponde a num_required_signatures no cabeçalho
  • Todos os program_id_index e account_indices de instruções estão dentro dos limites
  • O pagador de taxas (índice de conta 0) é um signatário com permissão de escrita

O wrapper RuntimeTransaction armazena em cache metadados pré-computados de TransactionMeta: o hash da mensagem, flag de transação de voto, contagens de assinaturas de precompilação (Ed25519/secp256k1/secp256r1), detalhes de instrução de orçamento de computação e comprimento total de dados de instrução.

4. Verificar orçamento de computação, idade e cache de estado

O método check_transactions realiza várias verificações por transação:

Orçamento de computação: As instruções de orçamento de computação da transação são analisadas e validadas primeiro. Os detalhes da taxa são calculados a partir dos limites do orçamento e taxa de priorização. Se o orçamento de computação for inválido ou conflituoso, a transação falha com erros de análise do orçamento de computação como DuplicateInstruction, InstructionError(..., InvalidInstructionData), ou InvalidLoadedAccountsDataSizeLimit.

Idade do blockhash: O recent_blockhash da transação é procurado no BlockhashQueue. Se o hash for encontrado e sua idade estiver dentro de MAX_PROCESSING_AGE (150 slots), a transação prossegue. Se não for encontrado, o validator verifica se há um nonce durável válido.

Cache de estado: O hash da mensagem da transação é verificado em relação a um cache de estado. Se encontrado, a transação é rejeitada com AlreadyProcessed.

5. Validar nonce e pagador de taxa

O método validate_transaction_nonce_and_fee_payer no SVM trata duas validações:

Validação de nonce (se aplicável): Para transações com nonce, o validator carrega a conta de nonce e verifica:

  • A conta é propriedade do System Program
  • É analisada como State::Initialized
  • O nonce durável armazenado corresponde ao recent_blockhash da transação
  • O nonce pode ser avançado (seu nonce durável atual difere do próximo nonce durável, ou seja, o nonce ainda não foi usado no bloco atual)
  • A autoridade do nonce assinou a transação

Se válido, o nonce é avançado para o próximo valor de nonce durável. Consulte validate_transaction_nonce.

Validação do pagador de taxa: A conta do pagador de taxa (sempre índice 0) é carregada e verificada por validate_fee_payer:

  • A conta deve existir (lamports > 0), caso contrário AccountNotFound
  • A conta deve ser uma conta de sistema ou conta de nonce, caso contrário InvalidAccountForFee
  • Os lamports devem cobrir min_balance + total_fee, onde min_balance é 0 para contas de sistema ou rent.minimum_balance(NonceState::size()) para contas de nonce; caso contrário InsufficientFundsForFee
  • Após a dedução da taxa, a conta deve permanecer isenta de renda (não pode fazer transição de isenta de renda para pagadora de renda)

A taxa é deduzida do pagador de taxas nesta etapa. Um instantâneo do pagador de taxas com a taxa subtraída (e nonce avançado, se aplicável) é salvo como RollbackAccounts, que são as contas que são confirmadas mesmo se a execução falhar.

6. Carregar contas

load_transaction carrega todas as contas referenciadas pela transação. O AccountLoader encapsula o armazenamento de contas externo e mantém um cache local do lote para que contas modificadas por transações anteriores no mesmo lote sejam visíveis para as posteriores.

Para cada conta que não seja pagadora de taxas, o carregador:

  1. Busca a conta do cache ou accounts-db
  2. Atualiza o estado de isenção de aluguel se necessário
  3. Acumula o tamanho dos dados da conta em direção ao loaded_accounts_data_size_limit (padrão 64 MiB). Cada conta incorre numa sobrecarga base de TRANSACTION_ACCOUNT_BASE_SIZE (64 bytes) mais o comprimento dos seus dados

Para cada programa invocado pelas instruções da transação, o carregador verifica que a conta do programa existe e é propriedade de um carregador válido (NativeLoader ou um dos PROGRAM_OWNERS). Programas inválidos falham com ProgramAccountNotFound ou InvalidProgramForExecution.

Programas LoaderV3 (atualizáveis) carregam implicitamente a sua conta programdata associada, que também conta para o limite de tamanho de dados carregados.

Se o carregamento da conta falhar mas o pagador de taxas foi validado com sucesso, a transação torna-se um resultado FeesOnly: a taxa ainda é cobrada mas nenhuma instrução é executada.

7. Executar instruções

execute_loaded_transaction cria um TransactionContext com todas as contas carregadas e invoca process_message. As instruções são executadas sequencialmente na ordem em que aparecem na mensagem. Cada invocação de instrução cria um InvokeContext e chama o programa alvo.

Detalhes do processamento de instruções

A função process_message do runtime itera através de cada instrução e chama o programa alvo:

  1. Para cada instrução, o runtime chama prepare_next_top_level_instruction, que constrói o InstructionContext. Este contexto contém referências às contas da instrução (resolvidas a partir dos índices compilados), os dados da instrução e o índice da conta do programa.
  2. O runtime verifica se o programa é um precompile (Ed25519, Secp256k1, Secp256r1). Precompiles são verificados diretamente sem invocar a VM BPF.
  3. Para todos os outros programas, o runtime invoca process_instruction, que carrega o programa do cache e o executa na máquina virtual BPF.
  4. Após a conclusão da instrução, o runtime verifica que o saldo total de lamports em todas as contas da instrução não foi alterado (verificação UnbalancedInstruction).
  5. Se qualquer instrução falhar, toda a transação é revertida. Nenhuma alteração de estado intermediária é confirmada.

Cada instrução incrementa o rastreamento de instruções. O rastreamento inclui tanto instruções de nível superior quanto quaisquer CPIs que elas invocam. O comprimento total do rastreamento (instruções de nível superior mais todos os CPIs aninhados) não pode exceder 64 (MAX_INSTRUCTION_TRACE_LENGTH). Exceder este limite retorna InstructionError::MaxInstructionTraceLengthExceeded.

Após a execução, o runtime verifica que:

  • A soma de lamports em todas as contas não foi alterada
  • Nenhuma conta transitou de isenta de aluguel para pagadora de aluguel

8. Confirmar ou reverter

Se a execução for bem-sucedida, os estados de conta modificados do TransactionContext são escritos de volta no cache local do lote do AccountLoader. Se a execução falhar, apenas o RollbackAccounts (pagador de taxa com taxa deduzida e nonce avançado) são escritos de volta. A taxa ainda é cobrada, mas todas as outras alterações de conta são descartadas.

Resumo do pipeline

Receive packet (UDP/QUIC)
--> Deserialize into VersionedTransaction
--> Sigverify (parallel Ed25519 verification)
--> Sanitize (structural validation, metadata extraction)
--> Parse compute budget, calculate fees
--> Check blockhash age (or verify nonce account)
--> Check status cache (dedup)
--> Validate nonce authority and advanceability (if nonce transaction)
--> Validate fee payer (load, check balance, deduct fee)
--> Load all accounts (with data size limits)
--> Load programs (verify loaders)
--> Execute instructions sequentially
--> Verify post-conditions (lamport balance, rent state)
--> Commit account changes (or rollback on failure)

Referência de erros de transação

A tabela a seguir lista todas as variantes de TransactionError e em qual estágio do pipeline elas ocorrem:

ErroFaseCausa
AccountInUseAgendamentoA conta já está bloqueada por outra transação no mesmo lote
AccountLoadedTwiceAgendamentoUma pubkey aparece duas vezes no account_keys da transação
AccountNotFoundValidação do pagador de taxaA conta do pagador de taxa não existe
ProgramAccountNotFoundCarregamento de contaUm programa invocado não existe
InsufficientFundsForFeeValidação do pagador de taxaO pagador de taxa não pode cobrir a taxa + mínimo isento de rent
InvalidAccountForFeeValidação do pagador de taxaO pagador de taxa não é uma conta de sistema ou nonce
AlreadyProcessedCache de estadoA transação já foi processada
BlockhashNotFoundVerificação de idadeBlockhash não está na fila e não é um nonce válido
InstructionErrorExecuçãoOcorreu um erro ao processar uma instrução (inclui índice da instrução e InstructionError específico)
CallChainTooDeepCarregamento de contaA cadeia de chamadas do carregador é muito profunda
MissingSignatureForFeeSanitizaçãoA transação requer uma taxa mas não possui assinatura presente
InvalidAccountIndexSanitizaçãoA transação contém uma referência de conta inválida
SignatureFailureVerificação de assinaturaA assinatura Ed25519 não verifica (o pacote é descartado)
InvalidProgramForExecutionCarregamento de contaO programa não pertence a um carregador válido
SanitizeFailureSanitizaçãoA transação falhou ao sanitizar os deslocamentos de contas corretamente
ClusterMaintenanceAgendamentoAs transações estão atualmente desativadas devido à manutenção do cluster
AccountBorrowOutstandingExecuçãoO processamento da transação deixou uma conta com uma referência emprestada pendente
WouldExceedMaxBlockCostLimitAgendamentoA transação excederia o limite máximo de custo do bloco
UnsupportedVersionSanitizaçãoA versão da transação não é suportada
InvalidWritableAccountCarregamento de contaA transação carrega uma conta gravável que não pode ser escrita
WouldExceedMaxAccountCostLimitAgendamentoA transação excederia o limite máximo de custo de conta dentro do bloco
WouldExceedAccountDataBlockLimitAgendamentoA transação excederia o limite de dados da conta dentro do bloco
TooManyAccountLocksAgendamentoA transação bloqueou muitas contas
AddressLookupTableNotFoundCarregamento de contaA conta da tabela de pesquisa de endereços não existe
InvalidAddressLookupTableOwnerCarregamento de contaA tabela de pesquisa de endereços pertence ao programa errado
InvalidAddressLookupTableDataCarregamento de contaA tabela de pesquisa de endereços contém dados inválidos
InvalidAddressLookupTableIndexCarregamento de contaA pesquisa na tabela de endereços usa um índice inválido
InvalidRentPayingAccountVerificação pós-execuçãoA conta transitou de isenta de rent para pagadora de rent
WouldExceedMaxVoteCostLimitAgendamentoA transação excederia o limite máximo de custo de voto
WouldExceedAccountDataTotalLimitAgendamentoA transação excederia o limite total de dados da conta
DuplicateInstructionAnálise do orçamento de computaçãoVariante de instrução de orçamento de computação duplicada na mesma transação
InsufficientFundsForRentVerificação pós-execuçãoA conta não possui lamports suficientes para cobrir o rent pelo tamanho dos seus dados
MaxLoadedAccountsDataSizeExceededCarregamento de contaO total de dados carregados excede o limite de 64 MiB
InvalidLoadedAccountsDataSizeLimitAnálise do orçamento de computaçãoSetLoadedAccountsDataSizeLimit definido como 0
ResanitizationNeededSanitizaçãoA transação diferiu antes/depois da ativação da funcionalidade e precisa de ressanitização
ProgramExecutionTemporarilyRestrictedCarregamento de contaA execução do programa está temporariamente restrita na conta referenciada
UnbalancedTransactionVerificação pós-execuçãoO saldo total de lamports antes da transação não é igual ao saldo depois
ProgramCacheHitMaxLimitCarregamento de contaO cache do programa atingiu o limite máximo
CommitCancelledCommitCommit cancelado internamente

Is this page helpful?

Gerenciado por

© 2026 Fundação Solana.
Todos os direitos reservados.
Conecte-se
  • Blog