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_signaturesno cabeçalho - Todos os
program_id_indexeaccount_indicesde 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_blockhashda 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, ondemin_balanceé 0 para contas de sistema ourent.minimum_balance(NonceState::size())para contas de nonce; caso contrárioInsufficientFundsForFee - 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:
- Busca a conta do cache ou accounts-db
- Atualiza o estado de isenção de aluguel se necessário
- 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 deTRANSACTION_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:
- Para cada instrução, o runtime chama
prepare_next_top_level_instruction, que constrói oInstructionContext. 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. - O runtime verifica se o programa é um precompile (Ed25519, Secp256k1, Secp256r1). Precompiles são verificados diretamente sem invocar a VM BPF.
- Para todos os outros programas, o runtime invoca
process_instruction, que carrega o programa do cache e o executa na máquina virtual BPF. - 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). - 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:
| Erro | Fase | Causa |
|---|---|---|
AccountInUse | Agendamento | A conta já está bloqueada por outra transação no mesmo lote |
AccountLoadedTwice | Agendamento | Uma pubkey aparece duas vezes no account_keys da transação |
AccountNotFound | Validação do pagador de taxa | A conta do pagador de taxa não existe |
ProgramAccountNotFound | Carregamento de conta | Um programa invocado não existe |
InsufficientFundsForFee | Validação do pagador de taxa | O pagador de taxa não pode cobrir a taxa + mínimo isento de rent |
InvalidAccountForFee | Validação do pagador de taxa | O pagador de taxa não é uma conta de sistema ou nonce |
AlreadyProcessed | Cache de estado | A transação já foi processada |
BlockhashNotFound | Verificação de idade | Blockhash não está na fila e não é um nonce válido |
InstructionError | Execução | Ocorreu um erro ao processar uma instrução (inclui índice da instrução e InstructionError específico) |
CallChainTooDeep | Carregamento de conta | A cadeia de chamadas do carregador é muito profunda |
MissingSignatureForFee | Sanitização | A transação requer uma taxa mas não possui assinatura presente |
InvalidAccountIndex | Sanitização | A transação contém uma referência de conta inválida |
SignatureFailure | Verificação de assinatura | A assinatura Ed25519 não verifica (o pacote é descartado) |
InvalidProgramForExecution | Carregamento de conta | O programa não pertence a um carregador válido |
SanitizeFailure | Sanitização | A transação falhou ao sanitizar os deslocamentos de contas corretamente |
ClusterMaintenance | Agendamento | As transações estão atualmente desativadas devido à manutenção do cluster |
AccountBorrowOutstanding | Execução | O processamento da transação deixou uma conta com uma referência emprestada pendente |
WouldExceedMaxBlockCostLimit | Agendamento | A transação excederia o limite máximo de custo do bloco |
UnsupportedVersion | Sanitização | A versão da transação não é suportada |
InvalidWritableAccount | Carregamento de conta | A transação carrega uma conta gravável que não pode ser escrita |
WouldExceedMaxAccountCostLimit | Agendamento | A transação excederia o limite máximo de custo de conta dentro do bloco |
WouldExceedAccountDataBlockLimit | Agendamento | A transação excederia o limite de dados da conta dentro do bloco |
TooManyAccountLocks | Agendamento | A transação bloqueou muitas contas |
AddressLookupTableNotFound | Carregamento de conta | A conta da tabela de pesquisa de endereços não existe |
InvalidAddressLookupTableOwner | Carregamento de conta | A tabela de pesquisa de endereços pertence ao programa errado |
InvalidAddressLookupTableData | Carregamento de conta | A tabela de pesquisa de endereços contém dados inválidos |
InvalidAddressLookupTableIndex | Carregamento de conta | A pesquisa na tabela de endereços usa um índice inválido |
InvalidRentPayingAccount | Verificação pós-execução | A conta transitou de isenta de rent para pagadora de rent |
WouldExceedMaxVoteCostLimit | Agendamento | A transação excederia o limite máximo de custo de voto |
WouldExceedAccountDataTotalLimit | Agendamento | A transação excederia o limite total de dados da conta |
DuplicateInstruction | Análise do orçamento de computação | Variante de instrução de orçamento de computação duplicada na mesma transação |
InsufficientFundsForRent | Verificação pós-execução | A conta não possui lamports suficientes para cobrir o rent pelo tamanho dos seus dados |
MaxLoadedAccountsDataSizeExceeded | Carregamento de conta | O total de dados carregados excede o limite de 64 MiB |
InvalidLoadedAccountsDataSizeLimit | Análise do orçamento de computação | SetLoadedAccountsDataSizeLimit definido como 0 |
ResanitizationNeeded | Sanitização | A transação diferiu antes/depois da ativação da funcionalidade e precisa de ressanitização |
ProgramExecutionTemporarilyRestricted | Carregamento de conta | A execução do programa está temporariamente restrita na conta referenciada |
UnbalancedTransaction | Verificação pós-execução | O saldo total de lamports antes da transação não é igual ao saldo depois |
ProgramCacheHitMaxLimit | Carregamento de conta | O cache do programa atingiu o limite máximo |
CommitCancelled | Commit | Commit cancelado internamente |
Is this page helpful?