
Versionamento de transações da Solana: legacy, v0 e v1
Índice
- Uma breve história dos formatos de transação da Solana
- Transações legacy
- Transação v0
- Adaptação das taxas de prioridade
- Transações maiores
- Especificações e comparação dos formatos
- Especificação da transação legacy
- Especificação da transação v0
- Especificação da transação v1
- Exemplo de transação
- Transações legacy e v0
- Configurações de recursos
- Transação v1
- Verificação de assinaturas
- Análise aprimorada de instruções
- A máscara de configuração da transação
- Conclusão
- Outros recursos
Uma breve história dos formatos de transação da Solana
A Solana tem três formatos de transação: legacy, v0 e v1. Em linhas gerais, todos têm a mesma finalidade: empacotar assinaturas, contas e instruções para que os validadores as executem em programas onchain.
O que mudou ao longo do tempo foi o formato de transmissão, ou seja, como essas informações são serializadas e transmitidas pela rede.
Cada novo formato foi introduzido para resolver limitações que surgiram à medida que os aplicativos da Solana se tornaram mais complexos, preservando a compatibilidade retroativa para que os formatos mais antigos continuassem funcionando.
Os três formatos coexistem na rede, e os desenvolvedores podem usar aquele que melhor se adapta à transação que estão criando.
Transações legacy
O formato original é conhecido como legacy porque antecede o versionamento de transações e não contém um identificador de versão.
Seu tamanho máximo de 1.232 bytes remonta ao projeto original de rede da Solana: as transações foram dimensionadas para caber na MTU mínima de 1.280 bytes do IPv6, restando 1.232 bytes após a sobrecarga da rede.
Isso funcionava bem para transações pequenas, mas o espaço se tornou cada vez mais restritivo conforme os aplicativos cresceram. Cada endereço de conta incluído diretamente em uma transação ocupa 32 bytes, enquanto cada assinatura Ed25519 ocupa outros 64 bytes.
Na prática, transações com muitas contas podiam esgotar o limite de bytes muito antes de atingir o limite de bloqueio de contas do runtime.
Transação v0
A primeira tentativa de aliviar essa pressão foi a transação v0, que entrou em operação na mainnet-beta em outubro de 2022. Em vez de aumentar o limite de 1.232 bytes, a v0 introduziu as Address Lookup Tables (ALTs).
Uma ALT armazena endereços de contas onchain, permitindo que uma transação v0 faça referência a eles usando índices de um byte em vez de repetir cada chave pública de 32 bytes. Isso permite que as transações atinjam o limite de 64 contas do runtime sem ultrapassar a restrição existente de tamanho do pacote.
A v0 foi uma extensão incremental do formato legacy: adicionou o prefixo de versão 0x80 e uma seção de consulta de endereços, mantendo a mesma codificação básica de mensagens e instruções.
A introdução do versionamento também estabeleceu uma estrutura na qual novos formatos de transação poderiam coexistir com transações legacy, em vez de substituí-las.
As ALTs resolveram o problema imediato do tamanho das contas, mas introduziram novas complexidades. Os aplicativos precisam criar e manter tabelas de consulta onchain, os validadores precisam resolver suas entradas antes da execução, e serviços RPC e indexadores precisam dos endereços resolvidos para reconstruir a transação completa.
A decodificação histórica também se torna muito mais difícil se a tabela de consulta referenciada por uma transação bruta tiver sido encerrada. Portanto, a v0 comprimiu a lista de contas com eficiência, mas fez isso ao introduzir uma dependência onchain externa na representação da transação durante a transmissão.
As ALTs foram amplamente adotadas pelos desenvolvedores da Solana. Uma pesquisa realizada pouco antes da introdução das transações v1 constatou que cerca de 62% das transações v0 referenciam pelo menos uma ALT.
Adaptação das taxas de prioridade
As taxas de prioridade também foram introduzidas na Solana em 2022, antes da ativação das transações v0 na mainnet, embora o próprio formato v0 já tivesse sido projetado naquele momento. Em vez de reformular qualquer um dos formatos de transação, a Solana adicionou a configuração de taxas por meio do Compute Budget Program existente. As transações podiam incluir instruções como SetComputeUnitLimit e SetComputeUnitPrice, permitindo que os usuários solicitassem um orçamento computacional e adicionassem uma taxa extra para obter prioridade no agendamento. Isso funcionava igualmente bem para transações legacy e v0, evitando um mecanismo de taxas separado para cada formato.
Essa foi uma solução pragmática e retrocompatível, mas não particularmente elegante. As taxas de prioridade e os limites computacionais são, na verdade, metadados no nível da transação. No entanto, legacy e v0 não tinham campos dedicados para eles, então foram adaptados ao fluxo de instruções como chamadas de programa. Isso significa que a transação precisa referenciar o Compute Budget Program, que a configuração consome bytes de instrução e que os validadores precisam examinar essas instruções durante a ingestão da transação para recuperar as configurações de taxa e recursos.
Portanto, a v0 não foi uma reformulação ampla do formato de transação da Solana. Seu principal objetivo era resolver o gargalo dos endereços de contas por meio de Address Lookup Tables, mantendo intacta a maior parte da estrutura de mensagens legacy. Como as instruções do Compute Budget já ofereciam uma forma compatível de implementar taxas de prioridade nos dois formatos, havia poucos motivos para ampliar ainda mais o escopo da v0.
Transações maiores
Com o tempo, a Solana migrou a ingestão de transações de UDP para QUIC, cujos streams não estão mais limitados a um único payload do tamanho de uma MTU. Portanto, o teto original de 1.232 bytes deixou de ser um requisito de transporte. Dois SIMDs foram apresentados em 2025 para introduzir um novo formato v1. O SIMD-0296 aumentou o tamanho máximo de uma transação v1 para 4.096 bytes, aproximadamente 3,3 vezes o limite anterior, enquanto o SIMD-0385 definiu um formato de transmissão totalmente novo, projetado para aproveitar esse espaço adicional e tornar a ingestão pelos validadores mais eficiente.
O espaço adicional permite que a v1 elimine completamente as ALTs. O limite de 64 contas com endereços de 32 bytes exige no máximo 2.048 bytes, portanto a lista completa de contas pode ser incluída diretamente.
Uma análise da Solana Foundation indica que o impacto de incluir todos os endereços diretamente é moderado ao migrar a maioria das transações v0 existentes: 50% crescem menos de 420 bytes e 90% menos de 1.400 bytes, deixando uma margem considerável dentro do limite de 4.096 bytes da v1, mesmo para transações que usam muitas ALTs.
Mais importante: a v1 não é apenas uma transação v0 maior. Ela reorganiza substancialmente o formato de transmissão: as assinaturas passam para o final, a configuração de recursos sai das instruções do Compute Budget e vai para uma seção de configuração da transação, e os payloads de instruções com tamanho variável são separados dos cabeçalhos de largura fixa. Essas mudanças tornam as transações mais baratas e simples para os validadores analisarem e priorizarem.
O limite maior de 4.096 bytes também viabiliza cargas de trabalho para as quais a compactação de endereços jamais seria suficiente. Provas de conhecimento zero, multisigs aninhadas, assinaturas Winternitz não truncadas, assinaturas BLS e outras operações criptográficas podem exigir centenas ou milhares de bytes de dados de instrução. Os saldos confidenciais do Token-2022, por exemplo, agora podem ser construídos como uma única transação v1 atômica, em vez de serem divididos em várias transações.
Vale destacar que o formato v1 inicial não aumenta os limites de computação, assinaturas, quantidade de instruções ou 64 contas da Solana. Ele remove principalmente o gargalo de tamanho e reformula a maneira como a transação é representada na transmissão.
O SIMD-0596, proposto pela Anza, aumentaria de 64 para 96 o limite de bloqueio de contas da v1, abrangendo todas as contas referenciadas pela transação, incluindo signatários, IDs de programas e contas graváveis e somente leitura. No momento da redação, essa proposta ainda está em análise. As transações legacy e v0 continuariam limitadas a 64.
Especificações e comparação dos formatos
| Limite | Legacy | v0 | v1 |
| Tamanho máximo | 1.232 bytes | 1.232 bytes | 4.096 bytes |
| Endereços de contas | ~32, limitado pelo tamanho | 64, via tabelas de consulta | 64, incluídos diretamente |
| Tabelas de consulta de endereços | Não compatível | Compatível | Não compatível |
| Endereços duplicados | permitidos | permitidos | rejeitados |
| Prefixo | Nenhum | 0x80 | 0x81 |
| Taxa de prioridade | Instrução SetComputeUnitPrice, microlamports por CU | Instrução SetComputeUnitPrice, microlamports por CU | config.priorityFeeLamports, total de lamports |
| Limite de unidades computacionais | Instrução SetComputeUnitLimit | Instrução SetComputeUnitLimit | config.computeUnitLimit |
| Tamanho do heap | Instrução RequestHeapFrame | Instrução RequestHeapFrame | config.heapSize |
| Limite de contas carregadas | Instrução SetLoadedAccountsDataSizeLimit | Instrução SetLoadedAccountsDataSizeLimit | config.loadedAccountsDataSizeLimit |
Especificação da transação legacy
Uma transação legacy serializada começa com uma contagem compact-u16 de assinaturas, seguida pelas assinaturas Ed25519 de 64 bytes.
A mensagem vem em seguida e contém o cabeçalho de três bytes, a lista de contas prefixada por um comprimento compacto, o blockhash recente de 32 bytes e o array de instruções compiladas prefixado por um comprimento compacto.
As transações legacy não têm prefixo de versão.
NumSignatures (compact-u16)
Signatures [[u8; 64]]
-- Length = NumSignatures
MessageHeader (u8, u8, u8)
-- (NumRequiredSignatures,
NumReadonlySignedAccounts,
NumReadonlyUnsignedAccounts)
NumAccountKeys (compact-u16)
AccountKeys [[u8; 32]]
-- Length = NumAccountKeys
RecentBlockhash [u8; 32]
NumInstructions (compact-u16)
Instructions [CompiledInstruction]
-- Length = NumInstructions
CompiledInstruction:
ProgramIdIndex (u8)
NumInstructionAccounts (compact-u16)
InstructionAccountIndexes [u8]
-- Length = NumInstructionAccounts
InstructionDataLength (compact-u16)
InstructionData [u8]
-- Length = InstructionDataLengthUma característica importante é que cada CompiledInstruction é serializada por completo antes do início da próxima instrução.
Os arrays de índices de contas e de dados têm tamanho variável, com prefixos compact-u16 que definem seus tamanhos.
Especificação da transação v0
A transação v0 mantém quase todo o formato de transmissão legacy. A transação ainda começa com a contagem de assinaturas e as assinaturas, mas a mensagem começa com o prefixo de versão 0x80.
Em seguida, a mensagem usa o mesmo cabeçalho de três bytes, a lista estática de contas, o blockhash e o formato de instruções compiladas do legacy, antes de acrescentar uma seção de Address Lookup Table.
Isso permite que as transações v0 carreguem contas adicionais por meio de ALTs sem ultrapassar o limite de 1.232 bytes por transação.
NumSignatures (compact-u16)
Signatures [[u8; 64]]
-- Length = NumSignatures
VersionByte (u8)
-- 0x80
MessageHeader (u8, u8, u8)
NumStaticAccountKeys (compact-u16)
StaticAccountKeys [[u8; 32]]
-- Length = NumStaticAccountKeys
RecentBlockhash [u8; 32]
NumInstructions (compact-u16)
Instructions [CompiledInstruction]
-- Same encoding as legacy
NumAddressTableLookups (compact-u16)
AddressTableLookups [MessageAddressTableLookup]
-- Length = NumAddressTableLookups
MessageAddressTableLookup:
AccountKey [u8; 32]
-- Address of the lookup table account
NumWritableIndexes (compact-u16)
WritableIndexes [u8]
-- Indices into the lookup table
NumReadonlyIndexes (compact-u16)
ReadonlyIndexes [u8]
-- Indices into the lookup tableA distinção entre chaves estáticas de contas e endereços carregados é importante. Somente as chaves estáticas são serializadas diretamente na mensagem. Os endereços referenciados por meio de ALTs são resolvidos no runtime e acrescentados à lista efetiva de contas.
Para uma transação v0 que usa ALTs, o validador primeiro precisa carregar cada tabela de consulta referenciada do Bank e do AccountsDB, verificar se a conta é uma Address Lookup Table válida, desserializar seu estado, resolver os índices solicitados em endereços completos de 32 bytes e, então, combinar esses endereços com as chaves estáticas de contas da transação. Somente após essa etapa de resolução o validador tem a lista completa de contas necessária para determinar bloqueios de leitura e gravação e conflitos com outras transações.
Especificação da transação v1
Uma transação v1 serializada começa com o byte de versão 0x81. Em seguida, vêm um cabeçalho de mensagem de três bytes no estilo legacy, uma máscara de configuração de transação de 32 bits, um especificador de validade de 32 bytes, as contagens de instruções e endereços, o array completo de endereços de 32 bytes, os valores de configuração, os cabeçalhos de instrução de tamanho fixo, os payloads contíguos das instruções e, por fim, as assinaturas.
VersionByte (u8)
-- 0x81
LegacyHeader (u8, u8, u8)
TransactionConfigMask (u32)
-- Bitmask describing which configuration values are present
LifetimeSpecifier [u8; 32]
NumInstructions (u8)
NumAddresses (u8)
Addresses [[u8; 32]]
-- Length = NumAddresses
ConfigValues [[u8; 4]]
-- Length = popcount(TransactionConfigMask)
-- Multi-word values, such as priority fee, consume multiple entries
InstructionHeaders [(u8, u8, u16)]
-- Length = NumInstructions
-- (ProgramAccountIndex,
NumInstructionAccounts,
NumInstructionDataBytes)
InstructionPayloads [InstructionPayload]
-- Length = NumInstructions
InstructionPayload:
InstructionAccountIndexes [u8]
-- Length = NumInstructionAccounts
InstructionData [u8]
-- Length = NumInstructionDataBytes
Signatures [[u8; 64]]
-- Length = LegacyHeader.NumRequiredSignaturesPortanto, a v1 difere dos formatos anteriores de várias maneiras fundamentais. O identificador da versão fica no byte zero, as assinaturas passam para o final e não precisam mais de um prefixo próprio de comprimento, as contagens de contas e instruções tornam-se valores u8 de largura fixa, a configuração de recursos é codificada diretamente na transação e os cabeçalhos de instrução de tamanho fixo são separados de seus payloads de tamanho variável. Ao contrário de legacy e v0, a v1 não usa Address Lookup Tables.
A transação v1 substitui as instruções de configuração do Compute Budget Program por campos no cabeçalho da transação. O TransactionConfigMask inicial pode declarar:
- taxa de prioridade total em lamports
- limite de unidades computacionais
- limite de tamanho dos dados de contas carregadas
- tamanho de heap solicitado
Cada bit definido identifica um valor de configuração correspondente de quatro bytes, enquanto a taxa de prioridade de 64 bits ocupa duas posições na máscara. A máscara foi projetada para permitir extensões em futuras versões de transação.
Exemplo de transação
Para comparar os diferentes formatos de transação da Solana, consideraremos o exemplo útil mais simples: transferir SOL de um endereço para outro.
Nossa transação de exemplo tem um único signatário, ou seja, o remetente, e invoca o System Program uma vez para transferir 1.000 lamports ao destinatário. Ela referencia três endereços: o remetente, o destinatário e o System Program.
Transações legacy e v0
As transações legacy e v0 usam quase o mesmo layout de nível superior. Ambas começam com o número de assinaturas, seguido imediatamente pelas próprias assinaturas. Depois vem a mensagem da transação. Para uma transferência com um único signatário, o primeiro byte é 01, que especifica uma única assinatura, seguido pela assinatura Ed25519 de 64 bytes do remetente.
A mensagem contém um cabeçalho de três bytes, endereços de contas, um blockhash recente — o especificador de validade — e instruções compiladas.
O remetente é o endereço 0, o destinatário é o endereço 1 e o System Program é o endereço 2. A instrução de transferência referencia o programa no índice 2 e passa ao programa os índices de conta 00 01.
Os formatos legacy e v0 diferem apenas ligeiramente:
- Uma mensagem legacy começa diretamente com seu cabeçalho de três bytes, enquanto uma mensagem v0 começa com o byte de versão
0x80, seguido pelo cabeçalho. - A v0 também acrescenta uma seção de Address Lookup Table após as instruções. Nosso exemplo simples não usa uma tabela de consulta, portanto essa seção consiste em um único byte
00, indicando zero consultas.
Isso faz com que a transação de transferência de SOL do exemplo tenha 215 bytes como transação legacy e 217 bytes como v0. A v0 adiciona 1 byte para o prefixo de versão e 1 byte para o array vazio de tabelas de consulta. O restante da transação é idêntico.
O remetente assina apenas a mensagem. Em nosso exemplo legacy, isso significa que a assinatura abrange os bytes 65–214. Na v0, ela abrange os bytes 65–216, começando pelo prefixo de versão da mensagem 0x80.
A tabela abaixo detalha o layout de bytes da transferência de SOL do exemplo entre dois endereços, usando o formato de transação v0.
| Offset | Tamanho | Campo | Valor de exemplo | Descrição |
| 0 | 1 B | Contagem de assinaturas | 01 | Uma assinatura; compact-u16 |
| 1–64 | 64 B | Assinatura 0 | <Ed25519 signature> | Assinatura do remetente |
| 65 | 1 B | Prefixo de versão | 80 | Mensagem versionada, versão 0 |
| 66–68 | 3 B | Cabeçalho da mensagem | 01 00 01 | 1 signatário obrigatório, 0 assinado somente leitura, 1 não assinado somente leitura |
| 69 | 1 B | Contagem de contas estáticas | 03 | Três contas incluídas diretamente |
| 70–101 | 32 B | Conta 0 | <sender pubkey> | Remetente/pagador da taxa; signatário gravável |
| 102–133 | 32 B | Conta 1 | <recipient pubkey> | Destinatário; não assinado e gravável |
| 134–165 | 32 B | Conta 2 | 11111111111111111111111111111111 | System Program; não assinado e somente leitura |
| 166–197 | 32 B | Blockhash recente | <recent blockhash> | Validade da transação |
| 198 | 1 B | Contagem de instruções | 01 | Uma instrução |
| 199 | 1 B | Índice do ID do programa | 02 | System Program |
| 200 | 1 B | Contagem de contas da instrução | 02 | Duas contas da instrução |
| 201–202 | 2 B | Índices de contas | 00 01 | Remetente, destinatário |
| 203 | 1 B | Comprimento dos dados | 0C | 12 bytes |
| 204–207 | 4 B | Discriminador de transferência | 02 00 00 00 | SystemInstruction::Transfer |
| 208–215 | 8 B | Lamports | E8 03 00 00 00 00 00 00 | 1.000 lamports |
| 216 | 1 B | Contagem de consultas à tabela de endereços | 00 | Nenhuma consulta de ALT |
| Total | 217 B |
Configurações de recursos
Nas transações legacy e v0, as configurações de recursos são expressas como instruções para o Compute Budget Program. Entre as mais comuns estão SetComputeUnitLimit e SetComputeUnitPrice. SetLoadedAccountsDataSizeLimit e RequestHeapFrame também são usadas quando necessário.
Em nosso exemplo de transação mínima, essas instruções estão ausentes. Em vez disso, o runtime fornece os valores padrão.
Sem preço por unidade computacional, não há taxa de prioridade, e o limite de dados de contas carregadas assume o máximo do runtime por padrão.
A ineficiência de exigir que os validadores examinem a lista de instruções em busca de instruções do Compute Budget durante a ingestão da transação é uma das motivações para o formato v1 atualizado, no qual essas informações são transferidas para TransactionConfigMask e ConfigValues.
Também existe uma sobrecarga inerente ao representar configurações no nível da transação como instruções. Legacy e v0 não tinham campos dedicados à computação solicitada ou às taxas de prioridade, portanto essas configurações foram adaptadas por meio do Compute Budget Program: o ID do programa precisa ser referenciado pela transação, cada configuração ocupa espaço na lista de instruções e a própria invocação do programa não executa nenhum trabalho do aplicativo, embora ainda consuma recursos computacionais.
A v1 oferece a esses valores um lugar nativo no formato de transmissão, evitando a sobrecarga extra das instruções e tornando a configuração de recursos mais barata e fácil de inspecionar pelos validadores.
Transação v1
Em vez de começar com uma contagem de assinaturas e as assinaturas, uma transação v1 começa diretamente com o byte de versão 0x81.
O cabeçalho de três bytes vem em seguida, acompanhado por um novo TransactionConfigMask de quatro bytes, o especificador de validade — normalmente um blockhash, mas também pode ser um nonce —, as contagens de instruções e endereços, os endereços de contas, os valores de configuração, as instruções e, por fim, as assinaturas.
| Offset | Tamanho | Campo | Valor de exemplo | Descrição |
| 0 | 1 B | Byte de versão | 81 | Transação v1 |
| 1–3 | 3 B | Cabeçalho legacy | 01 00 01 | 1 assinatura obrigatória, 0 contas assinadas somente leitura, 1 conta não assinada somente leitura |
| 4–7 | 4 B | Máscara de configuração da transação | 0C 00 00 00 | 0x0000000C: bits 2 e 3 definidos, especificando dois valores de configuração de 4 bytes |
| 8–39 | 32 B | Especificador de validade | <recent blockhash> | Blockhash recente (ou nonce) |
| 40 | 1 B | Número de instruções | 01 | 1 instrução do System Program |
| 41 | 1 B | Número de endereços | 03 | Remetente, destinatário, System Program |
| 42–73 | 32 B | Endereço 0 | <sender pubkey> | Remetente, pagador da taxa, signatário gravável |
| 74–105 | 32 B | Endereço 1 | <recipient pubkey> | Destinatário, conta não assinada e gravável |
| 106–137 | 32 B | Endereço 2 | 11111111111111111111111111111111 | System Program, somente leitura, não assinado |
| 138–141 | 4 B | Valor de configuração: limite de unidades computacionais | 10 27 00 00 | 10.000 CU como u32 little-endian (corresponde ao bit 2 da máscara) |
| 142–145 | 4 B | Valor de configuração: limite de tamanho dos dados de contas carregadas | 00 00 01 00 | 65.536 bytes / 64 KiB como u32 little-endian (corresponde ao bit 3 da máscara) |
| 146–149 | 4 B | Instrução: cabeçalho | 02 02 0C 00 | Índice de programa 2, 2 contas, 12 bytes de dados |
| 150–151 | 2 B | Instrução: índices de contas | 00 01 | Endereço 0 = remetente, endereço 1 = destinatário |
| 152–155 | 4 B | Instrução: discriminador de transferência | 02 00 00 00 | SystemInstruction::Transfer |
| 156–163 | 8 B | Instrução: lamports | por exemplo, E8 03 00 00 00 00 00 00 | 1.000 lamports como u64 little-endian |
| 164–227 | 64 B | Assinatura 0 | <Ed25519 signature> | Assinatura do remetente sobre os bytes 0–155 |
| Total | 228 B |
Verificação de assinaturas
A verificação de assinaturas é uma das primeiras operações realizadas quando uma transação chega a um validador. Transações com assinaturas inválidas precisam ser filtradas e descartadas antes de chegar ao agendador. Colocar as assinaturas depois de uma mensagem de tamanho variável poderia parecer que dificultaria seu acesso, mas, na prática, isso não acontece.
Antes de executar a custosa verificação Ed25519, o validador só precisa identificar onde cada parte da transação começa e termina. O formato v1 foi projetado para tornar isso barato e determinístico.
O primeiro byte, 0x81, identifica imediatamente a transação como v1. O cabeçalho seguinte contém num_required_signatures, portanto o validador sabe desde o início quantas assinaturas de 64 bytes deve encontrar.
Em seguida, o parser TransactionView da Agave percorre a transação, registrando os offsets. Ele lê os campos de tamanho fixo e usa NumAddresses para avançar além do array de endereços.
Ele usa a máscara de configuração para determinar o tamanho da seção de configuração e lê cada cabeçalho de instrução de 4 bytes para descobrir o tamanho do payload da instrução correspondente.
Depois que o parser avança além do payload da instrução final, sua posição atual é registrada como o offset das assinaturas. A partir dessa posição, a Agave espera exatamente num_required_signatures × 64 bytes.
A implementação armazena um SignatureFrame contendo o número de assinaturas e o offset em bytes da primeira assinatura no pacote. A mensagem assinada da transação é o intervalo de bytes desde o início da mensagem v1 até o offset de assinatura registrado. Assim, o validador pode realizar a custosa verificação Ed25519 sem executar a transação nem carregar contas.
A v1 também proíbe bytes adicionais após o array de assinaturas. Quando o parser chega às assinaturas, os bytes restantes têm um tamanho inequívoco, determinado pela contagem de assinaturas. O SIMD-0385 exige exatamente uma assinatura de 64 bytes para cada signatário obrigatório e nenhum dado após a assinatura final.
Análise aprimorada de instruções
O layout da transação v1 facilita a análise das instruções. Para localizar uma instrução posterior em transações legacy e v0, o parser precisa percorrer as instruções anteriores, decodificar seus comprimentos e avançar por cada payload de tamanho variável.
A v1 separa os cabeçalhos de instrução de largura fixa dos payloads de tamanho variável. Cada instrução primeiro acrescenta um cabeçalho de quatro bytes contendo o índice da conta do programa, o número de contas da instrução e o comprimento dos dados da instrução. Esses cabeçalhos são agrupados antes dos payloads de índices de contas e dados. Isso fornece de antemão uma descrição compacta de todas as instruções. Também reduz o custo de delimitar e ignorar a seção de instruções e ajuda a determinar o offset do array de assinaturas no final sem analisar os dados das instruções.
A máscara de configuração da transação
A outra grande adição do formato v1 é o TransactionConfigMask de quatro bytes.
As transações legacy e v0 configuram recursos por meio de instruções para o Compute Budget Program. Por exemplo, uma transação pode conter instruções SetComputeUnitLimit, SetComputeUnitPrice ou SetLoadedAccountsDataSizeLimit. Um validador interessado nos requisitos de recursos ou na prioridade da transação precisa localizar e interpretar essas instruções. A transação v1 retira essas informações do fluxo de instruções e as incorpora ao próprio formato da transação.
A máscara é um campo de bits little-endian de 32 bits. Cada bit definido corresponde a uma palavra de quatro bytes na seção ConfigValues, mais adiante na transação:
- Bits 0 e 1: taxa de prioridade total em lamports (codificada como um
u64little-endian de 8 bytes) - Bit 2: limite de unidades computacionais (um
u32de 4 bytes) - Bit 3: limite de tamanho dos dados de contas carregadas (um
u32de 4 bytes) - Bit 4: tamanho de heap solicitado (um
u32de 4 bytes)
Observação: a especificação inicial da v1 atribui significados somente aos bits 0–4. Os bits restantes ainda não foram atribuídos, deixando espaço para futuros campos de configuração no nível da transação.
A máscara funciona como um esquema compacto que informa ao parser quais campos existem e quantos bytes esperar. Por exemplo, nossa transferência simples de SOL precisa de um limite de unidades computacionais e de um limite de tamanho dos dados de contas carregadas, mas não precisa de uma taxa de prioridade nem de um tamanho de heap personalizado.
Como esses dois bits estão definidos, exatamente dois valores de configuração de 4 bytes seguem o array de endereços da transação, solicitando, em nosso caso, um limite de 20.000 CU e um limite de 64 KiB para dados de contas carregadas.
A máscara informa ao validador que a primeira configuração de 4 bytes pertence ao bit 2, o limite de unidades computacionais, e que a segunda pertence ao bit 3, o limite de dados de contas carregadas.
Nas transações legacy e v0, omitir uma instrução de limite de unidades computacionais fornece à transação um orçamento computacional implícito. A v1 não usa os mesmos valores padrão. Um limite de unidades computacionais não definido é resolvido como zero, assim como um limite de tamanho dos dados de contas carregadas não definido. Somente o heap mantém o padrão de 32 KiB. Portanto, uma transação v1 precisa solicitar explicitamente os recursos de que necessita.
Mover essas configurações para uma área bem definida oferece aos validadores acesso direto às informações necessárias durante a ingestão da transação, sem exigir que pesquisem as instruções. Isso também significa que as instruções do Compute Budget Program não configuram mais transações v1. Se forem incluídas, elas serão ignoradas e processadas como instruções no-op, embora ainda consumam unidades computacionais.
Isso também oferece um benefício adicional de espaço. Em transações legacy/v0, adicionar instruções do Compute Budget geralmente exige incluir o endereço de 32 bytes do Compute Budget Program na lista de contas, além das próprias instruções serializadas.
Conclusão
Os três formatos de transação da Solana refletem a evolução da rede. O legacy estabeleceu o modelo original, a v0 o ampliou com ALTs para aceitar conjuntos maiores de contas dentro do teto de 1.232 bytes, e a v1 repensa o formato de transmissão de maneira mais profunda, com transações maiores, análise mais simples, endereços incluídos diretamente e configuração nativa de recursos.
Os três continuam válidos, mas cada um representa uma etapa diferente do desenvolvimento da Solana. Juntos, eles mostram como o formato de transação se adaptou à evolução dos aplicativos, dos mercados de taxas e dos requisitos dos validadores da rede.
Outros recursos
- Transações v1 e o trade-off das ALTs - Umberto Natale, Solana Foundation
- Transações versionadas - Documentação da Solana
- Aumentar o tamanho das transações - Fóruns de discussão de SIMD
- Transações maiores - Atualizações da Solana
Artigos relacionados
Assine a Helius
Acompanhe as novidades mais recentes do desenvolvimento Solana e receba atualizações quando publicarmos


